diff --git a/examples/src/bin/basic-compute-shader.rs b/examples/src/bin/basic-compute-shader.rs index ecc423ee45..05093ebcc2 100644 --- a/examples/src/bin/basic-compute-shader.rs +++ b/examples/src/bin/basic-compute-shader.rs @@ -111,10 +111,10 @@ fn main() { " } } - let shader = cs::Shader::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()).unwrap(); ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &(), None, |_| {}, diff --git a/examples/src/bin/buffer-pool.rs b/examples/src/bin/buffer-pool.rs index d070a87186..7ec02d0bb4 100644 --- a/examples/src/bin/buffer-pool.rs +++ b/examples/src/bin/buffer-pool.rs @@ -143,8 +143,8 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); + let vs = vs::load(device.clone()).unwrap(); + let fs = fs::load(device.clone()).unwrap(); let render_pass = vulkano::single_pass_renderpass!( device.clone(), @@ -165,10 +165,10 @@ fn main() { let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .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 763f76f711..963e7832f9 100644 --- a/examples/src/bin/deferred/frame/ambient_lighting_system.rs +++ b/examples/src/bin/deferred/frame/ambient_lighting_system.rs @@ -56,17 +56,15 @@ impl AmbientLightingSystem { }; let pipeline = { - let vs = vs::Shader::load(gfx_queue.device().clone()) - .expect("failed to create shader module"); - let fs = fs::Shader::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"); + let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .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 5832dc77ce..224c1872d6 100644 --- a/examples/src/bin/deferred/frame/directional_lighting_system.rs +++ b/examples/src/bin/deferred/frame/directional_lighting_system.rs @@ -57,17 +57,15 @@ impl DirectionalLightingSystem { }; let pipeline = { - let vs = vs::Shader::load(gfx_queue.device().clone()) - .expect("failed to create shader module"); - let fs = fs::Shader::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"); + let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .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 fdf79e6e9b..830f018cfc 100644 --- a/examples/src/bin/deferred/frame/point_lighting_system.rs +++ b/examples/src/bin/deferred/frame/point_lighting_system.rs @@ -56,17 +56,15 @@ impl PointLightingSystem { }; let pipeline = { - let vs = vs::Shader::load(gfx_queue.device().clone()) - .expect("failed to create shader module"); - let fs = fs::Shader::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"); + let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .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 10d9b488b9..1652ddc1b6 100644 --- a/examples/src/bin/deferred/triangle_draw_system.rs +++ b/examples/src/bin/deferred/triangle_draw_system.rs @@ -51,17 +51,15 @@ impl TriangleDrawSystem { }; let pipeline = { - let vs = vs::Shader::load(gfx_queue.device().clone()) - .expect("failed to create shader module"); - let fs = fs::Shader::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"); + let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .depth_stencil_state(DepthStencilState::simple_depth_test()) .render_pass(subpass) .build(gfx_queue.device().clone()) diff --git a/examples/src/bin/dynamic-buffers.rs b/examples/src/bin/dynamic-buffers.rs index 0982c5b377..a228149ebe 100644 --- a/examples/src/bin/dynamic-buffers.rs +++ b/examples/src/bin/dynamic-buffers.rs @@ -95,10 +95,10 @@ fn main() { } } - let shader = shader::Shader::load(device.clone()).unwrap(); + let shader = shader::load(device.clone()).unwrap(); let pipeline = ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &(), None, |set_descs| { diff --git a/examples/src/bin/dynamic-local-size.rs b/examples/src/bin/dynamic-local-size.rs index cf62da3d74..49d1cefb76 100644 --- a/examples/src/bin/dynamic-local-size.rs +++ b/examples/src/bin/dynamic-local-size.rs @@ -137,7 +137,7 @@ fn main() { } } - let shader = cs::Shader::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()).unwrap(); // Fetching subgroup size from the Physical Device metadata to compute appropriate // Compute Shader local size properties. @@ -175,7 +175,7 @@ fn main() { }; let pipeline = ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &spec_consts, None, |_| {}, diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index d79ee27c0b..cba130bafc 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -130,8 +130,8 @@ fn main() { ) .unwrap(); - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).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: { @@ -193,10 +193,10 @@ fn main() { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .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.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .build(device.clone()) diff --git a/examples/src/bin/immutable-buffer-initialization.rs b/examples/src/bin/immutable-buffer-initialization.rs index e868c7ff00..389248abc8 100644 --- a/examples/src/bin/immutable-buffer-initialization.rs +++ b/examples/src/bin/immutable-buffer-initialization.rs @@ -88,10 +88,10 @@ void main() { }" } } - let shader = cs::Shader::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()).unwrap(); ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &(), None, |_| {}, diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index bc054a9096..3b39c2d346 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -136,8 +136,8 @@ fn main() { ) .unwrap(); - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).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: { @@ -199,10 +199,10 @@ fn main() { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .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.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .with_auto_layout(device.clone(), |set_descs| { diff --git a/examples/src/bin/indirect.rs b/examples/src/bin/indirect.rs index 75e605a7d3..6fd7c90cdb 100644 --- a/examples/src/bin/indirect.rs +++ b/examples/src/bin/indirect.rs @@ -199,9 +199,9 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); - let cs = cs::Shader::load(device.clone()).unwrap(); + let vs = vs::load(device.clone()).unwrap(); + let fs = fs::load(device.clone()).unwrap(); + let cs = cs::load(device.clone()).unwrap(); // Each frame we generate a new set of vertices and each frame we need a new DrawIndirectCommand struct to // set the number of vertices to draw @@ -209,8 +209,14 @@ fn main() { CpuBufferPool::new(device.clone(), BufferUsage::all()); let vertex_pool: CpuBufferPool = CpuBufferPool::new(device.clone(), BufferUsage::all()); - let compute_pipeline = - ComputePipeline::new(device.clone(), &cs.main_entry_point(), &(), None, |_| {}).unwrap(); + let compute_pipeline = ComputePipeline::new( + device.clone(), + cs.entry_point("main").unwrap(), + &(), + None, + |_| {}, + ) + .unwrap(); let render_pass = single_pass_renderpass!( device.clone(), @@ -231,10 +237,10 @@ fn main() { let render_pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/instancing.rs b/examples/src/bin/instancing.rs index b54ee3d22a..832574fd5d 100644 --- a/examples/src/bin/instancing.rs +++ b/examples/src/bin/instancing.rs @@ -213,8 +213,8 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); + let vs = vs::load(device.clone()).unwrap(); + let fs = fs::load(device.clone()).unwrap(); let render_pass = single_pass_renderpass!( device.clone(), @@ -241,10 +241,10 @@ fn main() { .vertex::() .instance::(), ) - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .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 019d9e8ec8..0c81c67498 100644 --- a/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs +++ b/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs @@ -48,10 +48,10 @@ impl FractalComputePipeline { let end_color = [0.0; 4]; let pipeline = { - let shader = cs::Shader::load(gfx_queue.device().clone()).unwrap(); + let shader = cs::load(gfx_queue.device().clone()).unwrap(); ComputePipeline::new( gfx_queue.device().clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &(), None, |_| {}, diff --git a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs index e86b44a704..d489f86189 100644 --- a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs +++ b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs @@ -80,15 +80,13 @@ impl PixelsDrawPipeline { .unwrap(); let pipeline = { - let vs = vs::Shader::load(gfx_queue.device().clone()) - .expect("failed to create shader module"); - let fs = fs::Shader::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"); + let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .render_pass(subpass) .build(gfx_queue.device().clone()) diff --git a/examples/src/bin/msaa-renderpass.rs b/examples/src/bin/msaa-renderpass.rs index 7c9c15eedc..6505479c1b 100644 --- a/examples/src/bin/msaa-renderpass.rs +++ b/examples/src/bin/msaa-renderpass.rs @@ -238,8 +238,8 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); + let vs = vs::load(device.clone()).unwrap(); + let fs = fs::load(device.clone()).unwrap(); #[derive(Default, Copy, Clone)] struct Vertex { @@ -266,9 +266,9 @@ fn main() { let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/multi-window.rs b/examples/src/bin/multi-window.rs index c0474d6aa5..4caa46872d 100644 --- a/examples/src/bin/multi-window.rs +++ b/examples/src/bin/multi-window.rs @@ -186,8 +186,8 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); + let vs = vs::load(device.clone()).unwrap(); + let fs = fs::load(device.clone()).unwrap(); let render_pass = vulkano::single_pass_renderpass!( device.clone(), @@ -208,10 +208,10 @@ fn main() { let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/multiview.rs b/examples/src/bin/multiview.rs index 529e85f190..bc37e3fa52 100644 --- a/examples/src/bin/multiview.rs +++ b/examples/src/bin/multiview.rs @@ -193,8 +193,8 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); + let vs = vs::load(device.clone()).unwrap(); + let fs = fs::load(device.clone()).unwrap(); let render_pass_description = RenderPassDesc::with_multiview( vec![AttachmentDesc { @@ -237,7 +237,7 @@ fn main() { let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([ Viewport { @@ -249,7 +249,7 @@ fn main() { depth_range: 0.0..1.0, }, ])) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/occlusion-query.rs b/examples/src/bin/occlusion-query.rs index 13bd295a7e..04c42fffc1 100644 --- a/examples/src/bin/occlusion-query.rs +++ b/examples/src/bin/occlusion-query.rs @@ -209,8 +209,8 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); + let vs = vs::load(device.clone()).unwrap(); + let fs = fs::load(device.clone()).unwrap(); let render_pass = vulkano::single_pass_renderpass!( device.clone(), @@ -237,10 +237,10 @@ fn main() { let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) // 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 diff --git a/examples/src/bin/pipeline-caching.rs b/examples/src/bin/pipeline-caching.rs index f57e3e2b53..5b71d72535 100644 --- a/examples/src/bin/pipeline-caching.rs +++ b/examples/src/bin/pipeline-caching.rs @@ -112,10 +112,10 @@ fn main() { " } } - let shader = cs::Shader::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()).unwrap(); ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &(), Some(pipeline_cache.clone()), |_| {}, diff --git a/examples/src/bin/push-constants.rs b/examples/src/bin/push-constants.rs index 06354a75aa..f83fd67b48 100644 --- a/examples/src/bin/push-constants.rs +++ b/examples/src/bin/push-constants.rs @@ -89,10 +89,10 @@ fn main() { } } - let shader = cs::Shader::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()).unwrap(); let pipeline = ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &(), None, |_| {}, diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index 205d97d0f2..c83c67b67f 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -128,8 +128,8 @@ fn main() { ) .unwrap(); - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).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: { @@ -191,10 +191,10 @@ fn main() { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .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.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .with_auto_layout(device.clone(), |set_descs| { diff --git a/examples/src/bin/runtime-shader/main.rs b/examples/src/bin/runtime-shader/main.rs index 43a90d0171..340aabab23 100644 --- a/examples/src/bin/runtime-shader/main.rs +++ b/examples/src/bin/runtime-shader/main.rs @@ -19,8 +19,6 @@ // $ glslangValidator frag.glsl -V -S frag -o frag.spv // Vulkano uses glslangValidator to build your shaders internally. -use std::borrow::Cow; -use std::ffi::CStr; use std::fs::File; use std::io::Read; use std::sync::Arc; @@ -29,19 +27,15 @@ use vulkano::buffer::{BufferUsage, TypedBufferAccess}; use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents}; use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType}; use vulkano::device::{Device, DeviceExtensions, Features}; -use vulkano::format::Format; use vulkano::image::view::ImageView; use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage}; use vulkano::instance::Instance; use vulkano::pipeline::input_assembly::InputAssemblyState; use vulkano::pipeline::rasterization::{CullMode, FrontFace, RasterizationState}; -use vulkano::pipeline::shader::{ - GraphicsShaderType, ShaderInterface, ShaderInterfaceEntry, ShaderModule, - SpecializationConstants, -}; use vulkano::pipeline::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; use vulkano::render_pass::{Framebuffer, RenderPass, Subpass}; +use vulkano::shader::ShaderModule; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano::Version; @@ -145,7 +139,7 @@ fn main() { f.read_to_end(&mut v).unwrap(); // 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::new(device.clone(), &v) }.unwrap() + unsafe { ShaderModule::from_bytes(device.clone(), &v) }.unwrap() }; let fs = { @@ -153,96 +147,15 @@ fn main() { .expect("Can't find file src/bin/runtime-shader/frag.spv"); let mut v = vec![]; f.read_to_end(&mut v).unwrap(); - unsafe { ShaderModule::new(device.clone(), &v) }.unwrap() - }; - - // This definition will tell Vulkan how input entries of our vertex shader look like - // There are things to consider when giving out entries: - // * There must be only one entry per one location, you can't have - // `color' and `position' entries both at 0..1 locations. They also - // should not overlap. - // * Format of each element must be no larger than 128 bits. - let vertex_input = unsafe { - ShaderInterface::new_unchecked(vec![ - ShaderInterfaceEntry { - location: 1..2, - format: Format::R32G32B32_SFLOAT, - name: Some(Cow::Borrowed("color")), - }, - ShaderInterfaceEntry { - location: 0..1, - format: Format::R32G32_SFLOAT, - name: Some(Cow::Borrowed("position")), - }, - ]) - }; - - // This definition will tell Vulkan how output entries (those passed to next - // stage) of our vertex shader look like. - let vertex_output = unsafe { - ShaderInterface::new_unchecked(vec![ShaderInterfaceEntry { - location: 0..1, - format: Format::R32G32B32_SFLOAT, - name: Some(Cow::Borrowed("v_color")), - }]) - }; - - // Same as with our vertex shader, but for fragment one instead. - let fragment_input = unsafe { - ShaderInterface::new_unchecked(vec![ShaderInterfaceEntry { - location: 0..1, - format: Format::R32G32B32_SFLOAT, - name: Some(Cow::Borrowed("v_color")), - }]) - }; - - // Note that color fragment color entry will be determined - // automatically by Vulkano. - let fragment_output = unsafe { - ShaderInterface::new_unchecked(vec![ShaderInterfaceEntry { - location: 0..1, - format: Format::R32G32B32A32_SFLOAT, - name: Some(Cow::Borrowed("f_color")), - }]) - }; - - // NOTE: ShaderModule::*_shader_entry_point calls do not do any error - // checking and you have to verify correctness of what you are doing by - // yourself. - // - // You must be extra careful to specify correct entry point, or program will - // crash at runtime outside of rust and you will get NO meaningful error - // information! - let vert_main = unsafe { - vs.graphics_entry_point( - CStr::from_bytes_with_nul_unchecked(b"main\0"), - [], // No descriptor sets. - None, // No push constants. - <()>::descriptors(), - vertex_input, - vertex_output, - GraphicsShaderType::Vertex, - ) - }; - - let frag_main = unsafe { - fs.graphics_entry_point( - CStr::from_bytes_with_nul_unchecked(b"main\0"), - [], // No descriptor sets. - None, // No push constants. - <()>::descriptors(), - fragment_input, - fragment_output, - GraphicsShaderType::Fragment, - ) + unsafe { ShaderModule::from_bytes(device.clone(), &v) }.unwrap() }; let graphics_pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vert_main, ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(frag_main, ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state( RasterizationState::new() .cull_mode(CullMode::Front) diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index cff674dc89..9938f3f8a4 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -190,8 +190,8 @@ fn main() { ) .unwrap(); - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).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: { @@ -281,8 +281,9 @@ fn main() { .unwrap(); let pipeline_layout = { - let mut descriptor_set_descs: Vec<_> = - DescriptorSetDesc::from_requirements(fs.main_entry_point().descriptor_requirements()); + let mut descriptor_set_descs: Vec<_> = DescriptorSetDesc::from_requirements( + fs.entry_point("main").unwrap().descriptor_requirements(), + ); // Set 0, Binding 0 descriptor_set_descs[0].set_variable_descriptor_count(0, 2); @@ -296,7 +297,10 @@ fn main() { PipelineLayout::new( device.clone(), descriptor_set_layouts, - fs.main_entry_point().push_constant_range().iter().cloned(), + fs.entry_point("main") + .unwrap() + .push_constant_requirements() + .cloned(), ) .unwrap() }; @@ -304,9 +308,9 @@ fn main() { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .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/shader-include/main.rs b/examples/src/bin/shader-include/main.rs index a5b123368a..dfba267f4d 100644 --- a/examples/src/bin/shader-include/main.rs +++ b/examples/src/bin/shader-include/main.rs @@ -90,10 +90,10 @@ fn main() { " } } - let shader = cs::Shader::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()).unwrap(); ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &(), None, |_| {}, diff --git a/examples/src/bin/shader-types-sharing.rs b/examples/src/bin/shader-types-sharing.rs index 4937bb2384..40fcd9be58 100644 --- a/examples/src/bin/shader-types-sharing.rs +++ b/examples/src/bin/shader-types-sharing.rs @@ -102,7 +102,7 @@ fn main() { // by default and the macro by default producing unique // structs(`MultSpecializationConstants`, `AddSpecializationConstants`) shared_constants: true, - Mult: { + mult: { ty: "compute", src: " #version 450 @@ -127,7 +127,7 @@ fn main() { } " }, - Add: { + add: { ty: "compute", src: " #version 450 @@ -216,9 +216,10 @@ fn main() { // Loading the first shader, and creating a Pipeline for the shader let mult_pipeline = ComputePipeline::new( device.clone(), - &shaders::MultShader::load(device.clone()) + shaders::load_mult(device.clone()) .unwrap() - .main_entry_point(), + .entry_point("main") + .unwrap(), &shaders::SpecializationConstants { enabled: 1 }, None, |_| {}, @@ -228,9 +229,10 @@ fn main() { // Loading the second shader, and creating a Pipeline for the shader let add_pipeline = ComputePipeline::new( device.clone(), - &shaders::AddShader::load(device.clone()) + shaders::load_add(device.clone()) .unwrap() - .main_entry_point(), + .entry_point("main") + .unwrap(), &shaders::SpecializationConstants { enabled: 1 }, None, |_| {}, diff --git a/examples/src/bin/specialization-constants.rs b/examples/src/bin/specialization-constants.rs index d558202734..8be4b323db 100644 --- a/examples/src/bin/specialization-constants.rs +++ b/examples/src/bin/specialization-constants.rs @@ -87,7 +87,7 @@ fn main() { } } - let shader = cs::Shader::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()).unwrap(); let spec_consts = cs::SpecializationConstants { enable: 1, @@ -96,7 +96,7 @@ fn main() { }; let pipeline = ComputePipeline::new( device.clone(), - &shader.main_entry_point(), + shader.entry_point("main").unwrap(), &spec_consts, None, |_| {}, diff --git a/examples/src/bin/teapot/main.rs b/examples/src/bin/teapot/main.rs index 96c43f8c32..5429a5b5a2 100644 --- a/examples/src/bin/teapot/main.rs +++ b/examples/src/bin/teapot/main.rs @@ -28,6 +28,7 @@ use vulkano::pipeline::vertex::BuffersDefinition; use vulkano::pipeline::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use vulkano::render_pass::{Framebuffer, RenderPass, Subpass}; +use vulkano::shader::ShaderModule; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano::Version; @@ -119,8 +120,8 @@ fn main() { let uniform_buffer = CpuBufferPool::::new(device.clone(), BufferUsage::all()); - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).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: { @@ -301,8 +302,8 @@ fn main() { /// This method is called once during initialization, then again whenever the window is resized fn window_size_dependent_setup( device: Arc, - vs: &vs::Shader, - fs: &fs::Shader, + vs: &ShaderModule, + fs: &ShaderModule, images: &[Arc>], render_pass: Arc, ) -> (Arc, Vec>) { @@ -337,7 +338,7 @@ fn window_size_dependent_setup( .vertex::() .vertex::(), ) - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([ Viewport { @@ -346,7 +347,7 @@ fn window_size_dependent_setup( depth_range: 0.0..1.0, }, ])) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .depth_stencil_state(DepthStencilState::simple_depth_test()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) diff --git a/examples/src/bin/tessellation.rs b/examples/src/bin/tessellation.rs index 5b206938a1..fd00bfce72 100644 --- a/examples/src/bin/tessellation.rs +++ b/examples/src/bin/tessellation.rs @@ -248,10 +248,10 @@ fn main() { ) .unwrap(); - let vs = vs::Shader::load(device.clone()).unwrap(); - let tcs = tcs::Shader::load(device.clone()).unwrap(); - let tes = tes::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).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(), @@ -272,9 +272,14 @@ fn main() { let pipeline = GraphicsPipeline::start() .vertex_input_single_buffer::() - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) // Actually use the tessellation shaders. - .tessellation_shaders(tcs.main_entry_point(), (), tes.main_entry_point(), ()) + .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( @@ -285,7 +290,7 @@ fn main() { .patch_control_points(3), ) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.entry_point("main").unwrap(), ()) .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 0b7b4620cf..2cab5715e9 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -272,8 +272,8 @@ fn main() { } } - let vs = vs::Shader::load(device.clone()).unwrap(); - let fs = fs::Shader::load(device.clone()).unwrap(); + 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 @@ -321,13 +321,13 @@ fn main() { // A Vulkan shader can in theory contain multiple entry points, so we have to specify // which one. The `main` word of `main_entry_point` actually corresponds to the name of // the entry point. - .vertex_shader(vs.main_entry_point(), ()) + .vertex_shader(vs.entry_point("main").unwrap(), ()) // The content of the vertex buffer describes a list of triangles. .input_assembly_state(InputAssemblyState::new()) // Use a resizable viewport set to draw over the entire window .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) // See `vertex_shader`. - .fragment_shader(fs.main_entry_point(), ()) + .fragment_shader(fs.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. .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) diff --git a/vulkano-shaders/Cargo.toml b/vulkano-shaders/Cargo.toml index f4cb5e0783..66eae5120b 100644 --- a/vulkano-shaders/Cargo.toml +++ b/vulkano-shaders/Cargo.toml @@ -16,6 +16,7 @@ proc-macro = true [dependencies] fnv = "1.0" +heck = "0.3" proc-macro2 = "1.0" quote = "1.0" shaderc = "0.7" diff --git a/vulkano-shaders/src/codegen.rs b/vulkano-shaders/src/codegen.rs index f77d64e727..f50ce020f3 100644 --- a/vulkano-shaders/src/codegen.rs +++ b/vulkano-shaders/src/codegen.rs @@ -9,11 +9,10 @@ use crate::entry_point; use crate::read_file_to_string; -use crate::spec_consts; use crate::structs; use crate::RegisteredType; use crate::TypesMeta; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; pub use shaderc::{CompilationArtifact, IncludeType, ResolvedInclude, ShaderKind}; use shaderc::{CompileOptions, Compiler, EnvVersion, SpirvVersion, TargetEnv}; use std::collections::HashMap; @@ -23,11 +22,8 @@ use std::{ cell::{RefCell, RefMut}, io::Error as IoError, }; -use syn::Ident; -use vulkano::{ - spirv::{Capability, Instruction, Spirv, SpirvError, StorageClass}, - Version, -}; +use vulkano::shader::reflect; +use vulkano::shader::spirv::{Spirv, SpirvError}; pub(super) fn path_to_str(path: &Path) -> &str { path.to_str().expect( @@ -218,113 +214,8 @@ pub(super) fn reflect<'a, I>( where I: IntoIterator, { - let struct_name = Ident::new(&format!("{}Shader", prefix), Span::call_site()); let spirv = Spirv::new(words)?; - // checking whether each required capability is enabled in the Vulkan device - let mut cap_checks: Vec = vec![]; - match spirv.version() { - Version::V1_0 => {} - Version::V1_1 | Version::V1_2 | Version::V1_3 => { - cap_checks.push(quote! { - if device.api_version() < Version::V1_1 { - panic!("Device API version 1.1 required"); - } - }); - } - Version::V1_4 => { - cap_checks.push(quote! { - if device.api_version() < Version::V1_2 - && !device.enabled_extensions().khr_spirv_1_4 { - panic!("Device API version 1.2 or extension VK_KHR_spirv_1_4 required"); - } - }); - } - Version::V1_5 => { - cap_checks.push(quote! { - if device.api_version() < Version::V1_2 { - panic!("Device API version 1.2 required"); - } - }); - } - _ => return Err(Error::UnsupportedSpirvVersion), - } - - for i in spirv.instructions() { - let dev_req = { - match i { - Instruction::Variable { - result_type_id: _, - result_id: _, - storage_class, - initializer: _, - } => storage_class_requirement(storage_class), - Instruction::TypePointer { - result_id: _, - storage_class, - ty: _, - } => storage_class_requirement(storage_class), - Instruction::Capability { capability } => capability_requirement(capability), - _ => &[], - } - }; - - if dev_req.len() == 0 { - continue; - } - - let (conditions, messages): (Vec<_>, Vec<_>) = dev_req - .iter() - .map(|req| match req { - DeviceRequirement::Extension(extension) => { - let ident = Ident::new(extension, Span::call_site()); - ( - quote! { device.enabled_extensions().#ident }, - format!("extension {}", extension), - ) - } - DeviceRequirement::Feature(feature) => { - let ident = Ident::new(feature, Span::call_site()); - ( - quote! { device.enabled_features().#ident }, - format!("feature {}", feature), - ) - } - DeviceRequirement::Version(major, minor) => { - let ident = format_ident!("V{}_{}", major, minor); - ( - quote! { device.api_version() >= crate::Version::#ident }, - format!("API version {}.{}", major, minor), - ) - } - }) - .unzip(); - let messages = messages.join(", "); - - cap_checks.push(quote! { - if !std::array::IntoIter::new([#(#conditions),*]).all(|x| x) { - panic!("One of the following must be enabled on the device: {}", #messages); - } - }); - } - - // writing one method for each entry point of this module - let mut entry_points_inside_impl: Vec = vec![]; - for instruction in spirv - .iter_entry_point() - .filter(|instruction| matches!(instruction, Instruction::EntryPoint { .. })) - { - let entry_point = entry_point::write_entry_point( - prefix, - &spirv, - instruction, - types_meta, - exact_entrypoint_interface, - shared_constants, - ); - entry_points_inside_impl.push(entry_point); - } - let include_bytes = input_paths.into_iter().map(|s| { quote! { // using include_bytes here ensures that changing the shader will force recompilation. @@ -333,57 +224,82 @@ where } }); - let structs = structs::write_structs(prefix, &spirv, types_meta, types_registry); - let specialization_constants = spec_consts::write_specialization_constants( + let spirv_version = { + let major = spirv.version().major; + let minor = spirv.version().minor; + let patch = spirv.version().patch; + quote! { + Version { + major: #major, + minor: #minor, + patch: #patch, + } + } + }; + let spirv_capabilities = reflect::spirv_capabilities(&spirv).map(|capability| { + let name = format_ident!("{}", format!("{:?}", capability)); + quote! { &Capability::#name } + }); + let spirv_extensions = reflect::spirv_extensions(&spirv); + let entry_points = reflect::entry_points(&spirv, exact_entrypoint_interface) + .map(|(name, info)| entry_point::write_entry_point(&name, &info)); + + let specialization_constants = structs::write_specialization_constants( prefix, &spirv, types_meta, shared_constants, types_registry, ); - let shader_code = quote! { - pub struct #struct_name { - shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>, - } - - impl #struct_name { - /// Loads the shader in Vulkan as a `ShaderModule`. - #[inline] - #[allow(unsafe_code)] - pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>) - -> Result<#struct_name, ::vulkano::OomError> - { - let _bytes = ( #( #include_bytes),* ); - #( #cap_checks )* - static WORDS: &[u32] = &[ #( #words ),* ]; - - unsafe { - Ok(#struct_name { - shader: ::vulkano::pipeline::shader::ShaderModule::from_words(device, WORDS)? - }) - } - } + let load_name = if prefix.is_empty() { + format_ident!("load") + } else { + format_ident!("load_{}", prefix) + }; - /// Returns the module that was created. - #[allow(dead_code)] - #[inline] - pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> { - &self.shader + let shader_code = quote! { + /// Loads the shader in Vulkan as a `ShaderModule`. + #[inline] + #[allow(unsafe_code)] + pub fn #load_name(device: ::std::sync::Arc<::vulkano::device::Device>) + -> Result<::std::sync::Arc<::vulkano::shader::ShaderModule>, ::vulkano::shader::ShaderCreationError> + { + use vulkano::shader::EntryPointInfo; + use vulkano::shader::GeometryShaderExecution; + use vulkano::shader::ShaderExecution; + use vulkano::shader::ShaderModule; + use vulkano::shader::ShaderStage; + use vulkano::shader::SpecializationConstantRequirements; + use vulkano::shader::spirv::Capability; + use vulkano::Version; + + let _bytes = ( #( #include_bytes),* ); + + static WORDS: &[u32] = &[ #( #words ),* ]; + + unsafe { + Ok(ShaderModule::from_words_with_data( + device, + WORDS, + #spirv_version, + [#(#spirv_capabilities),*], + [#(#spirv_extensions),*], + [#(#entry_points),*], + )?) } - - #( #entry_points_inside_impl )* } #specialization_constants }; + let structs = structs::write_structs(prefix, &spirv, types_meta, types_registry); + Ok((shader_code, structs)) } #[derive(Debug)] pub enum Error { - UnsupportedSpirvVersion, IoError(IoError), SpirvError(SpirvError), } @@ -402,361 +318,13 @@ impl From for Error { } } -/// Returns the Vulkan device requirement for a SPIR-V `OpCapability`. -#[rustfmt::skip] -fn capability_requirement(cap: &Capability) -> &'static [DeviceRequirement] { - match *cap { - Capability::Matrix => &[], - Capability::Shader => &[], - Capability::InputAttachment => &[], - Capability::Sampled1D => &[], - Capability::Image1D => &[], - Capability::SampledBuffer => &[], - Capability::ImageBuffer => &[], - Capability::ImageQuery => &[], - Capability::DerivativeControl => &[], - Capability::Geometry => &[DeviceRequirement::Feature("geometry_shader")], - Capability::Tessellation => &[DeviceRequirement::Feature("tessellation_shader")], - Capability::Float64 => &[DeviceRequirement::Feature("shader_float64")], - Capability::Int64 => &[DeviceRequirement::Feature("shader_int64")], - Capability::Int64Atomics => &[ - DeviceRequirement::Feature("shader_buffer_int64_atomics"), - DeviceRequirement::Feature("shader_shared_int64_atomics"), - DeviceRequirement::Feature("shader_image_int64_atomics"), - ], - /* Capability::AtomicFloat16AddEXT => &[ - DeviceRequirement::Feature("shader_buffer_float16_atomic_add"), - DeviceRequirement::Feature("shader_shared_float16_atomic_add"), - ], */ - Capability::AtomicFloat32AddEXT => &[ - DeviceRequirement::Feature("shader_buffer_float32_atomic_add"), - DeviceRequirement::Feature("shader_shared_float32_atomic_add"), - DeviceRequirement::Feature("shader_image_float32_atomic_add"), - ], - Capability::AtomicFloat64AddEXT => &[ - DeviceRequirement::Feature("shader_buffer_float64_atomic_add"), - DeviceRequirement::Feature("shader_shared_float64_atomic_add"), - ], - /* Capability::AtomicFloat16MinMaxEXT => &[ - DeviceRequirement::Feature("shader_buffer_float16_atomic_min_max"), - DeviceRequirement::Feature("shader_shared_float16_atomic_min_max"), - ], */ - /* Capability::AtomicFloat32MinMaxEXT => &[ - DeviceRequirement::Feature("shader_buffer_float32_atomic_min_max"), - DeviceRequirement::Feature("shader_shared_float32_atomic_min_max"), - DeviceRequirement::Feature("shader_image_float32_atomic_min_max"), - ], */ - /* Capability::AtomicFloat64MinMaxEXT => &[ - DeviceRequirement::Feature("shader_buffer_float64_atomic_min_max"), - DeviceRequirement::Feature("shader_shared_float64_atomic_min_max"), - ], */ - Capability::Int64ImageEXT => &[DeviceRequirement::Feature("shader_image_int64_atomics")], - Capability::Int16 => &[DeviceRequirement::Feature("shader_int16")], - Capability::TessellationPointSize => &[DeviceRequirement::Feature( - "shader_tessellation_and_geometry_point_size", - )], - Capability::GeometryPointSize => &[DeviceRequirement::Feature( - "shader_tessellation_and_geometry_point_size", - )], - Capability::ImageGatherExtended => { - &[DeviceRequirement::Feature("shader_image_gather_extended")] - } - Capability::StorageImageMultisample => &[DeviceRequirement::Feature( - "shader_storage_image_multisample", - )], - Capability::UniformBufferArrayDynamicIndexing => &[DeviceRequirement::Feature( - "shader_uniform_buffer_array_dynamic_indexing", - )], - Capability::SampledImageArrayDynamicIndexing => &[DeviceRequirement::Feature( - "shader_sampled_image_array_dynamic_indexing", - )], - Capability::StorageBufferArrayDynamicIndexing => &[DeviceRequirement::Feature( - "shader_storage_buffer_array_dynamic_indexing", - )], - Capability::StorageImageArrayDynamicIndexing => &[DeviceRequirement::Feature( - "shader_storage_image_array_dynamic_indexing", - )], - Capability::ClipDistance => &[DeviceRequirement::Feature("shader_clip_distance")], - Capability::CullDistance => &[DeviceRequirement::Feature("shader_cull_distance")], - Capability::ImageCubeArray => &[DeviceRequirement::Feature("image_cube_array")], - Capability::SampleRateShading => &[DeviceRequirement::Feature("sample_rate_shading")], - Capability::SparseResidency => &[DeviceRequirement::Feature("shader_resource_residency")], - Capability::MinLod => &[DeviceRequirement::Feature("shader_resource_min_lod")], - Capability::SampledCubeArray => &[DeviceRequirement::Feature("image_cube_array")], - Capability::ImageMSArray => &[DeviceRequirement::Feature( - "shader_storage_image_multisample", - )], - Capability::StorageImageExtendedFormats => &[], - Capability::InterpolationFunction => &[DeviceRequirement::Feature("sample_rate_shading")], - Capability::StorageImageReadWithoutFormat => &[DeviceRequirement::Feature( - "shader_storage_image_read_without_format", - )], - Capability::StorageImageWriteWithoutFormat => &[DeviceRequirement::Feature( - "shader_storage_image_write_without_format", - )], - Capability::MultiViewport => &[DeviceRequirement::Feature("multi_viewport")], - Capability::DrawParameters => &[ - DeviceRequirement::Feature("shader_draw_parameters"), - DeviceRequirement::Extension("khr_shader_draw_parameters"), - ], - Capability::MultiView => &[DeviceRequirement::Feature("multiview")], - Capability::DeviceGroup => &[ - DeviceRequirement::Version(1, 1), - DeviceRequirement::Extension("khr_device_group"), - ], - Capability::VariablePointersStorageBuffer => &[DeviceRequirement::Feature( - "variable_pointers_storage_buffer", - )], - Capability::VariablePointers => &[DeviceRequirement::Feature("variable_pointers")], - Capability::ShaderClockKHR => &[DeviceRequirement::Extension("khr_shader_clock")], - Capability::StencilExportEXT => { - &[DeviceRequirement::Extension("ext_shader_stencil_export")] - } - Capability::SubgroupBallotKHR => { - &[DeviceRequirement::Extension("ext_shader_subgroup_ballot")] - } - Capability::SubgroupVoteKHR => &[DeviceRequirement::Extension("ext_shader_subgroup_vote")], - Capability::ImageReadWriteLodAMD => &[DeviceRequirement::Extension( - "amd_shader_image_load_store_lod", - )], - Capability::ImageGatherBiasLodAMD => { - &[DeviceRequirement::Extension("amd_texture_gather_bias_lod")] - } - Capability::FragmentMaskAMD => &[DeviceRequirement::Extension("amd_shader_fragment_mask")], - Capability::SampleMaskOverrideCoverageNV => &[DeviceRequirement::Extension( - "nv_sample_mask_override_coverage", - )], - Capability::GeometryShaderPassthroughNV => &[DeviceRequirement::Extension( - "nv_geometry_shader_passthrough", - )], - Capability::ShaderViewportIndex => { - &[DeviceRequirement::Feature("shader_output_viewport_index")] - } - Capability::ShaderLayer => &[DeviceRequirement::Feature("shader_output_layer")], - Capability::ShaderViewportIndexLayerEXT => &[ - DeviceRequirement::Extension("ext_shader_viewport_index_layer"), - DeviceRequirement::Extension("nv_viewport_array2"), - ], - Capability::ShaderViewportMaskNV => &[DeviceRequirement::Extension("nv_viewport_array2")], - Capability::PerViewAttributesNV => &[DeviceRequirement::Extension( - "nvx_multiview_per_view_attributes", - )], - Capability::StorageBuffer16BitAccess => { - &[DeviceRequirement::Feature("storage_buffer16_bit_access")] - } - Capability::UniformAndStorageBuffer16BitAccess => &[DeviceRequirement::Feature( - "uniform_and_storage_buffer16_bit_access", - )], - Capability::StoragePushConstant16 => { - &[DeviceRequirement::Feature("storage_push_constant16")] - } - Capability::StorageInputOutput16 => &[DeviceRequirement::Feature("storage_input_output16")], - Capability::GroupNonUniform => todo!(), - Capability::GroupNonUniformVote => todo!(), - Capability::GroupNonUniformArithmetic => todo!(), - Capability::GroupNonUniformBallot => todo!(), - Capability::GroupNonUniformShuffle => todo!(), - Capability::GroupNonUniformShuffleRelative => todo!(), - Capability::GroupNonUniformClustered => todo!(), - Capability::GroupNonUniformQuad => todo!(), - Capability::GroupNonUniformPartitionedNV => todo!(), - Capability::SampleMaskPostDepthCoverage => { - &[DeviceRequirement::Extension("ext_post_depth_coverage")] - } - Capability::ShaderNonUniform => &[ - DeviceRequirement::Version(1, 2), - DeviceRequirement::Extension("ext_descriptor_indexing"), - ], - Capability::RuntimeDescriptorArray => { - &[DeviceRequirement::Feature("runtime_descriptor_array")] - } - Capability::InputAttachmentArrayDynamicIndexing => &[DeviceRequirement::Feature( - "shader_input_attachment_array_dynamic_indexing", - )], - Capability::UniformTexelBufferArrayDynamicIndexing => &[DeviceRequirement::Feature( - "shader_uniform_texel_buffer_array_dynamic_indexing", - )], - Capability::StorageTexelBufferArrayDynamicIndexing => &[DeviceRequirement::Feature( - "shader_storage_texel_buffer_array_dynamic_indexing", - )], - Capability::UniformBufferArrayNonUniformIndexing => &[DeviceRequirement::Feature( - "shader_uniform_buffer_array_non_uniform_indexing", - )], - Capability::SampledImageArrayNonUniformIndexing => &[DeviceRequirement::Feature( - "shader_sampled_image_array_non_uniform_indexing", - )], - Capability::StorageBufferArrayNonUniformIndexing => &[DeviceRequirement::Feature( - "shader_storage_buffer_array_non_uniform_indexing", - )], - Capability::StorageImageArrayNonUniformIndexing => &[DeviceRequirement::Feature( - "shader_storage_image_array_non_uniform_indexing", - )], - Capability::InputAttachmentArrayNonUniformIndexing => &[DeviceRequirement::Feature( - "shader_input_attachment_array_non_uniform_indexing", - )], - Capability::UniformTexelBufferArrayNonUniformIndexing => &[DeviceRequirement::Feature( - "shader_uniform_texel_buffer_array_non_uniform_indexing", - )], - Capability::StorageTexelBufferArrayNonUniformIndexing => &[DeviceRequirement::Feature( - "shader_storage_texel_buffer_array_non_uniform_indexing", - )], - Capability::Float16 => &[ - DeviceRequirement::Feature("shader_float16"), - DeviceRequirement::Extension("amd_gpu_shader_half_float"), - ], - Capability::Int8 => &[DeviceRequirement::Feature("shader_int8")], - Capability::StorageBuffer8BitAccess => { - &[DeviceRequirement::Feature("storage_buffer8_bit_access")] - } - Capability::UniformAndStorageBuffer8BitAccess => &[DeviceRequirement::Feature( - "uniform_and_storage_buffer8_bit_access", - )], - Capability::StoragePushConstant8 => &[DeviceRequirement::Feature("storage_push_constant8")], - Capability::VulkanMemoryModel => &[DeviceRequirement::Feature("vulkan_memory_model")], - Capability::VulkanMemoryModelDeviceScope => &[DeviceRequirement::Feature( - "vulkan_memory_model_device_scope", - )], - Capability::DenormPreserve => todo!(), - Capability::DenormFlushToZero => todo!(), - Capability::SignedZeroInfNanPreserve => todo!(), - Capability::RoundingModeRTE => todo!(), - Capability::RoundingModeRTZ => todo!(), - Capability::ComputeDerivativeGroupQuadsNV => { - &[DeviceRequirement::Feature("compute_derivative_group_quads")] - } - Capability::ComputeDerivativeGroupLinearNV => &[DeviceRequirement::Feature( - "compute_derivative_group_linear", - )], - Capability::FragmentBarycentricNV => { - &[DeviceRequirement::Feature("fragment_shader_barycentric")] - } - Capability::ImageFootprintNV => &[DeviceRequirement::Feature("image_footprint")], - Capability::MeshShadingNV => &[DeviceRequirement::Extension("nv_mesh_shader")], - Capability::RayTracingKHR | Capability::RayTracingProvisionalKHR => { - &[DeviceRequirement::Feature("ray_tracing_pipeline")] - } - Capability::RayQueryKHR | Capability::RayQueryProvisionalKHR => &[DeviceRequirement::Feature("ray_query")], - Capability::RayTraversalPrimitiveCullingKHR => &[DeviceRequirement::Feature( - "ray_traversal_primitive_culling", - )], - Capability::RayTracingNV => &[DeviceRequirement::Extension("nv_ray_tracing")], - // Capability::RayTracingMotionBlurNV => &[DeviceRequirement::Feature("ray_tracing_motion_blur")], - Capability::TransformFeedback => &[DeviceRequirement::Feature("transform_feedback")], - Capability::GeometryStreams => &[DeviceRequirement::Feature("geometry_streams")], - Capability::FragmentDensityEXT => &[ - DeviceRequirement::Feature("fragment_density_map"), - DeviceRequirement::Feature("shading_rate_image"), - ], - Capability::PhysicalStorageBufferAddresses => { - &[DeviceRequirement::Feature("buffer_device_address")] - } - Capability::CooperativeMatrixNV => &[DeviceRequirement::Feature("cooperative_matrix")], - Capability::IntegerFunctions2INTEL => { - &[DeviceRequirement::Feature("shader_integer_functions2")] - } - Capability::ShaderSMBuiltinsNV => &[DeviceRequirement::Feature("shader_sm_builtins")], - Capability::FragmentShaderSampleInterlockEXT => &[DeviceRequirement::Feature( - "fragment_shader_sample_interlock", - )], - Capability::FragmentShaderPixelInterlockEXT => &[DeviceRequirement::Feature( - "fragment_shader_pixel_interlock", - )], - Capability::FragmentShaderShadingRateInterlockEXT => &[ - DeviceRequirement::Feature("fragment_shader_shading_rate_interlock"), - DeviceRequirement::Feature("shading_rate_image"), - ], - Capability::DemoteToHelperInvocationEXT => &[DeviceRequirement::Feature( - "shader_demote_to_helper_invocation", - )], - Capability::FragmentShadingRateKHR => &[ - DeviceRequirement::Feature("pipeline_fragment_shading_rate"), - DeviceRequirement::Feature("primitive_fragment_shading_rate"), - DeviceRequirement::Feature("attachment_fragment_shading_rate"), - ], - // Capability::WorkgroupMemoryExplicitLayoutKHR => &[DeviceRequirement::Feature("workgroup_memory_explicit_layout")], - // Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR => &[DeviceRequirement::Feature("workgroup_memory_explicit_layout8_bit_access")], - // Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR => &[DeviceRequirement::Feature("workgroup_memory_explicit_layout16_bit_access")], - Capability::Addresses => panic!(), // not supported - Capability::Linkage => panic!(), // not supported - Capability::Kernel => panic!(), // not supported - Capability::Vector16 => panic!(), // not supported - Capability::Float16Buffer => panic!(), // not supported - Capability::ImageBasic => panic!(), // not supported - Capability::ImageReadWrite => panic!(), // not supported - Capability::ImageMipmap => panic!(), // not supported - Capability::Pipes => panic!(), // not supported - Capability::Groups => panic!(), // not supported - Capability::DeviceEnqueue => panic!(), // not supported - Capability::LiteralSampler => panic!(), // not supported - Capability::AtomicStorage => panic!(), // not supported - Capability::ImageRect => panic!(), // not supported - Capability::SampledRect => panic!(), // not supported - Capability::GenericPointer => panic!(), // not supported - Capability::SubgroupDispatch => panic!(), // not supported - Capability::NamedBarrier => panic!(), // not supported - Capability::PipeStorage => panic!(), // not supported - Capability::AtomicStorageOps => panic!(), // not supported - Capability::Float16ImageAMD => panic!(), // not supported - Capability::ShaderStereoViewNV => panic!(), // not supported - Capability::FragmentFullyCoveredEXT => panic!(), // not supported - Capability::SubgroupShuffleINTEL => panic!(), // not supported - Capability::SubgroupBufferBlockIOINTEL => panic!(), // not supported - Capability::SubgroupImageBlockIOINTEL => panic!(), // not supported - Capability::SubgroupImageMediaBlockIOINTEL => panic!(), // not supported - Capability::SubgroupAvcMotionEstimationINTEL => panic!(), // not supported - Capability::SubgroupAvcMotionEstimationIntraINTEL => panic!(), // not supported - Capability::SubgroupAvcMotionEstimationChromaINTEL => panic!(), // not supported - Capability::FunctionPointersINTEL => panic!(), // not supported - Capability::IndirectReferencesINTEL => panic!(), // not supported - Capability::FPGAKernelAttributesINTEL => panic!(), // not supported - Capability::FPGALoopControlsINTEL => panic!(), // not supported - Capability::FPGAMemoryAttributesINTEL => panic!(), // not supported - Capability::FPGARegINTEL => panic!(), // not supported - Capability::UnstructuredLoopControlsINTEL => panic!(), // not supported - Capability::KernelAttributesINTEL => panic!(), // not supported - Capability::BlockingPipesINTEL => panic!(), // not supported - } -} - -/// Returns the Vulkan device requirement for a SPIR-V storage class. -fn storage_class_requirement(storage_class: &StorageClass) -> &'static [DeviceRequirement] { - match *storage_class { - StorageClass::UniformConstant => &[], - StorageClass::Input => &[], - StorageClass::Uniform => &[], - StorageClass::Output => &[], - StorageClass::Workgroup => &[], - StorageClass::CrossWorkgroup => &[], - StorageClass::Private => &[], - StorageClass::Function => &[], - StorageClass::Generic => &[], - StorageClass::PushConstant => &[], - StorageClass::AtomicCounter => &[], - StorageClass::Image => &[], - StorageClass::StorageBuffer => &[DeviceRequirement::Extension( - "khr_storage_buffer_storage_class", - )], - StorageClass::CallableDataKHR => todo!(), - StorageClass::IncomingCallableDataKHR => todo!(), - StorageClass::RayPayloadKHR => todo!(), - StorageClass::HitAttributeKHR => todo!(), - StorageClass::IncomingRayPayloadKHR => todo!(), - StorageClass::ShaderRecordBufferKHR => todo!(), - StorageClass::PhysicalStorageBuffer => todo!(), - StorageClass::CodeSectionINTEL => todo!(), - } -} - -enum DeviceRequirement { - Feature(&'static str), - Extension(&'static str), - Version(u32, u32), -} - #[cfg(test)] mod tests { use super::*; - use std::path::PathBuf; + use crate::codegen::compile; + use shaderc::ShaderKind; + use std::path::{Path, PathBuf}; + use vulkano::shader::{reflect, spirv::Spirv}; #[cfg(not(target_os = "windows"))] pub fn path_separator() -> &'static str { @@ -775,6 +343,19 @@ mod tests { .collect() } + #[test] + fn spirv_parse() { + let data = include_bytes!("../tests/frag.spv"); + let insts: Vec<_> = data + .chunks(4) + .map(|c| { + ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32 + }) + .collect(); + + Spirv::new(&insts).unwrap(); + } + #[test] fn test_bad_alignment() { // vec3/mat3/mat3x* are problematic in arrays since their rust @@ -1051,4 +632,166 @@ mod tests { ); compile_defines.expect("Setting shader macros did not work"); } + + /// `entrypoint1.frag.glsl`: + /// ```glsl + /// #version 450 + /// + /// layout(set = 0, binding = 0) uniform Uniform { + /// uint data; + /// } ubo; + /// + /// layout(set = 0, binding = 1) buffer Buffer { + /// uint data; + /// } bo; + /// + /// layout(set = 0, binding = 2) uniform sampler textureSampler; + /// layout(set = 0, binding = 3) uniform texture2D imageTexture; + /// + /// layout(push_constant) uniform PushConstant { + /// uint data; + /// } push; + /// + /// layout(input_attachment_index = 0, set = 0, binding = 4) uniform subpassInput inputAttachment; + /// + /// layout(location = 0) out vec4 outColor; + /// + /// void entrypoint1() { + /// bo.data = 12; + /// outColor = vec4( + /// float(ubo.data), + /// float(push.data), + /// texture(sampler2D(imageTexture, textureSampler), vec2(0.0, 0.0)).x, + /// subpassLoad(inputAttachment).x + /// ); + /// } + /// ``` + /// + /// `entrypoint2.frag.glsl`: + /// ```glsl + /// #version 450 + /// + /// layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput inputAttachment2; + /// + /// layout(set = 0, binding = 1) buffer Buffer { + /// uint data; + /// } bo2; + /// + /// layout(set = 0, binding = 2) uniform Uniform { + /// uint data; + /// } ubo2; + /// + /// layout(push_constant) uniform PushConstant { + /// uint data; + /// } push2; + /// + /// void entrypoint2() { + /// bo2.data = ubo2.data + push2.data + int(subpassLoad(inputAttachment2).y); + /// } + /// ``` + /// + /// Compiled and linked with: + /// ```sh + /// glslangvalidator -e entrypoint1 --source-entrypoint entrypoint1 -V100 entrypoint1.frag.glsl -o entrypoint1.spv + /// glslangvalidator -e entrypoint2 --source-entrypoint entrypoint2 -V100 entrypoint2.frag.glsl -o entrypoint2.spv + /// spirv-link entrypoint1.spv entrypoint2.spv -o multiple_entrypoints.spv + /// ``` + #[test] + fn test_descriptor_calculation_with_multiple_entrypoints() { + let data = include_bytes!("../tests/multiple_entrypoints.spv"); + let instructions: Vec = data + .chunks(4) + .map(|c| { + ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32 + }) + .collect(); + let spirv = Spirv::new(&instructions).unwrap(); + + let mut descriptors = Vec::new(); + for (_, info) in reflect::entry_points(&spirv, true) { + descriptors.push(info.descriptor_requirements); + } + + // Check first entrypoint + let e1_descriptors = descriptors.get(0).expect("Could not find entrypoint1"); + let mut e1_bindings = Vec::new(); + for (loc, _reqs) in e1_descriptors { + e1_bindings.push(*loc); + } + assert_eq!(e1_bindings.len(), 5); + assert!(e1_bindings.contains(&(0, 0))); + assert!(e1_bindings.contains(&(0, 1))); + assert!(e1_bindings.contains(&(0, 2))); + assert!(e1_bindings.contains(&(0, 3))); + assert!(e1_bindings.contains(&(0, 4))); + + // Check second entrypoint + let e2_descriptors = descriptors.get(1).expect("Could not find entrypoint2"); + let mut e2_bindings = Vec::new(); + for (loc, _reqs) in e2_descriptors { + e2_bindings.push(*loc); + } + assert_eq!(e2_bindings.len(), 3); + assert!(e2_bindings.contains(&(0, 0))); + assert!(e2_bindings.contains(&(0, 1))); + assert!(e2_bindings.contains(&(0, 2))); + } + + #[test] + fn test_descriptor_calculation_with_multiple_functions() { + let includes: [PathBuf; 0] = []; + let defines: [(String, String); 0] = []; + let (comp, _) = compile( + None, + &Path::new(""), + " + #version 450 + + layout(set = 1, binding = 0) buffer Buffer { + vec3 data; + } bo; + + layout(set = 2, binding = 0) uniform Uniform { + float data; + } ubo; + + layout(set = 3, binding = 1) uniform sampler textureSampler; + layout(set = 3, binding = 2) uniform texture2D imageTexture; + + float withMagicSparkles(float data) { + return texture(sampler2D(imageTexture, textureSampler), vec2(data, data)).x; + } + + vec3 makeSecretSauce() { + return vec3(withMagicSparkles(ubo.data)); + } + + void main() { + bo.data = makeSecretSauce(); + } + ", + ShaderKind::Vertex, + &includes, + &defines, + None, + None, + ) + .unwrap(); + let spirv = Spirv::new(comp.as_binary()).unwrap(); + + for (_, info) in reflect::entry_points(&spirv, true) { + let mut bindings = Vec::new(); + for (loc, _reqs) in info.descriptor_requirements { + bindings.push(loc); + } + assert_eq!(bindings.len(), 4); + assert!(bindings.contains(&(1, 0))); + assert!(bindings.contains(&(2, 0))); + assert!(bindings.contains(&(3, 1))); + assert!(bindings.contains(&(3, 2))); + + return; + } + panic!("Could not find entrypoint"); + } } diff --git a/vulkano-shaders/src/descriptor_sets.rs b/vulkano-shaders/src/descriptor_sets.rs deleted file mode 100644 index a099a1fc7c..0000000000 --- a/vulkano-shaders/src/descriptor_sets.rs +++ /dev/null @@ -1,774 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -use crate::TypesMeta; -use fnv::FnvHashMap; -use proc_macro2::TokenStream; -use std::cmp; -use std::collections::HashSet; -use vulkano::{ - descriptor_set::layout::DescriptorType, - format::Format, - image::view::ImageViewType, - pipeline::shader::DescriptorRequirements, - spirv::{Decoration, Dim, Id, ImageFormat, Instruction, Spirv, StorageClass}, -}; - -pub(super) fn write_descriptor_requirements( - spirv: &Spirv, - entrypoint_id: Id, - interface: &[Id], - exact_entrypoint_interface: bool, - stages: &TokenStream, -) -> TokenStream { - let descriptor_requirements = - find_descriptors(spirv, entrypoint_id, interface, exact_entrypoint_interface); - - let descriptor_requirements = descriptor_requirements.into_iter().map(|(loc, reqs)| { - let (set_num, binding_num) = loc; - let DescriptorRequirements { - descriptor_types, - descriptor_count, - format, - image_view_type, - multisampled, - mutable, - stages: _, - } = reqs; - - let descriptor_types = descriptor_types.into_iter().map(|ty| { - let ident = format_ident!("{}", format!("{:?}", ty)); - quote! { DescriptorType::#ident } - }); - let format = match format { - Some(format) => { - let ident = format_ident!("{}", format!("{:?}", format)); - quote! { Some(Format::#ident) } - } - None => quote! { None }, - }; - let image_view_type = match image_view_type { - Some(image_view_type) => { - let ident = format_ident!("{}", format!("{:?}", image_view_type)); - quote! { Some(ImageViewType::#ident) } - } - None => quote! { None }, - }; - /*let stages = { - let ShaderStages { - vertex, - tessellation_control, - tessellation_evaluation, - geometry, - fragment, - compute, - } = stages; - - quote! { - ShaderStages { - vertex: #vertex, - tessellation_control: #tessellation_control, - tessellation_evaluation: #tessellation_evaluation, - geometry: #geometry, - fragment: #fragment, - compute: #compute, - } - } - };*/ - - quote! { - ( - (#set_num, #binding_num), - DescriptorRequirements { - descriptor_types: vec![#(#descriptor_types),*], - descriptor_count: #descriptor_count, - format: #format, - image_view_type: #image_view_type, - multisampled: #multisampled, - mutable: #mutable, - stages: #stages, - }, - ), - } - }); - - quote! { - [ - #( #descriptor_requirements )* - ] - } -} - -pub(super) fn write_push_constant_ranges( - shader: &str, - spirv: &Spirv, - stage: &TokenStream, - types_meta: &TypesMeta, -) -> TokenStream { - // TODO: somewhat implemented correctly - - // Looping to find all the push constant structs. - let mut push_constants_size = 0; - for type_id in spirv - .iter_global() - .filter_map(|instruction| match instruction { - &Instruction::TypePointer { - ty, - storage_class: StorageClass::PushConstant, - .. - } => Some(ty), - _ => None, - }) - { - let (_, _, size, _) = crate::structs::type_from_id(shader, spirv, type_id, types_meta); - let size = size.expect("Found runtime-sized push constants") as u32; - push_constants_size = cmp::max(push_constants_size, size); - } - - if push_constants_size == 0 { - quote! { - None - } - } else { - quote! { - Some( - PipelineLayoutPcRange { - offset: 0, // FIXME: not necessarily true - size: #push_constants_size, - stages: #stage, - } - ) - } - } -} - -fn find_descriptors( - spirv: &Spirv, - entrypoint_id: Id, - interface: &[Id], - exact: bool, -) -> FnvHashMap<(u32, u32), DescriptorRequirements> { - // For SPIR-V 1.4+, the entrypoint interface can specify variables of all storage classes, - // and most tools will put all used variables in the entrypoint interface. However, - // SPIR-V 1.0-1.3 do not specify variables other than Input/Output ones in the interface, - // and instead the function itself must be inspected. - let variables = if exact { - let mut found_variables: HashSet = interface.iter().cloned().collect(); - let mut inspected_functions: HashSet = HashSet::new(); - find_variables_in_function( - &spirv, - entrypoint_id, - &mut inspected_functions, - &mut found_variables, - ); - Some(found_variables) - } else { - None - }; - - // Looping to find all the global variables that have the `DescriptorSet` decoration. - spirv - .iter_global() - .filter_map(|instruction| { - let (variable_id, variable_type_id, storage_class) = match instruction { - Instruction::Variable { - result_id, - result_type_id, - .. - } => { - let (real_type, storage_class) = match spirv - .id(*result_type_id) - .instruction() - { - Instruction::TypePointer { - ty, storage_class, .. - } => (ty, storage_class), - _ => panic!( - "Variable {} result_type_id does not refer to a TypePointer instruction", result_id - ), - }; - - (*result_id, *real_type, storage_class) - } - _ => return None, - }; - - if exact && !variables.as_ref().unwrap().contains(&variable_id) { - return None; - } - - let variable_id_info = spirv.id(variable_id); - let set_num = match variable_id_info - .iter_decoration() - .find_map(|instruction| match instruction { - Instruction::Decorate { - decoration: Decoration::DescriptorSet { descriptor_set }, - .. - } => Some(*descriptor_set), - _ => None, - }) { - Some(x) => x, - None => return None, - }; - - let binding_num = variable_id_info - .iter_decoration() - .find_map(|instruction| match instruction { - Instruction::Decorate { - decoration: Decoration::Binding { binding_point }, - .. - } => Some(*binding_point), - _ => None, - }) - .unwrap(); - - let name = variable_id_info - .iter_name() - .find_map(|instruction| match instruction { - Instruction::Name { name, .. } => Some(name.as_str()), - _ => None, - }) - .unwrap_or("__unnamed"); - - let nonwritable = variable_id_info.iter_decoration().any(|instruction| { - matches!( - instruction, - Instruction::Decorate { - decoration: Decoration::NonWritable, - .. - } - ) - }); - - // Find information about the kind of binding for this descriptor. - let mut reqs = - descriptor_requirements(spirv, variable_type_id, storage_class, false).expect(&format!( - "Couldn't find relevant type for global variable `{}` (type {}, maybe unimplemented)", - name, variable_type_id, - )); - - reqs.mutable &= !nonwritable; - - Some(((set_num, binding_num), reqs)) - }) - .collect() -} - -// Recursively finds every pointer variable used in the execution of a function. -fn find_variables_in_function( - spirv: &Spirv, - function: Id, - inspected_functions: &mut HashSet, - found_variables: &mut HashSet, -) { - inspected_functions.insert(function); - let mut in_function = false; - for instruction in spirv.instructions() { - if !in_function { - match instruction { - Instruction::Function { result_id, .. } if result_id == &function => { - in_function = true; - } - _ => {} - } - } else { - // We only care about instructions that accept pointers. - // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_universal_validation_rules - match instruction { - Instruction::Load { pointer, .. } | Instruction::Store { pointer, .. } => { - found_variables.insert(*pointer); - } - Instruction::AccessChain { base, .. } - | Instruction::InBoundsAccessChain { base, .. } => { - found_variables.insert(*base); - } - Instruction::FunctionCall { - function, - arguments, - .. - } => { - arguments.iter().for_each(|&x| { - found_variables.insert(x); - }); - if !inspected_functions.contains(function) { - find_variables_in_function( - spirv, - *function, - inspected_functions, - found_variables, - ); - } - } - Instruction::ImageTexelPointer { - image, - coordinate, - sample, - .. - } => { - found_variables.insert(*image); - found_variables.insert(*coordinate); - found_variables.insert(*sample); - } - Instruction::CopyMemory { target, source, .. } => { - found_variables.insert(*target); - found_variables.insert(*source); - } - Instruction::CopyObject { operand, .. } => { - found_variables.insert(*operand); - } - Instruction::AtomicLoad { pointer, .. } - | Instruction::AtomicIIncrement { pointer, .. } - | Instruction::AtomicIDecrement { pointer, .. } - | Instruction::AtomicFlagTestAndSet { pointer, .. } - | Instruction::AtomicFlagClear { pointer, .. } => { - found_variables.insert(*pointer); - } - Instruction::AtomicStore { pointer, value, .. } - | Instruction::AtomicExchange { pointer, value, .. } - | Instruction::AtomicIAdd { pointer, value, .. } - | Instruction::AtomicISub { pointer, value, .. } - | Instruction::AtomicSMin { pointer, value, .. } - | Instruction::AtomicUMin { pointer, value, .. } - | Instruction::AtomicSMax { pointer, value, .. } - | Instruction::AtomicUMax { pointer, value, .. } - | Instruction::AtomicAnd { pointer, value, .. } - | Instruction::AtomicOr { pointer, value, .. } - | Instruction::AtomicXor { pointer, value, .. } => { - found_variables.insert(*pointer); - found_variables.insert(*value); - } - Instruction::AtomicCompareExchange { - pointer, - value, - comparator, - .. - } - | Instruction::AtomicCompareExchangeWeak { - pointer, - value, - comparator, - .. - } => { - found_variables.insert(*pointer); - found_variables.insert(*value); - found_variables.insert(*comparator); - } - Instruction::ExtInst { operands, .. } => { - // We don't know which extended instructions take pointers, - // so we must interpret every operand as a pointer. - operands.iter().for_each(|&o| { - found_variables.insert(o); - }); - } - Instruction::FunctionEnd => return, - _ => {} - } - } - } -} - -/// Returns a `DescriptorRequirements` value for the pointed type. -/// -/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface -fn descriptor_requirements( - spirv: &Spirv, - pointed_ty: Id, - pointer_storage: &StorageClass, - force_combined_image_sampled: bool, -) -> Option { - let id_info = spirv.id(pointed_ty); - - match id_info.instruction() { - Instruction::TypeStruct { .. } => { - let decoration_block = id_info.iter_decoration().any(|instruction| { - matches!( - instruction, - Instruction::Decorate { - decoration: Decoration::Block, - .. - } - ) - }); - - let decoration_buffer_block = id_info.iter_decoration().any(|instruction| { - matches!( - instruction, - Instruction::Decorate { - decoration: Decoration::BufferBlock, - .. - } - ) - }); - - assert!( - decoration_block ^ decoration_buffer_block, - "Structs in shader interface are expected to be decorated with one of Block or BufferBlock" - ); - - let mut reqs = DescriptorRequirements { - descriptor_count: 1, - ..Default::default() - }; - - if decoration_buffer_block - || decoration_block && *pointer_storage == StorageClass::StorageBuffer - { - // Determine whether all members have a NonWritable decoration. - let nonwritable = id_info.iter_members().all(|member_info| { - member_info.iter_decoration().any(|instruction| { - matches!( - instruction, - Instruction::MemberDecorate { - decoration: Decoration::NonWritable, - .. - } - ) - }) - }); - - reqs.descriptor_types = vec![ - DescriptorType::StorageBuffer, - DescriptorType::StorageBufferDynamic, - ]; - reqs.mutable = !nonwritable; - } else { - reqs.descriptor_types = vec![ - DescriptorType::UniformBuffer, - DescriptorType::UniformBufferDynamic, - ]; - }; - - Some(reqs) - } - &Instruction::TypeImage { - ref dim, - arrayed, - ms, - sampled, - ref image_format, - .. - } => { - let multisampled = ms != 0; - assert!(sampled != 0, "Vulkan requires that variables of type OpTypeImage have a Sampled operand of 1 or 2"); - let format: Option = image_format.clone().into(); - - match dim { - Dim::SubpassData => { - assert!( - !force_combined_image_sampled, - "An OpTypeSampledImage can't point to \ - an OpTypeImage whose dimension is \ - SubpassData" - ); - assert!( - *image_format == ImageFormat::Unknown, - "If Dim is SubpassData, Image Format must be Unknown" - ); - assert!(sampled == 2, "If Dim is SubpassData, Sampled must be 2"); - assert!(arrayed == 0, "If Dim is SubpassData, Arrayed must be 0"); - - Some(DescriptorRequirements { - descriptor_types: vec![DescriptorType::InputAttachment], - descriptor_count: 1, - multisampled, - ..Default::default() - }) - } - Dim::Buffer => { - let mut reqs = DescriptorRequirements { - descriptor_count: 1, - format, - ..Default::default() - }; - - if sampled == 1 { - reqs.descriptor_types = vec![DescriptorType::UniformTexelBuffer]; - } else { - reqs.descriptor_types = vec![DescriptorType::StorageTexelBuffer]; - reqs.mutable = true; - } - - Some(reqs) - } - _ => { - let image_view_type = Some(match (dim, arrayed) { - (Dim::Dim1D, 0) => ImageViewType::Dim1d, - (Dim::Dim1D, 1) => ImageViewType::Dim1dArray, - (Dim::Dim2D, 0) => ImageViewType::Dim2d, - (Dim::Dim2D, 1) => ImageViewType::Dim2dArray, - (Dim::Dim3D, 0) => ImageViewType::Dim3d, - (Dim::Dim3D, 1) => panic!("Vulkan doesn't support arrayed 3D textures"), - (Dim::Cube, 0) => ImageViewType::Cube, - (Dim::Cube, 1) => ImageViewType::CubeArray, - (Dim::Rect, _) => panic!("Vulkan doesn't support rectangle textures"), - _ => unreachable!(), - }); - - let mut reqs = DescriptorRequirements { - descriptor_count: 1, - format, - multisampled, - image_view_type, - ..Default::default() - }; - - if force_combined_image_sampled { - assert!( - sampled == 1, - "A combined image sampler must not reference a storage image" - ); - - reqs.descriptor_types = vec![DescriptorType::CombinedImageSampler]; - } else { - if sampled == 1 { - reqs.descriptor_types = vec![DescriptorType::SampledImage]; - } else { - reqs.descriptor_types = vec![DescriptorType::StorageImage]; - reqs.mutable = true; - } - }; - - Some(reqs) - } - } - } - - &Instruction::TypeSampledImage { image_type, .. } => { - descriptor_requirements(spirv, image_type, pointer_storage, true) - } - - &Instruction::TypeSampler { .. } => Some(DescriptorRequirements { - descriptor_types: vec![DescriptorType::Sampler], - descriptor_count: 1, - ..Default::default() - }), - - &Instruction::TypeArray { - element_type, - length, - .. - } => { - let reqs = match descriptor_requirements(spirv, element_type, pointer_storage, false) { - None => return None, - Some(v) => v, - }; - assert_eq!(reqs.descriptor_count, 1); // TODO: implement? - let len = match spirv.id(length).instruction() { - &Instruction::Constant { ref value, .. } => value, - _ => panic!("failed to find array length"), - }; - let len = len.iter().rev().fold(0, |a, &b| (a << 32) | b as u64); - - Some(DescriptorRequirements { - descriptor_count: len as u32, - ..reqs - }) - } - - &Instruction::TypeRuntimeArray { element_type, .. } => { - let reqs = match descriptor_requirements(spirv, element_type, pointer_storage, false) { - None => return None, - Some(v) => v, - }; - assert_eq!(reqs.descriptor_count, 1); // TODO: implement? - - Some(DescriptorRequirements { - descriptor_count: 0, - ..reqs - }) - } - - _ => None, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::codegen::compile; - use shaderc::ShaderKind; - use std::path::{Path, PathBuf}; - - /// `entrypoint1.frag.glsl`: - /// ```glsl - /// #version 450 - /// - /// layout(set = 0, binding = 0) uniform Uniform { - /// uint data; - /// } ubo; - /// - /// layout(set = 0, binding = 1) buffer Buffer { - /// uint data; - /// } bo; - /// - /// layout(set = 0, binding = 2) uniform sampler textureSampler; - /// layout(set = 0, binding = 3) uniform texture2D imageTexture; - /// - /// layout(push_constant) uniform PushConstant { - /// uint data; - /// } push; - /// - /// layout(input_attachment_index = 0, set = 0, binding = 4) uniform subpassInput inputAttachment; - /// - /// layout(location = 0) out vec4 outColor; - /// - /// void entrypoint1() { - /// bo.data = 12; - /// outColor = vec4( - /// float(ubo.data), - /// float(push.data), - /// texture(sampler2D(imageTexture, textureSampler), vec2(0.0, 0.0)).x, - /// subpassLoad(inputAttachment).x - /// ); - /// } - /// ``` - /// - /// `entrypoint2.frag.glsl`: - /// ```glsl - /// #version 450 - /// - /// layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput inputAttachment2; - /// - /// layout(set = 0, binding = 1) buffer Buffer { - /// uint data; - /// } bo2; - /// - /// layout(set = 0, binding = 2) uniform Uniform { - /// uint data; - /// } ubo2; - /// - /// layout(push_constant) uniform PushConstant { - /// uint data; - /// } push2; - /// - /// void entrypoint2() { - /// bo2.data = ubo2.data + push2.data + int(subpassLoad(inputAttachment2).y); - /// } - /// ``` - /// - /// Compiled and linked with: - /// ```sh - /// glslangvalidator -e entrypoint1 --source-entrypoint entrypoint1 -V100 entrypoint1.frag.glsl -o entrypoint1.spv - /// glslangvalidator -e entrypoint2 --source-entrypoint entrypoint2 -V100 entrypoint2.frag.glsl -o entrypoint2.spv - /// spirv-link entrypoint1.spv entrypoint2.spv -o multiple_entrypoints.spv - /// ``` - #[test] - fn test_descriptor_calculation_with_multiple_entrypoints() { - let data = include_bytes!("../tests/multiple_entrypoints.spv"); - let instructions: Vec = data - .chunks(4) - .map(|c| { - ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32 - }) - .collect(); - let spirv = Spirv::new(&instructions).unwrap(); - - let mut descriptors = Vec::new(); - for instruction in spirv.instructions() { - if let &Instruction::EntryPoint { - entry_point, - ref interface, - .. - } = instruction - { - descriptors.push(find_descriptors(&spirv, entry_point, interface, true)); - } - } - - // Check first entrypoint - let e1_descriptors = descriptors.get(0).expect("Could not find entrypoint1"); - let mut e1_bindings = Vec::new(); - for (loc, _reqs) in e1_descriptors { - e1_bindings.push(*loc); - } - assert_eq!(e1_bindings.len(), 5); - assert!(e1_bindings.contains(&(0, 0))); - assert!(e1_bindings.contains(&(0, 1))); - assert!(e1_bindings.contains(&(0, 2))); - assert!(e1_bindings.contains(&(0, 3))); - assert!(e1_bindings.contains(&(0, 4))); - - // Check second entrypoint - let e2_descriptors = descriptors.get(1).expect("Could not find entrypoint2"); - let mut e2_bindings = Vec::new(); - for (loc, _reqs) in e2_descriptors { - e2_bindings.push(*loc); - } - assert_eq!(e2_bindings.len(), 3); - assert!(e2_bindings.contains(&(0, 0))); - assert!(e2_bindings.contains(&(0, 1))); - assert!(e2_bindings.contains(&(0, 2))); - } - - #[test] - fn test_descriptor_calculation_with_multiple_functions() { - let includes: [PathBuf; 0] = []; - let defines: [(String, String); 0] = []; - let (comp, _) = compile( - None, - &Path::new(""), - " - #version 450 - - layout(set = 1, binding = 0) buffer Buffer { - vec3 data; - } bo; - - layout(set = 2, binding = 0) uniform Uniform { - float data; - } ubo; - - layout(set = 3, binding = 1) uniform sampler textureSampler; - layout(set = 3, binding = 2) uniform texture2D imageTexture; - - float withMagicSparkles(float data) { - return texture(sampler2D(imageTexture, textureSampler), vec2(data, data)).x; - } - - vec3 makeSecretSauce() { - return vec3(withMagicSparkles(ubo.data)); - } - - void main() { - bo.data = makeSecretSauce(); - } - ", - ShaderKind::Vertex, - &includes, - &defines, - None, - None, - ) - .unwrap(); - let spirv = Spirv::new(comp.as_binary()).unwrap(); - - for instruction in spirv.instructions() { - if let &Instruction::EntryPoint { - entry_point, - ref interface, - .. - } = instruction - { - let descriptors = find_descriptors(&spirv, entry_point, interface, true); - let mut bindings = Vec::new(); - for (loc, _reqs) in descriptors { - bindings.push(loc); - } - assert_eq!(bindings.len(), 4); - assert!(bindings.contains(&(1, 0))); - assert!(bindings.contains(&(2, 0))); - assert!(bindings.contains(&(3, 1))); - assert!(bindings.contains(&(3, 2))); - - return; - } - } - panic!("Could not find entrypoint"); - } -} diff --git a/vulkano-shaders/src/entry_point.rs b/vulkano-shaders/src/entry_point.rs index bee4ecfadb..030a5a33e6 100644 --- a/vulkano-shaders/src/entry_point.rs +++ b/vulkano-shaders/src/entry_point.rs @@ -7,347 +7,256 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use crate::descriptor_sets::{write_descriptor_requirements, write_push_constant_ranges}; -use crate::{spirv_search, TypesMeta}; -use proc_macro2::{Span, TokenStream}; -use syn::Ident; -use vulkano::spirv::{ - Decoration, ExecutionMode, ExecutionModel, Id, Instruction, Spirv, StorageClass, +use fnv::FnvHashMap; +use proc_macro2::TokenStream; +use vulkano::pipeline::layout::PipelineLayoutPcRange; +use vulkano::shader::{ + DescriptorRequirements, GeometryShaderExecution, ShaderExecution, ShaderInterfaceEntry, + SpecializationConstantRequirements, }; +use vulkano::shader::{EntryPointInfo, ShaderInterface, ShaderStages}; + +pub(super) fn write_entry_point(name: &str, info: &EntryPointInfo) -> TokenStream { + let execution = write_shader_execution(&info.execution); + let descriptor_requirements = write_descriptor_requirements(&info.descriptor_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 input_interface = write_interface(&info.input_interface); + let output_interface = write_interface(&info.output_interface); -pub(super) fn write_entry_point( - shader: &str, - spirv: &Spirv, - instruction: &Instruction, - types_meta: &TypesMeta, - exact_entrypoint_interface: bool, - shared_constants: bool, -) -> TokenStream { - let (execution, id, ep_name, interface) = match instruction { - &Instruction::EntryPoint { - ref execution_model, - entry_point, - ref name, - ref interface, - .. - } => (execution_model, entry_point, name, interface), - _ => unreachable!(), - }; - - let ignore_first_array_in = match *execution { - ExecutionModel::TessellationControl => true, - ExecutionModel::TessellationEvaluation => true, - ExecutionModel::Geometry => true, - _ => false, - }; - let ignore_first_array_out = match *execution { - ExecutionModel::TessellationControl => true, - _ => false, - }; - - let (input_interface, output_interface) = write_interfaces( - spirv, - interface, - ignore_first_array_in, - ignore_first_array_out, - ); + quote! { + ( + #name.to_owned(), + EntryPointInfo { + execution: #execution, + descriptor_requirements: std::array::IntoIter::new(#descriptor_requirements).collect(), + push_constant_requirements: #push_constant_requirements, + specialization_constant_requirements: std::array::IntoIter::new(#specialization_constant_requirements).collect(), + input_interface: #input_interface, + output_interface: #output_interface, + }, + ), + } +} - let stage = if let ExecutionModel::GLCompute = *execution { - quote! { ShaderStages { compute: true, ..ShaderStages::none() } } - } else { - match *execution { - ExecutionModel::Vertex => { - quote! { ShaderStages { vertex: true, ..ShaderStages::none() } } - } - ExecutionModel::TessellationControl => { - quote! { ShaderStages { tessellation_control: true, ..ShaderStages::none() } } - } - ExecutionModel::TessellationEvaluation => { - quote! { ShaderStages { tessellation_evaluation: true, ..ShaderStages::none() } } - } - ExecutionModel::Geometry => { - quote! { ShaderStages { geometry: true, ..ShaderStages::none() } } - } - ExecutionModel::Fragment => { - quote! { ShaderStages { fragment: true, ..ShaderStages::none() } } - } - ExecutionModel::GLCompute - | ExecutionModel::Kernel - | ExecutionModel::TaskNV - | ExecutionModel::MeshNV - | ExecutionModel::RayGenerationKHR - | ExecutionModel::IntersectionKHR - | ExecutionModel::AnyHitKHR - | ExecutionModel::ClosestHitKHR - | ExecutionModel::MissKHR - | ExecutionModel::CallableKHR => unreachable!(), +fn write_shader_execution(execution: &ShaderExecution) -> TokenStream { + match execution { + ShaderExecution::Vertex => quote! { ShaderExecution::Vertex }, + ShaderExecution::TessellationControl => quote! { ShaderExecution::TessellationControl }, + ShaderExecution::TessellationEvaluation => { + quote! { ShaderExecution::TessellationEvaluation } } - }; - - let descriptor_requirements = - write_descriptor_requirements(&spirv, id, interface, exact_entrypoint_interface, &stage); - let push_constant_ranges = write_push_constant_ranges(shader, &spirv, &stage, &types_meta); - - let spec_consts_struct = if crate::spec_consts::has_specialization_constants(spirv) { - let spec_consts_struct_name = Ident::new( - &format!( - "{}SpecializationConstants", - if shared_constants { "" } else { shader } - ), - Span::call_site(), - ); - quote! { #spec_consts_struct_name } - } else { - quote! { () } - }; - - let (ty, f_call) = { - if let ExecutionModel::GLCompute = *execution { - ( - quote! { ::vulkano::pipeline::shader::ComputeEntryPoint }, - quote! { compute_entry_point( - ::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _), - #descriptor_requirements, - #push_constant_ranges, - <#spec_consts_struct>::descriptors(), - )}, - ) - } else { - let entry_ty = match *execution { - ExecutionModel::Vertex => { - quote! { ::vulkano::pipeline::shader::GraphicsShaderType::Vertex } - } - - ExecutionModel::TessellationControl => { - quote! { ::vulkano::pipeline::shader::GraphicsShaderType::TessellationControl } - } - - ExecutionModel::TessellationEvaluation => { - quote! { ::vulkano::pipeline::shader::GraphicsShaderType::TessellationEvaluation } - } - - ExecutionModel::Geometry => { - let execution_mode = - spirv - .iter_execution_mode() - .find_map(|instruction| match instruction { - &Instruction::ExecutionMode { - entry_point, - ref mode, - .. - } if entry_point == id => match mode { - &ExecutionMode::InputPoints => Some(quote! { Points }), - &ExecutionMode::InputLines => Some(quote! { Lines }), - &ExecutionMode::InputLinesAdjacency => { - Some(quote! { LinesWithAdjacency }) - } - &ExecutionMode::Triangles => Some(quote! { Triangles }), - &ExecutionMode::InputTrianglesAdjacency => { - Some(quote! { TrianglesWithAdjacency }) - } - _ => None, - }, - _ => None, - }); - - quote! { - ::vulkano::pipeline::shader::GraphicsShaderType::Geometry( - ::vulkano::pipeline::shader::GeometryShaderExecutionMode::#execution_mode - ) - } - } - - ExecutionModel::Fragment => { - quote! { ::vulkano::pipeline::shader::GraphicsShaderType::Fragment } - } - - ExecutionModel::GLCompute => unreachable!(), - - ExecutionModel::Kernel - | ExecutionModel::TaskNV - | ExecutionModel::MeshNV - | ExecutionModel::RayGenerationKHR - | ExecutionModel::IntersectionKHR - | ExecutionModel::AnyHitKHR - | ExecutionModel::ClosestHitKHR - | ExecutionModel::MissKHR - | ExecutionModel::CallableKHR => { - panic!("Shaders with {:?} are not supported", execution) + ShaderExecution::Geometry(GeometryShaderExecution { input }) => { + let input = format_ident!("{}", format!("{:?}", input)); + quote! { + ShaderExecution::Geometry { + input: GeometryShaderInput::#input, } - }; - - let ty = quote! { ::vulkano::pipeline::shader::GraphicsEntryPoint }; - let f_call = quote! { - graphics_entry_point( - ::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _), - #descriptor_requirements, - #push_constant_ranges, - <#spec_consts_struct>::descriptors(), - #input_interface, - #output_interface, - #entry_ty - ) - }; - - (ty, f_call) - } - }; - - let mut method_name = ep_name.clone(); - method_name.push_str("_entry_point"); - let method_ident = Ident::new(&method_name, Span::call_site()); - - let ep_name_lenp1 = ep_name.chars().count() + 1; - let encoded_ep_name = ep_name.chars().map(|c| (c as u8)).collect::>(); - - let entry_point = quote! { - /// Returns a logical struct describing the entry point named `{ep_name}`. - #[inline] - #[allow(unsafe_code)] - pub fn #method_ident(&self) -> #ty { - unsafe { - #[allow(dead_code)] - static NAME: [u8; #ep_name_lenp1] = [ #( #encoded_ep_name ),* , 0]; - self.shader.#f_call } } - }; - - entry_point -} - -struct Element { - location: u32, - name: String, - format: String, - location_len: usize, + ShaderExecution::Fragment => quote! { ShaderExecution::Fragment }, + ShaderExecution::Compute => quote! { ShaderExecution::Compute }, + } } -fn write_interfaces( - spirv: &Spirv, - interface: &[Id], - ignore_first_array_in: bool, - ignore_first_array_out: bool, -) -> (TokenStream, TokenStream) { - let mut input_elements = vec![]; - let mut output_elements = vec![]; - - // Filling `input_elements` and `output_elements`. - for &interface in interface.iter() { - let interface_info = spirv.id(interface); +fn write_descriptor_requirements( + descriptor_requirements: &FnvHashMap<(u32, u32), DescriptorRequirements>, +) -> TokenStream { + let descriptor_requirements = descriptor_requirements.into_iter().map(|(loc, reqs)| { + let (set_num, binding_num) = loc; + let DescriptorRequirements { + descriptor_types, + descriptor_count, + format, + image_view_type, + multisampled, + mutable, + stages, + } = reqs; + + let descriptor_types = descriptor_types.into_iter().map(|ty| { + let ident = format_ident!("{}", format!("{:?}", ty)); + quote! { DescriptorType::#ident } + }); + let format = match format { + Some(format) => { + let ident = format_ident!("{}", format!("{:?}", format)); + quote! { Some(Format::#ident) } + } + None => quote! { None }, + }; + let image_view_type = match image_view_type { + Some(image_view_type) => { + let ident = format_ident!("{}", format!("{:?}", image_view_type)); + quote! { Some(ImageViewType::#ident) } + } + None => quote! { None }, + }; + let stages = { + let ShaderStages { + vertex, + tessellation_control, + tessellation_evaluation, + geometry, + fragment, + compute, + raygen, + any_hit, + closest_hit, + miss, + intersection, + callable, + } = stages; - match interface_info.instruction() { - &Instruction::Variable { - result_type_id, - result_id, - ref storage_class, - .. - } => { - if spirv_search::is_builtin(spirv, result_id) { - continue; + quote! { + ShaderStages { + vertex: #vertex, + tessellation_control: #tessellation_control, + tessellation_evaluation: #tessellation_evaluation, + geometry: #geometry, + fragment: #fragment, + compute: #compute, + raygen: #raygen, + any_hit: #any_hit, + closest_hit: #closest_hit, + miss: #miss, + intersection: #intersection, + callable: #callable, } + } + }; - let id_info = spirv.id(result_id); - - let (to_write, ignore_first_array) = match storage_class { - &StorageClass::Input => (&mut input_elements, ignore_first_array_in), - &StorageClass::Output => (&mut output_elements, ignore_first_array_out), - _ => continue, - }; + quote! { + ( + (#set_num, #binding_num), + DescriptorRequirements { + descriptor_types: vec![#(#descriptor_types),*], + descriptor_count: #descriptor_count, + format: #format, + image_view_type: #image_view_type, + multisampled: #multisampled, + mutable: #mutable, + stages: #stages, + }, + ), + } + }); - let name = match id_info - .iter_name() - .find_map(|instruction| match instruction { - Instruction::Name { name, .. } => Some(name.as_str()), - _ => None, - }) { - Some(name) => name, - None => continue, - }; + quote! { + [ + #( #descriptor_requirements )* + ] + } +} - let location = id_info - .iter_decoration() - .find_map(|instruction| match instruction { - Instruction::Decorate { - decoration: Decoration::Location { location }, - .. - } => Some(*location), - _ => None, - }) - .unwrap_or_else(|| { - panic!( - "Attribute `{}` (id {}) is missing a location", - name, result_id - ) - }); +fn write_push_constant_requirements( + push_constant_requirements: &Option, +) -> TokenStream { + match push_constant_requirements { + Some(PipelineLayoutPcRange { + offset, + size, + stages, + }) => { + let stages = { + let ShaderStages { + vertex, + tessellation_control, + tessellation_evaluation, + geometry, + fragment, + compute, + raygen, + any_hit, + closest_hit, + miss, + intersection, + callable, + } = stages; + + quote! { + ShaderStages { + vertex: #vertex, + tessellation_control: #tessellation_control, + tessellation_evaluation: #tessellation_evaluation, + geometry: #geometry, + fragment: #fragment, + compute: #compute, + raygen: #raygen, + any_hit: #any_hit, + closest_hit: #closest_hit, + miss: #miss, + intersection: #intersection, + callable: #callable, + } + } + }; - let (format, location_len) = - spirv_search::format_from_id(spirv, result_type_id, ignore_first_array); - to_write.push(Element { - location, - name: name.to_owned(), - format, - location_len, - }); + quote! { + Some(PipelineLayoutPcRange { + offset: #offset, + size: #size, + stages: #stages, + }) } - _ => (), } + None => quote! { + None + }, } - - ( - write_interface(&input_elements), - write_interface(&output_elements), - ) } -fn write_interface(attributes: &[Element]) -> TokenStream { - // Checking for overlapping elements. - for (offset, element1) in attributes.iter().enumerate() { - for element2 in attributes.iter().skip(offset + 1) { - if element1.location == element2.location - || (element1.location < element2.location - && element1.location + element1.location_len as u32 > element2.location) - || (element2.location < element1.location - && element2.location + element2.location_len as u32 > element1.location) - { - panic!( - "The locations of attributes `{}` (start={}, size={}) \ - and `{}` (start={}, size={}) overlap", - element1.name, - element1.location, - element1.location_len, - element2.name, - element2.location, - element2.location_len - ); +fn write_specialization_constant_requirements( + specialization_constant_requirements: &FnvHashMap, +) -> TokenStream { + let specialization_constant_requirements = specialization_constant_requirements + .into_iter() + .map(|(&constant_id, reqs)| { + let SpecializationConstantRequirements { size } = reqs; + quote! { + ( + #constant_id, + SpecializationConstantRequirements { + size: #size, + }, + ), } - } + }); + + quote! { + [ + #( #specialization_constant_requirements )* + ] } +} - let body = attributes - .iter() - .map(|element| { - assert!(element.location_len >= 1); - let loc = element.location; - let loc_end = element.location + element.location_len as u32; - let format = Ident::new(&element.format, Span::call_site()); - let name = &element.name; +fn write_interface(interface: &ShaderInterface) -> TokenStream { + let items = interface.elements().iter().map( + |ShaderInterfaceEntry { + location, + format, + name, + }| { + let start = location.start; + let end = location.end; + let format = format_ident!("{}", format!("{:?}", format)); quote! { - ::vulkano::pipeline::shader::ShaderInterfaceEntry { - location: #loc .. #loc_end, + ::vulkano::shader::ShaderInterfaceEntry { + location: #start .. #end, format: ::vulkano::format::Format::#format, name: Some(::std::borrow::Cow::Borrowed(#name)) }, } - }) - .collect::>(); + }, + ); quote! { - #[allow(unsafe_code)] unsafe { - ::vulkano::pipeline::shader::ShaderInterface::new_unchecked(vec![ - #( #body )* + ::vulkano::shader::ShaderInterface::new_unchecked(vec![ + #( #items )* ]) } } diff --git a/vulkano-shaders/src/lib.rs b/vulkano-shaders/src/lib.rs index 0842e80153..6392ea8e11 100644 --- a/vulkano-shaders/src/lib.rs +++ b/vulkano-shaders/src/lib.rs @@ -34,22 +34,14 @@ //! # Generated code overview //! //! The macro generates the following items of interest: -//! * The `Shader` struct. This contains a single field, `shader`, which is an -//! `Arc`. -//! * The `Shader::load` constructor. This method takes an `Arc`, calls -//! [`ShaderModule::new`][ShaderModule::new] with the passed-in device and the -//! shader data provided via the macro, and returns `Result`. +//! * The `load` constructor. This method takes an `Arc`, calls +//! [`ShaderModule::new`][ShaderModule::new] 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. **This function currently panics if a feature required by the shader -//! is not enabled on the device.** At some point in the future it will return -//! an error instead. -//! * The `Shader::module` method. This method simply returns a reference to the -//! `Arc` contained within the `shader` field of the `Shader` -//! struct. -//! * Methods for each entry point of the shader module. These construct and -//! return the various entry point structs that can be found in the -//! [vulkano::pipeline::shader][pipeline::shader] module. +//! 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` implementations. This //! behavior could be customized through the `types_meta` macro option(see below @@ -65,7 +57,7 @@ //! ``` //! # fn main() {} //! # use std::sync::Arc; -//! # use vulkano::OomError; +//! # use vulkano::shader::{ShaderCreationError, ShaderModule}; //! # use vulkano::device::Device; //! # //! # mod vs { @@ -85,13 +77,13 @@ //! // `vertex_shader` module with shader derive //! //! pub struct Shaders { -//! pub vs: vs::Shader +//! pub vs: Arc, //! } //! //! impl Shaders { -//! pub fn load(device: Arc) -> Result { +//! pub fn load(device: Arc) -> Result { //! Ok(Self { -//! vs: vs::Shader::load(device)?, +//! vs: vs::load(device)?, //! }) //! } //! } @@ -136,11 +128,10 @@ //! ## `shaders: { First: {src: "...", ty: "..."}, ... }` //! //! With these options the user can compile several shaders at a single macro invocation. -//! Each entry key is a prefix that will be put in front of generated `Shader` -//! struct(`FirstShader` in this case), and `SpecializationConstants` -//! struct(`FirstSpecializationConstants` 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 +//! Each entry key is a suffix that will be put after the name of the generated `load` function and +//! `SpecializationConstants` struct(`FirstSpecializationConstants` 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 values expecting `src`, `path`, `bytes`, and `ty` pairs same as above. @@ -248,11 +239,7 @@ use syn::{ }; mod codegen; -mod descriptor_sets; mod entry_point; -mod parse; -mod spec_consts; -mod spirv_search; mod structs; enum SourceKind { @@ -930,13 +917,13 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream { #[allow(unused_imports)] use vulkano::pipeline::layout::PipelineLayoutPcRange; #[allow(unused_imports)] - use vulkano::pipeline::shader::DescriptorRequirements; + use vulkano::shader::DescriptorRequirements; #[allow(unused_imports)] - use vulkano::pipeline::shader::ShaderStages; + use vulkano::shader::ShaderStages; #[allow(unused_imports)] - use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait; + use vulkano::shader::SpecializationConstants as SpecConstsTrait; #[allow(unused_imports)] - use vulkano::pipeline::shader::SpecializationMapEntry; + use vulkano::shader::SpecializationMapEntry; #[allow(unused_imports)] use vulkano::Version; diff --git a/vulkano-shaders/src/parse.rs b/vulkano-shaders/src/parse.rs deleted file mode 100644 index 19cd730ded..0000000000 --- a/vulkano-shaders/src/parse.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -#[cfg(test)] -mod test { - use vulkano::spirv::Spirv; - - #[test] - fn test() { - let data = include_bytes!("../tests/frag.spv"); - let insts: Vec<_> = data - .chunks(4) - .map(|c| { - ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32 - }) - .collect(); - - Spirv::new(&insts).unwrap(); - } -} diff --git a/vulkano-shaders/src/spec_consts.rs b/vulkano-shaders/src/spec_consts.rs deleted file mode 100644 index 5dbab57ce8..0000000000 --- a/vulkano-shaders/src/spec_consts.rs +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) 2017 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -use crate::TypesMeta; -use crate::{structs, RegisteredType}; -use proc_macro2::{Span, TokenStream}; -use std::borrow::Cow; -use std::collections::HashMap; -use std::mem; -use syn::Ident; -use vulkano::spirv::{Decoration, Instruction, Spirv}; - -/// Returns true if the document has specialization constants. -pub fn has_specialization_constants(spirv: &Spirv) -> bool { - for instruction in spirv.iter_global() { - match instruction { - &Instruction::SpecConstantTrue { .. } => return true, - &Instruction::SpecConstantFalse { .. } => return true, - &Instruction::SpecConstant { .. } => return true, - &Instruction::SpecConstantComposite { .. } => return true, - _ => (), - } - } - - false -} - -/// Writes the `SpecializationConstants` struct that contains the specialization constants and -/// implements the `Default` and the `vulkano::pipeline::shader::SpecializationConstants` traits. -pub(super) fn write_specialization_constants<'a>( - shader: &'a str, - spirv: &Spirv, - types_meta: &TypesMeta, - shared_constants: bool, - types_registry: &'a mut HashMap, -) -> TokenStream { - struct SpecConst { - name: String, - constant_id: u32, - rust_ty: TokenStream, - rust_signature: Cow<'static, str>, - rust_size: usize, - rust_alignment: u32, - default_value: TokenStream, - } - - let mut spec_consts = Vec::new(); - - for instruction in 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, - }; - - // Translate bool to u32 - let (rust_ty, rust_signature, rust_size, rust_alignment) = - match spirv.id(result_type_id).instruction() { - Instruction::TypeBool { .. } => ( - quote! {u32}, - Cow::from("u32"), - Some(mem::size_of::()), - mem::align_of::(), - ), - _ => structs::type_from_id(shader, spirv, result_type_id, types_meta), - }; - let rust_size = rust_size.expect("Found runtime-sized specialization constant"); - - let id_info = 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 name = match id_info - .iter_name() - .find_map(|instruction| match instruction { - Instruction::Name { name, .. } => Some(name.as_str()), - _ => None, - }) { - Some(name) => name.to_owned(), - None => format!("constant_{}", constant_id), - }; - - spec_consts.push(SpecConst { - name, - constant_id, - rust_ty, - rust_signature, - rust_size, - rust_alignment: rust_alignment as u32, - default_value, - }); - } - } - - let struct_name = Ident::new( - &format!( - "{}SpecializationConstants", - if shared_constants { "" } else { shader } - ), - Span::call_site(), - ); - - // For multi-constants mode registration mechanism skipped - if shared_constants { - let target_type = RegisteredType { - shader: shader.to_string(), - signature: spec_consts - .iter() - .map(|member| (member.name.to_string(), member.rust_signature.clone())) - .collect(), - }; - - let name = struct_name.to_string(); - - // Checking with Registry if this struct already registered by another shader, and if their - // signatures match. - if let Some(registered) = types_registry.get(name.as_str()) { - registered.assert_signatures(name.as_str(), &target_type); - - // If the struct already registered and matches this one, skip duplicate. - return quote! {}; - } - - debug_assert!(types_registry.insert(name, target_type).is_none()); - } - - let map_entries = { - let mut map_entries = Vec::new(); - let mut curr_offset = 0; - for spec_const in &spec_consts { - let constant_id = spec_const.constant_id; - let rust_size = spec_const.rust_size; - map_entries.push(quote! { - SpecializationMapEntry { - constant_id: #constant_id, - offset: #curr_offset, - size: #rust_size, - } - }); - - assert_ne!(spec_const.rust_size, 0); - curr_offset += spec_const.rust_size as u32; - curr_offset = - spec_const.rust_alignment * (1 + (curr_offset - 1) / spec_const.rust_alignment); - } - map_entries - }; - - let num_map_entries = map_entries.len(); - - let mut struct_members = vec![]; - let mut struct_member_defaults = vec![]; - for spec_const in spec_consts { - let name = Ident::new(&spec_const.name, Span::call_site()); - let rust_ty = spec_const.rust_ty; - let default_value = spec_const.default_value; - struct_members.push(quote! { pub #name: #rust_ty }); - struct_member_defaults.push(quote! { #name: #default_value }); - } - - quote! { - #[derive(Debug, Copy, Clone)] - #[allow(non_snake_case)] - #[repr(C)] - pub struct #struct_name { - #( #struct_members ),* - } - - impl Default for #struct_name { - fn default() -> #struct_name { - #struct_name { - #( #struct_member_defaults ),* - } - } - } - - unsafe impl SpecConstsTrait for #struct_name { - fn descriptors() -> &'static [SpecializationMapEntry] { - static DESCRIPTORS: [SpecializationMapEntry; #num_map_entries] = [ - #( #map_entries ),* - ]; - &DESCRIPTORS - } - } - } -} diff --git a/vulkano-shaders/src/spirv_search.rs b/vulkano-shaders/src/spirv_search.rs deleted file mode 100644 index d8907d91b5..0000000000 --- a/vulkano-shaders/src/spirv_search.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -use vulkano::spirv::{Decoration, Id, Instruction, Spirv}; - -/// Returns the vulkano `Format` and number of occupied locations from an id. -/// -/// If `ignore_first_array` is true, the function expects the outermost instruction to be -/// `OpTypeArray`. If it's the case, the OpTypeArray will be ignored. If not, the function will -/// panic. -pub fn format_from_id(spirv: &Spirv, searched: Id, ignore_first_array: bool) -> (String, usize) { - let id_info = spirv.id(searched); - - match id_info.instruction() { - &Instruction::TypeInt { - width, signedness, .. - } => { - assert!(!ignore_first_array); - let format = match (width, signedness) { - (8, 1) => "R8_SINT", - (8, 0) => "R8_UINT", - (16, 1) => "R16_SINT", - (16, 0) => "R16_UINT", - (32, 1) => "R32_SINT", - (32, 0) => "R32_UINT", - (64, 1) => "R64_SINT", - (64, 0) => "R64_UINT", - _ => panic!(), - }; - (format.to_string(), 1) - } - &Instruction::TypeFloat { width, .. } => { - assert!(!ignore_first_array); - let format = match width { - 32 => "R32_SFLOAT", - 64 => "R64_SFLOAT", - _ => panic!(), - }; - (format.to_string(), 1) - } - &Instruction::TypeVector { - component_type, - component_count, - .. - } => { - assert!(!ignore_first_array); - let (format, sz) = format_from_id(spirv, component_type, false); - assert!(format.starts_with("R32")); - assert_eq!(sz, 1); - let format = match component_count { - 1 => format, - 2 => format!("R32G32{}", &format[3..]), - 3 => format!("R32G32B32{}", &format[3..]), - 4 => format!("R32G32B32A32{}", &format[3..]), - _ => panic!("Found vector type with more than 4 elements"), - }; - (format, sz) - } - &Instruction::TypeMatrix { - column_type, - column_count, - .. - } => { - assert!(!ignore_first_array); - let (format, sz) = format_from_id(spirv, column_type, false); - (format, sz * column_count as usize) - } - &Instruction::TypeArray { - element_type, - length, - .. - } => { - if ignore_first_array { - format_from_id(spirv, element_type, false) - } else { - let (format, sz) = format_from_id(spirv, element_type, false); - let len = spirv - .instructions() - .iter() - .filter_map(|e| match e { - &Instruction::Constant { - result_id, - ref value, - .. - } if result_id == length => Some(value.clone()), - _ => None, - }) - .next() - .expect("failed to find array length"); - let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64); - (format, sz * len as usize) - } - } - &Instruction::TypePointer { ty, .. } => format_from_id(spirv, ty, ignore_first_array), - _ => panic!("Type #{} not found or invalid", searched), - } -} - -/// Returns true if a `BuiltIn` decorator is applied on an id. -pub fn is_builtin(spirv: &Spirv, id: Id) -> bool { - let id_info = spirv.id(id); - - if id_info.iter_decoration().any(|instruction| { - matches!( - instruction, - Instruction::Decorate { - decoration: Decoration::BuiltIn { .. }, - .. - } - ) - }) { - return true; - } - - if id_info - .iter_members() - .flat_map(|member_info| member_info.iter_decoration()) - .any(|instruction| { - matches!( - instruction, - Instruction::MemberDecorate { - decoration: Decoration::BuiltIn { .. }, - .. - } - ) - }) - { - return true; - } - - match id_info.instruction() { - Instruction::Variable { result_type_id, .. } => { - return is_builtin(spirv, *result_type_id); - } - Instruction::TypeArray { element_type, .. } => { - return is_builtin(spirv, *element_type); - } - Instruction::TypeRuntimeArray { element_type, .. } => { - return is_builtin(spirv, *element_type); - } - Instruction::TypeStruct { member_types, .. } => { - if member_types.iter().any(|ty| is_builtin(spirv, *ty)) { - return true; - } - } - Instruction::TypePointer { ty, .. } => { - return is_builtin(spirv, *ty); - } - _ => (), - } - - false -} diff --git a/vulkano-shaders/src/structs.rs b/vulkano-shaders/src/structs.rs index 1668155634..d955747245 100644 --- a/vulkano-shaders/src/structs.rs +++ b/vulkano-shaders/src/structs.rs @@ -8,13 +8,14 @@ // according to those terms. use crate::{RegisteredType, TypesMeta}; +use heck::CamelCase; use proc_macro2::{Span, TokenStream}; use std::borrow::Cow; use std::collections::HashMap; use std::mem; use syn::Ident; use syn::LitStr; -use vulkano::spirv::{Decoration, Id, Instruction, Spirv}; +use vulkano::shader::spirv::{Decoration, Id, Instruction, Spirv}; /// Translates all the structs that are contained in the SPIR-V document as Rust structs. pub(super) fn write_structs<'a>( @@ -667,3 +668,202 @@ pub(super) fn type_from_id( _ => panic!("Type #{} not found", searched), } } + +/// 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<'a>( + shader: &'a str, + spirv: &Spirv, + types_meta: &TypesMeta, + shared_constants: bool, + types_registry: &'a mut HashMap, +) -> TokenStream { + struct SpecConst { + name: String, + constant_id: u32, + rust_ty: TokenStream, + rust_signature: Cow<'static, str>, + rust_size: usize, + rust_alignment: u32, + default_value: TokenStream, + } + + let mut spec_consts = Vec::new(); + + for instruction in 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, + }; + + // Translate bool to u32 + let (rust_ty, rust_signature, rust_size, rust_alignment) = + match spirv.id(result_type_id).instruction() { + Instruction::TypeBool { .. } => ( + quote! {u32}, + Cow::from("u32"), + Some(mem::size_of::()), + mem::align_of::(), + ), + _ => type_from_id(shader, spirv, result_type_id, types_meta), + }; + let rust_size = rust_size.expect("Found runtime-sized specialization constant"); + + let id_info = 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 name = match id_info + .iter_name() + .find_map(|instruction| match instruction { + Instruction::Name { name, .. } => Some(name.as_str()), + _ => None, + }) { + Some(name) => name.to_owned(), + None => format!("constant_{}", constant_id), + }; + + spec_consts.push(SpecConst { + name, + constant_id, + rust_ty, + rust_signature, + rust_size, + rust_alignment: rust_alignment as u32, + default_value, + }); + } + } + + let struct_name = if shared_constants { + format_ident!("SpecializationConstants") + } else { + format_ident!("{}SpecializationConstants", shader.to_camel_case()) + }; + + // For multi-constants mode registration mechanism skipped + if shared_constants { + let target_type = RegisteredType { + shader: shader.to_string(), + signature: spec_consts + .iter() + .map(|member| (member.name.to_string(), member.rust_signature.clone())) + .collect(), + }; + + let name = struct_name.to_string(); + + // Checking with Registry if this struct already registered by another shader, and if their + // signatures match. + if let Some(registered) = types_registry.get(name.as_str()) { + registered.assert_signatures(name.as_str(), &target_type); + + // If the struct already registered and matches this one, skip duplicate. + return quote! {}; + } + + debug_assert!(types_registry.insert(name, target_type).is_none()); + } + + let map_entries = { + let mut map_entries = Vec::new(); + let mut curr_offset = 0; + for spec_const in &spec_consts { + let constant_id = spec_const.constant_id; + let rust_size = spec_const.rust_size; + map_entries.push(quote! { + SpecializationMapEntry { + constant_id: #constant_id, + offset: #curr_offset, + size: #rust_size, + } + }); + + assert_ne!(spec_const.rust_size, 0); + curr_offset += spec_const.rust_size as u32; + curr_offset = + spec_const.rust_alignment * (1 + (curr_offset - 1) / spec_const.rust_alignment); + } + map_entries + }; + + let num_map_entries = map_entries.len(); + + let mut struct_members = vec![]; + let mut struct_member_defaults = vec![]; + for spec_const in spec_consts { + let name = Ident::new(&spec_const.name, Span::call_site()); + let rust_ty = spec_const.rust_ty; + let default_value = spec_const.default_value; + struct_members.push(quote! { pub #name: #rust_ty }); + struct_member_defaults.push(quote! { #name: #default_value }); + } + + quote! { + #[derive(Debug, Copy, Clone)] + #[allow(non_snake_case)] + #[repr(C)] + pub struct #struct_name { + #( #struct_members ),* + } + + impl Default for #struct_name { + fn default() -> #struct_name { + #struct_name { + #( #struct_member_defaults ),* + } + } + } + + unsafe impl SpecConstsTrait for #struct_name { + fn descriptors() -> &'static [SpecializationMapEntry] { + static DESCRIPTORS: [SpecializationMapEntry; #num_map_entries] = [ + #( #map_entries ),* + ]; + &DESCRIPTORS + } + } + } +} diff --git a/vulkano/autogen/mod.rs b/vulkano/autogen/mod.rs index 7d1b36a00e..cdc40838be 100644 --- a/vulkano/autogen/mod.rs +++ b/vulkano/autogen/mod.rs @@ -22,7 +22,7 @@ use std::{ }; use vk_parse::{ EnumSpec, EnumsChild, Extension, ExtensionChild, Feature, InterfaceItem, Registry, - RegistryChild, Type, TypeCodeMarkup, TypeSpec, TypesChild, + RegistryChild, SpirvExtOrCap, Type, TypeCodeMarkup, TypeSpec, TypesChild, }; mod extensions; @@ -30,8 +30,9 @@ mod features; mod fns; mod formats; mod properties; -mod spirv; mod spirv_grammar; +mod spirv_parse; +mod spirv_reqs; pub fn autogen() { let registry = get_vk_registry("vk.xml"); @@ -43,7 +44,8 @@ pub fn autogen() { formats::write(&vk_data); fns::write(&vk_data); properties::write(&vk_data); - spirv::write(&spirv_grammar); + spirv_parse::write(&spirv_grammar); + spirv_reqs::write(&vk_data, &spirv_grammar); } fn write_file(file: impl AsRef, source: impl AsRef, content: impl Display) { @@ -84,6 +86,8 @@ pub struct VkRegistryData<'r> { pub extensions: IndexMap<&'r str, &'r Extension>, pub features: IndexMap<&'r str, &'r Feature>, pub formats: Vec<&'r str>, + pub spirv_capabilities: Vec<&'r SpirvExtOrCap>, + pub spirv_extensions: Vec<&'r SpirvExtOrCap>, pub types: HashMap<&'r str, (&'r Type, Vec<&'r str>)>, } @@ -97,6 +101,8 @@ impl<'r> VkRegistryData<'r> { features.values().map(|x| x.children.iter()).flatten(), extensions.values().map(|x| x.children.iter()).flatten(), ); + let spirv_capabilities = Self::get_spirv_capabilities(registry); + let spirv_extensions = Self::get_spirv_extensions(registry); let types = Self::get_types(®istry, &aliases, &features, &extensions); let header_version = Self::get_header_version(®istry); @@ -105,6 +111,8 @@ impl<'r> VkRegistryData<'r> { extensions, features, formats, + spirv_capabilities, + spirv_extensions, types, } } @@ -247,6 +255,34 @@ impl<'r> VkRegistryData<'r> { .collect() } + fn get_spirv_capabilities<'a>(registry: &'a Registry) -> Vec<&'a SpirvExtOrCap> { + registry + .0 + .iter() + .filter_map(|child| { + if let RegistryChild::SpirvCapabilities(capabilities) = child { + return Some(capabilities.children.iter()); + } + None + }) + .flatten() + .collect() + } + + fn get_spirv_extensions<'a>(registry: &'a Registry) -> Vec<&'a SpirvExtOrCap> { + registry + .0 + .iter() + .filter_map(|child| { + if let RegistryChild::SpirvExtensions(extensions) = child { + return Some(extensions.children.iter()); + } + None + }) + .flatten() + .collect() + } + fn get_types<'a>( registry: &'a Registry, aliases: &HashMap<&'a str, &'a str>, @@ -337,15 +373,6 @@ pub fn get_spirv_grammar + ?Sized>(path: &P) -> SpirvGrammar { suffix_key(&enumerant.enumerant), ) }); - operand_kind.enumerants.dedup_by_key(|enumerant| { - let value = enumerant - .value - .as_str() - .unwrap() - .strip_prefix("0x") - .unwrap(); - u32::from_str_radix(value, 16).unwrap() - }); }); grammar @@ -356,9 +383,6 @@ pub fn get_spirv_grammar + ?Sized>(path: &P) -> SpirvGrammar { operand_kind.enumerants.sort_by_key(|enumerant| { (enumerant.value.as_u64(), suffix_key(&enumerant.enumerant)) }); - operand_kind - .enumerants - .dedup_by_key(|enumerant| enumerant.value.as_u64()); }); grammar diff --git a/vulkano/autogen/spirv.rs b/vulkano/autogen/spirv_parse.rs similarity index 96% rename from vulkano/autogen/spirv.rs rename to vulkano/autogen/spirv_parse.rs index 46dc5678bd..8cf929f47d 100644 --- a/vulkano/autogen/spirv.rs +++ b/vulkano/autogen/spirv_parse.rs @@ -102,7 +102,7 @@ pub fn write(grammar: &SpirvGrammar) { let value_enum_output = value_enum_output(&value_enum_members(grammar)); write_file( - "spirv.rs", + "spirv_parse.rs", format!( "SPIR-V grammar version {}.{}.{}", grammar.major_version, grammar.minor_version, grammar.revision @@ -421,10 +421,19 @@ fn bit_enum_members(grammar: &SpirvGrammar) -> Vec<(Ident, Vec)> .iter() .filter(|operand_kind| operand_kind.category == "BitEnum") .map(|operand_kind| { + let mut previous_value = None; + let members = operand_kind .enumerants .iter() .filter_map(|enumerant| { + // Skip enumerants with the same value as the previous. + if previous_value == Some(&enumerant.value) { + return None; + } + + previous_value = Some(&enumerant.value); + let value = enumerant .value .as_str() @@ -552,10 +561,19 @@ fn value_enum_members(grammar: &SpirvGrammar) -> Vec<(Ident, Vec .iter() .filter(|operand_kind| operand_kind.category == "ValueEnum") .map(|operand_kind| { + let mut previous_value = None; + let members = operand_kind .enumerants .iter() - .map(|enumerant| { + .filter_map(|enumerant| { + // Skip enumerants with the same value as the previous. + if previous_value == Some(&enumerant.value) { + return None; + } + + previous_value = Some(&enumerant.value); + let name = match enumerant.enumerant.as_str() { "1D" => format_ident!("Dim1D"), "2D" => format_ident!("Dim2D"), @@ -576,11 +594,11 @@ fn value_enum_members(grammar: &SpirvGrammar) -> Vec<(Ident, Vec }) .collect(); - KindEnumMember { + Some(KindEnumMember { name, value: enumerant.value.as_u64().unwrap() as u32, parameters, - } + }) }) .collect(); diff --git a/vulkano/autogen/spirv_reqs.rs b/vulkano/autogen/spirv_reqs.rs new file mode 100644 index 0000000000..28cc613ca5 --- /dev/null +++ b/vulkano/autogen/spirv_reqs.rs @@ -0,0 +1,262 @@ +// Copyright (c) 2021 The Vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use super::{ + spirv_grammar::{SpirvGrammar, SpirvKindEnumerant}, + write_file, VkRegistryData, +}; +use heck::SnakeCase; +use indexmap::{map::Entry, IndexMap}; +use lazy_static::lazy_static; +use proc_macro2::{Ident, TokenStream}; +use quote::{format_ident, quote}; +use regex::Regex; +use vk_parse::SpirvExtOrCap; + +pub fn write(vk_data: &VkRegistryData, grammar: &SpirvGrammar) { + let grammar_enumerants = grammar + .operand_kinds + .iter() + .find(|operand_kind| operand_kind.kind == "Capability") + .unwrap() + .enumerants + .as_slice(); + let spirv_capabilities_output = spirv_reqs_output( + &spirv_capabilities_members(&vk_data.spirv_capabilities, grammar_enumerants), + false, + ); + let spirv_extensions_output = + spirv_reqs_output(&spirv_extensions_members(&vk_data.spirv_extensions), true); + write_file( + "spirv_reqs.rs", + format!("vk.xml header version {}", vk_data.header_version), + quote! { + #spirv_capabilities_output + #spirv_extensions_output + }, + ); +} + +#[derive(Clone, Debug)] +struct SpirvReqsMember { + name: String, + enables: Vec<(Enable, String)>, +} + +#[derive(Clone, Debug, PartialEq)] +enum Enable { + Core((String, String)), + Extension(Ident), + Feature(Ident), + Property((Ident, PropertyValue)), +} + +#[derive(Clone, Debug, PartialEq)] +enum PropertyValue { + Bool, + BoolMember(Ident), +} + +fn spirv_reqs_output(members: &[SpirvReqsMember], extension: bool) -> TokenStream { + let items = members.iter().map(|SpirvReqsMember { name, enables }| { + let arm = if extension { + quote! { #name } + } else { + let name = format_ident!("{}", name); + quote! { Capability::#name } + }; + + if enables.is_empty() { + quote! { + #arm => (), + } + } else { + let enables_items = enables.iter().map(|(enable, _description)| match enable { + Enable::Core((major, minor)) => { + let version = format_ident!("V{}_{}", major, minor); + quote! { + device.api_version() >= Version::#version + } + } + Enable::Extension(extension) => quote! { + device.enabled_extensions().#extension + }, + Enable::Feature(feature) => quote! { + device.enabled_features().#feature + }, + Enable::Property((name, value)) => { + let access = match value { + PropertyValue::Bool => quote! {}, + PropertyValue::BoolMember(member) => quote! { + .map(|x| x.#member) + }, + }; + + quote! { + device.physical_device().properties().#name #access .unwrap_or(false) + } + } + }); + + let description_items = enables.iter().map(|(_enable, description)| description); + + quote! { + #arm => { + if !(#(#enables_items)||*) { + return Err(ShaderSupportError::RequirementsNotMet(&[ + #(#description_items),* + ])); + } + }, + } + } + }); + + if extension { + quote! { + fn check_spirv_extension(device: &Device, extension: &str) -> Result<(), ShaderSupportError> { + match extension { + #(#items)* + _ => return Err(ShaderSupportError::NotSupportedByVulkan), + } + Ok(()) + } + } + } else { + quote! { + fn check_spirv_capability(device: &Device, capability: Capability) -> Result<(), ShaderSupportError> { + match capability { + #(#items)* + _ => return Err(ShaderSupportError::NotSupportedByVulkan), + } + Ok(()) + } + } + } +} + +fn spirv_capabilities_members( + capabilities: &[&SpirvExtOrCap], + grammar_enumerants: &[SpirvKindEnumerant], +) -> Vec { + let mut members: IndexMap = IndexMap::new(); + + for ext_or_cap in capabilities { + let mut enables: Vec<_> = ext_or_cap.enables.iter().filter_map(make_enable).collect(); + enables.dedup(); + + // Find the capability in the list of enumerants, then go backwards through the list to find + // the first enumerant with the same value. + let enumerant_pos = grammar_enumerants + .iter() + .position(|enumerant| enumerant.enumerant == ext_or_cap.name) + .unwrap(); + let enumerant_value = &grammar_enumerants[enumerant_pos].value; + + let name = if let Some(enumerant) = grammar_enumerants[..enumerant_pos] + .iter() + .rev() + .take_while(|enumerant| &enumerant.value == enumerant_value) + .last() + { + // Another enumerant was found with the same value, so this one is an alias. + &enumerant.enumerant + } else { + // No other enumerant was found, so this is its canonical name. + &ext_or_cap.name + }; + + match members.entry(name.clone()) { + Entry::Occupied(entry) => { + entry.into_mut().enables.extend(enables); + } + Entry::Vacant(entry) => { + entry.insert(SpirvReqsMember { + name: name.clone(), + enables, + }); + } + } + } + + members.into_iter().map(|(_, v)| v).collect() +} + +fn spirv_extensions_members(extensions: &[&SpirvExtOrCap]) -> Vec { + extensions + .iter() + .map(|ext_or_cap| { + let enables: Vec<_> = ext_or_cap.enables.iter().filter_map(make_enable).collect(); + + SpirvReqsMember { + name: ext_or_cap.name.clone(), + enables, + } + }) + .collect() +} + +lazy_static! { + static ref BIT: Regex = Regex::new(r"_BIT(?:_NV)?$").unwrap(); +} + +fn make_enable(enable: &vk_parse::Enable) -> Option<(Enable, String)> { + if matches!(enable, vk_parse::Enable::Version(version) if version == "VK_API_VERSION_1_0") { + return None; + } + + Some(match enable { + vk_parse::Enable::Version(version) => { + let version = version.strip_prefix("VK_API_VERSION_").unwrap(); + let (major, minor) = version.split_once('_').unwrap(); + + ( + Enable::Core((major.parse().unwrap(), minor.parse().unwrap())), + format!("Vulkan API version {}.{}", major, minor), + ) + } + vk_parse::Enable::Extension(extension) => { + let extension_name = extension.strip_prefix("VK_").unwrap().to_snake_case(); + + ( + Enable::Extension(format_ident!("{}", extension_name)), + format!("device extension `{}`", extension_name), + ) + } + vk_parse::Enable::Feature(feature) => { + let feature_name = feature.feature.to_snake_case(); + + ( + Enable::Feature(format_ident!("{}", feature_name)), + format!("feature `{}`", feature_name), + ) + } + vk_parse::Enable::Property(property) => { + let property_name = property.member.to_snake_case(); + + let (value, description) = if property.value == "VK_TRUE" { + (PropertyValue::Bool, format!("property `{}`", property_name)) + } else if let Some(member) = property.value.strip_prefix("VK_SUBGROUP_FEATURE_") { + let member = BIT.replace(member, "").to_snake_case(); + ( + PropertyValue::BoolMember(format_ident!("{}", member)), + format!("property `{}.{}`", property_name, member), + ) + } else { + unimplemented!() + }; + + ( + Enable::Property((format_ident!("{}", property_name), value)), + description, + ) + } + _ => unimplemented!(), + }) +} diff --git a/vulkano/spirv.core.grammar.json b/vulkano/spirv.core.grammar.json index 44e7298d08..67baec754e 100644 --- a/vulkano/spirv.core.grammar.json +++ b/vulkano/spirv.core.grammar.json @@ -2157,7 +2157,7 @@ { "kind" : "IdRef", "name" : "'Offset'" }, { "kind" : "IdRef", "name" : "'Count'" } ], - "capabilities" : [ "Shader" ] + "capabilities" : [ "Shader", "BitInstructions" ] }, { "opname" : "OpBitFieldSExtract", @@ -2170,7 +2170,7 @@ { "kind" : "IdRef", "name" : "'Offset'" }, { "kind" : "IdRef", "name" : "'Count'" } ], - "capabilities" : [ "Shader" ] + "capabilities" : [ "Shader", "BitInstructions" ] }, { "opname" : "OpBitFieldUExtract", @@ -2183,7 +2183,7 @@ { "kind" : "IdRef", "name" : "'Offset'" }, { "kind" : "IdRef", "name" : "'Count'" } ], - "capabilities" : [ "Shader" ] + "capabilities" : [ "Shader", "BitInstructions" ] }, { "opname" : "OpBitReverse", @@ -2194,7 +2194,7 @@ { "kind" : "IdResult" }, { "kind" : "IdRef", "name" : "'Base'" } ], - "capabilities" : [ "Shader" ] + "capabilities" : [ "Shader", "BitInstructions" ] }, { "opname" : "OpBitCount", @@ -4260,6 +4260,93 @@ "extensions" : [ "SPV_KHR_ray_tracing" ], "version" : "None" }, + { + "opname" : "OpSDotKHR", + "class" : "Arithmetic", + "opcode" : 4450, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProductKHR" ], + "version" : "None" + }, + { + "opname" : "OpUDotKHR", + "class" : "Arithmetic", + "opcode" : 4451, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProductKHR" ], + "version" : "None" + }, + { + "opname" : "OpSUDotKHR", + "class" : "Arithmetic", + "opcode" : 4452, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProductKHR" ], + "version" : "None" + }, + { + "opname" : "OpSDotAccSatKHR", + "class" : "Arithmetic", + "opcode" : 4453, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "IdRef", "name" : "'Accumulator'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProductKHR" ], + "version" : "None" + }, + { + "opname" : "OpUDotAccSatKHR", + "class" : "Arithmetic", + "opcode" : 4454, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "IdRef", "name" : "'Accumulator'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProductKHR" ], + "version" : "None" + }, + { + "opname" : "OpSUDotAccSatKHR", + "class" : "Arithmetic", + "opcode" : 4455, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "IdRef", "name" : "'Accumulator'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProductKHR" ], + "version" : "None" + }, { "opname" : "OpTypeRayQueryKHR", "class" : "Reserved", @@ -4552,7 +4639,7 @@ "operands" : [ { "kind" : "IdResultType" }, { "kind" : "IdResult" }, - { "kind" : "IdScope", "name" : "'Execution'" } + { "kind" : "IdScope", "name" : "'Scope'" } ], "capabilities" : [ "ShaderClockKHR" ], "extensions" : [ "SPV_KHR_shader_clock" ], @@ -4666,6 +4753,52 @@ "extensions" : [ "SPV_NV_ray_tracing" ], "version" : "None" }, + { + "opname" : "OpTraceMotionNV", + "class" : "Reserved", + "opcode" : 5338, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'Time'" }, + { "kind" : "IdRef", "name" : "'PayloadId'" } + ], + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, + { + "opname" : "OpTraceRayMotionNV", + "class" : "Reserved", + "opcode" : 5339, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'Time'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, { "opname" : "OpTypeAccelerationStructureNV", "class" : "Reserved", @@ -4811,6 +4944,88 @@ "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], "version" : "None" }, + { + "opname" : "OpConvertUToImageNV", + "class" : "Reserved", + "opcode" : 5391, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToSamplerNV", + "class" : "Reserved", + "opcode" : 5392, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertImageToUNV", + "class" : "Reserved", + "opcode" : 5393, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertSamplerToUNV", + "class" : "Reserved", + "opcode" : 5394, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToSampledImageNV", + "class" : "Reserved", + "opcode" : 5395, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertSampledImageToUNV", + "class" : "Reserved", + "opcode" : 5396, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpSamplerImageAddressingModeNV", + "class" : "Reserved", + "opcode" : 5397, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Bit Width'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, { "opname" : "OpSubgroupShuffleINTEL", "class" : "Group", @@ -5123,7 +5338,7 @@ "version" : "None" }, { - "opname" : "OpFunctionPointerINTEL", + "opname" : "OpConstFunctionPointerINTEL", "class" : "@exclude", "opcode" : 5600, "operands" : [ @@ -5148,6 +5363,101 @@ "extensions" : [ "SPV_INTEL_function_pointers" ], "version" : "None" }, + { + "opname" : "OpAsmTargetINTEL", + "class" : "@exclude", + "opcode" : 5609, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Asm target'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "opname" : "OpAsmINTEL", + "class" : "@exclude", + "opcode" : 5610, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Asm type'" }, + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Asm instructions'" }, + { "kind" : "LiteralString", "name" : "'Constraints'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "opname" : "OpAsmCallINTEL", + "class" : "@exclude", + "opcode" : 5611, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Asm'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "opname" : "OpAtomicFMinEXT", + "class" : "Atomic", + "opcode" : 5614, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "AtomicFloat16MinMaxEXT", "AtomicFloat32MinMaxEXT", "AtomicFloat64MinMaxEXT" ], + "version" : "None" + }, + { + "opname" : "OpAtomicFMaxEXT", + "class" : "Atomic", + "opcode" : 5615, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "AtomicFloat16MinMaxEXT", "AtomicFloat32MinMaxEXT", "AtomicFloat64MinMaxEXT" ], + "version" : "None" + }, + { + "opname" : "OpAssumeTrueKHR", + "class" : "Miscellaneous", + "opcode" : 5630, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" } + ], + "capabilities" : [ "ExpectAssumeKHR" ], + "extensions" : [ "SPV_KHR_expect_assume" ], + "version" : "None" + }, + { + "opname" : "OpExpectKHR", + "class" : "Miscellaneous", + "opcode" : 5631, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ExpectedValue'" } + ], + "capabilities" : [ "ExpectAssumeKHR" ], + "extensions" : [ "SPV_KHR_expect_assume" ], + "version" : "None" + }, { "opname" : "OpDecorateString", "class" : "Annotation", @@ -6712,60 +7022,1019 @@ "version" : "None" }, { - "opname" : "OpLoopControlINTEL", - "class" : "Reserved", - "opcode" : 5887, + "opname" : "OpVariableLengthArrayINTEL", + "class" : "@exclude", + "opcode" : 5818, "operands" : [ - { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Loop Control Parameters'" } + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Lenght'" } ], - "capabilities" : [ "UnstructuredLoopControlsINTEL" ], - "extensions" : [ "SPV_INTEL_unstructured_loop_controls" ], + "capabilities" : [ "VariableLengthArrayINTEL" ], "version" : "None" }, { - "opname" : "OpReadPipeBlockingINTEL", - "class" : "Pipe", - "opcode" : 5946, + "opname" : "OpSaveMemoryINTEL", + "class" : "@exclude", + "opcode" : 5819, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "VariableLengthArrayINTEL" ], + "version" : "None" + }, + { + "opname" : "OpRestoreMemoryINTEL", + "class" : "@exclude", + "opcode" : 5820, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "VariableLengthArrayINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinCosPiINTEL", + "class" : "@exclude", + "opcode" : 5840, "operands" : [ { "kind" : "IdResultType" }, { "kind" : "IdResult" }, - { "kind" : "IdRef", "name" : "'Packet Size'" }, - { "kind" : "IdRef", "name" : "'Packet Alignment'" } + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'FromSign'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } ], - "capabilities" : [ "BlockingPipesINTEL" ], - "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], "version" : "None" }, { - "opname" : "OpWritePipeBlockingINTEL", - "class" : "Pipe", - "opcode" : 5947, + "opname" : "OpArbitraryFloatCastINTEL", + "class" : "@exclude", + "opcode" : 5841, "operands" : [ { "kind" : "IdResultType" }, { "kind" : "IdResult" }, - { "kind" : "IdRef", "name" : "'Packet Size'" }, - { "kind" : "IdRef", "name" : "'Packet Alignment'" } + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } ], - "capabilities" : [ "BlockingPipesINTEL" ], - "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], "version" : "None" }, { - "opname" : "OpFPGARegINTEL", - "class" : "Reserved", - "opcode" : 5949, + "opname" : "OpArbitraryFloatCastFromIntINTEL", + "class" : "@exclude", + "opcode" : 5842, "operands" : [ { "kind" : "IdResultType" }, { "kind" : "IdResult" }, - { "kind" : "IdRef", "name" : "'Result'" }, - { "kind" : "IdRef", "name" : "'Input'" } + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'FromSign'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } ], - "capabilities" : [ "FPGARegINTEL" ], - "extensions" : [ "SPV_INTEL_fpga_reg" ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], "version" : "None" }, { - "opname" : "OpRayQueryGetRayTMinKHR", + "opname" : "OpArbitraryFloatCastToIntINTEL", + "class" : "@exclude", + "opcode" : 5843, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatAddINTEL", + "class" : "@exclude", + "opcode" : 5846, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSubINTEL", + "class" : "@exclude", + "opcode" : 5847, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatMulINTEL", + "class" : "@exclude", + "opcode" : 5848, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatDivINTEL", + "class" : "@exclude", + "opcode" : 5849, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatGTINTEL", + "class" : "@exclude", + "opcode" : 5850, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatGEINTEL", + "class" : "@exclude", + "opcode" : 5851, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLTINTEL", + "class" : "@exclude", + "opcode" : 5852, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLEINTEL", + "class" : "@exclude", + "opcode" : 5853, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatEQINTEL", + "class" : "@exclude", + "opcode" : 5854, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatRecipINTEL", + "class" : "@exclude", + "opcode" : 5855, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatRSqrtINTEL", + "class" : "@exclude", + "opcode" : 5856, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCbrtINTEL", + "class" : "@exclude", + "opcode" : 5857, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatHypotINTEL", + "class" : "@exclude", + "opcode" : 5858, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSqrtINTEL", + "class" : "@exclude", + "opcode" : 5859, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLogINTEL", + "class" : "@exclude", + "opcode" : 5860, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLog2INTEL", + "class" : "@exclude", + "opcode" : 5861, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLog10INTEL", + "class" : "@exclude", + "opcode" : 5862, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLog1pINTEL", + "class" : "@exclude", + "opcode" : 5863, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExpINTEL", + "class" : "@exclude", + "opcode" : 5864, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExp2INTEL", + "class" : "@exclude", + "opcode" : 5865, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExp10INTEL", + "class" : "@exclude", + "opcode" : 5866, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExpm1INTEL", + "class" : "@exclude", + "opcode" : 5867, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinINTEL", + "class" : "@exclude", + "opcode" : 5868, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCosINTEL", + "class" : "@exclude", + "opcode" : 5869, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinCosINTEL", + "class" : "@exclude", + "opcode" : 5870, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinPiINTEL", + "class" : "@exclude", + "opcode" : 5871, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCosPiINTEL", + "class" : "@exclude", + "opcode" : 5872, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatASinINTEL", + "class" : "@exclude", + "opcode" : 5873, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatASinPiINTEL", + "class" : "@exclude", + "opcode" : 5874, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatACosINTEL", + "class" : "@exclude", + "opcode" : 5875, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatACosPiINTEL", + "class" : "@exclude", + "opcode" : 5876, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatATanINTEL", + "class" : "@exclude", + "opcode" : 5877, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatATanPiINTEL", + "class" : "@exclude", + "opcode" : 5878, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatATan2INTEL", + "class" : "@exclude", + "opcode" : 5879, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatPowINTEL", + "class" : "@exclude", + "opcode" : 5880, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatPowRINTEL", + "class" : "@exclude", + "opcode" : 5881, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'M2'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatPowNINTEL", + "class" : "@exclude", + "opcode" : 5882, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpLoopControlINTEL", + "class" : "Reserved", + "opcode" : 5887, + "operands" : [ + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Loop Control Parameters'" } + ], + "capabilities" : [ "UnstructuredLoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_unstructured_loop_controls" ], + "version" : "None" + }, + { + "opname" : "OpFixedSqrtINTEL", + "class" : "@exclude", + "opcode" : 5923, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedRecipINTEL", + "class" : "@exclude", + "opcode" : 5924, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedRsqrtINTEL", + "class" : "@exclude", + "opcode" : 5925, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinINTEL", + "class" : "@exclude", + "opcode" : 5926, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedCosINTEL", + "class" : "@exclude", + "opcode" : 5927, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinCosINTEL", + "class" : "@exclude", + "opcode" : 5928, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinPiINTEL", + "class" : "@exclude", + "opcode" : 5929, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedCosPiINTEL", + "class" : "@exclude", + "opcode" : 5930, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinCosPiINTEL", + "class" : "@exclude", + "opcode" : 5931, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedLogINTEL", + "class" : "@exclude", + "opcode" : 5932, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedExpINTEL", + "class" : "@exclude", + "opcode" : 5933, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input Type'" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpPtrCastToCrossWorkgroupINTEL", + "class" : "@exclude", + "opcode" : 5934, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" + }, + { + "opname" : "OpCrossWorkgroupCastToPtrINTEL", + "class" : "@exclude", + "opcode" : 5938, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" + }, + { + "opname" : "OpReadPipeBlockingINTEL", + "class" : "Pipe", + "opcode" : 5946, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "BlockingPipesINTEL" ], + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "opname" : "OpWritePipeBlockingINTEL", + "class" : "Pipe", + "opcode" : 5947, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "BlockingPipesINTEL" ], + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "opname" : "OpFPGARegINTEL", + "class" : "Reserved", + "opcode" : 5949, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Result'" }, + { "kind" : "IdRef", "name" : "'Input'" } + ], + "capabilities" : [ "FPGARegINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_reg" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetRayTMinKHR", "class" : "Reserved", "opcode" : 6016, "operands" : [ @@ -7096,9 +8365,53 @@ { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, { "kind" : "IdRef", "name" : "'Value'" } ], - "capabilities" : [ "AtomicFloat32AddEXT", "AtomicFloat64AddEXT" ], + "capabilities" : [ "AtomicFloat16AddEXT", "AtomicFloat32AddEXT", "AtomicFloat64AddEXT" ], "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], "version" : "None" + }, + { + "opname" : "OpTypeBufferSurfaceINTEL", + "class" : "Type-Declaration", + "opcode" : 6086, + "operands" : [ + { "kind" : "IdResult" }, + { + "kind" : "AccessQualifier", + "name" : "'AccessQualifier'" + } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeStructContinuedINTEL", + "class" : "Type-Declaration", + "opcode" : 6090, + "operands" : [ + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ], + "capabilities" : [ "LongConstantCompositeINTEL" ], + "version" : "None" + }, + { + "opname" : "OpConstantCompositeContinuedINTEL", + "class" : "Constant-Creation", + "opcode" : 6091, + "operands" : [ + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "capabilities" : [ "LongConstantCompositeINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSpecConstantCompositeContinuedINTEL", + "class" : "Constant-Creation", + "opcode" : 6092, + "operands" : [ + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "capabilities" : [ "LongConstantCompositeINTEL" ], + "version" : "None" } ], "operand_kinds" : [ @@ -7244,6 +8557,13 @@ "enumerant" : "ZeroExtend", "value" : "0x2000", "version" : "1.4" + }, + { + "enumerant" : "Offsets", + "value" : "0x10000", + "parameters" : [ + { "kind" : "IdRef" } + ] } ] }, @@ -7257,28 +8577,35 @@ }, { "enumerant" : "NotNaN", - "value" : "0x0001", - "capabilities" : [ "Kernel" ] + "value" : "0x0001" }, { "enumerant" : "NotInf", - "value" : "0x0002", - "capabilities" : [ "Kernel" ] + "value" : "0x0002" }, { "enumerant" : "NSZ", - "value" : "0x0004", - "capabilities" : [ "Kernel" ] + "value" : "0x0004" }, { "enumerant" : "AllowRecip", - "value" : "0x0008", - "capabilities" : [ "Kernel" ] + "value" : "0x0008" }, { "enumerant" : "Fast", - "value" : "0x0010", - "capabilities" : [ "Kernel" ] + "value" : "0x0010" + }, + { + "enumerant" : "AllowContractFastINTEL", + "value" : "0x10000", + "capabilities" : [ "FPFastMathModeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "AllowReassocINTEL", + "value" : "0x20000", + "capabilities" : [ "FPFastMathModeINTEL" ], + "version" : "None" } ] }, @@ -7438,6 +8765,16 @@ "capabilities" : [ "FPGALoopControlsINTEL" ], "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], "version" : "None" + }, + { + "enumerant" : "NoFusionINTEL", + "value" : "0x800000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" } ] }, @@ -7464,6 +8801,12 @@ { "enumerant" : "Const", "value" : "0x0008" + }, + { + "enumerant" : "OptNoneINTEL", + "value" : "0x10000", + "capabilities" : [ "OptNoneINTEL" ], + "version" : "None" } ] }, @@ -7789,6 +9132,10 @@ { "enumerant" : "HLSL", "value" : 5 + }, + { + "enumerant" : "CPP_for_OpenCL", + "value" : 6 } ] }, @@ -8216,10 +9563,19 @@ "value" : 39, "capabilities" : [ "Kernel" ], "parameters" : [ - { "kind" : "IdRef", "name" : "'Local Size Hint'" } + { "kind" : "IdRef", "name" : "'x size hint'" }, + { "kind" : "IdRef", "name" : "'y size hint'" }, + { "kind" : "IdRef", "name" : "'z size hint'" } ], "version" : "1.2" }, + { + "enumerant" : "SubgroupUniformControlFlowKHR", + "value" : 4421, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_subgroup_uniform_control_flow" ], + "version" : "None" + }, { "enumerant" : "PostDepthCoverage", "value" : 4446, @@ -8364,6 +9720,51 @@ "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], "version" : "None" }, + { + "enumerant" : "SharedLocalMemorySizeINTEL", + "value" : 5618, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "RoundingModeRTPINTEL", + "value" : 5620, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, + { + "enumerant" : "RoundingModeRTNINTEL", + "value" : 5621, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FloatingPointModeALTINTEL", + "value" : 5622, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FloatingPointModeIEEEINTEL", + "value" : 5623, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, { "enumerant" : "MaxWorkgroupSizeINTEL", "value" : 5893, @@ -8402,6 +9803,15 @@ "capabilities" : [ "FPGAKernelAttributesINTEL" ], "extensions" : [ "SPV_INTEL_kernel_attributes" ], "version" : "None" + }, + { + "enumerant" : "SchedulerTargetFmaxMhzINTEL", + "value" : 5903, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'target_fmax'" } + ], + "capabilities" : [ "FPGAKernelAttributesINTEL" ], + "version" : "None" } ] }, @@ -8438,7 +9848,7 @@ { "enumerant" : "Private", "value" : 6, - "capabilities" : [ "Shader" ] + "capabilities" : [ "Shader", "VectorComputeINTEL" ] }, { "enumerant" : "Function", @@ -8577,6 +9987,24 @@ "extensions" : [ "SPV_INTEL_function_pointers" ], "capabilities" : [ "FunctionPointersINTEL" ], "version" : "None" + }, + { + "enumerant" : "DeviceOnlyINTEL", + "value" : 5936, + "extensions" : [ + "SPV_INTEL_usm_storage_classes" + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "HostOnlyINTEL", + "value" : 5937, + "extensions" : [ + "SPV_INTEL_usm_storage_classes" + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" } ] }, @@ -9101,6 +10529,126 @@ } ] }, + { + "category" : "ValueEnum", + "kind" : "FPDenormMode", + "enumerants" : [ + { + "enumerant" : "Preserve", + "value" : 0, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FlushToZero", + "value" : 1, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "QuantizationModes", + "enumerants" : [ + { + "enumerant" : "TRN", + "value" : 0, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "TRN_ZERO", + "value" : 1, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND", + "value" : 2, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_ZERO", + "value" : 3, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_INF", + "value" : 4, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_MIN_INF", + "value" : 5, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_CONV", + "value" : 6, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_CONV_ODD", + "value" : 7, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPOperationMode", + "enumerants" : [ + { + "enumerant" : "IEEE", + "value" : 0, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ALT", + "value" : 1, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "OverflowModes", + "enumerants" : [ + { + "enumerant" : "WRAP", + "value" : 0, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "SAT", + "value" : 1, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "SAT_ZERO", + "value" : 2, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "SAT_SYM", + "value" : 3, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + } + ] + }, { "category" : "ValueEnum", "kind" : "LinkageType", @@ -9114,6 +10662,13 @@ "enumerant" : "Import", "value" : 1, "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "LinkOnceODR", + "value" : 2, + "capabilities" : [ "Linkage" ], + "extensions" : [ "SPV_KHR_linkonce_odr" ], + "version" : "None" } ] }, @@ -9607,6 +11162,39 @@ "extensions" : [ "SPV_EXT_physical_storage_buffer" ], "version" : "1.5" }, + { + "enumerant" : "BindlessSamplerNV", + "value" : 5398, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BindlessImageNV", + "value" : 5399, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BoundSamplerNV", + "value" : 5400, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BoundImageNV", + "value" : 5401, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "SIMTCallINTEL", + "value" : 5599, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'N'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, { "enumerant" : "ReferencedIndirectlyINTEL", "value" : 5602, @@ -9614,6 +11202,57 @@ "extensions" : [ "SPV_INTEL_function_pointers" ], "version" : "None" }, + { + "enumerant" : "ClobberINTEL", + "value" : 5607, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Register'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "enumerant" : "SideEffectsINTEL", + "value" : 5608, + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeVariableINTEL", + "value" : 5624, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FuncParamIOKindINTEL", + "value" : 5625, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Kind'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeFunctionINTEL", + "value" : 5626, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "StackCallINTEL", + "value" : 5627, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "GlobalVariableOffsetINTEL", + "value" : 5628, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, { "enumerant" : "CounterBuffer", "value" : 5634, @@ -9657,6 +11296,26 @@ "extensions" : [ "SPV_GOOGLE_user_type" ], "version" : "None" }, + { + "enumerant" : "FunctionRoundingModeINTEL", + "value" : 5822, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" }, + { "kind" : "FPRoundingMode", "name" : "'FP Rounding Mode'" } + ], + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FunctionDenormModeINTEL", + "value" : 5823, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" }, + { "kind" : "FPDenormMode", "name" : "'FP Denorm Mode'" } + ], + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, { "enumerant" : "RegisterINTEL", "value" : 5825, @@ -9765,6 +11424,88 @@ "capabilities" : [ "FPGAMemoryAttributesINTEL" ], "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], "version" : "None" + }, + { + "enumerant" : "BurstCoalesceINTEL", + "value" : 5899, + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "CacheSizeINTEL", + "value" : 5900, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Cache Size in bytes'" } + ], + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "DontStaticallyCoalesceINTEL", + "value" : 5901, + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "PrefetchINTEL", + "value" : 5902, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Prefetcher Size in bytes'" } + ], + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "StallEnableINTEL", + "value" : 5905, + "capabilities" : [ "FPGAClusterAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FuseLoopsInFunctionINTEL", + "value" : 5907, + "capabilities" : [ "LoopFuseINTEL" ], + "version" : "None" + }, + { + "enumerant" : "BufferLocationINTEL", + "value" : 5921, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Buffer Location ID'" } + ], + "capabilities" : [ "FPGABufferLocationINTEL" ], + "version" : "None" + }, + { + "enumerant" : "IOPipeStorageINTEL", + "value" : 5944, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'IO Pipe ID'" } + ], + "capabilities" : [ "IOPipesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FunctionFloatingPointModeINTEL", + "value" : 6080, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" }, + { "kind" : "FPOperationMode", "name" : "'FP Operation Mode'" } + ], + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "SingleElementVectorINTEL", + "value" : 6085, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeCallableFunctionINTEL", + "value" : 6087, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" } ] }, @@ -9978,55 +11719,55 @@ "version" : "1.3" }, { - "enumerant" : "SubgroupGeMask", - "value" : 4417, + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], "version" : "1.3" }, { - "enumerant" : "SubgroupGtMask", - "value" : 4418, + "enumerant" : "SubgroupGeMask", + "value" : 4417, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], "version" : "1.3" }, { - "enumerant" : "SubgroupLeMask", - "value" : 4419, + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], "version" : "1.3" }, { - "enumerant" : "SubgroupLtMask", - "value" : 4420, + "enumerant" : "SubgroupGtMask", + "value" : 4418, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], "version" : "1.3" }, { - "enumerant" : "SubgroupEqMaskKHR", - "value" : 4416, + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], "extensions" : [ "SPV_KHR_shader_ballot" ], "version" : "1.3" }, { - "enumerant" : "SubgroupGeMaskKHR", - "value" : 4417, + "enumerant" : "SubgroupLeMask", + "value" : 4419, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], - "extensions" : [ "SPV_KHR_shader_ballot" ], "version" : "1.3" }, { - "enumerant" : "SubgroupGtMaskKHR", - "value" : 4418, + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], "extensions" : [ "SPV_KHR_shader_ballot" ], "version" : "1.3" }, { - "enumerant" : "SubgroupLeMaskKHR", - "value" : 4419, + "enumerant" : "SubgroupLtMask", + "value" : 4420, "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], - "extensions" : [ "SPV_KHR_shader_ballot" ], "version" : "1.3" }, { @@ -10449,6 +12190,13 @@ "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], "version" : "None" }, + { + "enumerant" : "CurrentRayTimeNV", + "value" : 5334, + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, { "enumerant" : "IncomingRayFlagsNV", "value" : 5351, @@ -10981,6 +12729,27 @@ "extensions" : [ "SPV_KHR_shader_draw_parameters" ], "version" : "1.3" }, + { + "enumerant" : "WorkgroupMemoryExplicitLayoutKHR", + "value" : 4428, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_workgroup_memory_explicit_layout" ], + "version" : "None" + }, + { + "enumerant" : "WorkgroupMemoryExplicitLayout8BitAccessKHR", + "value" : 4429, + "capabilities" : [ "WorkgroupMemoryExplicitLayoutKHR" ], + "extensions" : [ "SPV_KHR_workgroup_memory_explicit_layout" ], + "version" : "None" + }, + { + "enumerant" : "WorkgroupMemoryExplicitLayout16BitAccessKHR", + "value" : 4430, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_workgroup_memory_explicit_layout" ], + "version" : "None" + }, { "enumerant" : "SubgroupVoteKHR", "value" : 4431, @@ -11460,6 +13229,13 @@ "extensions" : [ "SPV_NV_ray_tracing" ], "version" : "None" }, + { + "enumerant" : "RayTracingMotionBlurNV", + "value" : 5341, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, { "enumerant" : "VulkanMemoryModel", "value" : 5345, @@ -11551,6 +13327,12 @@ "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], "version" : "None" }, + { + "enumerant" : "BindlessTextureNV", + "value" : 5390, + "extensions" : [ "SPV_NV_bindless_texture" ], + "version" : "None" + }, { "enumerant" : "SubgroupShuffleINTEL", "value" : 5568, @@ -11575,6 +13357,18 @@ "extensions" : [ "SPV_INTEL_media_block_io" ], "version" : "None" }, + { + "enumerant" : "RoundToInfinityINTEL", + "value" : 5582, + "extensions" : [ "SPV_INTEL_float_controls2" ], + "version" : "None" + }, + { + "enumerant" : "FloatingPointModeINTEL", + "value" : 5583, + "extensions" : [ "SPV_INTEL_float_controls2" ], + "version" : "None" + }, { "enumerant" : "IntegerFunctions2INTEL", "value" : 5584, @@ -11594,6 +13388,49 @@ "extensions" : [ "SPV_INTEL_function_pointers" ], "version" : "None" }, + { + "enumerant" : "AsmINTEL", + "value" : 5606, + "extensions" : [ "SPV_INTEL_inline_assembly" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat32MinMaxEXT", + "value" : 5612, + "extensions" : [ "SPV_EXT_shader_atomic_float_min_max" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat64MinMaxEXT", + "value" : 5613, + "extensions" : [ "SPV_EXT_shader_atomic_float_min_max" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat16MinMaxEXT", + "value" : 5616, + "extensions" : [ "SPV_EXT_shader_atomic_float_min_max" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeINTEL", + "value" : 5617, + "capabilities" : [ "VectorAnyINTEL" ], + "extensions" : [ "SPV_INTEL_vector_compute" ], + "version" : "None" + }, + { + "enumerant" : "VectorAnyINTEL", + "value" : 5619, + "extensions" : [ "SPV_INTEL_vector_compute" ], + "version" : "None" + }, + { + "enumerant" : "ExpectAssumeKHR", + "value" : 5629, + "extensions" : [ "SPV_KHR_expect_assume" ], + "version" : "None" + }, { "enumerant" : "SubgroupAvcMotionEstimationINTEL", "value" : 5696, @@ -11612,12 +13449,43 @@ "extensions" : [ "SPV_INTEL_device_side_avc_motion_estimation" ], "version" : "None" }, + { + "enumerant" : "VariableLengthArrayINTEL", + "value" : 5817, + "extensions" : [ "SPV_INTEL_variable_length_array" ], + "version" : "None" + }, + { + "enumerant" : "FunctionFloatControlINTEL", + "value" : 5821, + "extensions" : [ "SPV_INTEL_float_controls2" ], + "version" : "None" + }, { "enumerant" : "FPGAMemoryAttributesINTEL", "value" : 5824, "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], "version" : "None" }, + { + "enumerant" : "FPFastMathModeINTEL", + "value" : 5837, + "capabilities" : [ "Kernel" ], + "extensions" : [ "SPV_INTEL_fp_fast_math_mode" ], + "version" : "None" + }, + { + "enumerant" : "ArbitraryPrecisionIntegersINTEL", + "value" : 5844, + "extensions" : [ "SPV_INTEL_arbitrary_precision_integers" ], + "version" : "None" + }, + { + "enumerant" : "ArbitraryPrecisionFloatingPointINTEL", + "value" : 5845, + "extensions" : [ "SPV_INTEL_arbitrary_precision_floating_point" ], + "version" : "None" + }, { "enumerant" : "UnstructuredLoopControlsINTEL", "value" : 5886, @@ -11642,6 +13510,48 @@ "extensions" : [ "SPV_INTEL_kernel_attributes" ], "version" : "None" }, + { + "enumerant" : "FPGAMemoryAccessesINTEL", + "value" : 5898, + "extensions" : [ "SPV_INTEL_fpga_memory_accesses" ], + "version" : "None" + }, + { + "enumerant" : "FPGAClusterAttributesINTEL", + "value" : 5904, + "extensions" : [ "SPV_INTEL_fpga_cluster_attributes" ], + "version" : "None" + }, + { + "enumerant" : "LoopFuseINTEL", + "value" : 5906, + "extensions" : [ "SPV_INTEL_loop_fuse" ], + "version" : "None" + }, + { + "enumerant" : "FPGABufferLocationINTEL", + "value" : 5920, + "extensions" : [ "SPV_INTEL_fpga_buffer_location" ], + "version" : "None" + }, + { + "enumerant" : "ArbitraryPrecisionFixedPointINTEL", + "value" : 5922, + "extensions" : [ "SPV_INTEL_arbitrary_precision_fixed_point" ], + "version" : "None" + }, + { + "enumerant" : "USMStorageClassesINTEL", + "value" : 5935, + "extensions" : [ "SPV_INTEL_usm_storage_classes" ], + "version" : "None" + }, + { + "enumerant" : "IOPipesINTEL", + "value" : 5943, + "extensions" : [ "SPV_INTEL_io_pipes" ], + "version" : "None" + }, { "enumerant" : "BlockingPipesINTEL", "value" : 5945, @@ -11654,6 +13564,37 @@ "extensions" : [ "SPV_INTEL_fpga_reg" ], "version" : "None" }, + { + "enumerant" : "DotProductInputAllKHR", + "value" : 6016, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "None" + }, + { + "enumerant" : "DotProductInput4x8BitKHR", + "value" : 6017, + "capabilities" : [ "Int8" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "None" + }, + { + "enumerant" : "DotProductInput4x8BitPackedKHR", + "value" : 6018, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "None" + }, + { + "enumerant" : "DotProductKHR", + "value" : 6019, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "None" + }, + { + "enumerant" : "BitInstructions", + "value" : 6025, + "extensions" : [ "SPV_KHR_bit_instructions" ], + "version" : "None" + }, { "enumerant" : "AtomicFloat32AddEXT", "value" : 6033, @@ -11667,6 +13608,31 @@ "capabilities" : [ "Shader" ], "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], "version" : "None" + }, + { + "enumerant" : "LongConstantCompositeINTEL", + "value" : 6089, + "extensions" : [ "SPV_INTEL_long_constant_composite" ], + "version" : "None" + }, + { + "enumerant" : "OptNoneINTEL", + "value" : 6094, + "extensions" : [ "SPV_INTEL_optnone" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat16AddEXT", + "value" : 6095, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_atomic_float16_add" ], + "version" : "None" + }, + { + "enumerant" : "DebugInfoModuleINTEL", + "value" : 6114, + "extensions" : [ "SPV_INTEL_debug_module" ], + "version" : "None" } ] }, @@ -11730,6 +13696,18 @@ } ] }, + { + "category" : "ValueEnum", + "kind" : "PackedVectorFormat", + "enumerants" : [ + { + "enumerant" : "PackedVectorFormat4x8BitKHR", + "value" : 0, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "None" + } + ] + }, { "category" : "Id", "kind" : "IdResultType", diff --git a/vulkano/src/command_buffer/auto.rs b/vulkano/src/command_buffer/auto.rs index 17b1085882..ebea1e6614 100644 --- a/vulkano/src/command_buffer/auto.rs +++ b/vulkano/src/command_buffer/auto.rs @@ -60,7 +60,6 @@ use crate::pipeline::input_assembly::PrimitiveTopology; use crate::pipeline::layout::PipelineLayout; use crate::pipeline::rasterization::CullMode; use crate::pipeline::rasterization::FrontFace; -use crate::pipeline::shader::ShaderStages; use crate::pipeline::vertex::VertexBuffersCollection; use crate::pipeline::viewport::Scissor; use crate::pipeline::viewport::Viewport; @@ -79,6 +78,7 @@ use crate::render_pass::Framebuffer; use crate::render_pass::LoadOp; use crate::render_pass::Subpass; use crate::sampler::Filter; +use crate::shader::ShaderStages; use crate::sync::AccessCheckError; use crate::sync::AccessFlags; use crate::sync::GpuFuture; diff --git a/vulkano/src/command_buffer/synced/commands.rs b/vulkano/src/command_buffer/synced/commands.rs index 22334cffd4..78a4de4839 100644 --- a/vulkano/src/command_buffer/synced/commands.rs +++ b/vulkano/src/command_buffer/synced/commands.rs @@ -35,8 +35,6 @@ use crate::image::ImageLayout; use crate::pipeline::depth_stencil::StencilFaces; use crate::pipeline::input_assembly::IndexType; use crate::pipeline::layout::PipelineLayout; -use crate::pipeline::shader::DescriptorRequirements; -use crate::pipeline::shader::ShaderStages; use crate::pipeline::vertex::VertexInput; use crate::pipeline::viewport::Scissor; use crate::pipeline::viewport::Viewport; @@ -50,6 +48,8 @@ use crate::query::QueryResultFlags; use crate::render_pass::Framebuffer; use crate::render_pass::LoadOp; use crate::sampler::Filter; +use crate::shader::DescriptorRequirements; +use crate::shader::ShaderStages; use crate::sync::AccessFlags; use crate::sync::Event; use crate::sync::PipelineMemoryAccess; diff --git a/vulkano/src/command_buffer/synced/mod.rs b/vulkano/src/command_buffer/synced/mod.rs index 6177bb9a97..4dcecd0193 100644 --- a/vulkano/src/command_buffer/synced/mod.rs +++ b/vulkano/src/command_buffer/synced/mod.rs @@ -494,9 +494,9 @@ mod tests { use crate::descriptor_set::PersistentDescriptorSet; use crate::device::Device; use crate::pipeline::layout::PipelineLayout; - use crate::pipeline::shader::ShaderStages; use crate::pipeline::PipelineBindPoint; use crate::sampler::Sampler; + use crate::shader::ShaderStages; use crate::sync::GpuFuture; use std::sync::Arc; diff --git a/vulkano/src/command_buffer/sys.rs b/vulkano/src/command_buffer/sys.rs index 40ed92b01d..de290a6288 100644 --- a/vulkano/src/command_buffer/sys.rs +++ b/vulkano/src/command_buffer/sys.rs @@ -38,7 +38,6 @@ use crate::pipeline::input_assembly::PrimitiveTopology; use crate::pipeline::layout::PipelineLayout; use crate::pipeline::rasterization::CullMode; use crate::pipeline::rasterization::FrontFace; -use crate::pipeline::shader::ShaderStages; use crate::pipeline::viewport::Scissor; use crate::pipeline::viewport::Viewport; use crate::pipeline::ComputePipeline; @@ -51,6 +50,7 @@ use crate::query::QueryResultElement; use crate::query::QueryResultFlags; use crate::render_pass::Framebuffer; use crate::sampler::Filter; +use crate::shader::ShaderStages; use crate::sync::AccessFlags; use crate::sync::Event; use crate::sync::PipelineStage; diff --git a/vulkano/src/command_buffer/validity/descriptor_sets.rs b/vulkano/src/command_buffer/validity/descriptor_sets.rs index 802f14c4ef..b4bc5d270d 100644 --- a/vulkano/src/command_buffer/validity/descriptor_sets.rs +++ b/vulkano/src/command_buffer/validity/descriptor_sets.rs @@ -13,8 +13,8 @@ use crate::format::Format; use crate::image::view::ImageViewType; use crate::image::ImageViewAbstract; use crate::image::SampleCount; -use crate::pipeline::shader::DescriptorRequirements; use crate::pipeline::Pipeline; +use crate::shader::DescriptorRequirements; use std::error; use std::fmt; use std::sync::Arc; diff --git a/vulkano/src/command_buffer/validity/dynamic_state.rs b/vulkano/src/command_buffer/validity/dynamic_state.rs index 344f0246f5..ad7018abd0 100644 --- a/vulkano/src/command_buffer/validity/dynamic_state.rs +++ b/vulkano/src/command_buffer/validity/dynamic_state.rs @@ -9,10 +9,10 @@ use crate::command_buffer::synced::CommandBufferState; use crate::pipeline::input_assembly::PrimitiveTopology; -use crate::pipeline::shader::ShaderStage; use crate::pipeline::DynamicState; use crate::pipeline::GraphicsPipeline; use crate::pipeline::PartialStateMode; +use crate::shader::ShaderStage; use std::error; use std::fmt; diff --git a/vulkano/src/descriptor_set/layout/desc.rs b/vulkano/src/descriptor_set/layout/desc.rs index 4d2bdfa4d5..fe9037ec02 100644 --- a/vulkano/src/descriptor_set/layout/desc.rs +++ b/vulkano/src/descriptor_set/layout/desc.rs @@ -41,9 +41,9 @@ //! in a render pass. Can only give access to the same pixel as the one you're processing. //! -use crate::pipeline::shader::DescriptorRequirements; -use crate::pipeline::shader::ShaderStages; use crate::sampler::Sampler; +use crate::shader::DescriptorRequirements; +use crate::shader::ShaderStages; use smallvec::SmallVec; use std::cmp; use std::error; diff --git a/vulkano/src/descriptor_set/layout/sys.rs b/vulkano/src/descriptor_set/layout/sys.rs index d0497feda2..86b264335b 100644 --- a/vulkano/src/descriptor_set/layout/sys.rs +++ b/vulkano/src/descriptor_set/layout/sys.rs @@ -439,7 +439,7 @@ mod tests { use crate::descriptor_set::layout::DescriptorSetLayout; use crate::descriptor_set::layout::DescriptorType; use crate::descriptor_set::pool::DescriptorsCount; - use crate::pipeline::shader::ShaderStages; + use crate::shader::ShaderStages; use std::iter; #[test] diff --git a/vulkano/src/descriptor_set/pool/standard.rs b/vulkano/src/descriptor_set/pool/standard.rs index 1d82172a53..3a077b3936 100644 --- a/vulkano/src/descriptor_set/pool/standard.rs +++ b/vulkano/src/descriptor_set/pool/standard.rs @@ -183,7 +183,7 @@ mod tests { use crate::descriptor_set::layout::DescriptorType; use crate::descriptor_set::pool::DescriptorPool; use crate::descriptor_set::pool::StdDescriptorPool; - use crate::pipeline::shader::ShaderStages; + use crate::shader::ShaderStages; use std::iter; use std::sync::Arc; diff --git a/vulkano/src/descriptor_set/pool/sys.rs b/vulkano/src/descriptor_set/pool/sys.rs index 1626c0d73c..5689612885 100644 --- a/vulkano/src/descriptor_set/pool/sys.rs +++ b/vulkano/src/descriptor_set/pool/sys.rs @@ -365,7 +365,7 @@ mod tests { use crate::descriptor_set::layout::DescriptorType; use crate::descriptor_set::pool::DescriptorsCount; use crate::descriptor_set::pool::UnsafeDescriptorPool; - use crate::pipeline::shader::ShaderStages; + use crate::shader::ShaderStages; use std::iter; #[test] diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index 03a62a3ae8..b7e165f3c6 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -853,6 +853,7 @@ pub struct SubgroupFeatures { pub shuffle_relative: bool, pub clustered: bool, pub quad: bool, + pub partitioned: bool, } impl From for SubgroupFeatures { @@ -867,6 +868,7 @@ impl From for SubgroupFeatures { shuffle_relative: val.intersects(ash::vk::SubgroupFeatureFlags::SHUFFLE_RELATIVE), clustered: val.intersects(ash::vk::SubgroupFeatureFlags::CLUSTERED), quad: val.intersects(ash::vk::SubgroupFeatureFlags::QUAD), + partitioned: val.intersects(ash::vk::SubgroupFeatureFlags::PARTITIONED_NV), } } } diff --git a/vulkano/src/device/properties.rs b/vulkano/src/device/properties.rs index 13aab9598c..de8c82fc08 100644 --- a/vulkano/src/device/properties.rs +++ b/vulkano/src/device/properties.rs @@ -5,8 +5,8 @@ use crate::device::physical::{ use crate::device::DeviceExtensions; use crate::image::{SampleCount, SampleCounts}; use crate::instance::InstanceExtensions; -use crate::pipeline::shader::ShaderStages; use crate::render_pass::ResolveModes; +use crate::shader::ShaderStages; use crate::DeviceSize; use crate::Version; use std::convert::TryInto; diff --git a/vulkano/src/format.rs b/vulkano/src/format.rs index aa7f71a30b..fbdbc44b58 100644 --- a/vulkano/src/format.rs +++ b/vulkano/src/format.rs @@ -95,7 +95,7 @@ use crate::device::physical::PhysicalDevice; use crate::image::ImageAspects; -use crate::spirv::ImageFormat; +use crate::shader::spirv::ImageFormat; use crate::DeviceSize; use crate::VulkanObject; use half::f16; diff --git a/vulkano/src/lib.rs b/vulkano/src/lib.rs index decbf2e0c1..16599a6710 100644 --- a/vulkano/src/lib.rs +++ b/vulkano/src/lib.rs @@ -91,7 +91,7 @@ pub mod pipeline; pub mod query; pub mod range_set; pub mod sampler; -pub mod spirv; +pub mod shader; pub mod swapchain; pub mod sync; diff --git a/vulkano/src/pipeline/cache.rs b/vulkano/src/pipeline/cache.rs index cb74ad71ba..e1c0a6835f 100644 --- a/vulkano/src/pipeline/cache.rs +++ b/vulkano/src/pipeline/cache.rs @@ -247,10 +247,9 @@ impl Drop for PipelineCache { #[cfg(test)] mod tests { use crate::pipeline::cache::PipelineCache; - use crate::pipeline::shader::ShaderModule; - use crate::pipeline::shader::SpecializationConstants; + use crate::shader::ShaderModule; use crate::pipeline::ComputePipeline; - use std::{ffi::CStr, sync::Arc}; + use std::sync::Arc; #[test] fn merge_self_forbidden() { @@ -283,22 +282,18 @@ 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::new(device.clone(), &MODULE).unwrap() - }; - - let shader = unsafe { - static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main" - module.compute_entry_point( - CStr::from_ptr(NAME.as_ptr() as *const _), - [], - None, - <()>::descriptors(), - ) + ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() }; let pipeline = Arc::new( - ComputePipeline::new(device.clone(), &shader, &(), Some(cache.clone()), |_| {}) - .unwrap(), + ComputePipeline::new( + device.clone(), + module.entry_point("main").unwrap(), + &(), + Some(cache.clone()), + |_| {}, + ) + .unwrap(), ); let cache_data = cache.get_data().unwrap(); @@ -329,17 +324,7 @@ 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::new(device.clone(), &MODULE).unwrap() - }; - - let first_shader = unsafe { - static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main" - first_module.compute_entry_point( - CStr::from_ptr(NAME.as_ptr() as *const _), - [], - None, - <()>::descriptors(), - ) + ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() }; let second_module = unsafe { @@ -370,23 +355,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::new(device.clone(), &SECOND_MODULE).unwrap() - }; - - let second_shader = unsafe { - static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main" - second_module.compute_entry_point( - CStr::from_ptr(NAME.as_ptr() as *const _), - [], - None, - <()>::descriptors(), - ) + ShaderModule::from_bytes(device.clone(), &SECOND_MODULE).unwrap() }; let pipeline = Arc::new( ComputePipeline::new( device.clone(), - &first_shader, + first_module.entry_point("main").unwrap(), &(), Some(cache.clone()), |_| {}, @@ -399,7 +374,7 @@ mod tests { let second_pipeline = Arc::new( ComputePipeline::new( device.clone(), - &second_shader, + second_module.entry_point("main").unwrap(), &(), Some(cache.clone()), |_| {}, @@ -438,29 +413,31 @@ 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::new(device.clone(), &MODULE).unwrap() - }; - - let shader = unsafe { - static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main" - module.compute_entry_point( - CStr::from_ptr(NAME.as_ptr() as *const _), - [], - None, - <()>::descriptors(), - ) + ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() }; let pipeline = Arc::new( - ComputePipeline::new(device.clone(), &shader, &(), Some(cache.clone()), |_| {}) - .unwrap(), + ComputePipeline::new( + device.clone(), + module.entry_point("main").unwrap(), + &(), + Some(cache.clone()), + |_| {}, + ) + .unwrap(), ); let cache_data = cache.get_data().unwrap(); let second_pipeline = Arc::new( - ComputePipeline::new(device.clone(), &shader, &(), Some(cache.clone()), |_| {}) - .unwrap(), + ComputePipeline::new( + device.clone(), + module.entry_point("main").unwrap(), + &(), + Some(cache.clone()), + |_| {}, + ) + .unwrap(), ); let second_data = cache.get_data().unwrap(); diff --git a/vulkano/src/pipeline/compute_pipeline.rs b/vulkano/src/pipeline/compute_pipeline.rs index 69e9ef2741..fa62710f93 100644 --- a/vulkano/src/pipeline/compute_pipeline.rs +++ b/vulkano/src/pipeline/compute_pipeline.rs @@ -14,8 +14,9 @@ use crate::pipeline::cache::PipelineCache; use crate::pipeline::layout::{ PipelineLayout, PipelineLayoutCreationError, PipelineLayoutSupersetError, }; -use crate::pipeline::shader::{ComputeEntryPoint, DescriptorRequirements, SpecializationConstants}; use crate::pipeline::{Pipeline, PipelineBindPoint}; +use crate::shader::{DescriptorRequirements, EntryPoint, SpecializationConstants}; +use crate::DeviceSize; use crate::Error; use crate::OomError; use crate::VulkanObject; @@ -51,8 +52,8 @@ impl ComputePipeline { /// to add dynamic buffers or immutable samplers. pub fn new( device: Arc, - shader: &ComputeEntryPoint, - spec_constants: &Css, + shader: EntryPoint, + specialization_constants: &Css, cache: Option>, func: F, ) -> Result, ComputePipelineCreationError> @@ -63,22 +64,22 @@ impl ComputePipeline { let mut descriptor_set_layout_descs = DescriptorSetDesc::from_requirements(shader.descriptor_requirements()); func(&mut descriptor_set_layout_descs); - let descriptor_set_layouts = descriptor_set_layout_descs .iter() .map(|desc| Ok(DescriptorSetLayout::new(device.clone(), desc.clone())?)) .collect::, PipelineLayoutCreationError>>()?; + let layout = PipelineLayout::new( device.clone(), descriptor_set_layouts, - shader.push_constant_range().iter().cloned(), + shader.push_constant_requirements().cloned(), )?; unsafe { ComputePipeline::with_unchecked_pipeline_layout( device, shader, - spec_constants, + specialization_constants, layout, cache, ) @@ -91,27 +92,37 @@ impl ComputePipeline { /// uses. pub fn with_pipeline_layout( device: Arc, - shader: &ComputeEntryPoint, - spec_constants: &Css, + shader: EntryPoint, + specialization_constants: &Css, layout: Arc, cache: Option>, ) -> Result, ComputePipelineCreationError> where Css: SpecializationConstants, { - if Css::descriptors() != shader.spec_constants() { - return Err(ComputePipelineCreationError::IncompatibleSpecializationConstants); + let spec_descriptors = Css::descriptors(); + + 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)?; + + if map_entry.size as DeviceSize != reqs.size { + return Err(ComputePipelineCreationError::IncompatibleSpecializationConstants); + } } + layout.ensure_compatible_with_shader( + shader.descriptor_requirements(), + shader.push_constant_requirements(), + )?; + unsafe { - layout.ensure_compatible_with_shader( - shader.descriptor_requirements(), - shader.push_constant_range(), - )?; ComputePipeline::with_unchecked_pipeline_layout( device, shader, - spec_constants, + specialization_constants, layout, cache, ) @@ -122,8 +133,8 @@ impl ComputePipeline { /// superset of what the shader expects. pub unsafe fn with_unchecked_pipeline_layout( device: Arc, - shader: &ComputeEntryPoint, - spec_constants: &Css, + shader: EntryPoint, + specialization_constants: &Css, layout: Arc, cache: Option>, ) -> Result, ComputePipelineCreationError> @@ -137,8 +148,8 @@ impl ComputePipeline { 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(spec_constants), - p_data: spec_constants as *const Css as *const _, + data_size: mem::size_of_val(specialization_constants), + p_data: specialization_constants as *const Css as *const _, }; let stage = ash::vk::PipelineShaderStageCreateInfo { @@ -366,25 +377,21 @@ mod tests { use crate::buffer::CpuAccessibleBuffer; use crate::command_buffer::AutoCommandBufferBuilder; use crate::command_buffer::CommandBufferUsage; - use crate::descriptor_set::layout::DescriptorType; use crate::descriptor_set::PersistentDescriptorSet; - use crate::pipeline::shader::DescriptorRequirements; - use crate::pipeline::shader::ShaderModule; - use crate::pipeline::shader::ShaderStages; - use crate::pipeline::shader::SpecializationConstants; - use crate::pipeline::shader::SpecializationMapEntry; use crate::pipeline::ComputePipeline; use crate::pipeline::Pipeline; use crate::pipeline::PipelineBindPoint; + use crate::shader::ShaderModule; + use crate::shader::SpecializationConstants; + use crate::shader::SpecializationMapEntry; use crate::sync::now; use crate::sync::GpuFuture; - use std::ffi::CStr; // TODO: test for basic creation // TODO: test for pipeline layout error #[test] - fn spec_constants() { + fn specialization_constants() { // This test checks whether specialization constants work. // It executes a single compute shader (one invocation) that writes the value of a spec. // constant to a buffer. The buffer content is then checked for the right value. @@ -428,31 +435,7 @@ mod tests { 0, 5, 0, 0, 0, 65, 0, 5, 0, 12, 0, 0, 0, 13, 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 62, 0, 3, 0, 13, 0, 0, 0, 11, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0, ]; - ShaderModule::new(device.clone(), &MODULE).unwrap() - }; - - let shader = unsafe { - static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main" - module.compute_entry_point( - CStr::from_ptr(NAME.as_ptr() as *const _), - [( - (0, 0), - DescriptorRequirements { - descriptor_types: vec![DescriptorType::StorageBuffer], - descriptor_count: 1, - format: None, - image_view_type: None, - multisampled: false, - mutable: false, - stages: ShaderStages { - compute: true, - ..ShaderStages::none() - }, - }, - )], - None, - SpecConsts::descriptors(), - ) + ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() }; #[derive(Debug, Copy, Clone)] @@ -474,7 +457,7 @@ mod tests { let pipeline = ComputePipeline::new( device.clone(), - &shader, + module.entry_point("main").unwrap(), &SpecConsts { VALUE: 0x12345678 }, None, |_| {}, diff --git a/vulkano/src/pipeline/graphics_pipeline/builder.rs b/vulkano/src/pipeline/graphics_pipeline/builder.rs index a326daa6a2..d25a940c04 100644 --- a/vulkano/src/pipeline/graphics_pipeline/builder.rs +++ b/vulkano/src/pipeline/graphics_pipeline/builder.rs @@ -25,15 +25,15 @@ use crate::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology}; use crate::pipeline::layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutPcRange}; use crate::pipeline::multisample::MultisampleState; use crate::pipeline::rasterization::{CullMode, FrontFace, PolygonMode, RasterizationState}; -use crate::pipeline::shader::{ - DescriptorRequirements, GraphicsEntryPoint, GraphicsShaderType, ShaderStage, - SpecializationConstants, -}; use crate::pipeline::tessellation::TessellationState; use crate::pipeline::vertex::{BuffersDefinition, Vertex, VertexDefinition, VertexInputRate}; use crate::pipeline::viewport::{Scissor, Viewport, ViewportState}; use crate::pipeline::{DynamicState, PartialStateMode, StateMode}; use crate::render_pass::Subpass; +use crate::shader::{ + DescriptorRequirements, EntryPoint, ShaderExecution, ShaderStage, SpecializationConstants, +}; +use crate::DeviceSize; use crate::VulkanObject; use fnv::FnvHashMap; use smallvec::SmallVec; @@ -49,10 +49,10 @@ pub struct GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, T subpass: Option, cache: Option>, - vertex_shader: Option<(GraphicsEntryPoint<'vs>, Vss)>, + vertex_shader: Option<(EntryPoint<'vs>, Vss)>, tessellation_shaders: Option>, - geometry_shader: Option<(GraphicsEntryPoint<'gs>, Gss)>, - fragment_shader: Option<(GraphicsEntryPoint<'fs>, Fss)>, + geometry_shader: Option<(EntryPoint<'gs>, Gss)>, + fragment_shader: Option<(EntryPoint<'fs>, Fss)>, vertex_definition: Vdef, input_assembly_state: InputAssemblyState, @@ -68,8 +68,8 @@ pub struct GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, T // Additional parameters if tessellation is used. #[derive(Clone, Debug)] struct TessellationShaders<'tcs, 'tes, Tcss, Tess> { - tessellation_control_shader: (GraphicsEntryPoint<'tcs>, Tcss), - tessellation_evaluation_shader: (GraphicsEntryPoint<'tes>, Tess), + tessellation_control_shader: (EntryPoint<'tcs>, Tcss), + tessellation_evaluation_shader: (EntryPoint<'tes>, Tess), } impl @@ -141,7 +141,7 @@ where F: FnOnce(&mut [DescriptorSetDesc]), { let (descriptor_set_layout_descs, push_constant_ranges) = { - let stages: SmallVec<[&GraphicsEntryPoint; 5]> = std::array::IntoIter::new([ + let stages: SmallVec<[&EntryPoint; 5]> = std::array::IntoIter::new([ self.vertex_shader.as_ref().map(|s| &s.0), self.tessellation_shaders .as_ref() @@ -156,7 +156,7 @@ where .collect(); for (output, input) in stages.iter().zip(stages.iter().skip(1)) { - if let Err(err) = input.input().matches(output.output()) { + if let Err(err) = input.input_interface().matches(output.output_interface()) { return Err(GraphicsPipelineCreationError::ShaderStagesMismatch(err)); } } @@ -201,7 +201,7 @@ where // Vertex and a subrange available to Fragment, like [0, 8) let mut range_map = HashMap::new(); for stage in stages.iter() { - if let Some(range) = stage.push_constant_range() { + if let Some(range) = stage.push_constant_requirements() { match range_map.entry((range.offset, range.size)) { Entry::Vacant(entry) => { entry.insert(range.stages); @@ -258,7 +258,7 @@ where let shader = &self.vertex_shader.as_ref().unwrap().0; pipeline_layout.ensure_compatible_with_shader( shader.descriptor_requirements(), - shader.push_constant_range(), + shader.push_constant_requirements(), )?; for (loc, reqs) in shader.descriptor_requirements() { match descriptor_requirements.entry(loc) { @@ -277,7 +277,7 @@ where let shader = &geometry_shader.0; pipeline_layout.ensure_compatible_with_shader( shader.descriptor_requirements(), - shader.push_constant_range(), + shader.push_constant_requirements(), )?; for (loc, reqs) in shader.descriptor_requirements() { match descriptor_requirements.entry(loc) { @@ -297,7 +297,7 @@ where let shader = &tess.tessellation_control_shader.0; pipeline_layout.ensure_compatible_with_shader( shader.descriptor_requirements(), - shader.push_constant_range(), + shader.push_constant_requirements(), )?; for (loc, reqs) in shader.descriptor_requirements() { match descriptor_requirements.entry(loc) { @@ -316,7 +316,7 @@ where let shader = &tess.tessellation_evaluation_shader.0; pipeline_layout.ensure_compatible_with_shader( shader.descriptor_requirements(), - shader.push_constant_range(), + shader.push_constant_requirements(), )?; for (loc, reqs) in shader.descriptor_requirements() { match descriptor_requirements.entry(loc) { @@ -336,7 +336,7 @@ where let shader = &fragment_shader.0; pipeline_layout.ensure_compatible_with_shader( shader.descriptor_requirements(), - shader.push_constant_range(), + shader.push_constant_requirements(), )?; for (loc, reqs) in shader.descriptor_requirements() { match descriptor_requirements.entry(loc) { @@ -352,7 +352,7 @@ where // Check that the subpass can accept the output of the fragment shader. // TODO: If there is no fragment shader, what should be checked then? The previous stage? - if !subpass.is_compatible_with(shader.output()) { + if !subpass.is_compatible_with(shader.output_interface()) { return Err(GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible); } } @@ -369,13 +369,20 @@ where // Creating the specialization constants of the various stages. let vertex_shader_specialization = { - let shader = self.vertex_shader.as_ref().unwrap(); + let (shader, constants) = self.vertex_shader.as_ref().unwrap(); let spec_descriptors = Vss::descriptors(); - if spec_descriptors != shader.0.spec_constants() { - return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + + for (constant_id, reqs) in shader.specialization_constant_requirements() { + let map_entry = spec_descriptors + .iter() + .find(|desc| desc.constant_id == constant_id) + .ok_or(GraphicsPipelineCreationError::IncompatibleSpecializationConstants)?; + + if map_entry.size as DeviceSize != reqs.size { + return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + } } - let constants = &shader.1; ash::vk::SpecializationInfo { map_entry_count: spec_descriptors.len() as u32, p_map_entries: spec_descriptors.as_ptr() as *const _, @@ -386,13 +393,24 @@ where let tess_shader_specialization = if let Some(ref tess) = self.tessellation_shaders { let tcs_spec = { - let shader = &tess.tessellation_control_shader; + let (shader, constants) = &tess.tessellation_control_shader; let spec_descriptors = Tcss::descriptors(); - if spec_descriptors != shader.0.spec_constants() { - return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + + for (constant_id, reqs) in shader.specialization_constant_requirements() { + let map_entry = spec_descriptors + .iter() + .find(|desc| desc.constant_id == constant_id) + .ok_or( + GraphicsPipelineCreationError::IncompatibleSpecializationConstants, + )?; + + if map_entry.size as DeviceSize != reqs.size { + return Err( + GraphicsPipelineCreationError::IncompatibleSpecializationConstants, + ); + } } - let constants = &shader.1; ash::vk::SpecializationInfo { map_entry_count: spec_descriptors.len() as u32, p_map_entries: spec_descriptors.as_ptr() as *const _, @@ -401,13 +419,24 @@ where } }; let tes_spec = { - let shader = &tess.tessellation_evaluation_shader; + let (shader, constants) = &tess.tessellation_evaluation_shader; let spec_descriptors = Tess::descriptors(); - if spec_descriptors != shader.0.spec_constants() { - return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + + for (constant_id, reqs) in shader.specialization_constant_requirements() { + let map_entry = spec_descriptors + .iter() + .find(|desc| desc.constant_id == constant_id) + .ok_or( + GraphicsPipelineCreationError::IncompatibleSpecializationConstants, + )?; + + if map_entry.size as DeviceSize != reqs.size { + return Err( + GraphicsPipelineCreationError::IncompatibleSpecializationConstants, + ); + } } - let constants = &shader.1; ash::vk::SpecializationInfo { map_entry_count: spec_descriptors.len() as u32, p_map_entries: spec_descriptors.as_ptr() as *const _, @@ -420,13 +449,22 @@ where None }; - let geometry_shader_specialization = if let Some(ref shader) = self.geometry_shader { + let geometry_shader_specialization = if let Some((shader, constants)) = + &self.geometry_shader + { let spec_descriptors = Gss::descriptors(); - if spec_descriptors != shader.0.spec_constants() { - return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + + for (constant_id, reqs) in shader.specialization_constant_requirements() { + let map_entry = spec_descriptors + .iter() + .find(|desc| desc.constant_id == constant_id) + .ok_or(GraphicsPipelineCreationError::IncompatibleSpecializationConstants)?; + + if map_entry.size as DeviceSize != reqs.size { + return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + } } - let constants = &shader.1; Some(ash::vk::SpecializationInfo { map_entry_count: spec_descriptors.len() as u32, p_map_entries: spec_descriptors.as_ptr() as *const _, @@ -437,13 +475,22 @@ where None }; - let fragment_shader_specialization = if let Some(ref shader) = self.fragment_shader { + let fragment_shader_specialization = if let Some((shader, constants)) = + &self.fragment_shader + { let spec_descriptors = Fss::descriptors(); - if spec_descriptors != shader.0.spec_constants() { - return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + + for (constant_id, reqs) in shader.specialization_constant_requirements() { + let map_entry = spec_descriptors + .iter() + .find(|desc| desc.constant_id == constant_id) + .ok_or(GraphicsPipelineCreationError::IncompatibleSpecializationConstants)?; + + if map_entry.size as DeviceSize != reqs.size { + return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + } } - let constants = &shader.1; Some(ash::vk::SpecializationInfo { map_entry_count: spec_descriptors.len() as u32, p_map_entries: spec_descriptors.as_ptr() as *const _, @@ -459,8 +506,8 @@ where let stages = { let mut stages = SmallVec::<[_; 5]>::new(); - match self.vertex_shader.as_ref().unwrap().0.ty() { - GraphicsShaderType::Vertex => {} + match self.vertex_shader.as_ref().unwrap().0.execution() { + ShaderExecution::Vertex => {} _ => return Err(GraphicsPipelineCreationError::WrongShaderType), }; @@ -490,13 +537,13 @@ where }); } - match tess.tessellation_control_shader.0.ty() { - GraphicsShaderType::TessellationControl => {} + match tess.tessellation_control_shader.0.execution() { + ShaderExecution::TessellationControl => {} _ => return Err(GraphicsPipelineCreationError::WrongShaderType), }; - match tess.tessellation_evaluation_shader.0.ty() { - GraphicsShaderType::TessellationEvaluation => {} + match tess.tessellation_evaluation_shader.0.execution() { + ShaderExecution::TessellationEvaluation => {} _ => return Err(GraphicsPipelineCreationError::WrongShaderType), }; @@ -539,13 +586,13 @@ where }); } - let shader_execution_mode = match geometry_shader.0.ty() { - GraphicsShaderType::Geometry(mode) => mode, + let input = match geometry_shader.0.execution() { + ShaderExecution::Geometry(execution) => execution.input, _ => return Err(GraphicsPipelineCreationError::WrongShaderType), }; if let PartialStateMode::Fixed(topology) = self.input_assembly_state.topology { - if !shader_execution_mode.matches(topology) { + if !input.is_compatible_with(topology) { return Err( GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader, ); @@ -572,8 +619,8 @@ where } if let Some(ref fragment_shader) = self.fragment_shader { - match fragment_shader.0.ty() { - GraphicsShaderType::Fragment => {} + match fragment_shader.0.execution() { + ShaderExecution::Fragment => {} _ => return Err(GraphicsPipelineCreationError::WrongShaderType), }; @@ -595,7 +642,7 @@ where // Vertex input state let vertex_input = self .vertex_definition - .definition(self.vertex_shader.as_ref().unwrap().0.input())?; + .definition(self.vertex_shader.as_ref().unwrap().0.input_interface())?; let (binding_descriptions, binding_divisor_descriptions) = { let mut binding_descriptions = SmallVec::<[_; 8]>::new(); @@ -1077,7 +1124,7 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> #[inline] pub fn vertex_shader<'vs2, Vss2>( self, - shader: GraphicsEntryPoint<'vs2>, + shader: EntryPoint<'vs2>, specialization_constants: Vss2, ) -> GraphicsPipelineBuilder<'vs2, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss2, Tcss, Tess, Gss, Fss> where @@ -1109,10 +1156,10 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> #[inline] pub fn tessellation_shaders<'tcs2, 'tes2, Tcss2, Tess2>( self, - tessellation_control_shader: GraphicsEntryPoint<'tcs2>, - tessellation_control_shader_spec_constants: Tcss2, - tessellation_evaluation_shader: GraphicsEntryPoint<'tes2>, - tessellation_evaluation_shader_spec_constants: Tess2, + 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, @@ -1124,13 +1171,10 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> vertex_shader: self.vertex_shader, tessellation_shaders: Some(TessellationShaders { - tessellation_control_shader: ( - tessellation_control_shader, - tessellation_control_shader_spec_constants, - ), + tessellation_control_shader: (control_shader, control_specialization_constants), tessellation_evaluation_shader: ( - tessellation_evaluation_shader, - tessellation_evaluation_shader_spec_constants, + evaluation_shader, + evaluation_specialization_constants, ), }), geometry_shader: self.geometry_shader, @@ -1153,7 +1197,7 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> #[inline] pub fn geometry_shader<'gs2, Gss2>( self, - shader: GraphicsEntryPoint<'gs2>, + shader: EntryPoint<'gs2>, specialization_constants: Gss2, ) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs2, 'fs, Vdef, Vss, Tcss, Tess, Gss2, Fss> where @@ -1187,7 +1231,7 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> #[inline] pub fn fragment_shader<'fs2, Fss2>( self, - shader: GraphicsEntryPoint<'fs2>, + shader: EntryPoint<'fs2>, specialization_constants: Fss2, ) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs2, Vdef, Vss, Tcss, Tess, Gss, Fss2> where diff --git a/vulkano/src/pipeline/graphics_pipeline/creation_error.rs b/vulkano/src/pipeline/graphics_pipeline/creation_error.rs index ea9e16d035..ee315eca41 100644 --- a/vulkano/src/pipeline/graphics_pipeline/creation_error.rs +++ b/vulkano/src/pipeline/graphics_pipeline/creation_error.rs @@ -9,8 +9,8 @@ use crate::pipeline::layout::PipelineLayoutCreationError; use crate::pipeline::layout::PipelineLayoutSupersetError; -use crate::pipeline::shader::ShaderInterfaceMismatchError; use crate::pipeline::vertex::IncompatibleVertexDefinitionError; +use crate::shader::ShaderInterfaceMismatchError; use crate::Error; use crate::OomError; use std::error; diff --git a/vulkano/src/pipeline/graphics_pipeline/mod.rs b/vulkano/src/pipeline/graphics_pipeline/mod.rs index d8c527adce..ec0ee9581e 100644 --- a/vulkano/src/pipeline/graphics_pipeline/mod.rs +++ b/vulkano/src/pipeline/graphics_pipeline/mod.rs @@ -17,12 +17,12 @@ use crate::pipeline::input_assembly::InputAssemblyState; use crate::pipeline::layout::PipelineLayout; use crate::pipeline::multisample::MultisampleState; use crate::pipeline::rasterization::RasterizationState; -use crate::pipeline::shader::{DescriptorRequirements, ShaderStage}; use crate::pipeline::tessellation::TessellationState; use crate::pipeline::vertex::{BuffersDefinition, VertexInput}; use crate::pipeline::viewport::ViewportState; use crate::pipeline::{DynamicState, Pipeline, PipelineBindPoint}; use crate::render_pass::Subpass; +use crate::shader::{DescriptorRequirements, ShaderStage}; use crate::VulkanObject; use fnv::FnvHashMap; use std::fmt; diff --git a/vulkano/src/pipeline/layout/limits_check.rs b/vulkano/src/pipeline/layout/limits_check.rs index ee06b47154..f3861e5c57 100644 --- a/vulkano/src/pipeline/layout/limits_check.rs +++ b/vulkano/src/pipeline/layout/limits_check.rs @@ -13,7 +13,7 @@ use crate::descriptor_set::layout::DescriptorSetLayout; use crate::descriptor_set::layout::DescriptorType; use crate::device::Properties; use crate::pipeline::layout::PipelineLayoutPcRange; -use crate::pipeline::shader::ShaderStages; +use crate::shader::ShaderStages; use std::error; use std::fmt; use std::sync::Arc; diff --git a/vulkano/src/pipeline/layout/sys.rs b/vulkano/src/pipeline/layout/sys.rs index adf1f3951c..fd79770909 100644 --- a/vulkano/src/pipeline/layout/sys.rs +++ b/vulkano/src/pipeline/layout/sys.rs @@ -15,8 +15,8 @@ use crate::descriptor_set::layout::DescriptorSetLayoutError; use crate::device::Device; use crate::device::DeviceOwned; use crate::pipeline::layout::PipelineLayoutLimitsError; -use crate::pipeline::shader::DescriptorRequirements; -use crate::pipeline::shader::ShaderStages; +use crate::shader::DescriptorRequirements; +use crate::shader::ShaderStages; use crate::Error; use crate::OomError; use crate::VulkanObject; @@ -217,7 +217,7 @@ impl PipelineLayout { pub fn ensure_compatible_with_shader<'a>( &self, descriptor_requirements: impl IntoIterator, - push_constant_range: &Option, + push_constant_range: Option<&PipelineLayoutPcRange>, ) -> Result<(), PipelineLayoutSupersetError> { for ((set_num, binding_num), reqs) in descriptor_requirements.into_iter() { let descriptor_desc = self @@ -475,7 +475,7 @@ impl fmt::Display for PipelineLayoutSupersetError { } /// Description of a range of the push constants of a pipeline layout. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct PipelineLayoutPcRange { /// Offset in bytes from the start of the push constants to this range. pub offset: u32, diff --git a/vulkano/src/pipeline/mod.rs b/vulkano/src/pipeline/mod.rs index 90b50a76ed..ac40712b61 100644 --- a/vulkano/src/pipeline/mod.rs +++ b/vulkano/src/pipeline/mod.rs @@ -93,7 +93,6 @@ pub mod input_assembly; pub mod layout; pub mod multisample; pub mod rasterization; -pub mod shader; pub mod tessellation; pub mod vertex; pub mod viewport; diff --git a/vulkano/src/pipeline/vertex/buffers.rs b/vulkano/src/pipeline/vertex/buffers.rs index 6b5ff5b31d..d4a534f7a5 100644 --- a/vulkano/src/pipeline/vertex/buffers.rs +++ b/vulkano/src/pipeline/vertex/buffers.rs @@ -8,7 +8,6 @@ // according to those terms. use super::VertexMemberInfo; -use crate::pipeline::shader::ShaderInterface; use crate::pipeline::vertex::IncompatibleVertexDefinitionError; use crate::pipeline::vertex::Vertex; use crate::pipeline::vertex::VertexDefinition; @@ -16,6 +15,7 @@ use crate::pipeline::vertex::VertexInput; use crate::pipeline::vertex::VertexInputAttribute; use crate::pipeline::vertex::VertexInputBinding; use crate::pipeline::vertex::VertexInputRate; +use crate::shader::ShaderInterface; use crate::DeviceSize; use std::mem; diff --git a/vulkano/src/pipeline/vertex/definition.rs b/vulkano/src/pipeline/vertex/definition.rs index b941b4afce..34a76652bd 100644 --- a/vulkano/src/pipeline/vertex/definition.rs +++ b/vulkano/src/pipeline/vertex/definition.rs @@ -8,9 +8,9 @@ // according to those terms. use crate::format::Format; -use crate::pipeline::shader::ShaderInterface; use crate::pipeline::vertex::VertexInput; use crate::pipeline::vertex::VertexMemberTy; +use crate::shader::ShaderInterface; use crate::SafeDeref; use std::error; use std::fmt; diff --git a/vulkano/src/render_pass/desc.rs b/vulkano/src/render_pass/desc.rs index 8d8928d87e..458fae2723 100644 --- a/vulkano/src/render_pass/desc.rs +++ b/vulkano/src/render_pass/desc.rs @@ -11,7 +11,7 @@ use crate::format::ClearValue; use crate::format::Format; use crate::image::ImageLayout; use crate::image::SampleCount; -use crate::pipeline::shader::ShaderInterface; +use crate::shader::ShaderInterface; use crate::sync::AccessFlags; use crate::sync::PipelineStages; diff --git a/vulkano/src/render_pass/render_pass.rs b/vulkano/src/render_pass/render_pass.rs index d2d0d0c4cd..2bfd4aca15 100644 --- a/vulkano/src/render_pass/render_pass.rs +++ b/vulkano/src/render_pass/render_pass.rs @@ -12,11 +12,11 @@ use crate::device::Device; use crate::device::DeviceOwned; use crate::image::ImageLayout; use crate::image::SampleCount; -use crate::pipeline::shader::ShaderInterface; use crate::render_pass::AttachmentDesc; use crate::render_pass::LoadOp; use crate::render_pass::RenderPassDesc; use crate::render_pass::SubpassDesc; +use crate::shader::ShaderInterface; use crate::Error; use crate::OomError; use crate::VulkanObject; diff --git a/vulkano/src/pipeline/shader.rs b/vulkano/src/shader/mod.rs similarity index 51% rename from vulkano/src/pipeline/shader.rs rename to vulkano/src/shader/mod.rs index b35d98b880..896dc3582e 100644 --- a/vulkano/src/pipeline/shader.rs +++ b/vulkano/src/shader/mod.rs @@ -7,15 +7,15 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Stage of a graphics pipeline. +//! A program that is run on the device. //! //! In Vulkan, shaders are grouped in *shader modules*. Each shader module is built from SPIR-V //! code and can contain one or more entry points. Note that for the moment the official //! GLSL-to-SPIR-V compiler does not support multiple entry points. //! -//! The vulkano library does not provide any functionality that checks and introspects the SPIR-V -//! code, therefore the whole shader-related API is unsafe. You are encouraged to use the -//! `vulkano-shaders` crate that will generate Rust code that wraps around vulkano's shaders API. +//! The vulkano library can parse and introspect SPIR-V code, but it does not fully validate the +//! code. You are encouraged to use the `vulkano-shaders` crate that will generate Rust code that +//! wraps around vulkano's shaders API. use crate::check_errors; use crate::descriptor_set::layout::DescriptorType; @@ -24,14 +24,19 @@ use crate::format::Format; use crate::image::view::ImageViewType; use crate::pipeline::input_assembly::PrimitiveTopology; use crate::pipeline::layout::PipelineLayoutPcRange; +use crate::shader::spirv::{Capability, Spirv, SpirvError}; use crate::sync::PipelineStages; +use crate::DeviceSize; use crate::OomError; +use crate::Version; use crate::VulkanObject; use fnv::FnvHashMap; use std::borrow::Cow; +use std::collections::HashMap; use std::error; use std::error::Error; use std::ffi::CStr; +use std::ffi::CString; use std::fmt; use std::fmt::Display; use std::mem; @@ -41,65 +46,108 @@ use std::ops::Range; use std::ptr; use std::sync::Arc; +pub mod reflect; +pub mod spirv; + +// Generated by build.rs +include!(concat!(env!("OUT_DIR"), "/spirv_reqs.rs")); + /// Contains SPIR-V code with one or more entry points. -/// -/// Note that it is advised to wrap around a `ShaderModule` with a struct that is different for -/// each shader. #[derive(Debug)] pub struct ShaderModule { - // The module. - module: ash::vk::ShaderModule, - // Pointer to the device. + handle: ash::vk::ShaderModule, device: Arc, + entry_points: HashMap, } impl ShaderModule { - /// Builds a new shader module from SPIR-V bytes. + /// Builds a new shader module from SPIR-V 32-bit words. The shader code is parsed and the + /// necessary information is extracted from it. /// /// # Safety /// - /// - The SPIR-V code is not validated. - /// - The SPIR-V code may require some features that are not enabled. This isn't checked by - /// this function either. - /// - pub unsafe fn new(device: Arc, spirv: &[u8]) -> Result, OomError> { - debug_assert!((spirv.len() % 4) == 0); - Self::from_ptr(device, spirv.as_ptr() as *const _, spirv.len()) + /// - The SPIR-V code is not validated beyond the minimum needed to extract the information. + pub unsafe fn from_words( + device: Arc, + words: &[u32], + ) -> Result, ShaderCreationError> { + let spirv = Spirv::new(words)?; + + Self::from_words_with_data( + device, + words, + spirv.version(), + reflect::spirv_capabilities(&spirv), + reflect::spirv_extensions(&spirv), + reflect::entry_points(&spirv, false), + ) } - /// Builds a new shader module from SPIR-V 32-bit words. - /// - /// # Safety + /// As `from_words`, but takes a slice of bytes. /// - /// - The SPIR-V code is not validated. - /// - The SPIR-V code may require some features that are not enabled. This isn't checked by - /// this function either. + /// # Panics /// - pub unsafe fn from_words( + /// - Panics if the length of `bytes` is not a multiple of 4. + pub unsafe fn from_bytes( device: Arc, - spirv: &[u32], - ) -> Result, OomError> { - Self::from_ptr(device, spirv.as_ptr(), spirv.len() * mem::size_of::()) + bytes: &[u8], + ) -> Result, ShaderCreationError> { + assert!((bytes.len() % 4) == 0); + Self::from_words( + device, + std::slice::from_raw_parts( + bytes.as_ptr() as *const _, + bytes.len() / mem::size_of::(), + ), + ) } - /// Builds a new shader module from SPIR-V. + /// As `from_words`, but does not parse the code. Instead, you must provide the needed + /// information yourself. This can be useful if you've already done parsing yourself and + /// want to prevent Vulkano from doing it a second time. /// /// # Safety /// - /// - The SPIR-V code is not validated. - /// - The SPIR-V code may require some features that are not enabled. This isn't checked by - /// this function either. - /// - unsafe fn from_ptr( + /// - The SPIR-V code is not validated at all. + /// - The provided information must match what the SPIR-V code contains. + pub unsafe fn from_words_with_data<'a>( device: Arc, - spirv: *const u32, - spirv_len: usize, - ) -> Result, OomError> { - let module = { + words: &[u32], + spirv_version: Version, + spirv_capabilities: impl IntoIterator, + spirv_extensions: impl IntoIterator, + entry_points: impl IntoIterator, + ) -> Result, ShaderCreationError> { + if let Err(reason) = check_spirv_version(&device, spirv_version) { + return Err(ShaderCreationError::SpirvVersionNotSupported { + version: spirv_version, + reason, + }); + } + + for capability in spirv_capabilities { + if let Err(reason) = check_spirv_capability(&device, capability.clone()) { + return Err(ShaderCreationError::SpirvCapabilityNotSupported { + capability: capability.clone(), + reason, + }); + } + } + + for extension in spirv_extensions { + if let Err(reason) = check_spirv_extension(&device, extension) { + return Err(ShaderCreationError::SpirvExtensionNotSupported { + extension: extension.to_owned(), + reason, + }); + } + } + + let handle = { let infos = ash::vk::ShaderModuleCreateInfo { flags: ash::vk::ShaderModuleCreateFlags::empty(), - code_size: spirv_len, - p_code: spirv, + code_size: words.len() * mem::size_of::(), + p_code: words.as_ptr(), ..Default::default() }; @@ -115,71 +163,47 @@ impl ShaderModule { }; Ok(Arc::new(ShaderModule { - module: module, - device: device, + handle, + device, + entry_points: entry_points.into_iter().collect(), })) } - /// Gets access to an entry point contained in this module. + /// As `from_words_with_data`, but takes a slice of bytes. /// - /// This is purely a *logical* operation. It returns a struct that *represents* the entry - /// point but doesn't actually do anything. + /// # Panics /// - /// # Safety - /// - /// - The user must check that the entry point exists in the module, as this is not checked - /// by Vulkan. - /// - The input, output and layout must correctly describe the input, output and layout used - /// by this stage. - /// - pub unsafe fn graphics_entry_point<'a>( - &'a self, - name: &'a CStr, - descriptor_requirements: impl IntoIterator, - push_constant_range: Option, - spec_constants: &'static [SpecializationMapEntry], - input: ShaderInterface, - output: ShaderInterface, - ty: GraphicsShaderType, - ) -> GraphicsEntryPoint<'a> { - GraphicsEntryPoint { - module: self, - name, - descriptor_requirements: descriptor_requirements.into_iter().collect(), - push_constant_range, - spec_constants, - input, - output, - ty, - } + /// - Panics if the length of `bytes` is not a multiple of 4. + pub unsafe fn from_bytes_with_data<'a>( + device: Arc, + bytes: &[u8], + spirv_version: Version, + spirv_capabilities: impl IntoIterator, + spirv_extensions: impl IntoIterator, + entry_points: impl IntoIterator, + ) -> Result, ShaderCreationError> { + assert!((bytes.len() % 4) == 0); + Self::from_words_with_data( + device, + std::slice::from_raw_parts( + bytes.as_ptr() as *const _, + bytes.len() / mem::size_of::(), + ), + spirv_version, + spirv_capabilities, + spirv_extensions, + entry_points, + ) } - /// Gets access to an entry point contained in this module. - /// - /// This is purely a *logical* operation. It returns a struct that *represents* the entry - /// point but doesn't actually do anything. - /// - /// # Safety - /// - /// - The user must check that the entry point exists in the module, as this is not checked - /// by Vulkan. - /// - The layout must correctly describe the layout used by this stage. - /// - #[inline] - pub unsafe fn compute_entry_point<'a>( - &'a self, - name: &'a CStr, - descriptor_requirements: impl IntoIterator, - push_constant_range: Option, - spec_constants: &'static [SpecializationMapEntry], - ) -> ComputeEntryPoint<'a> { - ComputeEntryPoint { + /// Returns information about the entry point with the provided name. Returns `None` if no entry + /// point with that name exists in the shader module. + pub fn entry_point<'a>(&'a self, name: &str) -> Option> { + self.entry_points.get(name).map(|info| EntryPoint { module: self, - name, - descriptor_requirements: descriptor_requirements.into_iter().collect(), - push_constant_range, - spec_constants, - } + name: CString::new(name).unwrap(), + info, + }) } } @@ -188,7 +212,7 @@ unsafe impl VulkanObject for ShaderModule { #[inline] fn internal_object(&self) -> ash::vk::ShaderModule { - self.module + self.handle } } @@ -198,38 +222,139 @@ impl Drop for ShaderModule { unsafe { let fns = self.device.fns(); fns.v1_0 - .destroy_shader_module(self.device.internal_object(), self.module, ptr::null()); + .destroy_shader_module(self.device.internal_object(), self.handle, ptr::null()); + } + } +} + +/// 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 { + #[inline] + fn source(&self) -> Option<&(dyn error::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 fmt::Formatter<'_>) -> fmt::Result { + 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: crate::Error) -> 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 fmt::Formatter<'_>) -> fmt::Result { + 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 execution: ShaderExecution, + pub descriptor_requirements: FnvHashMap<(u32, u32), DescriptorRequirements>, + pub push_constant_requirements: Option, + pub specialization_constant_requirements: FnvHashMap, + pub input_interface: ShaderInterface, + pub output_interface: ShaderInterface, +} + /// Represents a shader entry point in a shader module. /// -/// Can be obtained by calling `entry_point()` on the shader module. +/// Can be obtained by calling [`entry_point`](ShaderModule::entry_point) on the shader module. #[derive(Clone, Debug)] -pub struct GraphicsEntryPoint<'a> { +pub struct EntryPoint<'a> { module: &'a ShaderModule, - name: &'a CStr, - - descriptor_requirements: FnvHashMap<(u32, u32), DescriptorRequirements>, - push_constant_range: Option, - spec_constants: &'static [SpecializationMapEntry], - input: ShaderInterface, - output: ShaderInterface, - ty: GraphicsShaderType, + name: CString, + info: &'a EntryPointInfo, } -impl<'a> GraphicsEntryPoint<'a> { +impl<'a> EntryPoint<'a> { /// Returns the module this entry point comes from. #[inline] - pub fn module(&self) -> &ShaderModule { + pub fn module(&self) -> &'a ShaderModule { self.module } /// Returns the name of the entry point. #[inline] pub fn name(&self) -> &CStr { - self.name + &self.name + } + + /// Returns the execution model of the shader. + #[inline] + pub fn execution(&self) -> &ShaderExecution { + &self.info.execution } /// Returns the descriptor requirements. @@ -237,52 +362,80 @@ impl<'a> GraphicsEntryPoint<'a> { pub fn descriptor_requirements( &self, ) -> impl ExactSizeIterator { - self.descriptor_requirements.iter().map(|(k, v)| (*k, v)) + self.info + .descriptor_requirements + .iter() + .map(|(k, v)| (*k, v)) } - /// Returns the push constant ranges. + /// Returns the push constant requirements. #[inline] - pub fn push_constant_range(&self) -> &Option { - &self.push_constant_range + pub fn push_constant_requirements(&self) -> Option<&PipelineLayoutPcRange> { + self.info.push_constant_requirements.as_ref() } - /// Returns the layout of the specialization constants. + /// Returns the specialization constant requirements. #[inline] - pub fn spec_constants(&self) -> &[SpecializationMapEntry] { - self.spec_constants + 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(&self) -> &ShaderInterface { - &self.input + pub fn input_interface(&self) -> &ShaderInterface { + &self.info.input_interface } /// Returns the output attributes used by the shader stage. #[inline] - pub fn output(&self) -> &ShaderInterface { - &self.output - } - - /// Returns the type of shader. - #[inline] - pub fn ty(&self) -> GraphicsShaderType { - self.ty + pub fn output_interface(&self) -> &ShaderInterface { + &self.info.output_interface } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum GraphicsShaderType { +/// 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)] +pub enum ShaderExecution { Vertex, TessellationControl, TessellationEvaluation, - Geometry(GeometryShaderExecutionMode), + Geometry(GeometryShaderExecution), Fragment, + Compute, +} + +/*#[derive(Clone, Copy, Debug)] +pub struct TessellationShaderExecution { + pub num_output_vertices: u32, + pub point_mode: bool, + pub subdivision: TessellationShaderSubdivision, } -/// Declares which type of primitives are expected by the geometry shader. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum GeometryShaderExecutionMode { +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum TessellationShaderSubdivision { + Triangles, + Quads, + Isolines, +}*/ + +/// The mode in which a geometry shader executes. +#[derive(Clone, Copy, Debug)] +pub struct GeometryShaderExecution { + pub input: GeometryShaderInput, + /*pub max_output_vertices: u32, + pub num_invocations: u32, + pub output: GeometryShaderOutput,*/ +} + +/// The input primitive type that is expected by a geometry shader. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum GeometryShaderInput { Points, Lines, LinesWithAdjacency, @@ -290,81 +443,261 @@ pub enum GeometryShaderExecutionMode { TrianglesWithAdjacency, } -impl GeometryShaderExecutionMode { - /// Returns true if the given primitive topology can be used with this execution mode. +impl GeometryShaderInput { + /// Returns true if the given primitive topology can be used as input for this geometry shader. #[inline] - pub fn matches(&self, input: PrimitiveTopology) -> bool { - match (*self, input) { - (GeometryShaderExecutionMode::Points, PrimitiveTopology::PointList) => true, - (GeometryShaderExecutionMode::Lines, PrimitiveTopology::LineList) => true, - (GeometryShaderExecutionMode::Lines, PrimitiveTopology::LineStrip) => true, - ( - GeometryShaderExecutionMode::LinesWithAdjacency, - PrimitiveTopology::LineListWithAdjacency, - ) => true, - ( - GeometryShaderExecutionMode::LinesWithAdjacency, - PrimitiveTopology::LineStripWithAdjacency, - ) => true, - (GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleList) => true, - (GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleStrip) => true, - (GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleFan) => true, - ( - GeometryShaderExecutionMode::TrianglesWithAdjacency, - PrimitiveTopology::TriangleListWithAdjacency, - ) => true, - ( - GeometryShaderExecutionMode::TrianglesWithAdjacency, - PrimitiveTopology::TriangleStripWithAdjacency, - ) => true, - _ => false, - } - } -} - -/// Represents the entry point of a compute shader in a shader module. + pub fn is_compatible_with(&self, topology: PrimitiveTopology) -> bool { + match self { + Self::Points => matches!(topology, PrimitiveTopology::PointList), + Self::Lines => matches!( + topology, + PrimitiveTopology::LineList | PrimitiveTopology::LineStrip + ), + Self::LinesWithAdjacency => matches!( + topology, + PrimitiveTopology::LineListWithAdjacency + | PrimitiveTopology::LineStripWithAdjacency + ), + Self::Triangles => matches!( + topology, + PrimitiveTopology::TriangleList + | PrimitiveTopology::TriangleStrip + | PrimitiveTopology::TriangleFan, + ), + Self::TrianglesWithAdjacency => matches!( + topology, + PrimitiveTopology::TriangleListWithAdjacency + | PrimitiveTopology::TriangleStripWithAdjacency, + ), + } + } +} + +/*#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum GeometryShaderOutput { + Points, + LineStrip, + TriangleStrip, +}*/ + +/// The requirements imposed by a shader on a descriptor within a descriptor set layout, and on any +/// resource that is bound to that descriptor. +#[derive(Clone, Debug, Default)] +pub struct DescriptorRequirements { + /// The descriptor types that are allowed. + pub descriptor_types: Vec, + + /// The number of descriptors (array elements) that the shader requires. The descriptor set + /// layout can declare more than this, but never less. + pub descriptor_count: u32, + + /// The image format that is required for image views bound to this descriptor. If this is + /// `None`, then any image format is allowed. + pub format: Option, + + /// The view type that is required for image views bound to this descriptor. This is `None` for + /// non-image descriptors. + pub image_view_type: Option, + + /// Whether image views bound to this descriptor must have multisampling enabled or disabled. + pub multisampled: bool, + + /// Whether the shader requires mutable (exclusive) access to the resource bound to this + /// descriptor. + pub mutable: bool, + + /// The shader stages that the descriptor must be declared for. + pub stages: ShaderStages, +} + +impl DescriptorRequirements { + /// Produces the intersection of two descriptor requirements, so that the requirements of both + /// are satisfied. An error is returned if the requirements conflict. + pub fn intersection(&self, other: &Self) -> Result { + let descriptor_types: Vec<_> = self + .descriptor_types + .iter() + .copied() + .filter(|ty| other.descriptor_types.contains(&ty)) + .collect(); + + if descriptor_types.is_empty() { + return Err(DescriptorRequirementsIncompatible::DescriptorType); + } + + if let (Some(first), Some(second)) = (self.format, other.format) { + if first != second { + return Err(DescriptorRequirementsIncompatible::Format); + } + } + + if let (Some(first), Some(second)) = (self.image_view_type, other.image_view_type) { + if first != second { + return Err(DescriptorRequirementsIncompatible::ImageViewType); + } + } + + if self.multisampled != other.multisampled { + return Err(DescriptorRequirementsIncompatible::Multisampled); + } + + Ok(Self { + descriptor_types, + descriptor_count: self.descriptor_count.max(other.descriptor_count), + format: self.format.or(other.format), + image_view_type: self.image_view_type.or(other.image_view_type), + multisampled: self.multisampled, + mutable: self.mutable || other.mutable, + stages: self.stages | other.stages, + }) + } +} + +/// An error that can be returned when trying to create the intersection of two +/// `DescriptorRequirements` values. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DescriptorRequirementsIncompatible { + /// The allowed descriptor types of the descriptors do not overlap. + DescriptorType, + /// The descriptors require different formats. + Format, + /// The descriptors require different image view types. + ImageViewType, + /// The multisampling requirements of the descriptors differ. + Multisampled, +} + +impl Error for DescriptorRequirementsIncompatible {} + +impl Display for DescriptorRequirementsIncompatible { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DescriptorRequirementsIncompatible::DescriptorType => { + write!( + f, + "the allowed descriptor types of the two descriptors do not overlap" + ) + } + DescriptorRequirementsIncompatible::Format => { + write!(f, "the descriptors require different formats") + } + DescriptorRequirementsIncompatible::ImageViewType => { + write!(f, "the descriptors require different image view types") + } + DescriptorRequirementsIncompatible::Multisampled => { + write!( + f, + "the multisampling requirements of the descriptors differ" + ) + } + } + } +} + +/// 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. /// -/// Can be obtained by calling `compute_shader_entry_point()` on the shader module. -#[derive(Debug, Clone)] -pub struct ComputeEntryPoint<'a> { - module: &'a ShaderModule, - name: &'a CStr, - descriptor_requirements: FnvHashMap<(u32, u32), DescriptorRequirements>, - push_constant_range: Option, - spec_constants: &'static [SpecializationMapEntry], +/// 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. +/// +/// # Example +/// +/// ```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]; } -impl<'a> ComputeEntryPoint<'a> { - /// Returns the module this entry point comes from. +unsafe impl SpecializationConstants for () { #[inline] - pub fn module(&self) -> &ShaderModule { - self.module + fn descriptors() -> &'static [SpecializationMapEntry] { + &[] } +} - /// Returns the name of the entry point. - #[inline] - pub fn name(&self) -> &CStr { - self.name - } +/// 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. + /// + /// 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, - /// Returns the descriptor requirements. - #[inline] - pub fn descriptor_requirements( - &self, - ) -> impl ExactSizeIterator { - self.descriptor_requirements.iter().map(|(k, v)| (*k, v)) - } + /// Offset within the struct where the data can be found. + pub offset: u32, - /// Returns the push constant ranges. - #[inline] - pub fn push_constant_range(&self) -> &Option { - &self.push_constant_range - } + /// Size of the data in bytes. Must match the size of the constant (`4` for booleans). + pub size: usize, +} - /// Returns the layout of the specialization constants. +impl From for ash::vk::SpecializationMapEntry { #[inline] - pub fn spec_constants(&self) -> &[SpecializationMapEntry] { - self.spec_constants + fn from(val: SpecializationMapEntry) -> Self { + Self { + constant_id: val.constant_id, + offset: val.offset, + size: val.size, + } } } @@ -508,96 +841,6 @@ impl fmt::Display for ShaderInterfaceMismatchError { } } -/// 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. -/// -/// # Example -/// -/// ```rust -/// use vulkano::pipeline::shader::SpecializationConstants; -/// use vulkano::pipeline::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. - /// - /// 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, - - /// Offset within the struct where the data can be found. - pub offset: u32, - - /// Size of the data in bytes. Must match the size of the constant (`4` for booleans). - pub size: usize, -} - /// A single shader stage. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[repr(u32)] @@ -616,7 +859,78 @@ pub enum ShaderStage { Callable = ash::vk::ShaderStageFlags::CALLABLE_KHR.as_raw(), } +impl From for ShaderStage { + #[inline] + fn from(val: ShaderExecution) -> Self { + match val { + ShaderExecution::Vertex => Self::Vertex, + ShaderExecution::TessellationControl => Self::TessellationControl, + ShaderExecution::TessellationEvaluation => Self::TessellationEvaluation, + ShaderExecution::Geometry(_) => Self::Geometry, + ShaderExecution::Fragment => Self::Fragment, + ShaderExecution::Compute => Self::Compute, + } + } +} + +impl From for ShaderStages { + #[inline] + fn from(val: ShaderStage) -> Self { + match val { + ShaderStage::Vertex => Self { + vertex: true, + ..Self::none() + }, + ShaderStage::TessellationControl => Self { + tessellation_control: true, + ..Self::none() + }, + ShaderStage::TessellationEvaluation => Self { + tessellation_evaluation: true, + ..Self::none() + }, + ShaderStage::Geometry => Self { + geometry: true, + ..Self::none() + }, + ShaderStage::Fragment => Self { + fragment: true, + ..Self::none() + }, + ShaderStage::Compute => Self { + compute: true, + ..Self::none() + }, + ShaderStage::Raygen => Self { + raygen: true, + ..Self::none() + }, + ShaderStage::AnyHit => Self { + any_hit: true, + ..Self::none() + }, + ShaderStage::ClosestHit => Self { + closest_hit: true, + ..Self::none() + }, + ShaderStage::Miss => Self { + miss: true, + ..Self::none() + }, + ShaderStage::Intersection => Self { + intersection: true, + ..Self::none() + }, + ShaderStage::Callable => Self { + callable: true, + ..Self::none() + }, + } + } +} + impl From for ash::vk::ShaderStageFlags { + #[inline] fn from(val: ShaderStage) -> Self { Self::from_raw(val as u32) } @@ -632,6 +946,12 @@ pub struct ShaderStages { pub geometry: bool, pub fragment: bool, pub compute: bool, + pub raygen: bool, + pub any_hit: bool, + pub closest_hit: bool, + pub miss: bool, + pub intersection: bool, + pub callable: bool, } impl ShaderStages { @@ -646,6 +966,12 @@ impl ShaderStages { geometry: true, fragment: true, compute: true, + raygen: true, + any_hit: true, + closest_hit: true, + miss: true, + intersection: true, + callable: true, } } @@ -660,6 +986,12 @@ impl ShaderStages { geometry: false, fragment: false, compute: false, + raygen: false, + any_hit: false, + closest_hit: false, + miss: false, + intersection: false, + callable: false, } } @@ -673,7 +1005,7 @@ impl ShaderStages { tessellation_evaluation: true, geometry: true, fragment: true, - compute: false, + ..ShaderStages::none() } } @@ -682,12 +1014,8 @@ impl ShaderStages { #[inline] pub const fn compute() -> ShaderStages { ShaderStages { - vertex: false, - tessellation_control: false, - tessellation_evaluation: false, - geometry: false, - fragment: false, compute: true, + ..ShaderStages::none() } } @@ -695,24 +1023,66 @@ impl ShaderStages { // TODO: add example #[inline] pub const fn is_superset_of(&self, other: &ShaderStages) -> bool { - (self.vertex || !other.vertex) - && (self.tessellation_control || !other.tessellation_control) - && (self.tessellation_evaluation || !other.tessellation_evaluation) - && (self.geometry || !other.geometry) - && (self.fragment || !other.fragment) - && (self.compute || !other.compute) + let Self { + vertex, + tessellation_control, + tessellation_evaluation, + geometry, + fragment, + compute, + raygen, + any_hit, + closest_hit, + miss, + intersection, + callable, + } = *self; + + (vertex || !other.vertex) + && (tessellation_control || !other.tessellation_control) + && (tessellation_evaluation || !other.tessellation_evaluation) + && (geometry || !other.geometry) + && (fragment || !other.fragment) + && (compute || !other.compute) + && (raygen || !other.raygen) + && (any_hit || !other.any_hit) + && (closest_hit || !other.closest_hit) + && (miss || !other.miss) + && (intersection || !other.intersection) + && (callable || !other.callable) } /// Checks whether any of the stages in `self` are also present in `other`. // TODO: add example #[inline] pub const fn intersects(&self, other: &ShaderStages) -> bool { - (self.vertex && other.vertex) - || (self.tessellation_control && other.tessellation_control) - || (self.tessellation_evaluation && other.tessellation_evaluation) - || (self.geometry && other.geometry) - || (self.fragment && other.fragment) - || (self.compute && other.compute) + let Self { + vertex, + tessellation_control, + tessellation_evaluation, + geometry, + fragment, + compute, + raygen, + any_hit, + closest_hit, + miss, + intersection, + callable, + } = *self; + + (vertex && other.vertex) + || (tessellation_control && other.tessellation_control) + || (tessellation_evaluation && other.tessellation_evaluation) + || (geometry && other.geometry) + || (fragment && other.fragment) + || (compute && other.compute) + || (raygen && other.raygen) + || (any_hit && other.any_hit) + || (closest_hit && other.closest_hit) + || (miss && other.miss) + || (intersection && other.intersection) + || (callable && other.callable) } /// Returns the union of the stages in `self` and `other`. @@ -725,6 +1095,12 @@ impl ShaderStages { geometry: self.geometry || other.geometry, fragment: self.fragment || other.fragment, compute: self.compute || other.compute, + raygen: self.raygen || other.raygen, + any_hit: self.any_hit || other.any_hit, + closest_hit: self.closest_hit || other.closest_hit, + miss: self.miss || other.miss, + intersection: self.intersection || other.intersection, + callable: self.callable || other.callable, } } } @@ -733,24 +1109,57 @@ impl From for ash::vk::ShaderStageFlags { #[inline] fn from(val: ShaderStages) -> Self { let mut result = ash::vk::ShaderStageFlags::empty(); - if val.vertex { + let ShaderStages { + vertex, + tessellation_control, + tessellation_evaluation, + geometry, + fragment, + compute, + raygen, + any_hit, + closest_hit, + miss, + intersection, + callable, + } = val; + + if vertex { result |= ash::vk::ShaderStageFlags::VERTEX; } - if val.tessellation_control { + if tessellation_control { result |= ash::vk::ShaderStageFlags::TESSELLATION_CONTROL; } - if val.tessellation_evaluation { + if tessellation_evaluation { result |= ash::vk::ShaderStageFlags::TESSELLATION_EVALUATION; } - if val.geometry { + if geometry { result |= ash::vk::ShaderStageFlags::GEOMETRY; } - if val.fragment { + if fragment { result |= ash::vk::ShaderStageFlags::FRAGMENT; } - if val.compute { + if compute { result |= ash::vk::ShaderStageFlags::COMPUTE; } + if raygen { + result |= ash::vk::ShaderStageFlags::RAYGEN_KHR; + } + if any_hit { + result |= ash::vk::ShaderStageFlags::ANY_HIT_KHR; + } + if closest_hit { + result |= ash::vk::ShaderStageFlags::CLOSEST_HIT_KHR; + } + if miss { + result |= ash::vk::ShaderStageFlags::MISS_KHR; + } + if intersection { + result |= ash::vk::ShaderStageFlags::INTERSECTION_KHR; + } + if callable { + result |= ash::vk::ShaderStageFlags::CALLABLE_KHR; + } result } } @@ -766,6 +1175,12 @@ impl From for ShaderStages { geometry: val.intersects(ash::vk::ShaderStageFlags::GEOMETRY), fragment: val.intersects(ash::vk::ShaderStageFlags::FRAGMENT), compute: val.intersects(ash::vk::ShaderStageFlags::COMPUTE), + raygen: val.intersects(ash::vk::ShaderStageFlags::RAYGEN_KHR), + any_hit: val.intersects(ash::vk::ShaderStageFlags::ANY_HIT_KHR), + closest_hit: val.intersects(ash::vk::ShaderStageFlags::CLOSEST_HIT_KHR), + miss: val.intersects(ash::vk::ShaderStageFlags::MISS_KHR), + intersection: val.intersects(ash::vk::ShaderStageFlags::INTERSECTION_KHR), + callable: val.intersects(ash::vk::ShaderStageFlags::CALLABLE_KHR), } } } @@ -782,6 +1197,12 @@ impl BitOr for ShaderStages { geometry: self.geometry || other.geometry, fragment: self.fragment || other.fragment, compute: self.compute || other.compute, + raygen: self.raygen || other.raygen, + any_hit: self.any_hit || other.any_hit, + closest_hit: self.closest_hit || other.closest_hit, + miss: self.miss || other.miss, + intersection: self.intersection || other.intersection, + callable: self.callable || other.callable, } } } @@ -789,128 +1210,63 @@ impl BitOr for ShaderStages { impl From for PipelineStages { #[inline] fn from(stages: ShaderStages) -> PipelineStages { + let ShaderStages { + vertex, + tessellation_control, + tessellation_evaluation, + geometry, + fragment, + compute, + raygen, + any_hit, + closest_hit, + miss, + intersection, + callable, + } = stages; + PipelineStages { - vertex_shader: stages.vertex, - tessellation_control_shader: stages.tessellation_control, - tessellation_evaluation_shader: stages.tessellation_evaluation, - geometry_shader: stages.geometry, - fragment_shader: stages.fragment, - compute_shader: stages.compute, + vertex_shader: vertex, + tessellation_control_shader: tessellation_control, + tessellation_evaluation_shader: tessellation_evaluation, + geometry_shader: geometry, + fragment_shader: fragment, + compute_shader: compute, + ray_tracing_shader: raygen | any_hit | closest_hit | miss | intersection | callable, ..PipelineStages::none() } } } -/// The requirements imposed by a shader on a descriptor within a descriptor set layout, and on any -/// resource that is bound to that descriptor. -#[derive(Clone, Debug, Default)] -pub struct DescriptorRequirements { - /// The descriptor types that are allowed. - pub descriptor_types: Vec, - - /// The number of descriptors (array elements) that the shader requires. The descriptor set - /// layout can declare more than this, but never less. - pub descriptor_count: u32, - - /// The image format that is required for image views bound to this descriptor. If this is - /// `None`, then any image format is allowed. - pub format: Option, - - /// The view type that is required for image views bound to this descriptor. This is `None` for - /// non-image descriptors. - pub image_view_type: Option, - - /// Whether image views bound to this descriptor must have multisampling enabled or disabled. - pub multisampled: bool, - - /// Whether the shader requires mutable (exclusive) access to the resource bound to this - /// descriptor. - pub mutable: bool, - - /// The shader stages that the descriptor must be declared for. - pub stages: ShaderStages, -} - -impl DescriptorRequirements { - /// Produces the intersection of two descriptor requirements, so that the requirements of both - /// are satisfied. An error is returned if the requirements conflict. - pub fn intersection(&self, other: &Self) -> Result { - let descriptor_types: Vec<_> = self - .descriptor_types - .iter() - .copied() - .filter(|ty| other.descriptor_types.contains(&ty)) - .collect(); - - if descriptor_types.is_empty() { - return Err(DescriptorRequirementsIncompatible::DescriptorType); - } +fn check_spirv_version(device: &Device, mut version: Version) -> Result<(), ShaderSupportError> { + version.patch = 0; // Ignore the patch version - if let (Some(first), Some(second)) = (self.format, other.format) { - if first != second { - return Err(DescriptorRequirementsIncompatible::Format); + match version { + Version::V1_0 => {} + Version::V1_1 | Version::V1_2 | Version::V1_3 => { + if !(device.api_version() >= Version::V1_1) { + return Err(ShaderSupportError::RequirementsNotMet(&[ + "Vulkan API version 1.1", + ])); } } - - if let (Some(first), Some(second)) = (self.image_view_type, other.image_view_type) { - if first != second { - return Err(DescriptorRequirementsIncompatible::ImageViewType); + Version::V1_4 => { + if !(device.api_version() >= Version::V1_2 || device.enabled_extensions().khr_spirv_1_4) + { + return Err(ShaderSupportError::RequirementsNotMet(&[ + "Vulkan API version 1.2", + "extension `khr_spirv_1_4`", + ])); } } - - if self.multisampled != other.multisampled { - return Err(DescriptorRequirementsIncompatible::Multisampled); - } - - Ok(Self { - descriptor_types, - descriptor_count: self.descriptor_count.max(other.descriptor_count), - format: self.format.or(other.format), - image_view_type: self.image_view_type.or(other.image_view_type), - multisampled: self.multisampled, - mutable: self.mutable || other.mutable, - stages: self.stages | other.stages, - }) - } -} - -/// An error that can be returned when trying to create the intersection of two -/// `DescriptorRequirements` values. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum DescriptorRequirementsIncompatible { - /// The allowed descriptor types of the descriptors do not overlap. - DescriptorType, - /// The descriptors require different formats. - Format, - /// The descriptors require different image view types. - ImageViewType, - /// The multisampling requirements of the descriptors differ. - Multisampled, -} - -impl Error for DescriptorRequirementsIncompatible {} - -impl Display for DescriptorRequirementsIncompatible { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - DescriptorRequirementsIncompatible::DescriptorType => { - write!( - f, - "the allowed descriptor types of the two descriptors do not overlap" - ) - } - DescriptorRequirementsIncompatible::Format => { - write!(f, "the descriptors require different formats") - } - DescriptorRequirementsIncompatible::ImageViewType => { - write!(f, "the descriptors require different image view types") - } - DescriptorRequirementsIncompatible::Multisampled => { - write!( - f, - "the multisampling requirements of the descriptors differ" - ) + Version::V1_5 => { + if !(device.api_version() >= Version::V1_2) { + return Err(ShaderSupportError::RequirementsNotMet(&[ + "Vulkan API version 1.2", + ])); } } + _ => return Err(ShaderSupportError::NotSupportedByVulkan), } + Ok(()) } diff --git a/vulkano/src/shader/reflect.rs b/vulkano/src/shader/reflect.rs new file mode 100644 index 0000000000..82cce79acb --- /dev/null +++ b/vulkano/src/shader/reflect.rs @@ -0,0 +1,1070 @@ +// Copyright (c) 2021 The Vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +//! Extraction of information from SPIR-V modules, that is needed by the rest of Vulkano. + +use crate::descriptor_set::layout::DescriptorType; +use crate::image::view::ImageViewType; +use crate::DeviceSize; +use crate::{ + format::Format, + pipeline::layout::PipelineLayoutPcRange, + shader::{ + spirv::{ + Capability, Decoration, Dim, ExecutionMode, ExecutionModel, Id, ImageFormat, + Instruction, Spirv, StorageClass, + }, + DescriptorRequirements, EntryPointInfo, GeometryShaderExecution, GeometryShaderInput, + ShaderExecution, ShaderInterface, ShaderInterfaceEntry, ShaderStage, + SpecializationConstantRequirements, + }, +}; +use fnv::FnvHashMap; +use std::borrow::Cow; +use std::collections::HashSet; + +/// Returns an iterator of the capabilities used by `spirv`. +pub fn spirv_capabilities<'a>(spirv: &'a Spirv) -> impl Iterator { + spirv + .iter_capability() + .filter_map(|instruction| match instruction { + Instruction::Capability { capability } => Some(capability), + _ => None, + }) +} + +/// Returns an iterator of the extensions used by `spirv`. +pub fn spirv_extensions<'a>(spirv: &'a Spirv) -> impl Iterator { + spirv + .iter_extension() + .filter_map(|instruction| match instruction { + Instruction::Extension { name } => Some(name.as_str()), + _ => None, + }) +} + +/// Returns an iterator over all entry points in `spirv`, with information about the entry point. +pub fn entry_points<'a>( + spirv: &'a Spirv, + exact_interface: bool, +) -> impl Iterator + 'a { + spirv.iter_entry_point().filter_map(move |instruction| { + let (execution_model, function_id, entry_point_name, interface) = match instruction { + &Instruction::EntryPoint { + ref execution_model, + entry_point, + ref name, + ref interface, + .. + } => (execution_model, entry_point, name, interface), + _ => return None, + }; + + let execution = shader_execution(&spirv, execution_model, function_id); + let stage = ShaderStage::from(execution); + let descriptor_requirements = + descriptor_requirements(&spirv, function_id, stage, interface, exact_interface); + let push_constant_requirements = push_constant_requirements(&spirv, stage); + let specialization_constant_requirements = specialization_constant_requirements(&spirv); + let input_interface = shader_interface( + &spirv, + interface, + StorageClass::Input, + matches!( + execution_model, + ExecutionModel::TessellationControl + | ExecutionModel::TessellationEvaluation + | ExecutionModel::Geometry + ), + ); + let output_interface = shader_interface( + &spirv, + interface, + StorageClass::Output, + matches!(execution_model, ExecutionModel::TessellationControl), + ); + + Some(( + entry_point_name.clone(), + EntryPointInfo { + execution, + descriptor_requirements, + push_constant_requirements, + specialization_constant_requirements, + input_interface, + output_interface, + }, + )) + }) +} + +/// Extracts the `ShaderExecution` for the entry point `function_id` from `spirv`. +fn shader_execution( + spirv: &Spirv, + execution_model: &ExecutionModel, + function_id: Id, +) -> ShaderExecution { + match execution_model { + ExecutionModel::Vertex => ShaderExecution::Vertex, + + ExecutionModel::TessellationControl => ShaderExecution::TessellationControl, + + ExecutionModel::TessellationEvaluation => ShaderExecution::TessellationEvaluation, + + ExecutionModel::Geometry => { + let input = spirv + .iter_execution_mode() + .into_iter() + .find_map(|instruction| match instruction { + Instruction::ExecutionMode { + entry_point, mode, .. + } if *entry_point == function_id => match mode { + ExecutionMode::InputPoints => Some(GeometryShaderInput::Points), + ExecutionMode::InputLines => Some(GeometryShaderInput::Lines), + ExecutionMode::InputLinesAdjacency => { + Some(GeometryShaderInput::LinesWithAdjacency) + } + ExecutionMode::Triangles => Some(GeometryShaderInput::Triangles), + ExecutionMode::InputTrianglesAdjacency => { + Some(GeometryShaderInput::TrianglesWithAdjacency) + } + _ => todo!(), + }, + _ => None, + }) + .expect("Geometry shader does not have an input primitive ExecutionMode"); + + ShaderExecution::Geometry(GeometryShaderExecution { input }) + } + + ExecutionModel::Fragment => ShaderExecution::Fragment, + + ExecutionModel::GLCompute => ShaderExecution::Compute, + + ExecutionModel::Kernel + | ExecutionModel::TaskNV + | ExecutionModel::MeshNV + | ExecutionModel::RayGenerationKHR + | ExecutionModel::IntersectionKHR + | ExecutionModel::AnyHitKHR + | ExecutionModel::ClosestHitKHR + | ExecutionModel::MissKHR + | ExecutionModel::CallableKHR => { + todo!() + } + } +} + +/// Extracts the `DescriptorRequirements` for the entry point `function_id` from `spirv`. +fn descriptor_requirements( + spirv: &Spirv, + function_id: Id, + stage: ShaderStage, + interface: &[Id], + exact: bool, +) -> FnvHashMap<(u32, u32), DescriptorRequirements> { + // For SPIR-V 1.4+, the entrypoint interface can specify variables of all storage classes, + // and most tools will put all used variables in the entrypoint interface. However, + // SPIR-V 1.0-1.3 do not specify variables other than Input/Output ones in the interface, + // and instead the function itself must be inspected. + let variables = if exact { + let mut found_variables: HashSet = interface.iter().cloned().collect(); + let mut inspected_functions: HashSet = HashSet::new(); + find_variables_in_function( + &spirv, + function_id, + &mut inspected_functions, + &mut found_variables, + ); + Some(found_variables) + } else { + None + }; + + // Looping to find all the global variables that have the `DescriptorSet` decoration. + spirv + .iter_global() + .filter_map(|instruction| { + let (variable_id, variable_type_id, storage_class) = match instruction { + Instruction::Variable { + result_id, + result_type_id, + .. + } => { + let (real_type, storage_class) = match spirv + .id(*result_type_id) + .instruction() + { + Instruction::TypePointer { + ty, storage_class, .. + } => (ty, storage_class), + _ => panic!( + "Variable {} result_type_id does not refer to a TypePointer instruction", result_id + ), + }; + + (*result_id, *real_type, storage_class) + } + _ => return None, + }; + + if exact && !variables.as_ref().unwrap().contains(&variable_id) { + return None; + } + + let variable_id_info = spirv.id(variable_id); + let set_num = match variable_id_info + .iter_decoration() + .find_map(|instruction| match instruction { + Instruction::Decorate { + decoration: Decoration::DescriptorSet { descriptor_set }, + .. + } => Some(*descriptor_set), + _ => None, + }) { + Some(x) => x, + None => return None, + }; + + let binding_num = variable_id_info + .iter_decoration() + .find_map(|instruction| match instruction { + Instruction::Decorate { + decoration: Decoration::Binding { binding_point }, + .. + } => Some(*binding_point), + _ => None, + }) + .unwrap(); + + let name = variable_id_info + .iter_name() + .find_map(|instruction| match instruction { + Instruction::Name { name, .. } => Some(name.as_str()), + _ => None, + }) + .unwrap_or("__unnamed"); + + let nonwritable = variable_id_info.iter_decoration().any(|instruction| { + matches!( + instruction, + Instruction::Decorate { + decoration: Decoration::NonWritable, + .. + } + ) + }); + + // Find information about the kind of binding for this descriptor. + let mut reqs = + descriptor_requirements_of(spirv, variable_type_id, storage_class, false).expect(&format!( + "Couldn't find relevant type for global variable `{}` (type {}, maybe unimplemented)", + name, variable_type_id, + )); + + reqs.stages = stage.into(); + reqs.mutable &= !nonwritable; + + Some(((set_num, binding_num), reqs)) + }) + .collect() +} + +// Recursively finds every pointer variable used in the execution of a function. +fn find_variables_in_function( + spirv: &Spirv, + function: Id, + inspected_functions: &mut HashSet, + found_variables: &mut HashSet, +) { + inspected_functions.insert(function); + let mut in_function = false; + for instruction in spirv.instructions() { + if !in_function { + match instruction { + Instruction::Function { result_id, .. } if result_id == &function => { + in_function = true; + } + _ => {} + } + } else { + // We only care about instructions that accept pointers. + // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_universal_validation_rules + match instruction { + Instruction::Load { pointer, .. } | Instruction::Store { pointer, .. } => { + found_variables.insert(*pointer); + } + Instruction::AccessChain { base, .. } + | Instruction::InBoundsAccessChain { base, .. } => { + found_variables.insert(*base); + } + Instruction::FunctionCall { + function, + arguments, + .. + } => { + arguments.iter().for_each(|&x| { + found_variables.insert(x); + }); + if !inspected_functions.contains(function) { + find_variables_in_function( + spirv, + *function, + inspected_functions, + found_variables, + ); + } + } + Instruction::ImageTexelPointer { + image, + coordinate, + sample, + .. + } => { + found_variables.insert(*image); + found_variables.insert(*coordinate); + found_variables.insert(*sample); + } + Instruction::CopyMemory { target, source, .. } => { + found_variables.insert(*target); + found_variables.insert(*source); + } + Instruction::CopyObject { operand, .. } => { + found_variables.insert(*operand); + } + Instruction::AtomicLoad { pointer, .. } + | Instruction::AtomicIIncrement { pointer, .. } + | Instruction::AtomicIDecrement { pointer, .. } + | Instruction::AtomicFlagTestAndSet { pointer, .. } + | Instruction::AtomicFlagClear { pointer, .. } => { + found_variables.insert(*pointer); + } + Instruction::AtomicStore { pointer, value, .. } + | Instruction::AtomicExchange { pointer, value, .. } + | Instruction::AtomicIAdd { pointer, value, .. } + | Instruction::AtomicISub { pointer, value, .. } + | Instruction::AtomicSMin { pointer, value, .. } + | Instruction::AtomicUMin { pointer, value, .. } + | Instruction::AtomicSMax { pointer, value, .. } + | Instruction::AtomicUMax { pointer, value, .. } + | Instruction::AtomicAnd { pointer, value, .. } + | Instruction::AtomicOr { pointer, value, .. } + | Instruction::AtomicXor { pointer, value, .. } => { + found_variables.insert(*pointer); + found_variables.insert(*value); + } + Instruction::AtomicCompareExchange { + pointer, + value, + comparator, + .. + } + | Instruction::AtomicCompareExchangeWeak { + pointer, + value, + comparator, + .. + } => { + found_variables.insert(*pointer); + found_variables.insert(*value); + found_variables.insert(*comparator); + } + Instruction::ExtInst { operands, .. } => { + // We don't know which extended instructions take pointers, + // so we must interpret every operand as a pointer. + operands.iter().for_each(|&o| { + found_variables.insert(o); + }); + } + Instruction::FunctionEnd => return, + _ => {} + } + } + } +} + +/// Returns a `DescriptorRequirements` value for the pointed type. +/// +/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface +fn descriptor_requirements_of( + spirv: &Spirv, + pointed_ty: Id, + pointer_storage: &StorageClass, + force_combined_image_sampled: bool, +) -> Option { + let id_info = spirv.id(pointed_ty); + + match id_info.instruction() { + Instruction::TypeStruct { .. } => { + let decoration_block = id_info.iter_decoration().any(|instruction| { + matches!( + instruction, + Instruction::Decorate { + decoration: Decoration::Block, + .. + } + ) + }); + + let decoration_buffer_block = id_info.iter_decoration().any(|instruction| { + matches!( + instruction, + Instruction::Decorate { + decoration: Decoration::BufferBlock, + .. + } + ) + }); + + assert!( + decoration_block ^ decoration_buffer_block, + "Structs in shader interface are expected to be decorated with one of Block or BufferBlock" + ); + + let mut reqs = DescriptorRequirements { + descriptor_count: 1, + ..Default::default() + }; + + if decoration_buffer_block + || decoration_block && *pointer_storage == StorageClass::StorageBuffer + { + // Determine whether all members have a NonWritable decoration. + let nonwritable = id_info.iter_members().all(|member_info| { + member_info.iter_decoration().any(|instruction| { + matches!( + instruction, + Instruction::MemberDecorate { + decoration: Decoration::NonWritable, + .. + } + ) + }) + }); + + reqs.descriptor_types = vec![ + DescriptorType::StorageBuffer, + DescriptorType::StorageBufferDynamic, + ]; + reqs.mutable = !nonwritable; + } else { + reqs.descriptor_types = vec![ + DescriptorType::UniformBuffer, + DescriptorType::UniformBufferDynamic, + ]; + }; + + Some(reqs) + } + &Instruction::TypeImage { + ref dim, + arrayed, + ms, + sampled, + ref image_format, + .. + } => { + let multisampled = ms != 0; + assert!(sampled != 0, "Vulkan requires that variables of type OpTypeImage have a Sampled operand of 1 or 2"); + let format: Option = image_format.clone().into(); + + match dim { + Dim::SubpassData => { + assert!( + !force_combined_image_sampled, + "An OpTypeSampledImage can't point to \ + an OpTypeImage whose dimension is \ + SubpassData" + ); + assert!( + *image_format == ImageFormat::Unknown, + "If Dim is SubpassData, Image Format must be Unknown" + ); + assert!(sampled == 2, "If Dim is SubpassData, Sampled must be 2"); + assert!(arrayed == 0, "If Dim is SubpassData, Arrayed must be 0"); + + Some(DescriptorRequirements { + descriptor_types: vec![DescriptorType::InputAttachment], + descriptor_count: 1, + multisampled, + ..Default::default() + }) + } + Dim::Buffer => { + let mut reqs = DescriptorRequirements { + descriptor_count: 1, + format, + ..Default::default() + }; + + if sampled == 1 { + reqs.descriptor_types = vec![DescriptorType::UniformTexelBuffer]; + } else { + reqs.descriptor_types = vec![DescriptorType::StorageTexelBuffer]; + reqs.mutable = true; + } + + Some(reqs) + } + _ => { + let image_view_type = Some(match (dim, arrayed) { + (Dim::Dim1D, 0) => ImageViewType::Dim1d, + (Dim::Dim1D, 1) => ImageViewType::Dim1dArray, + (Dim::Dim2D, 0) => ImageViewType::Dim2d, + (Dim::Dim2D, 1) => ImageViewType::Dim2dArray, + (Dim::Dim3D, 0) => ImageViewType::Dim3d, + (Dim::Dim3D, 1) => panic!("Vulkan doesn't support arrayed 3D textures"), + (Dim::Cube, 0) => ImageViewType::Cube, + (Dim::Cube, 1) => ImageViewType::CubeArray, + (Dim::Rect, _) => panic!("Vulkan doesn't support rectangle textures"), + _ => unreachable!(), + }); + + let mut reqs = DescriptorRequirements { + descriptor_count: 1, + format, + multisampled, + image_view_type, + ..Default::default() + }; + + if force_combined_image_sampled { + assert!( + sampled == 1, + "A combined image sampler must not reference a storage image" + ); + + reqs.descriptor_types = vec![DescriptorType::CombinedImageSampler]; + } else { + if sampled == 1 { + reqs.descriptor_types = vec![DescriptorType::SampledImage]; + } else { + reqs.descriptor_types = vec![DescriptorType::StorageImage]; + reqs.mutable = true; + } + }; + + Some(reqs) + } + } + } + + &Instruction::TypeSampledImage { image_type, .. } => { + descriptor_requirements_of(spirv, image_type, pointer_storage, true) + } + + &Instruction::TypeSampler { .. } => Some(DescriptorRequirements { + descriptor_types: vec![DescriptorType::Sampler], + descriptor_count: 1, + ..Default::default() + }), + + &Instruction::TypeArray { + element_type, + length, + .. + } => { + let reqs = match descriptor_requirements_of(spirv, element_type, pointer_storage, false) + { + None => return None, + Some(v) => v, + }; + assert_eq!(reqs.descriptor_count, 1); // TODO: implement? + let len = match spirv.id(length).instruction() { + &Instruction::Constant { ref value, .. } => value, + _ => panic!("failed to find array length"), + }; + let len = len.iter().rev().fold(0, |a, &b| (a << 32) | b as u64); + + Some(DescriptorRequirements { + descriptor_count: len as u32, + ..reqs + }) + } + + &Instruction::TypeRuntimeArray { element_type, .. } => { + let reqs = match descriptor_requirements_of(spirv, element_type, pointer_storage, false) + { + None => return None, + Some(v) => v, + }; + assert_eq!(reqs.descriptor_count, 1); // TODO: implement? + + Some(DescriptorRequirements { + descriptor_count: 0, + ..reqs + }) + } + + _ => None, + } +} + +/// Extracts the `PipelineLayoutPcRange` from `spirv`. +fn push_constant_requirements(spirv: &Spirv, stage: ShaderStage) -> Option { + spirv + .iter_global() + .find_map(|instruction| match instruction { + &Instruction::TypePointer { + ty, + storage_class: StorageClass::PushConstant, + .. + } => { + let id_info = spirv.id(ty); + assert!(matches!( + id_info.instruction(), + Instruction::TypeStruct { .. } + )); + let start = offset_of_struct(spirv, ty); + let end = + size_of_type(spirv, ty).expect("Found runtime-sized push constants") as u32; + Some(PipelineLayoutPcRange { + offset: start, + size: end - start, + stages: stage.into(), + }) + } + _ => None, + }) +} + +/// Extracts the `SpecializationConstantRequirements` from `spirv`. +fn specialization_constant_requirements( + spirv: &Spirv, +) -> FnvHashMap { + 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, + }) + .and_then(|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"), + }; + Some((constant_id, SpecializationConstantRequirements { size })) + }), + _ => None, + } + }) + .collect() +} + +/// Extracts the `ShaderInterface` with the given storage class from `spirv`. +fn shader_interface( + spirv: &Spirv, + interface: &[Id], + filter_storage_class: StorageClass, + ignore_first_array: bool, +) -> ShaderInterface { + let elements: Vec<_> = interface + .iter() + .filter_map(|&id| { + let (result_type_id, result_id) = match spirv.id(id).instruction() { + &Instruction::Variable { + result_type_id, + result_id, + ref storage_class, + .. + } if storage_class == &filter_storage_class => (result_type_id, result_id), + _ => return None, + }; + + if is_builtin(spirv, result_id) { + return None; + } + + let id_info = spirv.id(result_id); + + let name = id_info + .iter_name() + .find_map(|instruction| match instruction { + Instruction::Name { name, .. } => Some(Cow::Owned(name.to_owned())), + _ => None, + }); + + let location = id_info + .iter_decoration() + .find_map(|instruction| match instruction { + Instruction::Decorate { + decoration: Decoration::Location { location }, + .. + } => Some(*location), + _ => None, + }) + .unwrap_or_else(|| { + panic!( + "Input/output variable with id {} (name {:?}) is missing a location", + result_id, name, + ) + }); + + let (format, num_locations) = format_of_type(spirv, result_type_id, ignore_first_array); + assert!(num_locations >= 1); + Some(ShaderInterfaceEntry { + location: location..location + num_locations, + format, + name, + }) + }) + .collect(); + + // Checking for overlapping elements. + for (offset, element1) in elements.iter().enumerate() { + for element2 in elements.iter().skip(offset + 1) { + if element1.location.start == element2.location.start + || (element1.location.start < element2.location.start + && element1.location.end > element2.location.start) + || (element2.location.start < element1.location.start + && element2.location.end > element1.location.start) + { + panic!( + "The locations of attributes `{:?}` ({}..{}) and `{:?}` ({}..{}) overlap", + element1.name, + element1.location.start, + element1.location.end, + element2.name, + element2.location.start, + element2.location.end, + ); + } + } + } + + ShaderInterface { elements } +} + +/// Returns the size of a type, or `None` if its size cannot be determined. +fn size_of_type(spirv: &Spirv, id: Id) -> Option { + let id_info = spirv.id(id); + + match id_info.instruction() { + Instruction::TypeBool { .. } => { + panic!("Can't put booleans in structs") + } + Instruction::TypeInt { width, .. } | Instruction::TypeFloat { width, .. } => { + assert!(width % 8 == 0); + Some(*width as DeviceSize / 8) + } + &Instruction::TypeVector { + component_type, + component_count, + .. + } => size_of_type(spirv, component_type) + .map(|component_size| component_size * component_count as DeviceSize), + &Instruction::TypeMatrix { + column_type, + column_count, + .. + } => { + // FIXME: row-major or column-major + size_of_type(spirv, column_type) + .map(|column_size| column_size * column_count as DeviceSize) + } + &Instruction::TypeArray { length, .. } => { + let stride = id_info + .iter_decoration() + .find_map(|instruction| match instruction { + Instruction::Decorate { + decoration: Decoration::ArrayStride { array_stride }, + .. + } => Some(*array_stride), + _ => None, + }) + .unwrap(); + let length = match spirv.id(length).instruction() { + &Instruction::Constant { ref value, .. } => Some( + value + .iter() + .rev() + .fold(0u64, |a, &b| (a << 32) | b as DeviceSize), + ), + _ => None, + } + .unwrap(); + + Some(stride as DeviceSize * length) + } + Instruction::TypeRuntimeArray { .. } => None, + Instruction::TypeStruct { member_types, .. } => { + let mut end_of_struct = 0; + + for (&member, member_info) in member_types.iter().zip(id_info.iter_members()) { + // Built-ins have an unknown size. + if member_info.iter_decoration().any(|instruction| { + matches!( + instruction, + Instruction::MemberDecorate { + decoration: Decoration::BuiltIn { .. }, + .. + } + ) + }) { + return None; + } + + // Some structs don't have `Offset` decorations, in the case they are used as local + // variables only. Ignoring these. + let offset = + member_info + .iter_decoration() + .find_map(|instruction| match instruction { + Instruction::MemberDecorate { + decoration: Decoration::Offset { byte_offset }, + .. + } => Some(*byte_offset), + _ => None, + })?; + let size = size_of_type(spirv, member)?; + end_of_struct = end_of_struct.max(offset as DeviceSize + size); + } + + Some(end_of_struct) + } + _ => panic!("Type {} not found", id), + } +} + +/// Returns the smallest offset of all members of a struct, or 0 if `id` is not a struct. +fn offset_of_struct(spirv: &Spirv, id: Id) -> u32 { + spirv + .id(id) + .iter_members() + .map(|member_info| { + member_info + .iter_decoration() + .find_map(|instruction| match instruction { + Instruction::MemberDecorate { + decoration: Decoration::Offset { byte_offset }, + .. + } => Some(*byte_offset), + _ => None, + }) + }) + .flatten() + .min() + .unwrap_or(0) +} + +/// Returns the vulkano `Format` and number of occupied locations from an id. +/// +/// If `ignore_first_array` is true, the function expects the outermost instruction to be +/// `OpTypeArray`. If it's the case, the OpTypeArray will be ignored. If not, the function will +/// panic. +fn format_of_type(spirv: &Spirv, id: Id, ignore_first_array: bool) -> (Format, u32) { + match spirv.id(id).instruction() { + &Instruction::TypeInt { + width, signedness, .. + } => { + assert!(!ignore_first_array); + let format = match (width, signedness) { + (8, 1) => Format::R8_SINT, + (8, 0) => Format::R8_UINT, + (16, 1) => Format::R16_SINT, + (16, 0) => Format::R16_UINT, + (32, 1) => Format::R32_SINT, + (32, 0) => Format::R32_UINT, + (64, 1) => Format::R64_SINT, + (64, 0) => Format::R64_UINT, + _ => panic!(), + }; + (format, 1) + } + &Instruction::TypeFloat { width, .. } => { + assert!(!ignore_first_array); + let format = match width { + 16 => Format::R16_SFLOAT, + 32 => Format::R32_SFLOAT, + 64 => Format::R64_SFLOAT, + _ => panic!(), + }; + (format, 1) + } + &Instruction::TypeVector { + component_type, + component_count, + .. + } => { + assert!(!ignore_first_array); + // TODO: Add handling of 64-bit types, which need special care. See the sections + // "Attribute Location and Component Assignment" and "Vertex Input Extraction" in the spec: + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap22.html#fxvertex-attrib-location + let format = match spirv.id(component_type).instruction() { + Instruction::TypeInt { + width, signedness, .. + } => match (component_count, width, signedness) { + (1, 8, 1) => Format::R8_SINT, + (1, 8, 0) => Format::R8_UINT, + (1, 16, 1) => Format::R16_SINT, + (1, 16, 0) => Format::R16_UINT, + (1, 32, 1) => Format::R32_SINT, + (1, 32, 0) => Format::R32_UINT, + (2, 8, 1) => Format::R8G8_SINT, + (2, 8, 0) => Format::R8G8_UINT, + (2, 16, 1) => Format::R16G16_SINT, + (2, 16, 0) => Format::R16G16_UINT, + (2, 32, 1) => Format::R32G32_SINT, + (2, 32, 0) => Format::R32G32_UINT, + (3, 8, 1) => Format::R8G8B8_SINT, + (3, 8, 0) => Format::R8G8B8_UINT, + (3, 16, 1) => Format::R16G16B16_SINT, + (3, 16, 0) => Format::R16G16B16_UINT, + (3, 32, 1) => Format::R32G32B32_SINT, + (3, 32, 0) => Format::R32G32B32_UINT, + (4, 8, 1) => Format::R8G8B8A8_SINT, + (4, 8, 0) => Format::R8G8B8A8_UINT, + (4, 16, 1) => Format::R16G16B16A16_SINT, + (4, 16, 0) => Format::R16G16B16A16_UINT, + (4, 32, 1) => Format::R32G32B32A32_SINT, + (4, 32, 0) => Format::R32G32B32A32_UINT, + _ => panic!(), + }, + &Instruction::TypeFloat { width, .. } => match (component_count, width) { + (1, 16) => Format::R16_SFLOAT, + (1, 32) => Format::R32_SFLOAT, + (2, 16) => Format::R16G16_SFLOAT, + (2, 32) => Format::R32G32_SFLOAT, + (3, 16) => Format::R16G16B16_SFLOAT, + (3, 32) => Format::R32G32B32_SFLOAT, + (4, 16) => Format::R16G16B16A16_SFLOAT, + (4, 32) => Format::R32G32B32A32_SFLOAT, + _ => panic!(), + }, + _ => panic!(), + }; + (format, 1) + } + &Instruction::TypeMatrix { + column_type, + column_count, + .. + } => { + assert!(!ignore_first_array); + let (format, num_locations) = format_of_type(spirv, column_type, false); + (format, num_locations * column_count) + } + &Instruction::TypeArray { + element_type, + length, + .. + } => { + if ignore_first_array { + format_of_type(spirv, element_type, false) + } else { + let (format, num_locations) = format_of_type(spirv, element_type, false); + let len = spirv + .instructions() + .iter() + .filter_map(|e| match e { + &Instruction::Constant { + result_id, + ref value, + .. + } if result_id == length => Some(value.clone()), + _ => None, + }) + .next() + .expect("failed to find array length"); + let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64) as u32; + (format, num_locations * len) + } + } + &Instruction::TypePointer { ty, .. } => format_of_type(spirv, ty, ignore_first_array), + _ => panic!("Type {} not found or invalid", id), + } +} + +/// Returns true if a `BuiltIn` decorator is applied on an id. +fn is_builtin(spirv: &Spirv, id: Id) -> bool { + let id_info = spirv.id(id); + + if id_info.iter_decoration().any(|instruction| { + matches!( + instruction, + Instruction::Decorate { + decoration: Decoration::BuiltIn { .. }, + .. + } + ) + }) { + return true; + } + + if id_info + .iter_members() + .flat_map(|member_info| member_info.iter_decoration()) + .any(|instruction| { + matches!( + instruction, + Instruction::MemberDecorate { + decoration: Decoration::BuiltIn { .. }, + .. + } + ) + }) + { + return true; + } + + match id_info.instruction() { + Instruction::Variable { result_type_id, .. } => { + return is_builtin(spirv, *result_type_id); + } + Instruction::TypeArray { element_type, .. } => { + return is_builtin(spirv, *element_type); + } + Instruction::TypeRuntimeArray { element_type, .. } => { + return is_builtin(spirv, *element_type); + } + Instruction::TypeStruct { member_types, .. } => { + if member_types.iter().any(|ty| is_builtin(spirv, *ty)) { + return true; + } + } + Instruction::TypePointer { ty, .. } => { + return is_builtin(spirv, *ty); + } + _ => (), + } + + false +} diff --git a/vulkano/src/spirv.rs b/vulkano/src/shader/spirv.rs similarity index 99% rename from vulkano/src/spirv.rs rename to vulkano/src/shader/spirv.rs index dea07a45d8..cefcd25ddb 100644 --- a/vulkano/src/spirv.rs +++ b/vulkano/src/shader/spirv.rs @@ -25,7 +25,7 @@ use std::{ }; // Generated by build.rs -include!(concat!(env!("OUT_DIR"), "/spirv.rs")); +include!(concat!(env!("OUT_DIR"), "/spirv_parse.rs")); /// A parsed and analyzed SPIR-V module. #[derive(Clone, Debug)] diff --git a/vulkano/src/sync/pipeline.rs b/vulkano/src/sync/pipeline.rs index 62cd95ce64..93ad31493c 100644 --- a/vulkano/src/sync/pipeline.rs +++ b/vulkano/src/sync/pipeline.rs @@ -108,6 +108,7 @@ pipeline_stages! { host, Host => ash::vk::PipelineStageFlags::HOST, ash::vk::QueueFlags::empty(); all_graphics, AllGraphics => ash::vk::PipelineStageFlags::ALL_GRAPHICS, ash::vk::QueueFlags::GRAPHICS; all_commands, AllCommands => ash::vk::PipelineStageFlags::ALL_COMMANDS, ash::vk::QueueFlags::empty(); + ray_tracing_shader, RayTracingShader => ash::vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR, ash::vk::QueueFlags::GRAPHICS | ash::vk::QueueFlags::COMPUTE | ash::vk::QueueFlags::TRANSFER; } macro_rules! access_flags {