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},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
shader::PipelineShaderStageCreateInfo,
sync::{self, GpuFuture},
VulkanLibrary,
};
@ -134,12 +135,14 @@ fn main() {
",
}
}
let shader = cs::load(device.clone()).unwrap();
let shader = cs::load(device.clone())
.unwrap()
.entry_point("main")
.unwrap();
ComputePipeline::new(
device.clone(),
shader.entry_point("main").unwrap(),
&(),
PipelineShaderStageCreateInfo::entry_point(shader),
None,
|_| {},
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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