Rewrite shader and specialization handling in pipelines (#2181)

* Rewrite shader and specialization handling in pipelines

* Make the shader loading in examples a bit cleaner

* Forgot some

* Fix incorrect color blend states in examples

* Nicer fix

* Use mem::discriminant

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>

---------

Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com>
This commit is contained in:
Rua 2023-04-18 20:53:08 +02:00 committed by GitHub
parent e29324493b
commit b7679f8bbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 3882 additions and 4088 deletions

View File

@ -28,6 +28,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -134,12 +135,14 @@ fn main() {
", ",
} }
} }
let shader = cs::load(device.clone()).unwrap(); let shader = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
ComputePipeline::new( ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
None, None,
|_| {}, |_| {},
) )

View File

@ -31,13 +31,17 @@ use vulkano::{
memory::allocator::StandardMemoryAllocator, memory::allocator::StandardMemoryAllocator,
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -202,9 +206,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -222,13 +223,27 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -24,12 +24,15 @@ use vulkano::{
graphics::{ graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState}, color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState},
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, Pipeline, PipelineBindPoint, GraphicsPipeline, Pipeline, PipelineBindPoint,
}, },
render_pass::Subpass, render_pass::Subpass,
shader::PipelineShaderStageCreateInfo,
}; };
use super::LightingVertex; use super::LightingVertex;
@ -81,15 +84,25 @@ impl AmbientLightingSystem {
.expect("failed to create buffer"); .expect("failed to create buffer");
let pipeline = { let pipeline = {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); let vs = vs::load(gfx_queue.device().clone())
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); .expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
let fs = fs::load(gfx_queue.device().clone())
.expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
GraphicsPipeline::start() GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(LightingVertex::per_vertex()) .vertex_input_state(LightingVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend( .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend(
AttachmentBlend { AttachmentBlend {
color_op: BlendOp::Add, color_op: BlendOp::Add,

View File

@ -25,12 +25,15 @@ use vulkano::{
graphics::{ graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState}, color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState},
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, Pipeline, PipelineBindPoint, GraphicsPipeline, Pipeline, PipelineBindPoint,
}, },
render_pass::Subpass, render_pass::Subpass,
shader::PipelineShaderStageCreateInfo,
}; };
use super::LightingVertex; use super::LightingVertex;
@ -84,15 +87,25 @@ impl DirectionalLightingSystem {
}; };
let pipeline = { let pipeline = {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); let vs = vs::load(gfx_queue.device().clone())
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); .expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
let fs = fs::load(gfx_queue.device().clone())
.expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
GraphicsPipeline::start() GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(LightingVertex::per_vertex()) .vertex_input_state(LightingVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend( .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend(
AttachmentBlend { AttachmentBlend {
color_op: BlendOp::Add, color_op: BlendOp::Add,

View File

@ -25,12 +25,15 @@ use vulkano::{
graphics::{ graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState}, color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState},
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, Pipeline, PipelineBindPoint, GraphicsPipeline, Pipeline, PipelineBindPoint,
}, },
render_pass::Subpass, render_pass::Subpass,
shader::PipelineShaderStageCreateInfo,
}; };
use super::LightingVertex; use super::LightingVertex;
@ -81,15 +84,25 @@ impl PointLightingSystem {
.expect("failed to create buffer"); .expect("failed to create buffer");
let pipeline = { let pipeline = {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); let vs = vs::load(gfx_queue.device().clone())
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); .expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
let fs = fs::load(gfx_queue.device().clone())
.expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
GraphicsPipeline::start() GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(LightingVertex::per_vertex()) .vertex_input_state(LightingVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend( .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend(
AttachmentBlend { AttachmentBlend {
color_op: BlendOp::Add, color_op: BlendOp::Add,

View File

@ -18,14 +18,18 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
depth_stencil::DepthStencilState, depth_stencil::DepthStencilState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::Subpass, render_pass::Subpass,
shader::PipelineShaderStageCreateInfo,
}; };
pub struct TriangleDrawSystem { pub struct TriangleDrawSystem {
@ -70,16 +74,27 @@ impl TriangleDrawSystem {
.expect("failed to create buffer"); .expect("failed to create buffer");
let pipeline = { let pipeline = {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); let vs = vs::load(gfx_queue.device().clone())
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); .expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
let fs = fs::load(gfx_queue.device().clone())
.expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
GraphicsPipeline::start() GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(TriangleVertex::per_vertex()) .vertex_input_state(TriangleVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.depth_stencil_state(DepthStencilState::simple_depth_test()) .depth_stencil_state(DepthStencilState::simple_depth_test())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass.clone()) .render_pass(subpass.clone())
.build(gfx_queue.device().clone()) .build(gfx_queue.device().clone())
.unwrap() .unwrap()

View File

@ -30,6 +30,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
DeviceSize, VulkanLibrary, DeviceSize, VulkanLibrary,
}; };
@ -118,11 +119,13 @@ fn main() {
} }
} }
let shader = shader::load(device.clone()).unwrap(); let shader = shader::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let pipeline = ComputePipeline::new( let pipeline = ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
None, None,
|layout_create_infos| { |layout_create_infos| {
let binding = layout_create_infos[0].bindings.get_mut(&0).unwrap(); let binding = layout_create_infos[0].bindings.get_mut(&0).unwrap();

View File

@ -32,6 +32,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo, InstanceExtensions}, instance::{Instance, InstanceCreateInfo, InstanceExtensions},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -149,8 +150,6 @@ fn main() {
} }
} }
let shader = cs::load(device.clone()).unwrap();
// Fetching subgroup size from the physical device properties to determine an appropriate // Fetching subgroup size from the physical device properties to determine an appropriate
// compute shader local size. // compute shader local size.
// //
@ -175,18 +174,24 @@ fn main() {
println!("Local size will be set to: ({local_size_x}, {local_size_y}, 1)"); println!("Local size will be set to: ({local_size_x}, {local_size_y}, 1)");
let spec_consts = cs::SpecializationConstants { let shader = cs::load(device.clone())
red: 0.2, .unwrap()
green: 0.5, .entry_point("main")
blue: 1.0, .unwrap();
// Specify the local size constants.
constant_1: local_size_x,
constant_2: local_size_y,
};
let pipeline = ComputePipeline::new( let pipeline = ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo {
&spec_consts, specialization_info: [
(0, 0.2f32.into()),
(1, local_size_x.into()),
(2, local_size_y.into()),
(3, 0.5f32.into()),
(4, 1.0f32.into()),
]
.into_iter()
.collect(),
..PipelineShaderStageCreateInfo::entry_point(shader)
},
None, None,
|_| {}, |_| {},
) )

View File

@ -38,6 +38,8 @@ mod linux {
graphics::{ graphics::{
color_blend::ColorBlendState, color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Scissor, Viewport, ViewportState}, viewport::{Scissor, Viewport, ViewportState},
}, },
@ -45,6 +47,7 @@ mod linux {
}, },
render_pass::{Framebuffer, RenderPass, Subpass}, render_pass::{Framebuffer, RenderPass, Subpass},
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -582,9 +585,6 @@ mod linux {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!(device.clone(), let render_pass = vulkano::single_pass_renderpass!(device.clone(),
attachments: { attachments: {
color: { color: {
@ -612,11 +612,21 @@ mod linux {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(MyVertex::per_vertex()) .vertex_input_state(MyVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state( .input_assembly_state(
InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip), InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip),
) )
@ -624,8 +634,9 @@ mod linux {
scissors: (0..1).map(|_| Scissor::irrelevant()).collect(), scissors: (0..1).map(|_| Scissor::irrelevant()).collect(),
viewport_count_dynamic: false, viewport_count_dynamic: false,
}) })
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.color_blend_state(ColorBlendState::new(1).blend_alpha()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass) .render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -34,6 +34,8 @@ use vulkano::{
graphics::{ graphics::{
color_blend::ColorBlendState, color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -41,6 +43,7 @@ use vulkano::{
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -194,9 +197,6 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!(device.clone(), let render_pass = vulkano::single_pass_renderpass!(device.clone(),
attachments: { attachments: {
color: { color: {
@ -334,13 +334,26 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass) .render_pass(subpass)
.build(device.clone()) .build(device.clone())

View File

@ -32,6 +32,8 @@ use vulkano::{
graphics::{ graphics::{
color_blend::ColorBlendState, color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -39,6 +41,7 @@ use vulkano::{
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -192,9 +195,6 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -260,13 +260,25 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass) .render_pass(subpass)
.build(device.clone()) .build(device.clone())

View File

@ -41,6 +41,8 @@ use vulkano::{
graphics::{ graphics::{
color_blend::ColorBlendState, color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -48,6 +50,7 @@ use vulkano::{
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -198,9 +201,6 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -266,13 +266,25 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass) .render_pass(subpass)
.with_auto_layout(device.clone(), |layout_create_infos| { .with_auto_layout(device.clone(), |layout_create_infos| {

View File

@ -45,13 +45,17 @@ use vulkano::{
memory::allocator::StandardMemoryAllocator, memory::allocator::StandardMemoryAllocator,
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint, ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::PipelineShaderStageCreateInfo,
single_pass_renderpass, single_pass_renderpass,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
@ -242,10 +246,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let cs = cs::load(device.clone()).unwrap();
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
// Each frame we generate a new set of vertices and each frame we need a new // Each frame we generate a new set of vertices and each frame we need a new
@ -265,10 +265,13 @@ fn main() {
}, },
); );
let cs = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let compute_pipeline = ComputePipeline::new( let compute_pipeline = ComputePipeline::new(
device.clone(), device.clone(),
cs.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(cs),
&(),
None, None,
|_| {}, |_| {},
) )
@ -300,13 +303,27 @@ fn main() {
position: [f32; 2], position: [f32; 2],
} }
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let render_pipeline = GraphicsPipeline::start() let render_pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -28,13 +28,17 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::PipelineShaderStageCreateInfo,
single_pass_renderpass, single_pass_renderpass,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
@ -272,9 +276,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = single_pass_renderpass!( let render_pass = single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -292,15 +293,29 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
// Use the implementations of the `Vertex` trait to describe to vulkano how the two vertex // Use the implementations of the `Vertex` trait to describe to vulkano how the two vertex
// types are expected to be used. // types are expected to be used.
.vertex_input_state([TriangleVertex::per_vertex(), InstanceData::per_instance()]) .vertex_input_state([TriangleVertex::per_vertex(), InstanceData::per_instance()])
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -23,6 +23,7 @@ use vulkano::{
image::ImageAccess, image::ImageAccess,
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::GpuFuture, sync::GpuFuture,
}; };
use vulkano_util::renderer::DeviceImageView; use vulkano_util::renderer::DeviceImageView;
@ -71,11 +72,13 @@ impl FractalComputePipeline {
let end_color = [0.0; 4]; let end_color = [0.0; 4];
let pipeline = { let pipeline = {
let shader = cs::load(queue.device().clone()).unwrap(); let shader = cs::load(queue.device().clone())
.unwrap()
.entry_point("main")
.unwrap();
ComputePipeline::new( ComputePipeline::new(
queue.device().clone(), queue.device().clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
None, None,
|_| {}, |_| {},
) )

View File

@ -22,7 +22,10 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryUsage}, memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryUsage},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -30,6 +33,7 @@ use vulkano::{
}, },
render_pass::Subpass, render_pass::Subpass,
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode},
shader::PipelineShaderStageCreateInfo,
}; };
/// Vertex for textured quads. /// Vertex for textured quads.
@ -114,14 +118,26 @@ impl PixelsDrawPipeline {
.unwrap(); .unwrap();
let pipeline = { let pipeline = {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); let vs = vs::load(gfx_queue.device().clone())
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); .expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
let fs = fs::load(gfx_queue.device().clone())
.expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
GraphicsPipeline::start() GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(TexturedVertex::per_vertex()) .vertex_input_state(TexturedVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.fragment_shader(fs.entry_point("main").unwrap(), ())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass.clone()) .render_pass(subpass.clone())
.build(gfx_queue.device().clone()) .build(gfx_queue.device().clone())
.unwrap() .unwrap()

View File

@ -79,13 +79,17 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState,
multisample::MultisampleState, multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, Subpass},
shader::PipelineShaderStageCreateInfo,
sync::GpuFuture, sync::GpuFuture,
VulkanLibrary, VulkanLibrary,
}; };
@ -263,9 +267,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
#[derive(BufferContents, Vertex)] #[derive(BufferContents, Vertex)]
#[repr(C)] #[repr(C)]
struct Vertex { struct Vertex {
@ -298,16 +299,29 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass, 0).unwrap(); let subpass = Subpass::from(render_pass, 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState { .multisample_state(MultisampleState {
rasterization_samples: subpass.num_samples().unwrap(), rasterization_samples: subpass.num_samples().unwrap(),
..Default::default() ..Default::default()
}) })
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass) .render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -32,13 +32,17 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo,
SwapchainCreationError, SwapchainPresentInfo, SwapchainCreationError, SwapchainPresentInfo,
@ -241,9 +245,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -261,13 +262,27 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -25,6 +25,7 @@ use vulkano::{
image::{ImageAccess, ImageUsage, StorageImage}, image::{ImageAccess, ImageUsage, StorageImage},
memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryUsage}, memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryUsage},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::GpuFuture, sync::GpuFuture,
}; };
use vulkano_util::renderer::DeviceImageView; use vulkano_util::renderer::DeviceImageView;
@ -68,11 +69,13 @@ impl GameOfLifeComputePipeline {
let life_out = rand_grid(memory_allocator, size); let life_out = rand_grid(memory_allocator, size);
let compute_life_pipeline = { let compute_life_pipeline = {
let shader = compute_life_cs::load(compute_queue.device().clone()).unwrap(); let shader = compute_life_cs::load(compute_queue.device().clone())
.unwrap()
.entry_point("main")
.unwrap();
ComputePipeline::new( ComputePipeline::new(
compute_queue.device().clone(), compute_queue.device().clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
None, None,
|_| {}, |_| {},
) )

View File

@ -23,7 +23,10 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage}, memory::allocator::{AllocationCreateInfo, MemoryUsage},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -31,6 +34,7 @@ use vulkano::{
}, },
render_pass::Subpass, render_pass::Subpass,
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode},
shader::PipelineShaderStageCreateInfo,
}; };
/// Vertex for textured quads. /// Vertex for textured quads.
@ -110,14 +114,26 @@ impl PixelsDrawPipeline {
.unwrap(); .unwrap();
let pipeline = { let pipeline = {
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); let vs = vs::load(gfx_queue.device().clone())
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); .expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
let fs = fs::load(gfx_queue.device().clone())
.expect("failed to create shader module")
.entry_point("main")
.expect("shader entry point not found");
GraphicsPipeline::start() GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(TexturedVertex::per_vertex()) .vertex_input_state(TexturedVertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.fragment_shader(fs.entry_point("main").unwrap(), ())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass.clone()) .render_pass(subpass.clone())
.build(gfx_queue.device().clone()) .build(gfx_queue.device().clone())
.unwrap() .unwrap()

View File

@ -32,7 +32,10 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -42,6 +45,7 @@ use vulkano::{
AttachmentDescription, AttachmentReference, Framebuffer, FramebufferCreateInfo, LoadOp, AttachmentDescription, AttachmentReference, Framebuffer, FramebufferCreateInfo, LoadOp,
RenderPass, RenderPassCreateInfo, StoreOp, Subpass, SubpassDescription, RenderPass, RenderPassCreateInfo, StoreOp, Subpass, SubpassDescription,
}, },
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -214,9 +218,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass_description = RenderPassCreateInfo { let render_pass_description = RenderPassCreateInfo {
attachments: vec![AttachmentDescription { attachments: vec![AttachmentDescription {
format: Some(image.format()), format: Some(image.format()),
@ -257,10 +258,22 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass, 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([ .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([
Viewport { Viewport {
origin: [0.0, 0.0], origin: [0.0, 0.0],
@ -271,8 +284,10 @@ fn main() {
depth_range: 0.0..1.0, depth_range: 0.0..1.0,
}, },
])) ]))
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.render_pass(Subpass::from(render_pass, 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -28,8 +28,11 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
depth_stencil::DepthStencilState, depth_stencil::DepthStencilState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -37,6 +40,7 @@ use vulkano::{
}, },
query::{QueryControlFlags, QueryPool, QueryPoolCreateInfo, QueryResultFlags, QueryType}, query::{QueryControlFlags, QueryPool, QueryPoolCreateInfo, QueryResultFlags, QueryType},
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -276,9 +280,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -302,17 +303,31 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .multisample_state(MultisampleState::default())
// Enable depth testing, which is needed for occlusion queries to make sense at all. If you // Enable depth testing, which is needed for occlusion queries to make sense at all. If you
// disable depth testing, every pixel is considered to pass the depth test, so every query // disable depth testing, every pixel is considered to pass the depth test, so every query
// will return a nonzero result. // will return a nonzero result.
.depth_stencil_state(DepthStencilState::simple_depth_test()) .depth_stencil_state(DepthStencilState::simple_depth_test())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -35,6 +35,7 @@ use vulkano::{
}, },
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
pipeline::{cache::PipelineCache, ComputePipeline}, pipeline::{cache::PipelineCache, ComputePipeline},
shader::PipelineShaderStageCreateInfo,
VulkanLibrary, VulkanLibrary,
}; };
@ -127,11 +128,13 @@ fn main() {
", ",
} }
} }
let shader = cs::load(device.clone()).unwrap(); let shader = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
ComputePipeline::new( ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
Some(pipeline_cache.clone()), Some(pipeline_cache.clone()),
|_| {}, |_| {},
) )

View File

@ -27,6 +27,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -115,11 +116,13 @@ fn main() {
} }
} }
let shader = cs::load(device.clone()).unwrap(); let shader = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let pipeline = ComputePipeline::new( let pipeline = ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
None, None,
|_| {}, |_| {},
) )

View File

@ -30,6 +30,8 @@ use vulkano::{
graphics::{ graphics::{
color_blend::ColorBlendState, color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -37,6 +39,7 @@ use vulkano::{
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -188,9 +191,6 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -255,13 +255,25 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass) .render_pass(subpass)
.with_auto_layout(device.clone(), |layout_create_infos| { .with_auto_layout(device.clone(), |layout_create_infos| {

View File

@ -37,7 +37,9 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::{CullMode, FrontFace, RasterizationState}, rasterization::{CullMode, FrontFace, RasterizationState},
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
@ -45,7 +47,7 @@ use vulkano::{
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::ShaderModule, shader::{PipelineShaderStageCreateInfo, ShaderModule},
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -186,7 +188,8 @@ fn main() {
// Create a ShaderModule on a device the same Shader::load does it. // Create a ShaderModule on a device the same Shader::load does it.
// NOTE: You will have to verify correctness of the data by yourself! // NOTE: You will have to verify correctness of the data by yourself!
unsafe { ShaderModule::from_bytes(device.clone(), &v) }.unwrap() let module = unsafe { ShaderModule::from_bytes(device.clone(), &v).unwrap() };
module.entry_point("main").unwrap()
}; };
let fs = { let fs = {
@ -195,21 +198,26 @@ fn main() {
let mut v = vec![]; let mut v = vec![];
f.read_to_end(&mut v).unwrap(); f.read_to_end(&mut v).unwrap();
unsafe { ShaderModule::from_bytes(device.clone(), &v) }.unwrap() let module = unsafe { ShaderModule::from_bytes(device.clone(), &v).unwrap() };
module.entry_point("main").unwrap()
}; };
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let graphics_pipeline = GraphicsPipeline::start() let graphics_pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ())
.rasterization_state( .rasterization_state(
RasterizationState::new() RasterizationState::new()
.cull_mode(CullMode::Front) .cull_mode(CullMode::Front)
.front_face(FrontFace::CounterClockwise), .front_face(FrontFace::CounterClockwise),
) )
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -35,6 +35,9 @@ use vulkano::{
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState, color_blend::ColorBlendState,
input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -43,6 +46,7 @@ use vulkano::{
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -255,9 +259,6 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -352,11 +353,20 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let pipeline_layout = { let pipeline_layout = {
let mut layout_create_infos: Vec<_> = DescriptorSetLayoutCreateInfo::from_requirements( let mut layout_create_infos: Vec<_> = DescriptorSetLayoutCreateInfo::from_requirements(
fs.entry_point("main") fs.info()
.unwrap() .descriptor_binding_requirements
.descriptor_binding_requirements(), .iter()
.map(|(k, v)| (*k, v)),
); );
// Set 0, Binding 0. // Set 0, Binding 0.
@ -375,11 +385,10 @@ fn main() {
PipelineLayoutCreateInfo { PipelineLayoutCreateInfo {
set_layouts, set_layouts,
push_constant_ranges: fs push_constant_ranges: fs
.entry_point("main") .info()
.unwrap() .push_constant_requirements
.push_constant_requirements() .iter()
.cloned() .cloned()
.into_iter()
.collect(), .collect(),
..Default::default() ..Default::default()
}, },
@ -389,10 +398,15 @@ fn main() {
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass) .render_pass(subpass)
.with_pipeline_layout(device.clone(), pipeline_layout) .with_pipeline_layout(device.clone(), pipeline_layout)

View File

@ -27,6 +27,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -107,12 +108,14 @@ fn main() {
", ",
} }
} }
let shader = cs::load(device.clone()).unwrap(); let shader = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
ComputePipeline::new( ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
None, None,
|_| {}, |_| {},
) )

View File

@ -26,6 +26,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -115,11 +116,13 @@ fn main() {
"#, "#,
} }
} }
let shader = cs::load(device.clone()).unwrap(); let shader = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
ComputePipeline::new( ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
None, None,
|_| {}, |_| {},
) )

View File

@ -41,6 +41,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -118,11 +119,6 @@ fn main() {
// such types, and include it in each shader entry-point file using the `#include` // such types, and include it in each shader entry-point file using the `#include`
// directive. // directive.
shaders: { shaders: {
// Generate single unique `SpecializationConstants` struct for all shaders, since
// their specialization interfaces are the same. This option is turned off by
// default and the macro by default produces unique structs
// (`MultSpecializationConstants` and `AddSpecializationConstants` in this case).
shared_constants: true,
mult: { mult: {
ty: "compute", ty: "compute",
src: r" src: r"
@ -179,7 +175,6 @@ fn main() {
// The macro will create the following things in this module: // The macro will create the following things in this module:
// - `load_mult` for the first shader loader/entry-point. // - `load_mult` for the first shader loader/entry-point.
// - `load_add` for the second shader loader/entry-point. // - `load_add` for the second shader loader/entry-point.
// - `SpecializationConstants` struct for both shaders' specialization constants.
// - `Parameters` struct common for both shaders. // - `Parameters` struct common for both shaders.
} }
@ -251,26 +246,32 @@ fn main() {
.unwrap(); .unwrap();
// Load the first shader, and create a pipeline for the shader. // Load the first shader, and create a pipeline for the shader.
let mult_shader = shaders::load_mult(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let mult_pipeline = ComputePipeline::new( let mult_pipeline = ComputePipeline::new(
device.clone(), device.clone(),
shaders::load_mult(device.clone()) PipelineShaderStageCreateInfo {
.unwrap() specialization_info: [(0, true.into())].into_iter().collect(),
.entry_point("main") ..PipelineShaderStageCreateInfo::entry_point(mult_shader)
.unwrap(), },
&shaders::SpecializationConstants { enabled: 1 },
None, None,
|_| {}, |_| {},
) )
.unwrap(); .unwrap();
// Load the second shader, and create a pipeline for the shader. // Load the second shader, and create a pipeline for the shader.
let add_shader = shaders::load_add(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let add_pipeline = ComputePipeline::new( let add_pipeline = ComputePipeline::new(
device.clone(), device,
shaders::load_add(device) PipelineShaderStageCreateInfo {
.unwrap() specialization_info: [(0, true.into())].into_iter().collect(),
.entry_point("main") ..PipelineShaderStageCreateInfo::entry_point(add_shader)
.unwrap(), },
&shaders::SpecializationConstants { enabled: 1 },
None, None,
|_| {}, |_| {},
) )

View File

@ -31,13 +31,17 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, PipelineBindPoint, GraphicsPipeline, PipelineBindPoint,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, Subpass},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, PresentMode, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, acquire_next_image, PresentMode, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo,
}, },
@ -317,10 +321,6 @@ fn main() {
} }
} }
let cs = cs::load(device.clone()).unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let memory_allocator = StandardMemoryAllocator::new_default(device.clone()); let memory_allocator = StandardMemoryAllocator::new_default(device.clone());
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone()); let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
let command_buffer_allocator = let command_buffer_allocator =
@ -409,10 +409,13 @@ fn main() {
}; };
// Create a compute-pipeline for applying the compute shader to vertices. // Create a compute-pipeline for applying the compute shader to vertices.
let cs = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let compute_pipeline = vulkano::pipeline::ComputePipeline::new( let compute_pipeline = vulkano::pipeline::ComputePipeline::new(
device.clone(), device.clone(),
cs.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(cs),
&(),
None, None,
|_| {}, |_| {},
) )
@ -444,14 +447,28 @@ fn main() {
}; };
// Create a basic graphics pipeline for rendering particles. // Create a basic graphics pipeline for rendering particles.
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass, 0).unwrap();
let graphics_pipeline = GraphicsPipeline::start() let graphics_pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
// Vertices will be rendered as a list of points. // Vertices will be rendered as a list of points.
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PointList)) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PointList))
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([viewport])) .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([viewport]))
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.render_pass(Subpass::from(render_pass, 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -24,6 +24,7 @@ use vulkano::{
instance::{Instance, InstanceCreateInfo}, instance::{Instance, InstanceCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture}, sync::{self, GpuFuture},
VulkanLibrary, VulkanLibrary,
}; };
@ -110,17 +111,18 @@ fn main() {
} }
} }
let shader = cs::load(device.clone()).unwrap(); let shader = cs::load(device.clone())
.unwrap()
let spec_consts = cs::SpecializationConstants { .entry_point("main")
enable: 1, .unwrap();
multiple: 1,
addend: 1.0,
};
let pipeline = ComputePipeline::new( let pipeline = ComputePipeline::new(
device.clone(), device.clone(),
shader.entry_point("main").unwrap(), PipelineShaderStageCreateInfo {
&spec_consts, specialization_info: [(0, 1i32.into()), (1, 1.0f32.into()), (2, true.into())]
.into_iter()
.collect(),
..PipelineShaderStageCreateInfo::entry_point(shader)
},
None, None,
|_| {}, |_| {},
) )

View File

@ -32,15 +32,18 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
depth_stencil::DepthStencilState, depth_stencil::DepthStencilState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, Pipeline, PipelineBindPoint, GraphicsPipeline, Pipeline, PipelineBindPoint,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::ShaderModule, shader::{EntryPoint, PipelineShaderStageCreateInfo},
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -208,9 +211,6 @@ fn main() {
}, },
); );
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -234,8 +234,22 @@ fn main() {
) )
.unwrap(); .unwrap();
let (mut pipeline, mut framebuffers) = let vs = vs::load(device.clone())
window_size_dependent_setup(&memory_allocator, &vs, &fs, &images, render_pass.clone()); .unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let (mut pipeline, mut framebuffers) = window_size_dependent_setup(
&memory_allocator,
vs.clone(),
fs.clone(),
&images,
render_pass.clone(),
);
let mut recreate_swapchain = false; let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
@ -282,8 +296,8 @@ fn main() {
swapchain = new_swapchain; swapchain = new_swapchain;
let (new_pipeline, new_framebuffers) = window_size_dependent_setup( let (new_pipeline, new_framebuffers) = window_size_dependent_setup(
&memory_allocator, &memory_allocator,
&vs, vs.clone(),
&fs, fs.clone(),
&new_images, &new_images,
render_pass.clone(), render_pass.clone(),
); );
@ -418,8 +432,8 @@ fn main() {
/// This function is called once during initialization, then again whenever the window is resized. /// This function is called once during initialization, then again whenever the window is resized.
fn window_size_dependent_setup( fn window_size_dependent_setup(
memory_allocator: &StandardMemoryAllocator, memory_allocator: &StandardMemoryAllocator,
vs: &ShaderModule, vs: EntryPoint,
fs: &ShaderModule, fs: EntryPoint,
images: &[Arc<SwapchainImage>], images: &[Arc<SwapchainImage>],
render_pass: Arc<RenderPass>, render_pass: Arc<RenderPass>,
) -> (Arc<GraphicsPipeline>, Vec<Arc<Framebuffer>>) { ) -> (Arc<GraphicsPipeline>, Vec<Arc<Framebuffer>>) {
@ -449,10 +463,14 @@ fn window_size_dependent_setup(
// teapot example, we recreate the pipelines with a hardcoded viewport instead. This allows the // teapot example, we recreate the pipelines with a hardcoded viewport instead. This allows the
// driver to optimize things, at the cost of slower window resizes. // driver to optimize things, at the cost of slower window resizes.
// https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport // https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport
let subpass = Subpass::from(render_pass, 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state([Position::per_vertex(), Normal::per_vertex()]) .vertex_input_state([Position::per_vertex(), Normal::per_vertex()])
.vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::default())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([ .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([
Viewport { Viewport {
origin: [0.0, 0.0], origin: [0.0, 0.0],
@ -460,9 +478,11 @@ fn window_size_dependent_setup(
depth_range: 0.0..1.0, depth_range: 0.0..1.0,
}, },
])) ]))
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.depth_stencil_state(DepthStencilState::simple_depth_test()) .depth_stencil_state(DepthStencilState::simple_depth_test())
.render_pass(Subpass::from(render_pass, 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(memory_allocator.device().clone()) .build(memory_allocator.device().clone())
.unwrap(); .unwrap();

View File

@ -37,7 +37,9 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::{PolygonMode, RasterizationState}, rasterization::{PolygonMode, RasterizationState},
tessellation::TessellationState, tessellation::TessellationState,
vertex_input::Vertex, vertex_input::Vertex,
@ -46,6 +48,7 @@ use vulkano::{
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -315,11 +318,6 @@ fn main() {
) )
.unwrap(); .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!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -337,18 +335,32 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let tcs = tcs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let tes = tes::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(tcs),
PipelineShaderStageCreateInfo::entry_point(tes),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
// Actually use the tessellation shaders.
.tessellation_shaders(
tcs.entry_point("main").unwrap(),
(),
tes.entry_point("main").unwrap(),
(),
)
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PatchList)) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PatchList))
.rasterization_state(RasterizationState::new().polygon_mode(PolygonMode::Line))
.tessellation_state( .tessellation_state(
TessellationState::new() TessellationState::new()
// Use a patch_control_points of 3, because we want to convert one triangle into // Use a patch_control_points of 3, because we want to convert one triangle into
@ -357,8 +369,10 @@ fn main() {
.patch_control_points(3), .patch_control_points(3),
) )
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::new().polygon_mode(PolygonMode::Line))
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -32,6 +32,8 @@ use vulkano::{
graphics::{ graphics::{
color_blend::ColorBlendState, color_blend::ColorBlendState,
input_assembly::{InputAssemblyState, PrimitiveTopology}, input_assembly::{InputAssemblyState, PrimitiveTopology},
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
@ -39,6 +41,7 @@ use vulkano::{
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
sampler::{Sampler, SamplerCreateInfo}, sampler::{Sampler, SamplerCreateInfo},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -194,9 +197,6 @@ fn main() {
) )
.unwrap(); .unwrap();
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
let render_pass = vulkano::single_pass_renderpass!( let render_pass = vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
@ -265,13 +265,25 @@ fn main() {
let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap();
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
.stages([
PipelineShaderStageCreateInfo::entry_point(vs),
PipelineShaderStageCreateInfo::entry_point(fs),
])
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state(RasterizationState::default())
.multisample_state(MultisampleState::default())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass) .render_pass(subpass)
.build(device.clone()) .build(device.clone())

View File

@ -37,7 +37,10 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
render_pass::PipelineRenderingCreateInfo, render_pass::PipelineRenderingCreateInfo,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
@ -45,6 +48,7 @@ use vulkano::{
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{LoadOp, StoreOp}, render_pass::{LoadOp, StoreOp},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -369,37 +373,60 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
// At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL // 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 // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this
// manually. // manually.
// A Vulkan shader can in theory contain multiple entry points, so we have to specify which
// one.
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
// We describe the formats of attachment images where the colors, depth and/or stencil
// information will be written. The pipeline will only be usable with this particular
// configuration of the attachment images.
let subpass = PipelineRenderingCreateInfo {
// We specify a single color attachment that will be rendered to. When we begin
// rendering, we will specify a swapchain image to be used as this attachment, so here
// we set its format to be the same format as the swapchain.
color_attachment_formats: vec![Some(swapchain.image_format())],
..Default::default()
};
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
// program, but much more specific. // program, but much more specific.
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
// We describe the formats of attachment images where the colors, depth and/or stencil // Specify the shader stages that the pipeline will have.
// information will be written. The pipeline will only be usable with this particular .stages([
// configuration of the attachment images. PipelineShaderStageCreateInfo::entry_point(vs),
.render_pass(PipelineRenderingCreateInfo { PipelineShaderStageCreateInfo::entry_point(fs),
// We specify a single color attachment that will be rendered to. When we begin ])
// rendering, we will specify a swapchain image to be used as this attachment, so here // How vertex data is read from the vertex buffers into the vertex shader.
// we set its format to be the same format as the swapchain.
color_attachment_formats: vec![Some(swapchain.image_format())],
..Default::default()
})
// We need to indicate the layout of the vertices.
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
// The content of the vertex buffer describes a list of triangles. // How vertices are arranged into primitive shapes.
.input_assembly_state(InputAssemblyState::new()) // The default primitive shape is a triangle.
// A Vulkan shader can in theory contain multiple entry points, so we have to specify which .input_assembly_state(InputAssemblyState::default())
// one. // How primitives are transformed and clipped to fit the framebuffer.
.vertex_shader(vs.entry_point("main").unwrap(), ()) // We use a resizable viewport, set to draw over the entire window.
// Use a resizable viewport set to draw over the entire window
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
// See `vertex_shader`. // How polygons are culled and converted into a raster of pixels.
.fragment_shader(fs.entry_point("main").unwrap(), ()) // The default value does not perform any culling.
.rasterization_state(RasterizationState::default())
// How multiple fragment shader samples are converted to a single pixel value.
// The default value does not perform any multisampling.
.multisample_state(MultisampleState::default())
// How pixel values are combined with the values already present in the framebuffer.
// The default value overwrites the old value with the new one, without any blending.
.color_blend_state(ColorBlendState::new(
subpass.color_attachment_formats.len() as u32
))
.render_pass(subpass)
// Now that our builder is filled, we call `build()` to obtain an actual pipeline. // Now that our builder is filled, we call `build()` to obtain an actual pipeline.
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -32,13 +32,17 @@ use vulkano::{
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ pipeline::{
graphics::{ graphics::{
color_blend::ColorBlendState,
input_assembly::InputAssemblyState, input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::Vertex, vertex_input::Vertex,
viewport::{Viewport, ViewportState}, viewport::{Viewport, ViewportState},
}, },
GraphicsPipeline, GraphicsPipeline,
}, },
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::PipelineShaderStageCreateInfo,
swapchain::{ swapchain::{
acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError,
SwapchainPresentInfo, SwapchainPresentInfo,
@ -338,9 +342,6 @@ fn main() {
} }
} }
let vs = vs::load(device.clone()).unwrap();
let fs = fs::load(device.clone()).unwrap();
// At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL // 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 // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this
// manually. // manually.
@ -380,23 +381,47 @@ fn main() {
) )
.unwrap(); .unwrap();
// A Vulkan shader can in theory contain multiple entry points, so we have to specify which
// one.
let vs = vs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
let fs = fs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
// We have to indicate which subpass of which render pass this pipeline is going to be used
// in. The pipeline will only be usable from this particular subpass.
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
// program, but much more specific. // program, but much more specific.
let pipeline = GraphicsPipeline::start() let pipeline = GraphicsPipeline::start()
// We have to indicate which subpass of which render pass this pipeline is going to be used // Specify the shader stages that the pipeline will have.
// in. The pipeline will only be usable from this particular subpass. .stages([
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) PipelineShaderStageCreateInfo::entry_point(vs),
// We need to indicate the layout of the vertices. PipelineShaderStageCreateInfo::entry_point(fs),
])
// How vertex data is read from the vertex buffers into the vertex shader.
.vertex_input_state(Vertex::per_vertex()) .vertex_input_state(Vertex::per_vertex())
// The content of the vertex buffer describes a list of triangles. // How vertices are arranged into primitive shapes.
.input_assembly_state(InputAssemblyState::new()) // The default primitive shape is a triangle.
// A Vulkan shader can in theory contain multiple entry points, so we have to specify .input_assembly_state(InputAssemblyState::default())
// which one. // How primitives are transformed and clipped to fit the framebuffer.
.vertex_shader(vs.entry_point("main").unwrap(), ()) // We use a resizable viewport, set to draw over the entire window.
// Use a resizable viewport set to draw over the entire window
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
// See `vertex_shader`. // How polygons are culled and converted into a raster of pixels.
.fragment_shader(fs.entry_point("main").unwrap(), ()) // The default value does not perform any culling.
.rasterization_state(RasterizationState::default())
// How multiple fragment shader samples are converted to a single pixel value.
// The default value does not perform any multisampling.
.multisample_state(MultisampleState::default())
// How pixel values are combined with the values already present in the framebuffer.
// The default value overwrites the old value with the new one, without any blending.
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()))
.render_pass(subpass)
// Now that our builder is filled, we call `build()` to obtain an actual pipeline. // Now that our builder is filled, we call `build()` to obtain an actual pipeline.
.build(device.clone()) .build(device.clone())
.unwrap(); .unwrap();

View File

@ -258,11 +258,8 @@ pub(super) fn reflect(
quote! { &::vulkano::shader::spirv::Capability::#name } quote! { &::vulkano::shader::spirv::Capability::#name }
}); });
let spirv_extensions = reflect::spirv_extensions(&shader.spirv); let spirv_extensions = reflect::spirv_extensions(&shader.spirv);
let entry_points = reflect::entry_points(&shader.spirv) let entry_points =
.map(|(name, model, info)| entry_point::write_entry_point(&name, model, &info)); reflect::entry_points(&shader.spirv).map(|info| entry_point::write_entry_point(&info));
let specialization_constants =
structs::write_specialization_constants(input, &shader, type_registry)?;
let load_name = if shader.name.is_empty() { let load_name = if shader.name.is_empty() {
format_ident!("load") format_ident!("load")
@ -278,7 +275,7 @@ pub(super) fn reflect(
device: ::std::sync::Arc<::vulkano::device::Device>, device: ::std::sync::Arc<::vulkano::device::Device>,
) -> ::std::result::Result< ) -> ::std::result::Result<
::std::sync::Arc<::vulkano::shader::ShaderModule>, ::std::sync::Arc<::vulkano::shader::ShaderModule>,
::vulkano::shader::ShaderCreationError, ::vulkano::shader::ShaderModuleCreationError,
> { > {
let _bytes = ( #( #include_bytes ),* ); let _bytes = ( #( #include_bytes ),* );
@ -295,8 +292,6 @@ pub(super) fn reflect(
) )
} }
} }
#specialization_constants
}; };
let structs = structs::write_structs(input, &shader, type_registry)?; let structs = structs::write_structs(input, &shader, type_registry)?;
@ -589,7 +584,7 @@ mod tests {
let spirv = Spirv::new(&instructions).unwrap(); let spirv = Spirv::new(&instructions).unwrap();
let mut descriptors = Vec::new(); let mut descriptors = Vec::new();
for (_, _, info) in reflect::entry_points(&spirv) { for info in reflect::entry_points(&spirv) {
descriptors.push(info.descriptor_binding_requirements); descriptors.push(info.descriptor_binding_requirements);
} }
@ -657,7 +652,7 @@ mod tests {
.unwrap(); .unwrap();
let spirv = Spirv::new(comp.as_binary()).unwrap(); let spirv = Spirv::new(comp.as_binary()).unwrap();
if let Some((_, _, info)) = reflect::entry_points(&spirv).next() { if let Some(info) = reflect::entry_points(&spirv).next() {
let mut bindings = Vec::new(); let mut bindings = Vec::new();
for (loc, _reqs) in info.descriptor_binding_requirements { for (loc, _reqs) in info.descriptor_binding_requirements {
bindings.push(loc); bindings.push(loc);

View File

@ -8,46 +8,37 @@
// according to those terms. // according to those terms.
use ahash::HashMap; use ahash::HashMap;
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::TokenStream;
use vulkano::{ use vulkano::{
pipeline::layout::PushConstantRange, pipeline::layout::PushConstantRange,
shader::{ shader::{
spirv::ExecutionModel, DescriptorBindingRequirements, DescriptorIdentifier, DescriptorBindingRequirements, DescriptorIdentifier, DescriptorRequirements,
DescriptorRequirements, EntryPointInfo, ShaderExecution, ShaderInterface, EntryPointInfo, ShaderExecution, ShaderInterface, ShaderInterfaceEntry,
ShaderInterfaceEntry, ShaderInterfaceEntryType, ShaderStages, ShaderInterfaceEntryType, ShaderStages, SpecializationConstant,
SpecializationConstantRequirements,
}, },
}; };
pub(super) fn write_entry_point( pub(super) fn write_entry_point(info: &EntryPointInfo) -> TokenStream {
name: &str, let name = &info.name;
model: ExecutionModel,
info: &EntryPointInfo,
) -> TokenStream {
let execution = write_shader_execution(&info.execution); let execution = write_shader_execution(&info.execution);
let model = Ident::new(&format!("{:?}", model), Span::call_site());
let descriptor_binding_requirements = let descriptor_binding_requirements =
write_descriptor_binding_requirements(&info.descriptor_binding_requirements); write_descriptor_binding_requirements(&info.descriptor_binding_requirements);
let push_constant_requirements = let push_constant_requirements =
write_push_constant_requirements(&info.push_constant_requirements); write_push_constant_requirements(&info.push_constant_requirements);
let specialization_constant_requirements = let specialization_constants = write_specialization_constants(&info.specialization_constants);
write_specialization_constant_requirements(&info.specialization_constant_requirements);
let input_interface = write_interface(&info.input_interface); let input_interface = write_interface(&info.input_interface);
let output_interface = write_interface(&info.output_interface); let output_interface = write_interface(&info.output_interface);
quote! { quote! {
( ::vulkano::shader::EntryPointInfo {
#name.to_owned(), name: #name.to_owned(),
::vulkano::shader::spirv::ExecutionModel::#model, execution: #execution,
::vulkano::shader::EntryPointInfo { descriptor_binding_requirements: #descriptor_binding_requirements.into_iter().collect(),
execution: #execution, push_constant_requirements: #push_constant_requirements,
descriptor_binding_requirements: #descriptor_binding_requirements.into_iter().collect(), specialization_constants: #specialization_constants.into_iter().collect(),
push_constant_requirements: #push_constant_requirements, input_interface: #input_interface,
specialization_constant_requirements: #specialization_constant_requirements.into_iter().collect(), output_interface: #output_interface,
input_interface: #input_interface, },
output_interface: #output_interface,
},
)
} }
} }
@ -248,27 +239,47 @@ fn write_push_constant_requirements(
} }
} }
fn write_specialization_constant_requirements( fn write_specialization_constants(
specialization_constant_requirements: &HashMap<u32, SpecializationConstantRequirements>, specialization_constants: &HashMap<u32, SpecializationConstant>,
) -> TokenStream { ) -> TokenStream {
let specialization_constant_requirements = let specialization_constants = specialization_constants
specialization_constant_requirements .iter()
.iter() .map(|(&constant_id, value)| {
.map(|(&constant_id, reqs)| { let value = match value {
let SpecializationConstantRequirements { size } = reqs; SpecializationConstant::Bool(value) => quote! { Bool(#value) },
quote! { SpecializationConstant::I8(value) => quote! { I8(#value) },
( SpecializationConstant::I16(value) => quote! { I16(#value) },
#constant_id, SpecializationConstant::I32(value) => quote! { I32(#value) },
::vulkano::shader::SpecializationConstantRequirements { SpecializationConstant::I64(value) => quote! { I64(#value) },
size: #size, SpecializationConstant::U8(value) => quote! { U8(#value) },
}, SpecializationConstant::U16(value) => quote! { U16(#value) },
) SpecializationConstant::U32(value) => quote! { U32(#value) },
SpecializationConstant::U64(value) => quote! { U64(#value) },
SpecializationConstant::F16(value) => {
let bits = value.to_bits();
quote! { F16(f16::from_bits(#bits)) }
} }
}); SpecializationConstant::F32(value) => {
let bits = value.to_bits();
quote! { F32(f32::from_bits(#bits)) }
}
SpecializationConstant::F64(value) => {
let bits = value.to_bits();
quote! { F64(f64::from_bits(#bits)) }
}
};
quote! {
(
#constant_id,
::vulkano::shader::SpecializationConstant::#value,
)
}
});
quote! { quote! {
[ [
#( #specialization_constant_requirements ),* #( #specialization_constants ),*
] ]
} }
} }

View File

@ -33,18 +33,15 @@
//! //!
//! - The `load` constructor. This function takes an `Arc<Device>`, calls //! - The `load` constructor. This function takes an `Arc<Device>`, calls
//! [`ShaderModule::from_words_with_data`] with the passed-in device and the shader data provided //! [`ShaderModule::from_words_with_data`] with the passed-in device and the shader data provided
//! via the macro, and returns `Result<Arc<ShaderModule>, ShaderCreationError>`. Before doing so, //! via the macro, and returns `Result<Arc<ShaderModule>, ShaderModuleCreationError>`.
//! it loops through every capability instruction in the shader data, verifying that the //! Before doing so, it loops through every capability instruction in the shader data,
//! passed-in `Device` has the appropriate features enabled. //! verifying that the passed-in `Device` has the appropriate features enabled.
//! - If the `shaders` option is used, then instead of one `load` constructor, there is one for //! - 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. //! 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 //! - A Rust struct translated from each struct contained in the shader data. By default each
//! structure has a `Clone` and a `Copy` implementation. This behavior could be customized //! structure has a `Clone` and a `Copy` implementation. This behavior could be customized
//! through the `custom_derives` macro option (see below for details). Each struct also has an //! through the `custom_derives` macro option (see below for details). Each struct also has an
//! implementation of [`BufferContents`], so that it can be read from/written to a buffer. //! implementation of [`BufferContents`], so that it can be read from/written to a buffer.
//! - The `SpecializationConstants` struct. This contains a field for every specialization constant
//! found in the shader data. Implementations of [`Default`] and [`SpecializationConstants`] are
//! also generated for the struct.
//! //!
//! All of these generated items will be accessed through the module where the macro was invoked. //! All of these generated items will be accessed through the module where the macro was invoked.
//! If you wanted to store the `ShaderModule` in a struct of your own, you could do something like //! If you wanted to store the `ShaderModule` in a struct of your own, you could do something like
@ -53,7 +50,7 @@
//! ``` //! ```
//! # fn main() {} //! # fn main() {}
//! # use std::sync::Arc; //! # use std::sync::Arc;
//! # use vulkano::shader::{ShaderCreationError, ShaderModule}; //! # use vulkano::shader::{ShaderModuleCreationError, ShaderModule};
//! # use vulkano::device::Device; //! # use vulkano::device::Device;
//! # //! #
//! # mod vs { //! # mod vs {
@ -78,7 +75,7 @@
//! } //! }
//! //!
//! impl Shaders { //! impl Shaders {
//! pub fn load(device: Arc<Device>) -> Result<Self, ShaderCreationError> { //! pub fn load(device: Arc<Device>) -> Result<Self, ShaderModuleCreationError> {
//! Ok(Self { //! Ok(Self {
//! vs: vs::load(device)?, //! vs: vs::load(device)?,
//! }) //! })
@ -143,18 +140,13 @@
//! ## `shaders: { first: { src: "...", ty: "..." }, ... }` //! ## `shaders: { first: { src: "...", ty: "..." }, ... }`
//! //!
//! With these options the user can compile several shaders in a single macro invocation. Each //! With these options the user can compile several shaders in a single macro invocation. Each
//! entry key will be the suffix of the generated `load` function (`load_first` in this case) and //! entry key will be the suffix of the generated `load` function (`load_first` in this case).
//! the prefix of the `SpecializationConstants` struct (`FirstSpecializationConstants` in this //! However all other Rust structs translated from the shader source will be shared between
//! 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 //! 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. //! have the same declaration signature, and throws a compile-time error if they don't.
//! //!
//! Each entry expects a `src`, `path`, `bytes`, and `ty` pairs same as above. //! Each entry expects a `src`, `path`, `bytes`, and `ty` pairs same as above.
//! //!
//! Also, `SpecializationConstants` can be shared between all shaders by specifying the
//! `shared_constants: true,` entry-flag in the `shaders` map. This feature is turned off by
//! default.
//!
//! ## `include: ["...", "...", ...]` //! ## `include: ["...", "...", ...]`
//! //!
//! Specifies the standard include directories to be searched through when using the //! Specifies the standard include directories to be searched through when using the
@ -217,7 +209,6 @@
//! [`cargo-env-vars`]: https://doc.rust-lang.org/cargo/reference/environment-variables.html //! [`cargo-env-vars`]: https://doc.rust-lang.org/cargo/reference/environment-variables.html
//! [cargo-expand]: https://github.com/dtolnay/cargo-expand //! [cargo-expand]: https://github.com/dtolnay/cargo-expand
//! [`ShaderModule::from_words_with_data`]: vulkano::shader::ShaderModule::from_words_with_data //! [`ShaderModule::from_words_with_data`]: vulkano::shader::ShaderModule::from_words_with_data
//! [`SpecializationConstants`]: vulkano::shader::SpecializationConstants
//! [pipeline]: vulkano::pipeline //! [pipeline]: vulkano::pipeline
//! [`set_target_env`]: shaderc::CompileOptions::set_target_env //! [`set_target_env`]: shaderc::CompileOptions::set_target_env
//! [`set_target_spirv`]: shaderc::CompileOptions::set_target_spirv //! [`set_target_spirv`]: shaderc::CompileOptions::set_target_spirv
@ -390,7 +381,6 @@ struct MacroInput {
root_path_env: Option<LitStr>, root_path_env: Option<LitStr>,
include_directories: Vec<PathBuf>, include_directories: Vec<PathBuf>,
macro_defines: Vec<(String, String)>, macro_defines: Vec<(String, String)>,
shared_constants: bool,
shaders: HashMap<String, (ShaderKind, SourceKind)>, shaders: HashMap<String, (ShaderKind, SourceKind)>,
spirv_version: Option<SpirvVersion>, spirv_version: Option<SpirvVersion>,
vulkan_version: Option<EnvVersion>, vulkan_version: Option<EnvVersion>,
@ -406,7 +396,6 @@ impl MacroInput {
root_path_env: None, root_path_env: None,
include_directories: Vec::new(), include_directories: Vec::new(),
macro_defines: Vec::new(), macro_defines: Vec::new(),
shared_constants: false,
shaders: HashMap::default(), shaders: HashMap::default(),
vulkan_version: None, vulkan_version: None,
spirv_version: None, spirv_version: None,
@ -424,7 +413,6 @@ impl Parse for MacroInput {
let mut root_path_env = None; let mut root_path_env = None;
let mut include_directories = Vec::new(); let mut include_directories = Vec::new();
let mut macro_defines = Vec::new(); let mut macro_defines = Vec::new();
let mut shared_constants = None;
let mut shaders = HashMap::default(); let mut shaders = HashMap::default();
let mut vulkan_version = None; let mut vulkan_version = None;
let mut spirv_version = None; let mut spirv_version = None;
@ -535,22 +523,6 @@ impl Parse for MacroInput {
let name_ident = in_braces.parse::<Ident>()?; let name_ident = in_braces.parse::<Ident>()?;
let name = name_ident.to_string(); let name = name_ident.to_string();
if &name == "shared_constants" {
in_braces.parse::<Token![:]>()?;
let lit = in_braces.parse::<LitBool>()?;
if shared_constants.is_some() {
bail!(lit, "field `shared_constants` is already defined");
}
shared_constants = Some(lit.value);
if !in_braces.is_empty() {
in_braces.parse::<Token![,]>()?;
}
continue;
}
if shaders.contains_key(&name) { if shaders.contains_key(&name) {
bail!(name_ident, "shader entry `{name}` is already defined"); bail!(name_ident, "shader entry `{name}` is already defined");
} }
@ -759,7 +731,6 @@ impl Parse for MacroInput {
root_path_env, root_path_env,
include_directories, include_directories,
macro_defines, macro_defines,
shared_constants: shared_constants.unwrap_or(false),
shaders: shaders shaders: shaders
.into_iter() .into_iter()
.map(|(key, (shader_kind, shader_source))| { .map(|(key, (shader_kind, shader_source))| {

View File

@ -9,7 +9,6 @@
use crate::{bail, codegen::Shader, LinAlgType, MacroInput}; use crate::{bail, codegen::Shader, LinAlgType, MacroInput};
use ahash::HashMap; use ahash::HashMap;
use heck::ToUpperCamelCase;
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt}; use quote::{ToTokens, TokenStreamExt};
use std::{cmp::Ordering, num::NonZeroUsize}; use std::{cmp::Ordering, num::NonZeroUsize};
@ -173,159 +172,6 @@ fn has_defined_layout(shader: &Shader, struct_id: Id) -> bool {
true true
} }
/// Writes the `SpecializationConstants` struct that contains the specialization constants and
/// implements the `Default` and the `vulkano::shader::SpecializationConstants` traits.
pub(super) fn write_specialization_constants(
input: &MacroInput,
shader: &Shader,
type_registry: &mut TypeRegistry,
) -> Result<TokenStream> {
let mut members = Vec::new();
let mut member_defaults = Vec::new();
let mut map_entries = Vec::new();
let mut current_offset = 0;
for instruction in shader.spirv.iter_global() {
let (result_type_id, result_id, default_value) = match *instruction {
Instruction::SpecConstantTrue {
result_type_id,
result_id,
} => (result_type_id, result_id, quote! { 1u32 }),
Instruction::SpecConstantFalse {
result_type_id,
result_id,
} => (result_type_id, result_id, quote! { 0u32 }),
Instruction::SpecConstant {
result_type_id,
result_id,
ref value,
} => {
let def_val = quote! {
unsafe { ::std::mem::transmute([ #( #value ),* ]) }
};
(result_type_id, result_id, def_val)
}
Instruction::SpecConstantComposite {
result_type_id,
result_id,
ref constituents,
} => {
let constituents = constituents.iter().map(|&id| u32::from(id));
let def_val = quote! {
unsafe { ::std::mem::transmute([ #( #constituents ),* ]) }
};
(result_type_id, result_id, def_val)
}
_ => continue,
};
let id_info = shader.spirv.id(result_id);
let constant_id = id_info
.iter_decoration()
.find_map(|instruction| match *instruction {
Instruction::Decorate {
decoration:
Decoration::SpecId {
specialization_constant_id,
},
..
} => Some(specialization_constant_id),
_ => None,
});
if let Some(constant_id) = constant_id {
let ident = id_info
.iter_name()
.find_map(|instruction| match instruction {
Instruction::Name { name, .. } => {
Some(Ident::new(name.as_str(), Span::call_site()))
}
_ => None,
})
.unwrap_or_else(|| format_ident!("constant_{}", constant_id));
let ty = match *shader.spirv.id(result_type_id).instruction() {
// Translate bool to u32.
Instruction::TypeBool { .. } => Type::Scalar(TypeScalar::Int(TypeInt {
width: IntWidth::W32,
signed: false,
})),
_ => Type::new(shader, result_type_id)?,
};
let offset = current_offset;
let size = ty.size().ok_or_else(|| {
Error::new_spanned(
&shader.source,
"found runtime-sized specialization constant",
)
})?;
current_offset = align_up(current_offset + size, ty.scalar_alignment());
member_defaults.push(quote! { #ident: #default_value });
members.push(Member { ident, ty, offset });
map_entries.push(quote! {
::vulkano::shader::SpecializationMapEntry {
constant_id: #constant_id,
offset: #offset as u32,
size: #size,
}
});
}
}
let struct_ty = TypeStruct {
ident: if input.shared_constants {
format_ident!("SpecializationConstants")
} else {
format_ident!(
"{}SpecializationConstants",
shader.name.to_upper_camel_case(),
)
},
members,
};
// For multi-constants mode, the registration mechanism is skipped.
if input.shared_constants && !type_registry.register_struct(shader, &struct_ty)? {
return Ok(TokenStream::new());
}
let struct_ser = Serializer(&struct_ty, input);
let struct_ident = &struct_ty.ident;
let num_map_entries = map_entries.len();
Ok(quote! {
#[allow(non_snake_case)]
#[derive(::std::clone::Clone, ::std::marker::Copy, ::std::fmt::Debug)]
#[repr(C)]
#struct_ser
impl ::std::default::Default for #struct_ident {
#[inline]
fn default() -> #struct_ident {
#struct_ident {
#( #member_defaults ),*
}
}
}
#[allow(unsafe_code)]
unsafe impl ::vulkano::shader::SpecializationConstants for #struct_ident {
#[inline(always)]
fn descriptors() -> &'static [::vulkano::shader::SpecializationMapEntry] {
static DESCRIPTORS: [::vulkano::shader::SpecializationMapEntry; #num_map_entries] =
[ #( #map_entries ),* ];
&DESCRIPTORS
}
}
})
}
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)] #[repr(u8)]
enum Alignment { enum Alignment {

View File

@ -21,7 +21,10 @@
//! of [`get_data`](crate::pipeline::cache::PipelineCache::get_data) for example of how to store the data //! of [`get_data`](crate::pipeline::cache::PipelineCache::get_data) for example of how to store the data
//! on the disk, and [`with_data`](crate::pipeline::cache::PipelineCache::with_data) for how to reload it. //! on the disk, and [`with_data`](crate::pipeline::cache::PipelineCache::with_data) for how to reload it.
use crate::{device::Device, OomError, VulkanError, VulkanObject}; use crate::{
device::{Device, DeviceOwned},
OomError, VulkanError, VulkanObject,
};
use std::{mem::MaybeUninit, ptr, sync::Arc}; use std::{mem::MaybeUninit, ptr, sync::Arc};
/// Opaque cache that contains pipeline objects. /// Opaque cache that contains pipeline objects.
@ -30,7 +33,7 @@ use std::{mem::MaybeUninit, ptr, sync::Arc};
#[derive(Debug)] #[derive(Debug)]
pub struct PipelineCache { pub struct PipelineCache {
device: Arc<Device>, device: Arc<Device>,
cache: ash::vk::PipelineCache, handle: ash::vk::PipelineCache,
} }
impl PipelineCache { impl PipelineCache {
@ -131,7 +134,7 @@ impl PipelineCache {
Ok(Arc::new(PipelineCache { Ok(Arc::new(PipelineCache {
device: device.clone(), device: device.clone(),
cache, handle: cache,
})) }))
} }
@ -156,13 +159,13 @@ impl PipelineCache {
.into_iter() .into_iter()
.map(|pipeline| { .map(|pipeline| {
assert!(&***pipeline as *const _ != self as *const _); assert!(&***pipeline as *const _ != self as *const _);
pipeline.cache pipeline.handle
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
(fns.v1_0.merge_pipeline_caches)( (fns.v1_0.merge_pipeline_caches)(
self.device.handle(), self.device.handle(),
self.cache, self.handle,
pipelines.len() as u32, pipelines.len() as u32,
pipelines.as_ptr(), pipelines.as_ptr(),
) )
@ -210,7 +213,7 @@ impl PipelineCache {
let mut count = 0; let mut count = 0;
(fns.v1_0.get_pipeline_cache_data)( (fns.v1_0.get_pipeline_cache_data)(
self.device.handle(), self.device.handle(),
self.cache, self.handle,
&mut count, &mut count,
ptr::null_mut(), ptr::null_mut(),
) )
@ -220,7 +223,7 @@ impl PipelineCache {
let mut data: Vec<u8> = Vec::with_capacity(count); let mut data: Vec<u8> = Vec::with_capacity(count);
let result = (fns.v1_0.get_pipeline_cache_data)( let result = (fns.v1_0.get_pipeline_cache_data)(
self.device.handle(), self.device.handle(),
self.cache, self.handle,
&mut count, &mut count,
data.as_mut_ptr() as *mut _, data.as_mut_ptr() as *mut _,
); );
@ -240,30 +243,37 @@ impl PipelineCache {
} }
} }
unsafe impl VulkanObject for PipelineCache {
type Handle = ash::vk::PipelineCache;
#[inline]
fn handle(&self) -> Self::Handle {
self.cache
}
}
impl Drop for PipelineCache { impl Drop for PipelineCache {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
let fns = self.device.fns(); let fns = self.device.fns();
(fns.v1_0.destroy_pipeline_cache)(self.device.handle(), self.cache, ptr::null()); (fns.v1_0.destroy_pipeline_cache)(self.device.handle(), self.handle, ptr::null());
} }
} }
} }
unsafe impl VulkanObject for PipelineCache {
type Handle = ash::vk::PipelineCache;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for PipelineCache {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
pipeline::{cache::PipelineCache, ComputePipeline}, pipeline::{cache::PipelineCache, ComputePipeline},
shader::ShaderModule, shader::{PipelineShaderStageCreateInfo, ShaderModule},
}; };
#[test] #[test]
@ -281,7 +291,7 @@ mod tests {
let cache = PipelineCache::empty(device.clone()).unwrap(); let cache = PipelineCache::empty(device.clone()).unwrap();
let module = unsafe { let shader = unsafe {
/* /*
* #version 450 * #version 450
* void main() { * void main() {
@ -297,13 +307,13 @@ mod tests {
0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 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, 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0,
]; ];
ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap();
module.entry_point("main").unwrap()
}; };
let _pipeline = ComputePipeline::new( let _pipeline = ComputePipeline::new(
device, device,
module.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
Some(cache.clone()), Some(cache.clone()),
|_| {}, |_| {},
) )
@ -321,7 +331,7 @@ mod tests {
let cache = PipelineCache::empty(device.clone()).unwrap(); let cache = PipelineCache::empty(device.clone()).unwrap();
let first_module = unsafe { let first_shader = unsafe {
/* /*
* #version 450 * #version 450
* void main() { * void main() {
@ -337,10 +347,11 @@ mod tests {
0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 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, 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0,
]; ];
ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap();
module.entry_point("main").unwrap()
}; };
let second_module = unsafe { let second_shader = unsafe {
/* /*
* #version 450 * #version 450
* *
@ -348,7 +359,7 @@ mod tests {
* uint idx = gl_GlobalInvocationID.x; * uint idx = gl_GlobalInvocationID.x;
* } * }
*/ */
const SECOND_MODULE: [u8; 432] = [ const MODULE: [u8; 432] = [
3, 2, 35, 7, 0, 0, 1, 0, 10, 0, 8, 0, 16, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0, 3, 2, 35, 7, 0, 0, 1, 0, 10, 0, 8, 0, 16, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0,
0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48,
0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 6, 0, 5, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 6, 0, 5, 0, 0, 0, 4, 0, 0,
@ -368,13 +379,13 @@ mod tests {
0, 15, 0, 0, 0, 14, 0, 0, 0, 62, 0, 3, 0, 8, 0, 0, 0, 15, 0, 0, 0, 253, 0, 1, 0, 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, 56, 0, 1, 0,
]; ];
ShaderModule::from_bytes(device.clone(), &SECOND_MODULE).unwrap() let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap();
module.entry_point("main").unwrap()
}; };
let _pipeline = ComputePipeline::new( let _first_pipeline = ComputePipeline::new(
device.clone(), device.clone(),
first_module.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(first_shader),
&(),
Some(cache.clone()), Some(cache.clone()),
|_| {}, |_| {},
) )
@ -384,8 +395,7 @@ mod tests {
let _second_pipeline = ComputePipeline::new( let _second_pipeline = ComputePipeline::new(
device, device,
second_module.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(second_shader),
&(),
Some(cache.clone()), Some(cache.clone()),
|_| {}, |_| {},
) )
@ -406,7 +416,7 @@ mod tests {
let cache = PipelineCache::empty(device.clone()).unwrap(); let cache = PipelineCache::empty(device.clone()).unwrap();
let module = unsafe { let shader = unsafe {
/* /*
* #version 450 * #version 450
* void main() { * void main() {
@ -422,13 +432,13 @@ mod tests {
0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 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, 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0,
]; ];
ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap();
module.entry_point("main").unwrap()
}; };
let _pipeline = ComputePipeline::new( let _pipeline = ComputePipeline::new(
device.clone(), device.clone(),
module.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader.clone()),
&(),
Some(cache.clone()), Some(cache.clone()),
|_| {}, |_| {},
) )
@ -438,8 +448,7 @@ mod tests {
let _second_pipeline = ComputePipeline::new( let _second_pipeline = ComputePipeline::new(
device, device,
module.entry_point("main").unwrap(), PipelineShaderStageCreateInfo::entry_point(shader),
&(),
Some(cache.clone()), Some(cache.clone()),
|_| {}, |_| {},
) )

View File

@ -34,14 +34,17 @@ use crate::{
layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutSupersetError}, layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutSupersetError},
Pipeline, PipelineBindPoint, Pipeline, PipelineBindPoint,
}, },
shader::{DescriptorBindingRequirements, EntryPoint, SpecializationConstants}, shader::{
DeviceSize, OomError, VulkanError, VulkanObject, DescriptorBindingRequirements, PipelineShaderStageCreateInfo, ShaderExecution, ShaderStage,
SpecializationConstant,
},
OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
}; };
use ahash::HashMap; use ahash::HashMap;
use std::{ use std::{
error::Error, error::Error,
ffi::CString,
fmt::{Debug, Display, Error as FmtError, Formatter}, fmt::{Debug, Display, Error as FmtError, Formatter},
mem,
mem::MaybeUninit, mem::MaybeUninit,
num::NonZeroU64, num::NonZeroU64,
ptr, ptr,
@ -71,149 +74,192 @@ impl ComputePipeline {
/// `func` is a closure that is given a mutable reference to the inferred descriptor set /// `func` is a closure that is given a mutable reference to the inferred descriptor set
/// definitions. This can be used to make changes to the layout before it's created, for example /// definitions. This can be used to make changes to the layout before it's created, for example
/// to add dynamic buffers or immutable samplers. /// to add dynamic buffers or immutable samplers.
pub fn new<Css, F>( pub fn new<F>(
device: Arc<Device>, device: Arc<Device>,
shader: EntryPoint<'_>, stage: PipelineShaderStageCreateInfo,
specialization_constants: &Css,
cache: Option<Arc<PipelineCache>>, cache: Option<Arc<PipelineCache>>,
func: F, func: F,
) -> Result<Arc<ComputePipeline>, ComputePipelineCreationError> ) -> Result<Arc<ComputePipeline>, ComputePipelineCreationError>
where where
Css: SpecializationConstants,
F: FnOnce(&mut [DescriptorSetLayoutCreateInfo]), F: FnOnce(&mut [DescriptorSetLayoutCreateInfo]),
{ {
let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements( let layout = {
shader.descriptor_binding_requirements(), let entry_point_info = stage.entry_point.info();
); let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements(
func(&mut set_layout_create_infos); entry_point_info
let set_layouts = set_layout_create_infos .descriptor_binding_requirements
.iter() .iter()
.map(|desc| DescriptorSetLayout::new(device.clone(), desc.clone())) .map(|(k, v)| (*k, v)),
.collect::<Result<Vec<_>, _>>()?; );
func(&mut set_layout_create_infos);
let set_layouts = set_layout_create_infos
.iter()
.map(|desc| DescriptorSetLayout::new(device.clone(), desc.clone()))
.collect::<Result<Vec<_>, _>>()?;
let layout = PipelineLayout::new( PipelineLayout::new(
device.clone(), device.clone(),
PipelineLayoutCreateInfo { PipelineLayoutCreateInfo {
set_layouts, set_layouts,
push_constant_ranges: shader push_constant_ranges: entry_point_info
.push_constant_requirements() .push_constant_requirements
.cloned() .iter()
.into_iter() .cloned()
.collect(), .collect(),
..Default::default() ..Default::default()
}, },
)?; )?
};
unsafe { Self::with_pipeline_layout(device, stage, layout, cache)
ComputePipeline::with_unchecked_pipeline_layout(
device,
shader,
specialization_constants,
layout,
cache,
)
}
} }
/// Builds a new `ComputePipeline` with a specific pipeline layout. /// Builds a new `ComputePipeline` with a specific pipeline layout.
/// ///
/// An error will be returned if the pipeline layout isn't a superset of what the shader /// An error will be returned if the pipeline layout isn't a superset of what the shader
/// uses. /// uses.
pub fn with_pipeline_layout<Css>( pub fn with_pipeline_layout(
device: Arc<Device>, device: Arc<Device>,
shader: EntryPoint<'_>, stage: PipelineShaderStageCreateInfo,
specialization_constants: &Css,
layout: Arc<PipelineLayout>, layout: Arc<PipelineLayout>,
cache: Option<Arc<PipelineCache>>, cache: Option<Arc<PipelineCache>>,
) -> Result<Arc<ComputePipeline>, ComputePipelineCreationError> ) -> Result<Arc<ComputePipeline>, ComputePipelineCreationError> {
where // VUID-vkCreateComputePipelines-pipelineCache-parent
Css: SpecializationConstants, if let Some(cache) = &cache {
{ assert_eq!(&device, cache.device());
let spec_descriptors = Css::descriptors(); }
for (constant_id, reqs) in shader.specialization_constant_requirements() { let &PipelineShaderStageCreateInfo {
let map_entry = spec_descriptors flags,
.iter() ref entry_point,
.find(|desc| desc.constant_id == constant_id) ref specialization_info,
.ok_or(ComputePipelineCreationError::IncompatibleSpecializationConstants)?; _ne: _,
} = &stage;
if map_entry.size as DeviceSize != reqs.size { // VUID-VkPipelineShaderStageCreateInfo-flags-parameter
return Err(ComputePipelineCreationError::IncompatibleSpecializationConstants); flags.validate_device(&device)?;
let entry_point_info = entry_point.info();
// VUID-VkComputePipelineCreateInfo-stage-00701
// VUID-VkPipelineShaderStageCreateInfo-stage-parameter
if !matches!(entry_point_info.execution, ShaderExecution::Compute) {
return Err(ComputePipelineCreationError::ShaderStageInvalid {
stage: ShaderStage::from(&entry_point_info.execution),
});
}
for (&constant_id, provided_value) in specialization_info {
// Per `VkSpecializationMapEntry` spec:
// "If a constantID value is not a specialization constant ID used in the shader,
// that map entry does not affect the behavior of the pipeline."
// We *may* want to be stricter than this for the sake of catching user errors?
if let Some(default_value) = entry_point_info.specialization_constants.get(&constant_id)
{
// VUID-VkSpecializationMapEntry-constantID-00776
// Check for equal types rather than only equal size.
if !provided_value.eq_type(default_value) {
return Err(
ComputePipelineCreationError::ShaderSpecializationConstantTypeMismatch {
constant_id,
default_value: *default_value,
provided_value: *provided_value,
},
);
}
} }
} }
// VUID-VkComputePipelineCreateInfo-layout-07987
// VUID-VkComputePipelineCreateInfo-layout-07988
// VUID-VkComputePipelineCreateInfo-layout-07990
// VUID-VkComputePipelineCreateInfo-layout-07991
// TODO: Make sure that all of these are indeed checked.
layout.ensure_compatible_with_shader( layout.ensure_compatible_with_shader(
shader.descriptor_binding_requirements(), entry_point_info
shader.push_constant_requirements(), .descriptor_binding_requirements
.iter()
.map(|(k, v)| (*k, v)),
entry_point_info.push_constant_requirements.as_ref(),
)?; )?;
unsafe { // VUID-VkComputePipelineCreateInfo-stage-00702
ComputePipeline::with_unchecked_pipeline_layout( // VUID-VkComputePipelineCreateInfo-layout-01687
device, // TODO:
shader,
specialization_constants, unsafe { Self::new_unchecked(device, stage, layout, cache) }
layout,
cache,
)
}
} }
/// Same as `with_pipeline_layout`, but doesn't check whether the pipeline layout is a #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
/// superset of what the shader expects. pub unsafe fn new_unchecked(
pub unsafe fn with_unchecked_pipeline_layout<Css>(
device: Arc<Device>, device: Arc<Device>,
shader: EntryPoint<'_>, stage: PipelineShaderStageCreateInfo,
specialization_constants: &Css,
layout: Arc<PipelineLayout>, layout: Arc<PipelineLayout>,
cache: Option<Arc<PipelineCache>>, cache: Option<Arc<PipelineCache>>,
) -> Result<Arc<ComputePipeline>, ComputePipelineCreationError> ) -> Result<Arc<ComputePipeline>, ComputePipelineCreationError> {
where let &PipelineShaderStageCreateInfo {
Css: SpecializationConstants, flags,
{ ref entry_point,
let fns = device.fns(); ref specialization_info,
_ne: _,
} = &stage;
let entry_point_info = entry_point.info();
let name_vk = CString::new(entry_point_info.name.as_str()).unwrap();
let mut specialization_data_vk: Vec<u8> = Vec::new();
let specialization_map_entries_vk: Vec<_> = specialization_info
.iter()
.map(|(&constant_id, value)| {
let data = value.as_bytes();
let offset = specialization_data_vk.len() as u32;
let size = data.len();
specialization_data_vk.extend(data);
ash::vk::SpecializationMapEntry {
constant_id,
offset,
size,
}
})
.collect();
let specialization_info_vk = ash::vk::SpecializationInfo {
map_entry_count: specialization_map_entries_vk.len() as u32,
p_map_entries: specialization_map_entries_vk.as_ptr(),
data_size: specialization_data_vk.len(),
p_data: specialization_data_vk.as_ptr() as *const _,
};
let stage_vk = ash::vk::PipelineShaderStageCreateInfo {
flags: flags.into(),
stage: ShaderStage::from(&entry_point_info.execution).into(),
module: entry_point.module().handle(),
p_name: name_vk.as_ptr(),
p_specialization_info: if specialization_info_vk.data_size == 0 {
ptr::null()
} else {
&specialization_info_vk
},
..Default::default()
};
let create_infos_vk = ash::vk::ComputePipelineCreateInfo {
flags: ash::vk::PipelineCreateFlags::empty(),
stage: stage_vk,
layout: layout.handle(),
base_pipeline_handle: ash::vk::Pipeline::null(),
base_pipeline_index: 0,
..Default::default()
};
let handle = { let handle = {
let spec_descriptors = Css::descriptors(); let fns = device.fns();
let specialization = ash::vk::SpecializationInfo {
map_entry_count: spec_descriptors.len() as u32,
p_map_entries: spec_descriptors.as_ptr() as *const _,
data_size: mem::size_of_val(specialization_constants),
p_data: specialization_constants as *const Css as *const _,
};
let stage = ash::vk::PipelineShaderStageCreateInfo {
flags: ash::vk::PipelineShaderStageCreateFlags::empty(),
stage: ash::vk::ShaderStageFlags::COMPUTE,
module: shader.module().handle(),
p_name: shader.name().as_ptr(),
p_specialization_info: if specialization.data_size == 0 {
ptr::null()
} else {
&specialization
},
..Default::default()
};
let infos = ash::vk::ComputePipelineCreateInfo {
flags: ash::vk::PipelineCreateFlags::empty(),
stage,
layout: layout.handle(),
base_pipeline_handle: ash::vk::Pipeline::null(),
base_pipeline_index: 0,
..Default::default()
};
let cache_handle = match cache {
Some(ref cache) => cache.handle(),
None => ash::vk::PipelineCache::null(),
};
let mut output = MaybeUninit::uninit(); let mut output = MaybeUninit::uninit();
(fns.v1_0.create_compute_pipelines)( (fns.v1_0.create_compute_pipelines)(
device.handle(), device.handle(),
cache_handle, cache.as_ref().map_or(Default::default(), |c| c.handle()),
1, 1,
&infos, &create_infos_vk,
ptr::null(), ptr::null(),
output.as_mut_ptr(), output.as_mut_ptr(),
) )
@ -222,9 +268,28 @@ impl ComputePipeline {
output.assume_init() output.assume_init()
}; };
let descriptor_binding_requirements: HashMap<_, _> = shader Ok(Self::from_handle(device, handle, stage, layout))
.descriptor_binding_requirements() }
.map(|(loc, reqs)| (loc, reqs.clone()))
/// Creates a new `ComputePipeline` from a raw object handle.
///
/// # Safety
///
/// - `handle` must be a valid Vulkan object handle created from `device`.
/// - `create_info` must match the info used to create the object.
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::Pipeline,
stage: PipelineShaderStageCreateInfo,
layout: Arc<PipelineLayout>,
) -> Arc<ComputePipeline> {
let descriptor_binding_requirements: HashMap<_, _> = stage
.entry_point
.info()
.descriptor_binding_requirements
.iter()
.map(|(&loc, reqs)| (loc, reqs.clone()))
.collect(); .collect();
let num_used_descriptor_sets = descriptor_binding_requirements let num_used_descriptor_sets = descriptor_binding_requirements
.keys() .keys()
@ -233,14 +298,14 @@ impl ComputePipeline {
.map(|x| x + 1) .map(|x| x + 1)
.unwrap_or(0); .unwrap_or(0);
Ok(Arc::new(ComputePipeline { Arc::new(ComputePipeline {
handle, handle,
device: device.clone(), device,
id: Self::next_id(), id: Self::next_id(),
layout, layout,
descriptor_binding_requirements, descriptor_binding_requirements,
num_used_descriptor_sets, num_used_descriptor_sets,
})) })
} }
/// Returns the `Device` this compute pipeline was created with. /// Returns the `Device` this compute pipeline was created with.
@ -309,18 +374,35 @@ impl Drop for ComputePipeline {
} }
/// Error that can happen when creating a compute pipeline. /// Error that can happen when creating a compute pipeline.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq)]
pub enum ComputePipelineCreationError { pub enum ComputePipelineCreationError {
/// Not enough memory. /// Not enough memory.
OomError(OomError), OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
/// Error while creating a descriptor set layout object. /// Error while creating a descriptor set layout object.
DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError), DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError),
/// Error while creating the pipeline layout object. /// Error while creating the pipeline layout object.
PipelineLayoutCreationError(PipelineLayoutCreationError), PipelineLayoutCreationError(PipelineLayoutCreationError),
/// The pipeline layout is not compatible with what the shader expects. /// The pipeline layout is not compatible with what the shader expects.
IncompatiblePipelineLayout(PipelineLayoutSupersetError), IncompatiblePipelineLayout(PipelineLayoutSupersetError),
/// The provided specialization constants are not compatible with what the shader expects.
IncompatibleSpecializationConstants, /// The value provided for a shader specialization constant has a
/// different type than the constant's default value.
ShaderSpecializationConstantTypeMismatch {
constant_id: u32,
default_value: SpecializationConstant,
provided_value: SpecializationConstant,
},
/// The provided shader stage is not a compute shader.
ShaderStageInvalid { stage: ShaderStage },
} }
impl Error for ComputePipelineCreationError { impl Error for ComputePipelineCreationError {
@ -330,33 +412,49 @@ impl Error for ComputePipelineCreationError {
Self::DescriptorSetLayoutCreationError(err) => Some(err), Self::DescriptorSetLayoutCreationError(err) => Some(err),
Self::PipelineLayoutCreationError(err) => Some(err), Self::PipelineLayoutCreationError(err) => Some(err),
Self::IncompatiblePipelineLayout(err) => Some(err), Self::IncompatiblePipelineLayout(err) => Some(err),
Self::IncompatibleSpecializationConstants => None, _ => None,
} }
} }
} }
impl Display for ComputePipelineCreationError { impl Display for ComputePipelineCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!( match self {
f, Self::OomError(_) => write!(f, "not enough memory available"),
"{}", Self::RequirementNotMet {
match self { required_for,
ComputePipelineCreationError::OomError(_) => "not enough memory available", requires_one_of,
ComputePipelineCreationError::DescriptorSetLayoutCreationError(_) => { } => write!(
"error while creating a descriptor set layout object" f,
} "a requirement was not met for: {}; requires one of: {}",
ComputePipelineCreationError::PipelineLayoutCreationError(_) => { required_for, requires_one_of,
"error while creating the pipeline layout object" ),
} Self::DescriptorSetLayoutCreationError(_) => {
ComputePipelineCreationError::IncompatiblePipelineLayout(_) => { write!(f, "error while creating a descriptor set layout object",)
"the pipeline layout is not compatible with what the shader expects"
}
ComputePipelineCreationError::IncompatibleSpecializationConstants => {
"the provided specialization constants are not compatible with what the shader \
expects"
}
} }
) Self::PipelineLayoutCreationError(_) => {
write!(f, "error while creating the pipeline layout object",)
}
Self::IncompatiblePipelineLayout(_) => write!(
f,
"the pipeline layout is not compatible with what the shader expects",
),
Self::ShaderSpecializationConstantTypeMismatch {
constant_id,
default_value,
provided_value,
} => write!(
f,
"the value provided for shader specialization constant id {} ({:?}) has a \
different type than the constant's default value ({:?})",
constant_id, provided_value, default_value,
),
Self::ShaderStageInvalid { stage } => write!(
f,
"the provided shader stage ({:?}) is not a compute shader",
stage,
),
}
} }
} }
@ -366,6 +464,15 @@ impl From<OomError> for ComputePipelineCreationError {
} }
} }
impl From<RequirementNotMet> for ComputePipelineCreationError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
impl From<DescriptorSetLayoutCreationError> for ComputePipelineCreationError { impl From<DescriptorSetLayoutCreationError> for ComputePipelineCreationError {
fn from(err: DescriptorSetLayoutCreationError) -> Self { fn from(err: DescriptorSetLayoutCreationError) -> Self {
Self::DescriptorSetLayoutCreationError(err) Self::DescriptorSetLayoutCreationError(err)
@ -406,7 +513,7 @@ mod tests {
}, },
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::{ShaderModule, SpecializationConstants, SpecializationMapEntry}, shader::{PipelineShaderStageCreateInfo, ShaderModule},
sync::{now, GpuFuture}, sync::{now, GpuFuture},
}; };
@ -461,27 +568,12 @@ mod tests {
ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() ShaderModule::from_bytes(device.clone(), &MODULE).unwrap()
}; };
#[derive(Debug, Copy, Clone)]
#[allow(non_snake_case)]
#[repr(C)]
struct SpecConsts {
VALUE: i32,
}
unsafe impl SpecializationConstants for SpecConsts {
fn descriptors() -> &'static [SpecializationMapEntry] {
static DESCRIPTORS: [SpecializationMapEntry; 1] = [SpecializationMapEntry {
constant_id: 83,
offset: 0,
size: 4,
}];
&DESCRIPTORS
}
}
let pipeline = ComputePipeline::new( let pipeline = ComputePipeline::new(
device.clone(), device.clone(),
module.entry_point("main").unwrap(), PipelineShaderStageCreateInfo {
&SpecConsts { VALUE: 0x12345678 }, specialization_info: [(83, 0x12345678i32.into())].into_iter().collect(),
..PipelineShaderStageCreateInfo::entry_point(module.entry_point("main").unwrap())
},
None, None,
|_| {}, |_| {},
) )

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ use crate::{
descriptor_set::layout::DescriptorSetLayoutCreationError, descriptor_set::layout::DescriptorSetLayoutCreationError,
format::{Format, NumericType}, format::{Format, NumericType},
pipeline::layout::{PipelineLayoutCreationError, PipelineLayoutSupersetError}, pipeline::layout::{PipelineLayoutCreationError, PipelineLayoutSupersetError},
shader::{ShaderInterfaceMismatchError, ShaderScalarType}, shader::{ShaderInterfaceMismatchError, ShaderScalarType, ShaderStage, SpecializationConstant},
OomError, RequirementNotMet, RequiresOneOf, VulkanError, OomError, RequirementNotMet, RequiresOneOf, VulkanError,
}; };
use std::{ use std::{
@ -21,7 +21,7 @@ use std::{
}; };
/// Error that can happen when creating a graphics pipeline. /// Error that can happen when creating a graphics pipeline.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq)]
pub enum GraphicsPipelineCreationError { pub enum GraphicsPipelineCreationError {
RequirementNotMet { RequirementNotMet {
required_for: &'static str, required_for: &'static str,
@ -47,9 +47,6 @@ pub enum GraphicsPipelineCreationError {
/// The pipeline layout is not compatible with what the shaders expect. /// The pipeline layout is not compatible with what the shaders expect.
IncompatiblePipelineLayout(PipelineLayoutSupersetError), IncompatiblePipelineLayout(PipelineLayoutSupersetError),
/// The provided specialization constants are not compatible with what the shader expects.
IncompatibleSpecializationConstants,
/// The vertex definition is not compatible with the input of the vertex shader. /// The vertex definition is not compatible with the input of the vertex shader.
IncompatibleVertexDefinition(IncompatibleVertexDefinitionError), IncompatibleVertexDefinition(IncompatibleVertexDefinitionError),
@ -157,15 +154,48 @@ pub enum GraphicsPipelineCreationError {
/// Not enough memory. /// Not enough memory.
OomError(OomError), OomError(OomError),
/// Only one tessellation shader stage was provided, the other was not.
OtherTessellationShaderStageMissing,
/// Error while creating a descriptor set layout object. /// Error while creating a descriptor set layout object.
DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError), DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError),
/// Error while creating the pipeline layout object. /// Error while creating the pipeline layout object.
PipelineLayoutCreationError(PipelineLayoutCreationError), PipelineLayoutCreationError(PipelineLayoutCreationError),
/// The value provided for a shader specialization constant has a
/// different type than the constant's default value.
ShaderSpecializationConstantTypeMismatch {
stage_index: usize,
constant_id: u32,
default_value: SpecializationConstant,
provided_value: SpecializationConstant,
},
/// A shader stage was provided more than once.
ShaderStageDuplicate {
stage_index: usize,
stage: ShaderStage,
},
/// A shader stage is not a graphics shader.
ShaderStageInvalid {
stage_index: usize,
stage: ShaderStage,
},
/// The configuration of the pipeline does not use a shader stage, but it was provided.
ShaderStageUnused { stage: ShaderStage },
/// The output interface of one shader and the input interface of the next shader do not match. /// The output interface of one shader and the input interface of the next shader do not match.
ShaderStagesMismatch(ShaderInterfaceMismatchError), ShaderStagesMismatch(ShaderInterfaceMismatchError),
/// The configuration of the pipeline requires a state to be provided, but it was not.
StateMissing { state: &'static str },
/// The configuration of the pipeline does not use a state, but it was provided.
StateUnused { state: &'static str },
/// The stencil attachment has a format that does not support that usage. /// The stencil attachment has a format that does not support that usage.
StencilAttachmentFormatUsageNotSupported, StencilAttachmentFormatUsageNotSupported,
@ -198,6 +228,9 @@ pub enum GraphicsPipelineCreationError {
/// The format specified by a vertex input attribute is not supported for vertex buffers. /// The format specified by a vertex input attribute is not supported for vertex buffers.
VertexInputAttributeUnsupportedFormat { location: u32, format: Format }, VertexInputAttributeUnsupportedFormat { location: u32, format: Format },
/// No vertex shader stage was provided.
VertexShaderStageMissing,
/// The minimum or maximum bounds of viewports have been exceeded. /// The minimum or maximum bounds of viewports have been exceeded.
ViewportBoundsExceeded, ViewportBoundsExceeded,
@ -261,11 +294,6 @@ impl Display for GraphicsPipelineCreationError {
f, f,
"the pipeline layout is not compatible with what the shaders expect", "the pipeline layout is not compatible with what the shaders expect",
), ),
Self::IncompatibleSpecializationConstants => write!(
f,
"the provided specialization constants are not compatible with what the shader \
expects",
),
Self::IncompatibleVertexDefinition(_) => write!( Self::IncompatibleVertexDefinition(_) => write!(
f, f,
"the vertex definition is not compatible with the input of the vertex shader", "the vertex definition is not compatible with the input of the vertex shader",
@ -336,17 +364,65 @@ impl Display for GraphicsPipelineCreationError {
"the stencil attachment of the render pass does not match the stencil test", "the stencil attachment of the render pass does not match the stencil test",
), ),
Self::OomError(_) => write!(f, "not enough memory available"), Self::OomError(_) => write!(f, "not enough memory available"),
Self::OtherTessellationShaderStageMissing => write!(
f,
"only one tessellation shader stage was provided, the other was not",
),
Self::DescriptorSetLayoutCreationError(_) => { Self::DescriptorSetLayoutCreationError(_) => {
write!(f, "error while creating a descriptor set layout object") write!(f, "error while creating a descriptor set layout object")
} }
Self::PipelineLayoutCreationError(_) => { Self::PipelineLayoutCreationError(_) => {
write!(f, "error while creating the pipeline layout object") write!(f, "error while creating the pipeline layout object")
} }
Self::ShaderSpecializationConstantTypeMismatch {
stage_index,
constant_id,
default_value,
provided_value,
} => write!(
f,
"the value provided for shader {} specialization constant id {} ({:?}) has a \
different type than the constant's default value ({:?})",
stage_index, constant_id, provided_value, default_value,
),
Self::ShaderStageDuplicate {
stage_index,
stage,
} => write!(
f,
"the shader stage at index {} (stage: {:?}) was provided more than once",
stage_index, stage,
),
Self::ShaderStageInvalid {
stage_index,
stage,
} => write!(
f,
"the shader stage at index {} (stage: {:?}) is not a graphics shader",
stage_index, stage,
),
Self::ShaderStageUnused {
stage,
} => write!(
f,
"the configuration of the pipeline does not use the `{:?}` shader stage, but it was provided",
stage,
),
Self::ShaderStagesMismatch(_) => write!( Self::ShaderStagesMismatch(_) => write!(
f, f,
"the output interface of one shader and the input interface of the next shader do \ "the output interface of one shader and the input interface of the next shader do \
not match", not match",
), ),
Self::StateMissing { state } => write!(
f,
"the configuration of the pipeline requires `{}` to be provided, but it was not",
state,
),
Self::StateUnused { state } => write!(
f,
"the configuration of the pipeline does not use `{}`, but it was provided",
state,
),
Self::StencilAttachmentFormatUsageNotSupported => write!( Self::StencilAttachmentFormatUsageNotSupported => write!(
f, f,
"the stencil attachment has a format that does not support that usage", "the stencil attachment has a format that does not support that usage",
@ -391,6 +467,10 @@ impl Display for GraphicsPipelineCreationError {
for vertex buffers", for vertex buffers",
format, location, format, location,
), ),
Self::VertexShaderStageMissing => write!(
f,
"no vertex shader stage was provided",
),
Self::ViewportBoundsExceeded => write!( Self::ViewportBoundsExceeded => write!(
f, f,
"the minimum or maximum bounds of viewports have been exceeded", "the minimum or maximum bounds of viewports have been exceeded",

View File

@ -127,19 +127,7 @@ impl GraphicsPipeline {
/// Starts the building process of a graphics pipeline. Returns a builder object that you can /// Starts the building process of a graphics pipeline. Returns a builder object that you can
/// fill with the various parameters. /// fill with the various parameters.
#[inline] #[inline]
pub fn start() -> GraphicsPipelineBuilder< pub fn start() -> GraphicsPipelineBuilder<VertexInputState> {
'static,
'static,
'static,
'static,
'static,
VertexInputState,
(),
(),
(),
(),
(),
> {
GraphicsPipelineBuilder::new() GraphicsPipelineBuilder::new()
} }

View File

@ -136,18 +136,17 @@ use crate::{
device::{Device, DeviceOwned}, device::{Device, DeviceOwned},
format::{Format, NumericType}, format::{Format, NumericType},
image::view::ImageViewType, image::view::ImageViewType,
macros::{impl_id_counter, vulkan_bitflags_enum}, macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum},
pipeline::{graphics::input_assembly::PrimitiveTopology, layout::PushConstantRange}, pipeline::{graphics::input_assembly::PrimitiveTopology, layout::PushConstantRange},
shader::spirv::{Capability, Spirv, SpirvError}, shader::spirv::{Capability, Spirv, SpirvError},
sync::PipelineStages, sync::PipelineStages,
DeviceSize, OomError, Version, VulkanError, VulkanObject, OomError, Version, VulkanError, VulkanObject,
}; };
use ahash::{HashMap, HashSet}; use ahash::{HashMap, HashSet};
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::hash_map::Entry, collections::hash_map::Entry,
error::Error, error::Error,
ffi::{CStr, CString},
fmt::{Display, Error as FmtError, Formatter}, fmt::{Display, Error as FmtError, Formatter},
mem, mem,
mem::MaybeUninit, mem::MaybeUninit,
@ -159,6 +158,8 @@ use std::{
pub mod reflect; pub mod reflect;
pub mod spirv; pub mod spirv;
use bytemuck::bytes_of;
use half::f16;
use spirv::ExecutionModel; use spirv::ExecutionModel;
// Generated by build.rs // Generated by build.rs
@ -170,7 +171,8 @@ pub struct ShaderModule {
handle: ash::vk::ShaderModule, handle: ash::vk::ShaderModule,
device: Arc<Device>, device: Arc<Device>,
id: NonZeroU64, id: NonZeroU64,
entry_points: HashMap<String, HashMap<ExecutionModel, EntryPointInfo>>, entry_point_map: HashMap<String, HashMap<ExecutionModel, usize>>,
entry_point_infos: Vec<EntryPointInfo>,
} }
impl ShaderModule { impl ShaderModule {
@ -184,7 +186,7 @@ impl ShaderModule {
pub unsafe fn from_words( pub unsafe fn from_words(
device: Arc<Device>, device: Arc<Device>,
words: &[u32], words: &[u32],
) -> Result<Arc<ShaderModule>, ShaderCreationError> { ) -> Result<Arc<ShaderModule>, ShaderModuleCreationError> {
let spirv = Spirv::new(words)?; let spirv = Spirv::new(words)?;
Self::from_words_with_data( Self::from_words_with_data(
@ -206,7 +208,7 @@ impl ShaderModule {
pub unsafe fn from_bytes( pub unsafe fn from_bytes(
device: Arc<Device>, device: Arc<Device>,
bytes: &[u8], bytes: &[u8],
) -> Result<Arc<ShaderModule>, ShaderCreationError> { ) -> Result<Arc<ShaderModule>, ShaderModuleCreationError> {
assert!((bytes.len() % 4) == 0); assert!((bytes.len() % 4) == 0);
Self::from_words( Self::from_words(
@ -232,10 +234,10 @@ impl ShaderModule {
spirv_version: Version, spirv_version: Version,
spirv_capabilities: impl IntoIterator<Item = &'a Capability>, spirv_capabilities: impl IntoIterator<Item = &'a Capability>,
spirv_extensions: impl IntoIterator<Item = &'a str>, spirv_extensions: impl IntoIterator<Item = &'a str>,
entry_points: impl IntoIterator<Item = (String, ExecutionModel, EntryPointInfo)>, entry_points: impl IntoIterator<Item = EntryPointInfo>,
) -> Result<Arc<ShaderModule>, ShaderCreationError> { ) -> Result<Arc<ShaderModule>, ShaderModuleCreationError> {
if let Err(reason) = check_spirv_version(&device, spirv_version) { if let Err(reason) = check_spirv_version(&device, spirv_version) {
return Err(ShaderCreationError::SpirvVersionNotSupported { return Err(ShaderModuleCreationError::SpirvVersionNotSupported {
version: spirv_version, version: spirv_version,
reason, reason,
}); });
@ -243,7 +245,7 @@ impl ShaderModule {
for &capability in spirv_capabilities { for &capability in spirv_capabilities {
if let Err(reason) = check_spirv_capability(&device, capability) { if let Err(reason) = check_spirv_capability(&device, capability) {
return Err(ShaderCreationError::SpirvCapabilityNotSupported { return Err(ShaderModuleCreationError::SpirvCapabilityNotSupported {
capability, capability,
reason, reason,
}); });
@ -252,7 +254,7 @@ impl ShaderModule {
for extension in spirv_extensions { for extension in spirv_extensions {
if let Err(reason) = check_spirv_extension(&device, extension) { if let Err(reason) = check_spirv_extension(&device, extension) {
return Err(ShaderCreationError::SpirvExtensionNotSupported { return Err(ShaderModuleCreationError::SpirvExtensionNotSupported {
extension: extension.to_owned(), extension: extension.to_owned(),
reason, reason,
}); });
@ -280,26 +282,17 @@ impl ShaderModule {
output.assume_init() output.assume_init()
}; };
let entries = entry_points.into_iter().collect::<Vec<_>>(); let mut entry_point_map: HashMap<String, HashMap<ExecutionModel, usize>> =
let entry_points = entries HashMap::default();
.iter() let entry_point_infos: Vec<_> = entry_points
.map(|(name, _, _)| name) .into_iter()
.collect::<HashSet<_>>() .enumerate()
.iter() .map(|(index, info)| {
.map(|name| { entry_point_map
( .entry(info.name.clone())
(*name).clone(), .or_default()
entries .insert(ExecutionModel::from(&info.execution), index);
.iter() info
.filter_map(|(entry_name, entry_model, info)| {
if &entry_name == name {
Some((*entry_model, info.clone()))
} else {
None
}
})
.collect::<HashMap<_, _>>(),
)
}) })
.collect(); .collect();
@ -307,7 +300,8 @@ impl ShaderModule {
handle, handle,
device, device,
id: Self::next_id(), id: Self::next_id(),
entry_points, entry_point_map,
entry_point_infos,
})) }))
} }
@ -322,8 +316,8 @@ impl ShaderModule {
spirv_version: Version, spirv_version: Version,
spirv_capabilities: impl IntoIterator<Item = &'a Capability>, spirv_capabilities: impl IntoIterator<Item = &'a Capability>,
spirv_extensions: impl IntoIterator<Item = &'a str>, spirv_extensions: impl IntoIterator<Item = &'a str>,
entry_points: impl IntoIterator<Item = (String, ExecutionModel, EntryPointInfo)>, entry_points: impl IntoIterator<Item = EntryPointInfo>,
) -> Result<Arc<ShaderModule>, ShaderCreationError> { ) -> Result<Arc<ShaderModule>, ShaderModuleCreationError> {
assert!((bytes.len() % 4) == 0); assert!((bytes.len() % 4) == 0);
Self::from_words_with_data( Self::from_words_with_data(
@ -343,13 +337,12 @@ impl ShaderModule {
/// point with that name exists in the shader module or if multiple entry points with the same /// point with that name exists in the shader module or if multiple entry points with the same
/// name exist. /// name exist.
#[inline] #[inline]
pub fn entry_point<'a>(&'a self, name: &str) -> Option<EntryPoint<'a>> { pub fn entry_point(self: &Arc<Self>, name: &str) -> Option<EntryPoint> {
self.entry_points.get(name).and_then(|infos| { self.entry_point_map.get(name).and_then(|infos| {
if infos.len() == 1 { if infos.len() == 1 {
infos.iter().next().map(|(_, info)| EntryPoint { infos.iter().next().map(|(_, &info_index)| EntryPoint {
module: self, module: self.clone(),
name: CString::new(name).unwrap(), info_index,
info,
}) })
} else { } else {
None None
@ -360,16 +353,15 @@ impl ShaderModule {
/// Returns information about the entry point with the provided name and execution model. /// Returns information about the entry point with the provided name and execution model.
/// Returns `None` if no entry and execution model exists in the shader module. /// Returns `None` if no entry and execution model exists in the shader module.
#[inline] #[inline]
pub fn entry_point_with_execution<'a>( pub fn entry_point_with_execution(
&'a self, self: &Arc<Self>,
name: &str, name: &str,
execution: ExecutionModel, execution: ExecutionModel,
) -> Option<EntryPoint<'a>> { ) -> Option<EntryPoint> {
self.entry_points.get(name).and_then(|infos| { self.entry_point_map.get(name).and_then(|infos| {
infos.get(&execution).map(|info| EntryPoint { infos.get(&execution).map(|&info_index| EntryPoint {
module: self, module: self.clone(),
name: CString::new(name).unwrap(), info_index,
info,
}) })
}) })
} }
@ -403,102 +395,14 @@ unsafe impl DeviceOwned for ShaderModule {
impl_id_counter!(ShaderModule); impl_id_counter!(ShaderModule);
/// Error that can happen when creating a new shader module.
#[derive(Clone, Debug)]
pub enum ShaderCreationError {
OomError(OomError),
SpirvCapabilityNotSupported {
capability: Capability,
reason: ShaderSupportError,
},
SpirvError(SpirvError),
SpirvExtensionNotSupported {
extension: String,
reason: ShaderSupportError,
},
SpirvVersionNotSupported {
version: Version,
reason: ShaderSupportError,
},
}
impl Error for ShaderCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
Self::SpirvCapabilityNotSupported { reason, .. } => Some(reason),
Self::SpirvError(err) => Some(err),
Self::SpirvExtensionNotSupported { reason, .. } => Some(reason),
Self::SpirvVersionNotSupported { reason, .. } => Some(reason),
}
}
}
impl Display for ShaderCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::SpirvCapabilityNotSupported { capability, .. } => write!(
f,
"the SPIR-V capability {:?} enabled by the shader is not supported by the device",
capability,
),
Self::SpirvError(_) => write!(f, "the SPIR-V module could not be read"),
Self::SpirvExtensionNotSupported { extension, .. } => write!(
f,
"the SPIR-V extension {} enabled by the shader is not supported by the device",
extension,
),
Self::SpirvVersionNotSupported { version, .. } => write!(
f,
"the shader uses SPIR-V version {}.{}, which is not supported by the device",
version.major, version.minor,
),
}
}
}
impl From<VulkanError> for ShaderCreationError {
fn from(err: VulkanError) -> Self {
Self::OomError(err.into())
}
}
impl From<SpirvError> for ShaderCreationError {
fn from(err: SpirvError) -> Self {
Self::SpirvError(err)
}
}
/// Error that can happen when checking whether a shader is supported by a device.
#[derive(Clone, Copy, Debug)]
pub enum ShaderSupportError {
NotSupportedByVulkan,
RequirementsNotMet(&'static [&'static str]),
}
impl Error for ShaderSupportError {}
impl Display for ShaderSupportError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::NotSupportedByVulkan => write!(f, "not supported by Vulkan"),
Self::RequirementsNotMet(requirements) => write!(
f,
"at least one of the following must be available/enabled on the device: {}",
requirements.join(", "),
),
}
}
}
/// The information associated with a single entry point in a shader. /// The information associated with a single entry point in a shader.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EntryPointInfo { pub struct EntryPointInfo {
pub name: String,
pub execution: ShaderExecution, pub execution: ShaderExecution,
pub descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>, pub descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>,
pub push_constant_requirements: Option<PushConstantRange>, pub push_constant_requirements: Option<PushConstantRange>,
pub specialization_constant_requirements: HashMap<u32, SpecializationConstantRequirements>, pub specialization_constants: HashMap<u32, SpecializationConstant>,
pub input_interface: ShaderInterface, pub input_interface: ShaderInterface,
pub output_interface: ShaderInterface, pub output_interface: ShaderInterface,
} }
@ -507,75 +411,28 @@ pub struct EntryPointInfo {
/// ///
/// Can be obtained by calling [`entry_point`](ShaderModule::entry_point) on the shader module. /// Can be obtained by calling [`entry_point`](ShaderModule::entry_point) on the shader module.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EntryPoint<'a> { pub struct EntryPoint {
module: &'a ShaderModule, module: Arc<ShaderModule>,
name: CString, info_index: usize,
info: &'a EntryPointInfo,
} }
impl<'a> EntryPoint<'a> { impl EntryPoint {
/// Returns the module this entry point comes from. /// Returns the module this entry point comes from.
#[inline] #[inline]
pub fn module(&self) -> &'a ShaderModule { pub fn module(&self) -> &Arc<ShaderModule> {
self.module &self.module
} }
/// Returns the name of the entry point. /// Returns information about the entry point.
#[inline] #[inline]
pub fn name(&self) -> &CStr { pub fn info(&self) -> &EntryPointInfo {
&self.name &self.module.entry_point_infos[self.info_index]
}
/// Returns the execution model of the shader.
#[inline]
pub fn execution(&self) -> &ShaderExecution {
&self.info.execution
}
/// Returns the descriptor binding requirements.
#[inline]
pub fn descriptor_binding_requirements(
&self,
) -> impl ExactSizeIterator<Item = ((u32, u32), &DescriptorBindingRequirements)> {
self.info
.descriptor_binding_requirements
.iter()
.map(|(k, v)| (*k, v))
}
/// Returns the push constant requirements.
#[inline]
pub fn push_constant_requirements(&self) -> Option<&PushConstantRange> {
self.info.push_constant_requirements.as_ref()
}
/// Returns the specialization constant requirements.
#[inline]
pub fn specialization_constant_requirements(
&self,
) -> impl ExactSizeIterator<Item = (u32, &SpecializationConstantRequirements)> {
self.info
.specialization_constant_requirements
.iter()
.map(|(k, v)| (*k, v))
}
/// Returns the input attributes used by the shader stage.
#[inline]
pub fn input_interface(&self) -> &ShaderInterface {
&self.info.input_interface
}
/// Returns the output attributes used by the shader stage.
#[inline]
pub fn output_interface(&self) -> &ShaderInterface {
&self.info.output_interface
} }
} }
/// The mode in which a shader executes. This includes both information about the shader type/stage, /// The mode in which a shader executes. This includes both information about the shader type/stage,
/// and additional data relevant to particular shader types. /// and additional data relevant to particular shader types.
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum ShaderExecution { pub enum ShaderExecution {
Vertex, Vertex,
TessellationControl, TessellationControl,
@ -594,6 +451,28 @@ pub enum ShaderExecution {
SubpassShading, SubpassShading,
} }
impl From<&ShaderExecution> for ExecutionModel {
fn from(value: &ShaderExecution) -> Self {
match value {
ShaderExecution::Vertex => Self::Vertex,
ShaderExecution::TessellationControl => Self::TessellationControl,
ShaderExecution::TessellationEvaluation => Self::TessellationEvaluation,
ShaderExecution::Geometry(_) => Self::Geometry,
ShaderExecution::Fragment(_) => Self::Fragment,
ShaderExecution::Compute => Self::GLCompute,
ShaderExecution::RayGeneration => Self::RayGenerationKHR,
ShaderExecution::AnyHit => Self::AnyHitKHR,
ShaderExecution::ClosestHit => Self::ClosestHitKHR,
ShaderExecution::Miss => Self::MissKHR,
ShaderExecution::Intersection => Self::IntersectionKHR,
ShaderExecution::Callable => Self::CallableKHR,
ShaderExecution::Task => Self::TaskNV,
ShaderExecution::Mesh => Self::MeshNV,
ShaderExecution::SubpassShading => todo!(),
}
}
}
/*#[derive(Clone, Copy, Debug)] /*#[derive(Clone, Copy, Debug)]
pub struct TessellationShaderExecution { pub struct TessellationShaderExecution {
pub num_output_vertices: u32, pub num_output_vertices: u32,
@ -851,154 +730,197 @@ impl DescriptorRequirements {
} }
} }
/// An error that can be returned when trying to create the intersection of two /// Specifies a single shader stage when creating a pipeline.
/// `DescriptorBindingRequirements` values. #[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct PipelineShaderStageCreateInfo {
pub enum DescriptorBindingRequirementsIncompatible { /// Specifies how to create the shader stage.
/// The allowed descriptor types of the descriptors do not overlap.
DescriptorType,
/// The descriptors require different formats.
ImageFormat,
/// The descriptors require different scalar types.
ImageScalarType,
/// The multisampling requirements of the descriptors differ.
ImageMultisampled,
/// The descriptors require different image view types.
ImageViewType,
}
impl Error for DescriptorBindingRequirementsIncompatible {}
impl Display for DescriptorBindingRequirementsIncompatible {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
DescriptorBindingRequirementsIncompatible::DescriptorType => write!(
f,
"the allowed descriptor types of the two descriptors do not overlap",
),
DescriptorBindingRequirementsIncompatible::ImageFormat => {
write!(f, "the descriptors require different formats",)
}
DescriptorBindingRequirementsIncompatible::ImageMultisampled => write!(
f,
"the multisampling requirements of the descriptors differ",
),
DescriptorBindingRequirementsIncompatible::ImageScalarType => {
write!(f, "the descriptors require different scalar types",)
}
DescriptorBindingRequirementsIncompatible::ImageViewType => {
write!(f, "the descriptors require different image view types",)
}
}
}
}
/// The requirements imposed by a shader on a specialization constant.
#[derive(Clone, Copy, Debug)]
pub struct SpecializationConstantRequirements {
pub size: DeviceSize,
}
/// Trait for types that contain specialization data for shaders.
///
/// Shader modules can contain what is called *specialization constants*. They are the same as
/// constants except that their values can be defined when you create a compute pipeline or a
/// graphics pipeline. Doing so is done by passing a type that implements the
/// `SpecializationConstants` trait and that stores the values in question. The `descriptors()`
/// method of this trait indicates how to grab them.
///
/// Boolean specialization constants must be stored as 32bits integers, where `0` means `false` and
/// any non-zero value means `true`. Integer and floating-point specialization constants are
/// stored as their Rust equivalent.
///
/// This trait is implemented on `()` for shaders that don't have any specialization constant.
///
/// # Examples
///
/// ```rust
/// use vulkano::shader::SpecializationConstants;
/// use vulkano::shader::SpecializationMapEntry;
///
/// #[repr(C)] // `#[repr(C)]` guarantees that the struct has a specific layout
/// struct MySpecConstants {
/// my_integer_constant: i32,
/// a_boolean: u32,
/// floating_point: f32,
/// }
///
/// unsafe impl SpecializationConstants for MySpecConstants {
/// fn descriptors() -> &'static [SpecializationMapEntry] {
/// static DESCRIPTORS: [SpecializationMapEntry; 3] = [
/// SpecializationMapEntry {
/// constant_id: 0,
/// offset: 0,
/// size: 4,
/// },
/// SpecializationMapEntry {
/// constant_id: 1,
/// offset: 4,
/// size: 4,
/// },
/// SpecializationMapEntry {
/// constant_id: 2,
/// offset: 8,
/// size: 4,
/// },
/// ];
///
/// &DESCRIPTORS
/// }
/// }
/// ```
///
/// # Safety
///
/// - The `SpecializationMapEntry` returned must contain valid offsets and sizes.
/// - The size of each `SpecializationMapEntry` must match the size of the corresponding constant
/// (`4` for booleans).
pub unsafe trait SpecializationConstants {
/// Returns descriptors of the struct's layout.
fn descriptors() -> &'static [SpecializationMapEntry];
}
unsafe impl SpecializationConstants for () {
#[inline]
fn descriptors() -> &'static [SpecializationMapEntry] {
&[]
}
}
/// Describes an individual constant to set in the shader. Also a field in the struct.
// Implementation note: has the same memory representation as a `VkSpecializationMapEntry`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct SpecializationMapEntry {
/// Identifier of the constant in the shader that corresponds to this field.
/// ///
/// For SPIR-V, this must be the value of the `SpecId` decoration applied to the specialization /// The default value is empty.
/// constant. pub flags: PipelineShaderStageCreateFlags,
/// 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. /// The shader entry point for the stage.
pub offset: u32, ///
/// There is no default value.
pub entry_point: EntryPoint,
/// Size of the data in bytes. Must match the size of the constant (`4` for booleans). /// Values for the specialization constants in the shader, indexed by their `constant_id`.
pub size: usize, ///
/// Specialization constants are constants whose value can be overridden when you create
/// a pipeline. When provided, they must have the same type as defined in the shader.
/// Constants that are not given a value here will have the default value that was specified
/// for them in the shader code.
///
/// The default value is empty.
pub specialization_info: HashMap<u32, SpecializationConstant>,
pub _ne: crate::NonExhaustive,
} }
impl From<SpecializationMapEntry> for ash::vk::SpecializationMapEntry { impl PipelineShaderStageCreateInfo {
/// Returns a `PipelineShaderStageCreateInfo` with the specified `entry_point`.
#[inline] #[inline]
fn from(val: SpecializationMapEntry) -> Self { pub fn entry_point(entry_point: EntryPoint) -> Self {
Self { Self {
constant_id: val.constant_id, flags: PipelineShaderStageCreateFlags::empty(),
offset: val.offset, entry_point,
size: val.size, specialization_info: HashMap::default(),
_ne: crate::NonExhaustive(()),
} }
} }
} }
vulkan_bitflags! {
#[non_exhaustive]
/// Flags that control how a pipeline shader stage is created.
PipelineShaderStageCreateFlags = PipelineShaderStageCreateFlags(u32);
/* TODO: enable
// TODO: document
ALLOW_VARYING_SUBGROUP_SIZE = ALLOW_VARYING_SUBGROUP_SIZE {
api_version: V1_3,
device_extensions: [ext_subgroup_size_control],
},
*/
/* TODO: enable
// TODO: document
REQUIRE_FULL_SUBGROUPS = REQUIRE_FULL_SUBGROUPS {
api_version: V1_3,
device_extensions: [ext_subgroup_size_control],
},
*/
}
/// The value to provide for a specialization constant, when creating a pipeline.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SpecializationConstant {
Bool(bool),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
F16(f16),
F32(f32),
F64(f64),
}
impl SpecializationConstant {
/// Returns the value as a byte slice. Booleans are expanded to a `VkBool32` value.
#[inline]
pub fn as_bytes(&self) -> &[u8] {
match self {
Self::Bool(false) => bytes_of(&ash::vk::FALSE),
Self::Bool(true) => bytes_of(&ash::vk::TRUE),
Self::I8(value) => bytes_of(value),
Self::I16(value) => bytes_of(value),
Self::I32(value) => bytes_of(value),
Self::I64(value) => bytes_of(value),
Self::U8(value) => bytes_of(value),
Self::U16(value) => bytes_of(value),
Self::U32(value) => bytes_of(value),
Self::U64(value) => bytes_of(value),
Self::F16(value) => bytes_of(value),
Self::F32(value) => bytes_of(value),
Self::F64(value) => bytes_of(value),
}
}
/// Returns whether `self` and `other` have the same type, ignoring the value.
#[inline]
pub fn eq_type(&self, other: &Self) -> bool {
mem::discriminant(self) == mem::discriminant(other)
}
}
impl From<bool> for SpecializationConstant {
#[inline]
fn from(value: bool) -> Self {
SpecializationConstant::Bool(value)
}
}
impl From<i8> for SpecializationConstant {
#[inline]
fn from(value: i8) -> Self {
SpecializationConstant::I8(value)
}
}
impl From<i16> for SpecializationConstant {
#[inline]
fn from(value: i16) -> Self {
SpecializationConstant::I16(value)
}
}
impl From<i32> for SpecializationConstant {
#[inline]
fn from(value: i32) -> Self {
SpecializationConstant::I32(value)
}
}
impl From<i64> for SpecializationConstant {
#[inline]
fn from(value: i64) -> Self {
SpecializationConstant::I64(value)
}
}
impl From<u8> for SpecializationConstant {
#[inline]
fn from(value: u8) -> Self {
SpecializationConstant::U8(value)
}
}
impl From<u16> for SpecializationConstant {
#[inline]
fn from(value: u16) -> Self {
SpecializationConstant::U16(value)
}
}
impl From<u32> for SpecializationConstant {
#[inline]
fn from(value: u32) -> Self {
SpecializationConstant::U32(value)
}
}
impl From<u64> for SpecializationConstant {
#[inline]
fn from(value: u64) -> Self {
SpecializationConstant::U64(value)
}
}
impl From<f16> for SpecializationConstant {
#[inline]
fn from(value: f16) -> Self {
SpecializationConstant::F16(value)
}
}
impl From<f32> for SpecializationConstant {
#[inline]
fn from(value: f32) -> Self {
SpecializationConstant::F32(value)
}
}
impl From<f64> for SpecializationConstant {
#[inline]
fn from(value: f64) -> Self {
SpecializationConstant::F64(value)
}
}
/// Type that contains the definition of an interface between two shader stages, or between /// Type that contains the definition of an interface between two shader stages, or between
/// the outside and a shader stage. /// the outside and a shader stage.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -1149,54 +1071,6 @@ impl From<NumericType> for ShaderScalarType {
} }
} }
/// Error that can happen when the interface mismatches between two shader stages.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ShaderInterfaceMismatchError {
/// The number of elements is not the same between the two shader interfaces.
ElementsCountMismatch {
/// Number of elements in the first interface.
self_elements: u32,
/// Number of elements in the second interface.
other_elements: u32,
},
/// An element is missing from one of the interfaces.
MissingElement {
/// Location of the missing element.
location: u32,
},
/// The type of an element does not match.
TypeMismatch {
/// Location of the element that mismatches.
location: u32,
/// Type in the first interface.
self_ty: ShaderInterfaceEntryType,
/// Type in the second interface.
other_ty: ShaderInterfaceEntryType,
},
}
impl Error for ShaderInterfaceMismatchError {}
impl Display for ShaderInterfaceMismatchError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(
f,
"{}",
match self {
ShaderInterfaceMismatchError::ElementsCountMismatch { .. } => {
"the number of elements mismatches"
}
ShaderInterfaceMismatchError::MissingElement { .. } => "an element is missing",
ShaderInterfaceMismatchError::TypeMismatch { .. } => {
"the type of an element does not match"
}
}
)
}
}
vulkan_bitflags_enum! { vulkan_bitflags_enum! {
#[non_exhaustive] #[non_exhaustive]
@ -1282,10 +1156,10 @@ vulkan_bitflags_enum! {
}, },
} }
impl From<ShaderExecution> for ShaderStage { impl From<&ShaderExecution> for ShaderStage {
#[inline] #[inline]
fn from(val: ShaderExecution) -> Self { fn from(value: &ShaderExecution) -> Self {
match val { match value {
ShaderExecution::Vertex => Self::Vertex, ShaderExecution::Vertex => Self::Vertex,
ShaderExecution::TessellationControl => Self::TessellationControl, ShaderExecution::TessellationControl => Self::TessellationControl,
ShaderExecution::TessellationEvaluation => Self::TessellationEvaluation, ShaderExecution::TessellationEvaluation => Self::TessellationEvaluation,
@ -1400,3 +1274,182 @@ fn check_spirv_version(device: &Device, mut version: Version) -> Result<(), Shad
} }
Ok(()) Ok(())
} }
/// Error that can happen when creating a new shader module.
#[derive(Clone, Debug)]
pub enum ShaderModuleCreationError {
OomError(OomError),
SpirvCapabilityNotSupported {
capability: Capability,
reason: ShaderSupportError,
},
SpirvError(SpirvError),
SpirvExtensionNotSupported {
extension: String,
reason: ShaderSupportError,
},
SpirvVersionNotSupported {
version: Version,
reason: ShaderSupportError,
},
}
impl Error for ShaderModuleCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
Self::SpirvCapabilityNotSupported { reason, .. } => Some(reason),
Self::SpirvError(err) => Some(err),
Self::SpirvExtensionNotSupported { reason, .. } => Some(reason),
Self::SpirvVersionNotSupported { reason, .. } => Some(reason),
}
}
}
impl Display for ShaderModuleCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::SpirvCapabilityNotSupported { capability, .. } => write!(
f,
"the SPIR-V capability {:?} enabled by the shader is not supported by the device",
capability,
),
Self::SpirvError(_) => write!(f, "the SPIR-V module could not be read"),
Self::SpirvExtensionNotSupported { extension, .. } => write!(
f,
"the SPIR-V extension {} enabled by the shader is not supported by the device",
extension,
),
Self::SpirvVersionNotSupported { version, .. } => write!(
f,
"the shader uses SPIR-V version {}.{}, which is not supported by the device",
version.major, version.minor,
),
}
}
}
impl From<VulkanError> for ShaderModuleCreationError {
fn from(err: VulkanError) -> Self {
Self::OomError(err.into())
}
}
impl From<SpirvError> for ShaderModuleCreationError {
fn from(err: SpirvError) -> Self {
Self::SpirvError(err)
}
}
/// Error that can happen when checking whether a shader is supported by a device.
#[derive(Clone, Copy, Debug)]
pub enum ShaderSupportError {
NotSupportedByVulkan,
RequirementsNotMet(&'static [&'static str]),
}
impl Error for ShaderSupportError {}
impl Display for ShaderSupportError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::NotSupportedByVulkan => write!(f, "not supported by Vulkan"),
Self::RequirementsNotMet(requirements) => write!(
f,
"at least one of the following must be available/enabled on the device: {}",
requirements.join(", "),
),
}
}
}
/// An error that can be returned when trying to create the intersection of two
/// `DescriptorBindingRequirements` values.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DescriptorBindingRequirementsIncompatible {
/// The allowed descriptor types of the descriptors do not overlap.
DescriptorType,
/// The descriptors require different formats.
ImageFormat,
/// The descriptors require different scalar types.
ImageScalarType,
/// The multisampling requirements of the descriptors differ.
ImageMultisampled,
/// The descriptors require different image view types.
ImageViewType,
}
impl Error for DescriptorBindingRequirementsIncompatible {}
impl Display for DescriptorBindingRequirementsIncompatible {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
DescriptorBindingRequirementsIncompatible::DescriptorType => write!(
f,
"the allowed descriptor types of the two descriptors do not overlap",
),
DescriptorBindingRequirementsIncompatible::ImageFormat => {
write!(f, "the descriptors require different formats",)
}
DescriptorBindingRequirementsIncompatible::ImageMultisampled => write!(
f,
"the multisampling requirements of the descriptors differ",
),
DescriptorBindingRequirementsIncompatible::ImageScalarType => {
write!(f, "the descriptors require different scalar types",)
}
DescriptorBindingRequirementsIncompatible::ImageViewType => {
write!(f, "the descriptors require different image view types",)
}
}
}
}
/// Error that can happen when the interface mismatches between two shader stages.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ShaderInterfaceMismatchError {
/// The number of elements is not the same between the two shader interfaces.
ElementsCountMismatch {
/// Number of elements in the first interface.
self_elements: u32,
/// Number of elements in the second interface.
other_elements: u32,
},
/// An element is missing from one of the interfaces.
MissingElement {
/// Location of the missing element.
location: u32,
},
/// The type of an element does not match.
TypeMismatch {
/// Location of the element that mismatches.
location: u32,
/// Type in the first interface.
self_ty: ShaderInterfaceEntryType,
/// Type in the second interface.
other_ty: ShaderInterfaceEntryType,
},
}
impl Error for ShaderInterfaceMismatchError {}
impl Display for ShaderInterfaceMismatchError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(
f,
"{}",
match self {
ShaderInterfaceMismatchError::ElementsCountMismatch { .. } => {
"the number of elements mismatches"
}
ShaderInterfaceMismatchError::MissingElement { .. } => "an element is missing",
ShaderInterfaceMismatchError::TypeMismatch { .. } => {
"the type of an element does not match"
}
}
)
}
}

View File

@ -21,12 +21,12 @@ use crate::{
}, },
DescriptorIdentifier, DescriptorRequirements, EntryPointInfo, GeometryShaderExecution, DescriptorIdentifier, DescriptorRequirements, EntryPointInfo, GeometryShaderExecution,
GeometryShaderInput, ShaderExecution, ShaderInterface, ShaderInterfaceEntry, GeometryShaderInput, ShaderExecution, ShaderInterface, ShaderInterfaceEntry,
ShaderInterfaceEntryType, ShaderScalarType, ShaderStage, ShaderInterfaceEntryType, ShaderScalarType, ShaderStage, SpecializationConstant,
SpecializationConstantRequirements,
}, },
DeviceSize, DeviceSize,
}; };
use ahash::{HashMap, HashSet}; use ahash::{HashMap, HashSet};
use half::f16;
use std::borrow::Cow; use std::borrow::Cow;
/// Returns an iterator of the capabilities used by `spirv`. /// Returns an iterator of the capabilities used by `spirv`.
@ -53,9 +53,7 @@ pub fn spirv_extensions(spirv: &Spirv) -> impl Iterator<Item = &str> {
/// Returns an iterator over all entry points in `spirv`, with information about the entry point. /// Returns an iterator over all entry points in `spirv`, with information about the entry point.
#[inline] #[inline]
pub fn entry_points( pub fn entry_points(spirv: &Spirv) -> impl Iterator<Item = EntryPointInfo> + '_ {
spirv: &Spirv,
) -> impl Iterator<Item = (String, ExecutionModel, EntryPointInfo)> + '_ {
let interface_variables = interface_variables(spirv); let interface_variables = interface_variables(spirv);
spirv.iter_entry_point().filter_map(move |instruction| { spirv.iter_entry_point().filter_map(move |instruction| {
@ -71,7 +69,7 @@ pub fn entry_points(
}; };
let execution = shader_execution(spirv, execution_model, function_id); let execution = shader_execution(spirv, execution_model, function_id);
let stage = ShaderStage::from(execution); let stage = ShaderStage::from(&execution);
let descriptor_binding_requirements = inspect_entry_point( let descriptor_binding_requirements = inspect_entry_point(
&interface_variables.descriptor_binding, &interface_variables.descriptor_binding,
@ -80,7 +78,7 @@ pub fn entry_points(
function_id, function_id,
); );
let push_constant_requirements = push_constant_requirements(spirv, stage); let push_constant_requirements = push_constant_requirements(spirv, stage);
let specialization_constant_requirements = specialization_constant_requirements(spirv); let specialization_constants = specialization_constants(spirv);
let input_interface = shader_interface( let input_interface = shader_interface(
spirv, spirv,
interface, interface,
@ -99,18 +97,15 @@ pub fn entry_points(
matches!(execution_model, ExecutionModel::TessellationControl), matches!(execution_model, ExecutionModel::TessellationControl),
); );
Some(( Some(EntryPointInfo {
entry_point_name.clone(), name: entry_point_name.clone(),
execution_model, execution,
EntryPointInfo { descriptor_binding_requirements,
execution, push_constant_requirements,
descriptor_binding_requirements, specialization_constants,
push_constant_requirements, input_interface,
specialization_constant_requirements, output_interface,
input_interface, })
output_interface,
},
))
}) })
} }
@ -1029,57 +1024,87 @@ fn push_constant_requirements(spirv: &Spirv, stage: ShaderStage) -> Option<PushC
}) })
} }
/// Extracts the `SpecializationConstantRequirements` from `spirv`. /// Extracts the `SpecializationConstant` map from `spirv`.
fn specialization_constant_requirements( fn specialization_constants(spirv: &Spirv) -> HashMap<u32, SpecializationConstant> {
spirv: &Spirv, let get_constant_id = |result_id| {
) -> HashMap<u32, SpecializationConstantRequirements> { spirv
.id(result_id)
.iter_decoration()
.find_map(|instruction| match *instruction {
Instruction::Decorate {
decoration:
Decoration::SpecId {
specialization_constant_id,
},
..
} => Some(specialization_constant_id),
_ => None,
})
};
spirv spirv
.iter_global() .iter_global()
.filter_map(|instruction| { .filter_map(|instruction| match *instruction {
match *instruction { Instruction::SpecConstantFalse { result_id, .. } => get_constant_id(result_id)
Instruction::SpecConstantTrue { .map(|constant_id| (constant_id, SpecializationConstant::Bool(false))),
result_type_id, Instruction::SpecConstantTrue { result_id, .. } => get_constant_id(result_id)
result_id, .map(|constant_id| (constant_id, SpecializationConstant::Bool(true))),
} Instruction::SpecConstant {
| Instruction::SpecConstantFalse { result_type_id,
result_type_id, result_id,
result_id, ref value,
} } => get_constant_id(result_id).map(|constant_id| {
| Instruction::SpecConstant { let value = match *spirv.id(result_type_id).instruction() {
result_type_id, Instruction::TypeInt {
result_id, width, signedness, ..
.. } => {
} if width == 64 {
| Instruction::SpecConstantComposite { assert!(value.len() == 2);
result_type_id, } else {
result_id, assert!(value.len() == 1);
.. }
} => spirv
.id(result_id) match (signedness, width) {
.iter_decoration() (0, 8) => SpecializationConstant::U8(value[0] as u8),
.find_map(|instruction| match *instruction { (0, 16) => SpecializationConstant::U16(value[0] as u16),
Instruction::Decorate { (0, 32) => SpecializationConstant::U32(value[0]),
decoration: (0, 64) => SpecializationConstant::U64(
Decoration::SpecId { (value[0] as u64) | ((value[1] as u64) << 32),
specialization_constant_id, ),
}, (1, 8) => SpecializationConstant::I8(value[0] as i8),
.. (1, 16) => SpecializationConstant::I16(value[0] as i16),
} => Some(specialization_constant_id), (1, 32) => SpecializationConstant::I32(value[0] as i32),
_ => None, (1, 64) => SpecializationConstant::I64(
}) (value[0] as i64) | ((value[1] as i64) << 32),
.map(|constant_id| { ),
let size = match *spirv.id(result_type_id).instruction() { _ => unimplemented!(),
Instruction::TypeBool { .. } => { }
// Translate bool to Bool32 }
std::mem::size_of::<ash::vk::Bool32>() as DeviceSize Instruction::TypeFloat { width, .. } => {
} if width == 64 {
_ => size_of_type(spirv, result_type_id) assert!(value.len() == 2);
.expect("Found runtime-sized specialization constant"), } else {
}; assert!(value.len() == 1);
(constant_id, SpecializationConstantRequirements { size }) }
}),
_ => None, match width {
} 16 => SpecializationConstant::F16(f16::from_bits(value[0] as u16)),
32 => SpecializationConstant::F32(f32::from_bits(value[0])),
64 => SpecializationConstant::F64(f64::from_bits(
(value[0] as u64) | ((value[1] as u64) << 32),
)),
_ => unimplemented!(),
}
}
_ => panic!(
"Specialization constant {} has a non-scalar type",
constant_id
),
};
(constant_id, value)
}),
_ => None,
}) })
.collect() .collect()
} }