Lots of additional dynamic state, and some new pipeline state (#1724)

* Add extra dynamic state commands to the command buffer builders

* Add TessellationState

* Re-enable InputAssemblyState, add dynamic state, update Ash

* Rework depth_stencil module, add dynamic state

* Rename module raster > rasterization, add dynamic state

* Replace DynamicStateMode with bool, replace Option with StateMode enum for potentially dynamic states, rename Blend > ColorBlendState, add dynamic state

* Re-enable MultisampleState

* Re-enable ViewportState, add state to GraphicsPipeline with retrieval methods, add dynamic state

* Add builder methods for state types

* Color blend improvements

* Further checks on input assembly dynamic state

* Add color_write_enable

* Add topology class, check that it matches

* Add line_stipple

* Move some builder code to the individual state types

* Add discard rectangles

* Trim down GraphicsPipelineCreationError variants, order alphabetically
This commit is contained in:
Rua 2021-10-12 14:42:32 +02:00 committed by GitHub
parent 14f7ad57ac
commit 64ca6f78e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 6518 additions and 2508 deletions

View File

@ -28,13 +28,12 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -170,8 +169,8 @@ fn main() {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())

View File

@ -8,21 +8,17 @@
// according to those terms.
use std::sync::Arc;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Queue;
use vulkano::image::ImageViewAbstract;
use vulkano::pipeline::blend::AttachmentBlend;
use vulkano::pipeline::blend::BlendFactor;
use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::PipelineBindPoint;
use vulkano::pipeline::color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState};
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::Subpass;
/// Allows applying an ambient lighting to a scene.
@ -69,22 +65,19 @@ impl AmbientLightingSystem {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.blend_collective(AttachmentBlend {
enabled: true,
color_op: BlendOp::Add,
color_source: BlendFactor::One,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Max,
alpha_source: BlendFactor::One,
alpha_destination: BlendFactor::One,
mask_red: true,
mask_green: true,
mask_blue: true,
mask_alpha: true,
})
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend(
AttachmentBlend {
color_op: BlendOp::Add,
color_source: BlendFactor::One,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Max,
alpha_source: BlendFactor::One,
alpha_destination: BlendFactor::One,
},
))
.render_pass(subpass)
.build(gfx_queue.device().clone())
.unwrap(),

View File

@ -9,21 +9,17 @@
use cgmath::Vector3;
use std::sync::Arc;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Queue;
use vulkano::image::ImageViewAbstract;
use vulkano::pipeline::blend::AttachmentBlend;
use vulkano::pipeline::blend::BlendFactor;
use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::PipelineBindPoint;
use vulkano::pipeline::color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState};
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::Subpass;
/// Allows applying a directional light source to a scene.
@ -70,22 +66,19 @@ impl DirectionalLightingSystem {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.blend_collective(AttachmentBlend {
enabled: true,
color_op: BlendOp::Add,
color_source: BlendFactor::One,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Max,
alpha_source: BlendFactor::One,
alpha_destination: BlendFactor::One,
mask_red: true,
mask_green: true,
mask_blue: true,
mask_alpha: true,
})
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend(
AttachmentBlend {
color_op: BlendOp::Add,
color_source: BlendFactor::One,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Max,
alpha_source: BlendFactor::One,
alpha_destination: BlendFactor::One,
},
))
.render_pass(subpass)
.build(gfx_queue.device().clone())
.unwrap(),

View File

@ -7,24 +7,19 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use cgmath::Matrix4;
use cgmath::Vector3;
use cgmath::{Matrix4, Vector3};
use std::sync::Arc;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Queue;
use vulkano::image::ImageViewAbstract;
use vulkano::pipeline::blend::AttachmentBlend;
use vulkano::pipeline::blend::BlendFactor;
use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::PipelineBindPoint;
use vulkano::pipeline::color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState};
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::Subpass;
pub struct PointLightingSystem {
@ -70,22 +65,19 @@ impl PointLightingSystem {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.blend_collective(AttachmentBlend {
enabled: true,
color_op: BlendOp::Add,
color_source: BlendFactor::One,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Max,
alpha_source: BlendFactor::One,
alpha_destination: BlendFactor::One,
mask_red: true,
mask_green: true,
mask_blue: true,
mask_alpha: true,
})
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend(
AttachmentBlend {
color_op: BlendOp::Add,
color_source: BlendFactor::One,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Max,
alpha_source: BlendFactor::One,
alpha_destination: BlendFactor::One,
},
))
.render_pass(subpass)
.build(gfx_queue.device().clone())
.unwrap(),

View File

@ -8,14 +8,14 @@
// according to those terms.
use std::sync::Arc;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::device::Queue;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::depth_stencil::DepthStencilState;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::Subpass;
@ -60,10 +60,10 @@ impl TriangleDrawSystem {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.depth_stencil_simple_depth()
.depth_stencil_state(DepthStencilState::simple_depth_test())
.render_pass(subpass)
.build(gfx_queue.device().clone())
.unwrap(),

View File

@ -20,14 +20,14 @@ use vulkano::image::{
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::color_blend::ColorBlendState;
use vulkano::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology};
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -191,15 +191,18 @@ fn main() {
)
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = Arc::new(
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_strip()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(
InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip),
)
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.blend_alpha_blending()
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass)
.build(device.clone())
.unwrap(),
);

View File

@ -29,14 +29,14 @@ use vulkano::image::{
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::color_blend::ColorBlendState;
use vulkano::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology};
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -197,15 +197,18 @@ fn main() {
)
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = Arc::new(
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_strip()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(
InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip),
)
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.blend_alpha_blending()
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass)
.with_auto_layout(device.clone(), |set_descs| {
// Modify the auto-generated layout by setting an immutable sampler to
// set 0 binding 0.

View File

@ -42,13 +42,12 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{ComputePipeline, GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -237,8 +236,8 @@ fn main() {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())

View File

@ -26,14 +26,13 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::vertex::BuffersDefinition;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -246,8 +245,8 @@ fn main() {
.instance::<InstanceData>(),
)
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())

View File

@ -8,22 +8,18 @@
// according to those terms.
use std::sync::Arc;
use vulkano::buffer::TypedBufferAccess;
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::PipelineBindPoint;
use vulkano::sampler::{Filter, MipmapMode, Sampler};
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::SecondaryAutoCommandBuffer,
device::Queue,
image::ImageViewAbstract,
pipeline::GraphicsPipeline,
render_pass::Subpass,
sampler::SamplerAddressMode,
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Queue;
use vulkano::image::ImageViewAbstract;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::Subpass;
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
/// Vertex for textured quads
#[derive(Default, Debug, Clone, Copy)]
@ -92,10 +88,9 @@ impl PixelsDrawPipeline {
GraphicsPipeline::start()
.vertex_input_single_buffer::<TexturedVertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.input_assembly_state(InputAssemblyState::new())
.fragment_shader(fs.main_entry_point(), ())
.viewports_dynamic_scissors_irrelevant(1)
.depth_stencil_disabled()
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.render_pass(subpass)
.build(gfx_queue.device().clone())
.unwrap(),

View File

@ -81,7 +81,7 @@ use vulkano::image::{
view::ImageView, AttachmentImage, ImageDimensions, SampleCount, StorageImage,
};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{Framebuffer, Subpass};
use vulkano::sync::GpuFuture;
@ -273,7 +273,7 @@ fn main() {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.viewports_dynamic_scissors_irrelevant(1)
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())

View File

@ -25,14 +25,12 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::Surface;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Surface, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::ElementState;
@ -214,8 +212,8 @@ fn main() {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())

View File

@ -28,14 +28,15 @@ use vulkano::image::{
StorageImage,
};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{
AttachmentDesc, Framebuffer, LoadOp, MultiviewDesc, RenderPass, RenderPassDesc, StoreOp,
Subpass, SubpassDesc,
};
use vulkano::sync::GpuFuture;
use vulkano::{sync, Version};
use vulkano::sync::{self, GpuFuture};
use vulkano::Version;
fn main() {
let instance = Instance::new(
@ -240,15 +241,17 @@ fn main() {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports([Viewport {
origin: [0.0, 0.0],
dimensions: [
image.dimensions().width() as f32,
image.dimensions().height() as f32,
],
depth_range: 0.0..1.0,
}])
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([
Viewport {
origin: [0.0, 0.0],
dimensions: [
image.dimensions().width() as f32,
image.dimensions().height() as f32,
],
depth_range: 0.0..1.0,
},
]))
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())

View File

@ -19,14 +19,14 @@ use vulkano::device::{Device, DeviceExtensions, DeviceOwned, Features};
use vulkano::format::Format;
use vulkano::image::{view::ImageView, AttachmentImage, ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::depth_stencil::DepthStencilState;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::query::{QueryControlFlags, QueryPool, QueryResultFlags, QueryType};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -241,14 +241,14 @@ fn main() {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
// Enable depth testing, which is needed for occlusion queries to make sense at all.
// If you disable depth testing, every pixel is considered to pass the depth test, so
// every query will return a nonzero result.
.depth_stencil_simple_depth()
.depth_stencil_state(DepthStencilState::simple_depth_test())
.build(device.clone())
.unwrap(),
);

View File

@ -33,17 +33,17 @@ use vulkano::format::Format;
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::rasterization::{CullMode, FrontFace, RasterizationState};
use vulkano::pipeline::shader::{
GraphicsShaderType, ShaderInterface, ShaderInterfaceEntry, ShaderModule,
SpecializationConstants,
};
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -243,12 +243,14 @@ fn main() {
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vert_main, ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(frag_main, ())
.cull_mode_front()
.front_face_counter_clockwise()
.depth_stencil_disabled()
.rasterization_state(
RasterizationState::new()
.cull_mode(CullMode::Front)
.front_face(FrontFace::CounterClockwise),
)
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())
.unwrap(),

View File

@ -12,8 +12,7 @@ use std::io::Cursor;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::descriptor_set::layout::DescriptorSetLayout;
use vulkano::descriptor_set::layout::DescriptorSetLayoutError;
use vulkano::descriptor_set::layout::{DescriptorSetLayout, DescriptorSetLayoutError};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
@ -22,16 +21,15 @@ use vulkano::image::{
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
};
use vulkano::instance::Instance;
use vulkano::pipeline::color_blend::ColorBlendState;
use vulkano::pipeline::layout::PipelineLayout;
use vulkano::pipeline::shader::EntryPointAbstract;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -316,14 +314,15 @@ fn main() {
)
};
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = Arc::new(
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.viewports_dynamic_scissors_irrelevant(1)
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.blend_alpha_blending()
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass)
.with_pipeline_layout(device.clone(), pipeline_layout)
.unwrap(),
);

View File

@ -12,8 +12,7 @@ use examples::{Normal, Vertex, INDICES, NORMALS, VERTICES};
use std::sync::Arc;
use std::time::Instant;
use vulkano::buffer::cpu_pool::CpuBufferPool;
use vulkano::buffer::TypedBufferAccess;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
@ -23,14 +22,14 @@ use vulkano::image::attachment::AttachmentImage;
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::depth_stencil::DepthStencilState;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::vertex::BuffersDefinition;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -344,15 +343,16 @@ fn window_size_dependent_setup(
.vertex::<Normal>(),
)
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.viewports([Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
}])
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([
Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
},
]))
.fragment_shader(fs.main_entry_point(), ())
.depth_stencil_simple_depth()
.depth_stencil_state(DepthStencilState::simple_depth_test())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())
.unwrap(),

View File

@ -26,13 +26,14 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology};
use vulkano::pipeline::rasterization::{PolygonMode, RasterizationState};
use vulkano::pipeline::tessellation::TessellationState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -277,13 +278,16 @@ fn main() {
.vertex_shader(vs.main_entry_point(), ())
// Actually use the tessellation shaders.
.tessellation_shaders(tcs.main_entry_point(), (), tes.main_entry_point(), ())
// use PrimitiveTopology::PathList(3)
// Use a vertices_per_patch of 3, because we want to convert one triangle into lots of
// little ones. A value of 4 would convert a rectangle into lots of little triangles.
.patch_list(3)
// Enable line mode so we can see the generated vertices.
.polygon_mode_line()
.viewports_dynamic_scissors_irrelevant(1)
.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
// lots of little ones. A value of 4 would convert a rectangle into lots of little
// triangles.
.patch_control_points(3),
)
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())

View File

@ -24,13 +24,12 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::input_assembly::InputAssemblyState;
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync;
use vulkano::sync::{FlushError, GpuFuture};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
@ -327,9 +326,9 @@ fn main() {
// the entry point.
.vertex_shader(vs.main_entry_point(), ())
// The content of the vertex buffer describes a list of triangles.
.triangle_list()
.input_assembly_state(InputAssemblyState::new())
// Use a resizable viewport set to draw over the entire window
.viewports_dynamic_scissors_irrelevant(1)
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
// See `vertex_shader`.
.fragment_shader(fs.main_entry_point(), ())
// We have to indicate which subpass of which render pass this pipeline is going to be used

View File

@ -16,7 +16,7 @@ build = "build.rs"
[dependencies]
# When updating Ash, also update vk.xml to the same Vulkan patch version that Ash uses.
# All versions of vk.xml can be found at https://github.com/KhronosGroup/Vulkan-Headers/commits/master/registry/vk.xml.
ash = "0.33.0"
ash = "0.33.3"
crossbeam-queue = "0.3"
fnv = "1.0"
half = "1.7"

View File

@ -49,10 +49,16 @@ use crate::image::ImageAccess;
use crate::image::ImageAspect;
use crate::image::ImageAspects;
use crate::image::ImageLayout;
use crate::pipeline::color_blend::LogicOp;
use crate::pipeline::depth_stencil::CompareOp;
use crate::pipeline::depth_stencil::StencilFaces;
use crate::pipeline::depth_stencil::StencilOp;
use crate::pipeline::input_assembly::Index;
use crate::pipeline::input_assembly::IndexType;
use crate::pipeline::input_assembly::PrimitiveTopology;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::rasterization::CullMode;
use crate::pipeline::rasterization::FrontFace;
use crate::pipeline::shader::ShaderStages;
use crate::pipeline::vertex::VertexBuffersCollection;
use crate::pipeline::viewport::Scissor;
@ -70,7 +76,6 @@ use crate::query::QueryType;
use crate::render_pass::Framebuffer;
use crate::render_pass::FramebufferAbstract;
use crate::render_pass::LoadOp;
use crate::render_pass::RenderPass;
use crate::render_pass::Subpass;
use crate::sampler::Filter;
use crate::sync::AccessCheckError;
@ -125,7 +130,7 @@ pub struct AutoCommandBufferBuilder<L, P = StandardCommandPoolBuilder> {
// The state of the current render pass, specifying the pass, subpass index and its intended contents.
struct RenderPassState {
subpass: (Arc<RenderPass>, u32),
subpass: Subpass,
contents: SubpassContents,
framebuffer: ash::vk::Framebuffer, // Always null for secondary command buffers
}
@ -289,14 +294,13 @@ impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
framebuffer,
}) => {
let render_pass = CommandBufferInheritanceRenderPass {
subpass: Subpass::from(subpass.render_pass().clone(), subpass.index())
.unwrap(),
subpass: subpass.clone(),
framebuffer: framebuffer
.as_ref()
.map(|f| Box::new(f.clone()) as Box<_>),
};
let render_pass_state = RenderPassState {
subpass: (subpass.render_pass().clone(), subpass.index()),
subpass: subpass.clone(),
contents: SubpassContents::Inline,
framebuffer: ash::vk::Framebuffer::null(), // Only needed for primary command buffers
};
@ -475,7 +479,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
}
// Subpasses must be the same.
if pipeline.subpass().index() != render_pass_state.subpass.1 {
if pipeline.subpass().index() != render_pass_state.subpass.index() {
return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex);
}
@ -484,7 +488,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
.subpass()
.render_pass()
.desc()
.is_compatible_with_desc(&render_pass_state.subpass.0.desc())
.is_compatible_with_desc(&render_pass_state.subpass.render_pass().desc())
{
return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass);
}
@ -586,7 +590,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// - Panics if `index_buffer` does not have the
/// [`index_buffer`](crate::buffer::BufferUsage::index_buffer) usage enabled.
/// - If the index buffer contains `u8` indices, panics if the
/// [`ext_index_type_uint8`](crate::device::DeviceExtensions::ext_index_type_uint8) extension is not
/// [`index_type_uint8`](crate::device::Features::index_type_uint8) feature is not
/// enabled on the device.
pub fn bind_index_buffer<Ib, I>(&mut self, index_buffer: Ib) -> &mut Self
where
@ -617,7 +621,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
// VkDeviceMemory object
if !self.device().enabled_features().index_type_uint8 {
assert!(I::ty() != IndexType::U8, "if the index buffer contains u8 indices, the index_type_uint8 extension must be enabled on the device");
assert!(I::ty() != IndexType::U8, "if the index buffer contains u8 indices, the index_type_uint8 feature must be enabled on the device");
}
unsafe {
@ -1778,12 +1782,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
fn has_fixed_state(&self, state: DynamicState) -> bool {
self.state()
.pipeline_graphics()
.map(|pipeline| {
matches!(
pipeline.dynamic_state(state),
Some(crate::pipeline::DynamicStateMode::Fixed)
)
})
.map(|pipeline| matches!(pipeline.dynamic_state(state), Some(false)))
.unwrap_or(false)
}
@ -1810,6 +1809,152 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Sets whether dynamic color writes should be enabled for each attachment in the
/// framebuffer.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`color_write_enable`](crate::device::Features::color_write_enable)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - If there is a graphics pipeline with color blend state bound, `enables.len()` must equal
/// - [`attachments.len()`](crate::pipeline::color_blend::ColorBlendState::attachments).
#[inline]
pub fn set_color_write_enable<I>(&mut self, enables: I) -> &mut Self
where
I: IntoIterator<Item = bool>,
I::IntoIter: ExactSizeIterator,
{
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().color_write_enable,
"the color_write_enable feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::ColorWriteEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
let enables = enables.into_iter();
if let Some(color_blend_state) = self
.state()
.pipeline_graphics()
.and_then(|pipeline| pipeline.color_blend_state())
{
assert!(
enables.len() == color_blend_state.attachments.len(),
"if there is a graphics pipeline with color blend state bound, enables.len() must equal attachments.len()"
);
}
unsafe {
self.inner.set_color_write_enable(enables);
}
self
}
/// Sets the dynamic cull mode for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_cull_mode(&mut self, cull_mode: CullMode) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::CullMode),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_cull_mode(cull_mode);
}
self
}
/// Sets the dynamic depth bias values for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - If the [`depth_bias_clamp`](crate::device::Features::depth_bias_clamp)
/// feature is not enabled on the device, panics if `clamp` is not 0.0.
#[inline]
pub fn set_depth_bias(
&mut self,
constant_factor: f32,
clamp: f32,
slope_factor: f32,
) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::DepthBias),
"the currently bound graphics pipeline must not contain this state internally"
);
assert!(
clamp == 0.0 || self.device().enabled_features().depth_bias_clamp,
"if the depth_bias_clamp feature is not enabled, clamp must be 0.0"
);
unsafe {
self.inner
.set_depth_bias(constant_factor, clamp, slope_factor);
}
self
}
/// Sets whether dynamic depth bias is enabled for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_depth_bias_enable(&mut self, enable: bool) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state2,
"the extended_dynamic_state2 feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::DepthBiasEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_depth_bias_enable(enable);
}
self
}
/// Sets the dynamic depth bounds for future draw calls.
///
/// # Panics
@ -1847,6 +1992,238 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Sets whether dynamic depth bounds testing is enabled for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_depth_bounds_test_enable(&mut self, enable: bool) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::DepthBoundsTestEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_depth_bounds_test_enable(enable);
}
self
}
/// Sets the dynamic depth compare op for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_depth_compare_op(&mut self, compare_op: CompareOp) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::DepthCompareOp),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_depth_compare_op(compare_op);
}
self
}
/// Sets whether dynamic depth testing is enabled for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_depth_test_enable(&mut self, enable: bool) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::DepthTestEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_depth_test_enable(enable);
}
self
}
/// Sets whether dynamic depth write is enabled for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_depth_write_enable(&mut self, enable: bool) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::DepthWriteEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_depth_write_enable(enable);
}
self
}
/// Sets the dynamic discard rectangles for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`ext_discard_rectangles`](crate::device::Features::ext_discard_rectangles)
/// extension is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - Panics if the highest discard rectangle slot being set is greater than the
/// [`max_discard_rectangle`](crate::device::Properties::max_discard_rectangle) device property.
pub fn set_discard_rectangle<I>(&mut self, first_rectangle: u32, rectangles: I) -> &mut Self
where
I: IntoIterator<Item = Scissor>,
{
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_extensions().ext_discard_rectangles,
"the ext_discard_rectangles extension must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::DiscardRectangle),
"the currently bound graphics pipeline must not contain this state internally"
);
let rectangles: SmallVec<[Scissor; 2]> = rectangles.into_iter().collect();
assert!(
first_rectangle + rectangles.len() as u32 <= self.device().physical_device().properties().max_discard_rectangles.unwrap(),
"the highest discard rectangle slot being set must not be higher than the max_discard_rectangles device property"
);
// TODO: VUID-vkCmdSetDiscardRectangleEXT-viewportScissor2D-04788
// If this command is recorded in a secondary command buffer with
// VkCommandBufferInheritanceViewportScissorInfoNV::viewportScissor2D enabled, then this
// function must not be called
unsafe {
self.inner
.set_discard_rectangle(first_rectangle, rectangles);
}
self
}
/// Sets the dynamic front face for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_front_face(&mut self, face: FrontFace) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::FrontFace),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_front_face(face);
}
self
}
/// Sets the dynamic line stipple values for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`ext_line_rasterization`](crate::device::DeviceExtensions::ext_line_rasterization)
/// extension is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - Panics if `factor` is not between 1 and 256 inclusive.
#[inline]
pub fn set_line_stipple(&mut self, factor: u32, pattern: u16) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_extensions().ext_line_rasterization,
"the ext_line_rasterization extension must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::LineStipple),
"the currently bound graphics pipeline must not contain this state internally"
);
assert!(
factor >= 1 && factor <= 256,
"factor must be between 1 and 256 inclusive"
);
unsafe {
self.inner.set_line_stipple(factor, pattern);
}
self
}
/// Sets the dynamic line width for future draw calls.
///
/// # Panics
@ -1879,6 +2256,192 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Sets the dynamic logic op for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the
/// [`extended_dynamic_state2_logic_op`](crate::device::Features::extended_dynamic_state2_logic_op)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_logic_op(&mut self, logic_op: LogicOp) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device()
.enabled_features()
.extended_dynamic_state2_logic_op,
"the extended_dynamic_state2_logic_op feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::LogicOp),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_logic_op(logic_op);
}
self
}
/// Sets the dynamic number of patch control points for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the
/// [`extended_dynamic_state2_patch_control_points`](crate::device::Features::extended_dynamic_state2_patch_control_points)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - Panics if `num` is 0.
/// - Panics if `num` is greater than the
/// [`max_tessellation_patch_size`](crate::device::Properties::max_tessellation_patch_size)
/// property of the device.
#[inline]
pub fn set_patch_control_points(&mut self, num: u32) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state2_patch_control_points,
"the extended_dynamic_state2_patch_control_points feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::PatchControlPoints),
"the currently bound graphics pipeline must not contain this state internally"
);
assert!(num > 0, "num must be greater than 0");
assert!(
num <= self
.device()
.physical_device()
.properties()
.max_tessellation_patch_size,
"num must be less than or equal to max_tessellation_patch_size"
);
unsafe {
self.inner.set_patch_control_points(num);
}
self
}
/// Sets whether dynamic primitive restart is enabled for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_primitive_restart_enable(&mut self, enable: bool) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state2,
"the extended_dynamic_state2 feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::PrimitiveRestartEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_primitive_restart_enable(enable);
}
self
}
/// Sets the dynamic primitive topology for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - If the [`geometry_shader`](crate::device::Features::geometry_shader) feature is not
/// enabled, panics if `topology` is a `WithAdjacency` topology.
/// - If the [`tessellation_shader`](crate::device::Features::tessellation_shader) feature is
/// not enabled, panics if `topology` is `PatchList`.
#[inline]
pub fn set_primitive_topology(&mut self, topology: PrimitiveTopology) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::PrimitiveTopology),
"the currently bound graphics pipeline must not contain this state internally"
);
if !self.device().enabled_features().geometry_shader {
assert!(!matches!(topology, PrimitiveTopology::LineListWithAdjacency
| PrimitiveTopology::LineStripWithAdjacency
| PrimitiveTopology::TriangleListWithAdjacency
| PrimitiveTopology::TriangleStripWithAdjacency), "if the geometry_shader feature is not enabled, topology must not be a WithAdjacency topology");
}
if !self.device().enabled_features().tessellation_shader {
assert!(
!matches!(topology, PrimitiveTopology::PatchList),
"if the tessellation_shader feature is not enabled, topology must not be PatchList"
);
}
unsafe {
self.inner.set_primitive_topology(topology);
}
self
}
/// Sets whether dynamic rasterizer discard is enabled for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_rasterizer_discard_enable(&mut self, enable: bool) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state2,
"the extended_dynamic_state2 feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::RasterizerDiscardEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_rasterizer_discard_enable(enable);
}
self
}
/// Sets the dynamic scissors for future draw calls.
///
/// # Panics
@ -1933,6 +2496,62 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Sets the dynamic scissors with count for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - Panics if the highest scissor slot being set is greater than the
/// [`max_viewports`](crate::device::Properties::max_viewports) device property.
/// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled,
/// panics if more than 1 scissor is provided.
#[inline]
pub fn set_scissor_with_count<I>(&mut self, scissors: I) -> &mut Self
where
I: IntoIterator<Item = Scissor>,
{
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::ScissorWithCount),
"the currently bound graphics pipeline must not contain this state internally"
);
let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect();
assert!(
scissors.len() as u32 <= self.device().physical_device().properties().max_viewports,
"the highest scissor slot being set must not be higher than the max_viewports device property"
);
if !self.device().enabled_features().multi_viewport {
assert!(
scissors.len() <= 1,
"if the multi_viewport feature is not enabled, no more than 1 scissor must be provided"
);
}
// TODO: VUID-vkCmdSetScissorWithCountEXT-commandBuffer-04820
// commandBuffer must not have
// VkCommandBufferInheritanceViewportScissorInfoNV::viewportScissor2D enabled
unsafe {
self.inner.set_scissor_with_count(scissors);
}
self
}
/// Sets the dynamic stencil compare mask on one or both faces for future draw calls.
///
/// # Panics
@ -1960,6 +2579,45 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Sets the dynamic stencil ops on one or both faces for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_stencil_op(
&mut self,
faces: StencilFaces,
fail_op: StencilOp,
pass_op: StencilOp,
depth_fail_op: StencilOp,
compare_op: CompareOp,
) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::StencilOp),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner
.set_stencil_op(faces, fail_op, pass_op, depth_fail_op, compare_op);
}
self
}
/// Sets the dynamic stencil reference on one or both faces for future draw calls.
///
/// # Panics
@ -1983,6 +2641,36 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Sets whether dynamic stencil testing is enabled for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
#[inline]
pub fn set_stencil_test_enable(&mut self, enable: bool) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::StencilTestEnable),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_stencil_test_enable(enable);
}
self
}
/// Sets the dynamic stencil write mask on one or both faces for future draw calls.
///
/// # Panics
@ -2059,6 +2747,62 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Sets the dynamic viewports with count for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state)
/// feature is not enabled on the device.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - Panics if the highest viewport slot being set is greater than the
/// [`max_viewports`](crate::device::Properties::max_viewports) device property.
/// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled,
/// panics if more than 1 viewport is provided.
#[inline]
pub fn set_viewport_with_count<I>(&mut self, viewports: I) -> &mut Self
where
I: IntoIterator<Item = Viewport>,
{
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
self.device().enabled_features().extended_dynamic_state,
"the extended_dynamic_state feature must be enabled on the device"
);
assert!(
!self.has_fixed_state(DynamicState::ViewportWithCount),
"the currently bound graphics pipeline must not contain this state internally"
);
let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect();
assert!(
viewports.len() as u32 <= self.device().physical_device().properties().max_viewports,
"the highest viewport slot being set must not be higher than the max_viewports device property"
);
if !self.device().enabled_features().multi_viewport {
assert!(
viewports.len() <= 1,
"if the multi_viewport feature is not enabled, no more than 1 viewport must be provided"
);
}
// TODO: VUID-vkCmdSetViewportWithCountEXT-commandBuffer-04819
// commandBuffer must not have
// VkCommandBufferInheritanceViewportScissorInfoNV::viewportScissor2D enabled
unsafe {
self.inner.set_viewport_with_count(viewports);
}
self
}
/// Adds a command that writes data to a buffer.
///
/// If `data` is larger than the buffer, only the part of `data` that fits is written. If the
@ -2422,7 +3166,7 @@ where
self.inner
.begin_render_pass(framebuffer.clone(), contents, clear_values)?;
self.render_pass_state = Some(RenderPassState {
subpass: (framebuffer.render_pass().clone(), 0),
subpass: framebuffer.render_pass().clone().first_subpass(),
contents,
framebuffer: framebuffer_object,
});
@ -2438,12 +3182,15 @@ where
pub fn end_render_pass(&mut self) -> Result<&mut Self, AutoCommandBufferBuilderContextError> {
unsafe {
if let Some(render_pass_state) = self.render_pass_state.as_ref() {
let (ref rp, index) = render_pass_state.subpass;
if rp.desc().subpasses().len() as u32 != index + 1 {
if !render_pass_state.subpass.is_last_subpass() {
return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch {
actual: rp.desc().subpasses().len() as u32,
current: index,
actual: render_pass_state
.subpass
.render_pass()
.desc()
.subpasses()
.len() as u32,
current: render_pass_state.subpass.index(),
});
}
} else {
@ -2587,7 +3334,7 @@ where
}
// Subpasses must be the same.
if render_pass.subpass.index() != render_pass_state.subpass.1 {
if render_pass.subpass.index() != render_pass_state.subpass.index() {
return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex);
}
@ -2596,7 +3343,7 @@ where
.subpass
.render_pass()
.desc()
.is_compatible_with_desc(render_pass_state.subpass.0.desc())
.is_compatible_with_desc(render_pass_state.subpass.render_pass().desc())
{
return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass);
}
@ -2620,19 +3367,22 @@ where
) -> Result<&mut Self, AutoCommandBufferBuilderContextError> {
unsafe {
if let Some(render_pass_state) = self.render_pass_state.as_mut() {
let (ref rp, ref mut index) = render_pass_state.subpass;
if *index + 1 >= rp.desc().subpasses().len() as u32 {
return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch {
actual: rp.desc().subpasses().len() as u32,
current: *index,
});
} else {
*index += 1;
if render_pass_state.subpass.try_next_subpass() {
render_pass_state.contents = contents;
} else {
return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch {
actual: render_pass_state
.subpass
.render_pass()
.desc()
.subpasses()
.len() as u32,
current: render_pass_state.subpass.index(),
});
}
if let Some(multiview) = rp.desc().multiview() {
if let Some(multiview) = render_pass_state.subpass.render_pass().desc().multiview()
{
// When multiview is enabled, at the beginning of each subpass all non-render pass state is undefined
self.inner.reset_state();
}

View File

@ -27,8 +27,17 @@ use crate::descriptor_set::DescriptorSet;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::image::ImageLayout;
use crate::pipeline::color_blend::LogicOp;
use crate::pipeline::depth_stencil::CompareOp;
use crate::pipeline::depth_stencil::StencilOp;
use crate::pipeline::depth_stencil::StencilOps;
use crate::pipeline::input_assembly::IndexType;
use crate::pipeline::input_assembly::PrimitiveTopology;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::rasterization::CullMode;
use crate::pipeline::rasterization::DepthBias;
use crate::pipeline::rasterization::FrontFace;
use crate::pipeline::rasterization::LineStipple;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport;
use crate::pipeline::ComputePipeline;
@ -41,6 +50,7 @@ use crate::sync::PipelineMemoryAccess;
use crate::sync::PipelineStages;
use crate::OomError;
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::error;
@ -723,31 +733,76 @@ struct CurrentState {
push_constants: Option<PushConstantState>,
blend_constants: Option<[f32; 4]>,
depth_bias: Option<(f32, f32, f32)>,
color_write_enable: Option<SmallVec<[bool; 4]>>,
cull_mode: Option<CullMode>,
depth_bias: Option<DepthBias>,
depth_bias_enable: Option<bool>,
depth_bounds: Option<(f32, f32)>,
depth_bounds_test_enable: Option<bool>,
depth_compare_op: Option<CompareOp>,
depth_test_enable: Option<bool>,
depth_write_enable: Option<bool>,
discard_rectangle: FnvHashMap<u32, Scissor>,
front_face: Option<FrontFace>,
line_stipple: Option<LineStipple>,
line_width: Option<f32>,
stencil_compare_mask: StencilState,
stencil_reference: StencilState,
stencil_write_mask: StencilState,
logic_op: Option<LogicOp>,
patch_control_points: Option<u32>,
primitive_restart_enable: Option<bool>,
primitive_topology: Option<PrimitiveTopology>,
rasterizer_discard_enable: Option<bool>,
scissor: FnvHashMap<u32, Scissor>,
scissor_with_count: Option<SmallVec<[Scissor; 2]>>,
stencil_compare_mask: StencilStateDynamic,
stencil_op: StencilOpStateDynamic,
stencil_reference: StencilStateDynamic,
stencil_test_enable: Option<bool>,
stencil_write_mask: StencilStateDynamic,
viewport: FnvHashMap<u32, Viewport>,
viewport_with_count: Option<SmallVec<[Viewport; 2]>>,
}
impl CurrentState {
fn reset_dynamic_states(&mut self, states: impl IntoIterator<Item = DynamicState>) {
for state in states {
// TODO: If more dynamic states are added to CurrentState, add match arms here
match state {
DynamicState::BlendConstants => self.blend_constants = None,
DynamicState::ColorWriteEnable => self.color_write_enable = None,
DynamicState::CullMode => self.cull_mode = None,
DynamicState::DepthBias => self.depth_bias = None,
DynamicState::DepthBiasEnable => self.depth_bias_enable = None,
DynamicState::DepthBounds => self.depth_bounds = None,
DynamicState::DepthBoundsTestEnable => self.depth_bounds_test_enable = None,
DynamicState::DepthCompareOp => self.depth_compare_op = None,
DynamicState::DepthTestEnable => self.depth_test_enable = None,
DynamicState::DepthWriteEnable => self.depth_write_enable = None,
DynamicState::DiscardRectangle => self.discard_rectangle.clear(),
DynamicState::ExclusiveScissor => (), // TODO;
DynamicState::FragmentShadingRate => (), // TODO:
DynamicState::FrontFace => self.front_face = None,
DynamicState::LineStipple => self.line_stipple = None,
DynamicState::LineWidth => self.line_width = None,
DynamicState::StencilCompareMask => self.stencil_compare_mask = Default::default(),
DynamicState::StencilReference => self.stencil_reference = Default::default(),
DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(),
DynamicState::LogicOp => self.logic_op = None,
DynamicState::PatchControlPoints => self.patch_control_points = None,
DynamicState::PrimitiveRestartEnable => self.primitive_restart_enable = None,
DynamicState::PrimitiveTopology => self.primitive_topology = None,
DynamicState::RasterizerDiscardEnable => self.rasterizer_discard_enable = None,
DynamicState::RayTracingPipelineStackSize => (), // TODO:
DynamicState::SampleLocations => (), // TODO:
DynamicState::Scissor => self.scissor.clear(),
DynamicState::ScissorWithCount => self.scissor_with_count = None,
DynamicState::StencilCompareMask => self.stencil_compare_mask = Default::default(),
DynamicState::StencilOp => self.stencil_op = Default::default(),
DynamicState::StencilReference => self.stencil_reference = Default::default(),
DynamicState::StencilTestEnable => self.stencil_test_enable = None,
DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(),
DynamicState::VertexInput => (), // TODO:
DynamicState::VertexInputBindingStride => (), // TODO:
DynamicState::Viewport => self.viewport.clear(),
_ => (),
DynamicState::ViewportCoarseSampleOrder => (), // TODO:
DynamicState::ViewportShadingRatePalette => (), // TODO:
DynamicState::ViewportWScaling => (), // TODO:
DynamicState::ViewportWithCount => self.viewport_with_count = None,
}
}
}
@ -854,39 +909,159 @@ impl<'a> CommandBufferState<'a> {
self.current_state.blend_constants
}
/// Returns the current color write enable settings, or `None` if nothing has been set yet.
#[inline]
pub fn color_write_enable(&self) -> Option<&'a [bool]> {
self.current_state
.color_write_enable
.as_ref()
.map(|x| x.as_slice())
}
/// Returns the current cull mode, or `None` if nothing has been set yet.
#[inline]
pub fn cull_mode(&self) -> Option<CullMode> {
self.current_state.cull_mode
}
/// Returns the current depth bias settings, or `None` if nothing has been set yet.
#[inline]
pub fn depth_bias(&self) -> Option<(f32, f32, f32)> {
pub fn depth_bias(&self) -> Option<DepthBias> {
self.current_state.depth_bias
}
/// Returns whether depth bias is enabled, or `None` if nothing has been set yet.
#[inline]
pub fn depth_bias_enable(&self) -> Option<bool> {
self.current_state.depth_bias_enable
}
/// Returns the current depth bounds settings, or `None` if nothing has been set yet.
#[inline]
pub fn depth_bounds(&self) -> Option<(f32, f32)> {
self.current_state.depth_bounds
}
/// Returns whether depth bound testing is enabled, or `None` if nothing has been set yet.
#[inline]
pub fn depth_bounds_test_enable(&self) -> Option<bool> {
self.current_state.depth_bias_enable
}
/// Returns the current depth compare op, or `None` if nothing has been set yet.
#[inline]
pub fn depth_compare_op(&self) -> Option<CompareOp> {
self.current_state.depth_compare_op
}
/// Returns whether depth testing is enabled, or `None` if nothing has been set yet.
#[inline]
pub fn depth_test_enable(&self) -> Option<bool> {
self.current_state.depth_test_enable
}
/// Returns whether depth write is enabled, or `None` if nothing has been set yet.
#[inline]
pub fn depth_write_enable(&self) -> Option<bool> {
self.current_state.depth_write_enable
}
/// Returns the current discard rectangles, or `None` if nothing has been set yet.
#[inline]
pub fn discard_rectangle(&self, num: u32) -> Option<&'a Scissor> {
self.current_state.discard_rectangle.get(&num)
}
/// Returns the current front face, or `None` if nothing has been set yet.
#[inline]
pub fn front_face(&self) -> Option<FrontFace> {
self.current_state.front_face
}
/// Returns the current line stipple settings, or `None` if nothing has been set yet.
#[inline]
pub fn line_stipple(&self) -> Option<LineStipple> {
self.current_state.line_stipple
}
/// Returns the current line width, or `None` if nothing has been set yet.
#[inline]
pub fn line_width(&self) -> Option<f32> {
self.current_state.line_width
}
/// Returns the current logic op, or `None` if nothing has been set yet.
#[inline]
pub fn logic_op(&self) -> Option<LogicOp> {
self.current_state.logic_op
}
/// Returns the current number of patch control points, or `None` if nothing has been set yet.
#[inline]
pub fn patch_control_points(&self) -> Option<u32> {
self.current_state.patch_control_points
}
/// Returns whether primitive restart is enabled, or `None` if nothing has been set yet.
#[inline]
pub fn primitive_restart_enable(&self) -> Option<bool> {
self.current_state.primitive_restart_enable
}
/// Returns the current primitive topology, or `None` if nothing has been set yet.
#[inline]
pub fn primitive_topology(&self) -> Option<PrimitiveTopology> {
self.current_state.primitive_topology
}
/// Returns whether rasterizer discard is enabled, or `None` if nothing has been set yet.
#[inline]
pub fn rasterizer_discard_enable(&self) -> Option<bool> {
self.current_state.rasterizer_discard_enable
}
/// Returns the current scissor for a given viewport slot, or `None` if nothing has been set yet.
#[inline]
pub fn scissor(&self, num: u32) -> Option<&'a Scissor> {
self.current_state.scissor.get(&num)
}
/// Returns the current viewport-with-count settings, or `None` if nothing has been set yet.
#[inline]
pub fn scissor_with_count(&self) -> Option<&'a [Scissor]> {
self.current_state
.scissor_with_count
.as_ref()
.map(|x| x.as_slice())
}
/// Returns the current stencil compare masks.
#[inline]
pub fn stencil_compare_mask(&self) -> StencilState {
pub fn stencil_compare_mask(&self) -> StencilStateDynamic {
self.current_state.stencil_compare_mask
}
/// Returns the current stencil ops.
#[inline]
pub fn stencil_op(&self) -> StencilOpStateDynamic {
self.current_state.stencil_op
}
/// Returns the current stencil references.
#[inline]
pub fn stencil_reference(&self) -> StencilState {
pub fn stencil_reference(&self) -> StencilStateDynamic {
self.current_state.stencil_reference
}
/// Returns whether stencil testing is enabled, or `None` if nothing has been set yet.
#[inline]
pub fn stencil_test_enable(&self) -> Option<bool> {
self.current_state.stencil_test_enable
}
/// Returns the current stencil write masks.
#[inline]
pub fn stencil_write_mask(&self) -> StencilState {
pub fn stencil_write_mask(&self) -> StencilStateDynamic {
self.current_state.stencil_write_mask
}
@ -896,16 +1071,26 @@ impl<'a> CommandBufferState<'a> {
self.current_state.viewport.get(&num)
}
/// Returns the current scissor for a given viewport slot, or `None` if nothing has been set yet.
/// Returns the current viewport-with-count settings, or `None` if nothing has been set yet.
#[inline]
pub fn scissor(&self, num: u32) -> Option<&'a Scissor> {
self.current_state.scissor.get(&num)
pub fn viewport_with_count(&self) -> Option<&'a [Viewport]> {
self.current_state
.viewport_with_count
.as_ref()
.map(|x| x.as_slice())
}
}
/// Holds the current stencil state of a command buffer builder.
#[derive(Clone, Copy, Debug, Default)]
pub struct StencilState {
pub struct StencilStateDynamic {
pub front: Option<u32>,
pub back: Option<u32>,
}
/// Holds the current per-face stencil op state of a command buffer builder.
#[derive(Clone, Copy, Debug, Default)]
pub struct StencilOpStateDynamic {
pub front: Option<StencilOps>,
pub back: Option<StencilOps>,
}

View File

@ -39,7 +39,6 @@ use crate::pipeline::vertex::VertexInput;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport;
use crate::pipeline::ComputePipeline;
use crate::pipeline::DynamicStateMode;
use crate::pipeline::GraphicsPipeline;
use crate::pipeline::PipelineBindPoint;
use crate::query::QueryControlFlags;
@ -287,16 +286,14 @@ impl SyncCommandBufferBuilder {
}
}
self.current_state
.reset_dynamic_states(pipeline.dynamic_states().filter_map(|(state, mode)| {
// Reset any states that are fixed in the new pipeline. The pipeline bind command
// will overwrite these states.
if matches!(mode, DynamicStateMode::Fixed) {
Some(state)
} else {
None
}
}));
// Reset any states that are fixed in the new pipeline. The pipeline bind command will
// overwrite these states.
self.current_state.reset_dynamic_states(
pipeline
.dynamic_states()
.filter(|(_, d)| !d) // not dynamic
.map(|(s, _)| s),
);
self.append_command(Cmd { pipeline }, &[]).unwrap();
self.current_state.pipeline_graphics = self.commands.last().cloned();
}
@ -2352,6 +2349,63 @@ impl SyncCommandBufferBuilder {
self.current_state.blend_constants = Some(constants);
}
/// Calls `vkCmdSetColorWriteEnableEXT` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_color_write_enable<I>(&mut self, enables: I)
where
I: IntoIterator<Item = bool>,
{
struct Cmd<I> {
enables: Mutex<Option<I>>,
}
impl<I> Command for Cmd<I>
where
I: IntoIterator<Item = bool> + Send + Sync,
{
fn name(&self) -> &'static str {
"vkCmdSetColorWriteEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_color_write_enable(self.enables.lock().unwrap().take().unwrap());
}
}
let enables: SmallVec<[bool; 4]> = enables.into_iter().collect();
self.current_state.color_write_enable = Some(enables.clone());
self.append_command(
Cmd {
enables: Mutex::new(Some(enables)),
},
&[],
)
.unwrap();
}
/// Calls `vkCmdSetCullModeEXT` on the builder.
#[inline]
pub unsafe fn set_cull_mode(&mut self, cull_mode: CullMode) {
struct Cmd {
cull_mode: CullMode,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetCullModeEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_cull_mode(self.cull_mode);
}
}
self.append_command(Cmd { cull_mode }, &[]).unwrap();
self.current_state.cull_mode = Some(cull_mode);
}
/// Calls `vkCmdSetDepthBias` on the builder.
#[inline]
pub unsafe fn set_depth_bias(&mut self, constant_factor: f32, clamp: f32, slope_factor: f32) {
@ -2380,7 +2434,32 @@ impl SyncCommandBufferBuilder {
&[],
)
.unwrap();
self.current_state.depth_bias = Some((constant_factor, clamp, slope_factor));
self.current_state.depth_bias = Some(DepthBias {
constant_factor,
clamp,
slope_factor,
});
}
/// Calls `vkCmdSetDepthBiasEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_bias_enable(&mut self, enable: bool) {
struct Cmd {
enable: bool,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetDepthBiasEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_depth_bias_enable(self.enable);
}
}
self.append_command(Cmd { enable }, &[]).unwrap();
self.current_state.depth_bias_enable = Some(enable);
}
/// Calls `vkCmdSetDepthBounds` on the builder.
@ -2405,6 +2484,135 @@ impl SyncCommandBufferBuilder {
self.current_state.depth_bounds = Some((min, max));
}
/// Calls `vkCmdSetDepthBoundsTestEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_bounds_test_enable(&mut self, enable: bool) {
struct Cmd {
enable: bool,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetDepthBoundsTestEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_depth_bounds_test_enable(self.enable);
}
}
self.append_command(Cmd { enable }, &[]).unwrap();
self.current_state.depth_bounds_test_enable = Some(enable);
}
/// Calls `vkCmdSetDepthCompareOpEXT` on the builder.
#[inline]
pub unsafe fn set_depth_compare_op(&mut self, compare_op: CompareOp) {
struct Cmd {
compare_op: CompareOp,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetDepthCompareOpEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_depth_compare_op(self.compare_op);
}
}
self.append_command(Cmd { compare_op }, &[]).unwrap();
self.current_state.depth_compare_op = Some(compare_op);
}
/// Calls `vkCmdSetDepthTestEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_test_enable(&mut self, enable: bool) {
struct Cmd {
enable: bool,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetDepthTestEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_depth_test_enable(self.enable);
}
}
self.append_command(Cmd { enable }, &[]).unwrap();
self.current_state.depth_test_enable = Some(enable);
}
/// Calls `vkCmdSetDepthWriteEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_write_enable(&mut self, enable: bool) {
struct Cmd {
enable: bool,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetDepthWriteEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_depth_write_enable(self.enable);
}
}
self.append_command(Cmd { enable }, &[]).unwrap();
self.current_state.depth_write_enable = Some(enable);
}
/// Calls `vkCmdSetDiscardRectangle` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_discard_rectangle<I>(&mut self, first_rectangle: u32, rectangles: I)
where
I: IntoIterator<Item = Scissor>,
{
struct Cmd {
first_rectangle: u32,
rectangles: Mutex<SmallVec<[Scissor; 2]>>,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetRectangle"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_discard_rectangle(
self.first_rectangle,
self.rectangles.lock().unwrap().drain(..),
);
}
}
let rectangles: SmallVec<[Scissor; 2]> = rectangles.into_iter().collect();
for (num, rectangle) in rectangles.iter().enumerate() {
let num = num as u32 + first_rectangle;
self.current_state
.discard_rectangle
.insert(num, rectangle.clone());
}
self.append_command(
Cmd {
first_rectangle,
rectangles: Mutex::new(rectangles),
},
&[],
)
.unwrap();
}
/// Calls `vkCmdSetEvent` on the builder.
#[inline]
pub unsafe fn set_event(&mut self, event: Arc<Event>, stages: PipelineStages) {
@ -2426,6 +2634,49 @@ impl SyncCommandBufferBuilder {
self.append_command(Cmd { event, stages }, &[]).unwrap();
}
/// Calls `vkCmdSetFrontFaceEXT` on the builder.
#[inline]
pub unsafe fn set_front_face(&mut self, face: FrontFace) {
struct Cmd {
face: FrontFace,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetFrontFaceEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_front_face(self.face);
}
}
self.append_command(Cmd { face }, &[]).unwrap();
self.current_state.front_face = Some(face);
}
/// Calls `vkCmdSetLineStippleEXT` on the builder.
#[inline]
pub unsafe fn set_line_stipple(&mut self, factor: u32, pattern: u16) {
struct Cmd {
factor: u32,
pattern: u16,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetLineStippleEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_line_stipple(self.factor, self.pattern);
}
}
self.append_command(Cmd { factor, pattern }, &[]).unwrap();
self.current_state.line_stipple = Some(LineStipple { factor, pattern });
}
/// Calls `vkCmdSetLineWidth` on the builder.
#[inline]
pub unsafe fn set_line_width(&mut self, line_width: f32) {
@ -2447,6 +2698,111 @@ impl SyncCommandBufferBuilder {
self.current_state.line_width = Some(line_width);
}
/// Calls `vkCmdSetLogicOpEXT` on the builder.
#[inline]
pub unsafe fn set_logic_op(&mut self, logic_op: LogicOp) {
struct Cmd {
logic_op: LogicOp,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetLogicOpEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_logic_op(self.logic_op);
}
}
self.append_command(Cmd { logic_op }, &[]).unwrap();
self.current_state.logic_op = Some(logic_op);
}
/// Calls `vkCmdSetPatchControlPointsEXT` on the builder.
#[inline]
pub unsafe fn set_patch_control_points(&mut self, num: u32) {
struct Cmd {
num: u32,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetPatchControlPointsEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_patch_control_points(self.num);
}
}
self.append_command(Cmd { num }, &[]).unwrap();
self.current_state.patch_control_points = Some(num);
}
/// Calls `vkCmdSetPrimitiveRestartEnableEXT` on the builder.
#[inline]
pub unsafe fn set_primitive_restart_enable(&mut self, enable: bool) {
struct Cmd {
enable: bool,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetPrimitiveRestartEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_primitive_restart_enable(self.enable);
}
}
self.append_command(Cmd { enable }, &[]).unwrap();
self.current_state.primitive_restart_enable = Some(enable);
}
/// Calls `vkCmdSetPrimitiveTopologyEXT` on the builder.
#[inline]
pub unsafe fn set_primitive_topology(&mut self, topology: PrimitiveTopology) {
struct Cmd {
topology: PrimitiveTopology,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetPrimitiveTopologyEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_primitive_topology(self.topology);
}
}
self.append_command(Cmd { topology }, &[]).unwrap();
self.current_state.primitive_topology = Some(topology);
}
/// Calls `vkCmdSetRasterizerDiscardEnableEXT` on the builder.
#[inline]
pub unsafe fn set_rasterizer_discard_enable(&mut self, enable: bool) {
struct Cmd {
enable: bool,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetRasterizerDiscardEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_rasterizer_discard_enable(self.enable);
}
}
self.append_command(Cmd { enable }, &[]).unwrap();
self.current_state.rasterizer_discard_enable = Some(enable);
}
/// Calls `vkCmdSetStencilCompareMask` on the builder.
#[inline]
pub unsafe fn set_stencil_compare_mask(&mut self, faces: StencilFaces, compare_mask: u32) {
@ -2485,6 +2841,73 @@ impl SyncCommandBufferBuilder {
}
}
/// Calls `vkCmdSetStencilOpEXT` on the builder.
#[inline]
pub unsafe fn set_stencil_op(
&mut self,
faces: StencilFaces,
fail_op: StencilOp,
pass_op: StencilOp,
depth_fail_op: StencilOp,
compare_op: CompareOp,
) {
struct Cmd {
faces: StencilFaces,
fail_op: StencilOp,
pass_op: StencilOp,
depth_fail_op: StencilOp,
compare_op: CompareOp,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetStencilOpEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_stencil_op(
self.faces,
self.fail_op,
self.pass_op,
self.depth_fail_op,
self.compare_op,
);
}
}
self.append_command(
Cmd {
faces,
fail_op,
pass_op,
depth_fail_op,
compare_op,
},
&[],
)
.unwrap();
let faces = ash::vk::StencilFaceFlags::from(faces);
if faces.intersects(ash::vk::StencilFaceFlags::FRONT) {
self.current_state.stencil_op.front = Some(StencilOps {
fail_op,
pass_op,
depth_fail_op,
compare_op,
});
}
if faces.intersects(ash::vk::StencilFaceFlags::BACK) {
self.current_state.stencil_op.back = Some(StencilOps {
fail_op,
pass_op,
depth_fail_op,
compare_op,
});
}
}
/// Calls `vkCmdSetStencilReference` on the builder.
#[inline]
pub unsafe fn set_stencil_reference(&mut self, faces: StencilFaces, reference: u32) {
@ -2516,6 +2939,27 @@ impl SyncCommandBufferBuilder {
}
}
/// Calls `vkCmdSetStencilTestEnableEXT` on the builder.
#[inline]
pub unsafe fn set_stencil_test_enable(&mut self, enable: bool) {
struct Cmd {
enable: bool,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetStencilTestEnableEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_stencil_test_enable(self.enable);
}
}
self.append_command(Cmd { enable }, &[]).unwrap();
self.current_state.stencil_test_enable = Some(enable);
}
/// Calls `vkCmdSetStencilWriteMask` on the builder.
#[inline]
pub unsafe fn set_stencil_write_mask(&mut self, faces: StencilFaces, write_mask: u32) {
@ -2587,6 +3031,39 @@ impl SyncCommandBufferBuilder {
.unwrap();
}
/// Calls `vkCmdSetScissorWithCountEXT` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_scissor_with_count<I>(&mut self, scissors: I)
where
I: IntoIterator<Item = Scissor>,
{
struct Cmd {
scissors: Mutex<SmallVec<[Scissor; 2]>>,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetScissorWithCountEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_scissor_with_count(self.scissors.lock().unwrap().drain(..));
}
}
let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect();
self.current_state.scissor_with_count = Some(scissors.clone());
self.append_command(
Cmd {
scissors: Mutex::new(scissors),
},
&[],
)
.unwrap();
}
/// Calls `vkCmdSetViewport` on the builder.
///
/// If the list is empty then the command is automatically ignored.
@ -2630,6 +3107,39 @@ impl SyncCommandBufferBuilder {
.unwrap();
}
/// Calls `vkCmdSetViewportWithCountEXT` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_viewport_with_count<I>(&mut self, viewports: I)
where
I: IntoIterator<Item = Viewport>,
{
struct Cmd {
viewports: Mutex<SmallVec<[Viewport; 2]>>,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetViewportWithCountEXT"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_viewport_with_count(self.viewports.lock().unwrap().drain(..));
}
}
let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect();
self.current_state.viewport_with_count = Some(viewports.clone());
self.append_command(
Cmd {
viewports: Mutex::new(viewports),
},
&[],
)
.unwrap();
}
/// Calls `vkCmdUpdateBuffer` on the builder.
#[inline]
pub unsafe fn update_buffer<B, D, Dd>(&mut self, buffer: B, data: Dd)

View File

@ -65,7 +65,8 @@
//! queue with a fresh new barrier prototype.
pub use self::builder::CommandBufferState;
pub use self::builder::StencilState;
pub use self::builder::StencilOpStateDynamic;
pub use self::builder::StencilStateDynamic;
pub use self::builder::SyncCommandBufferBuilder;
pub use self::builder::SyncCommandBufferBuilderBindDescriptorSets;
pub use self::builder::SyncCommandBufferBuilderBindVertexBuffer;

View File

@ -27,9 +27,15 @@ use crate::image::ImageAspect;
use crate::image::ImageAspects;
use crate::image::ImageLayout;
use crate::image::SampleCount;
use crate::pipeline::color_blend::LogicOp;
use crate::pipeline::depth_stencil::CompareOp;
use crate::pipeline::depth_stencil::StencilFaces;
use crate::pipeline::depth_stencil::StencilOp;
use crate::pipeline::input_assembly::IndexType;
use crate::pipeline::input_assembly::PrimitiveTopology;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::rasterization::CullMode;
use crate::pipeline::rasterization::FrontFace;
use crate::pipeline::shader::ShaderStages;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport;
@ -400,6 +406,7 @@ impl UnsafeCommandBufferBuilder {
///
/// Does nothing if the list of buffers is empty, as it would be a no-op and isn't a valid
/// usage of the command anyway.
// TODO: vkCmdBindVertexBuffers2EXT
#[inline]
pub unsafe fn bind_vertex_buffers(
&mut self,
@ -1382,48 +1389,320 @@ impl UnsafeCommandBufferBuilder {
fns.v1_0.cmd_set_blend_constants(cmd, &constants);
}
/// Calls `vkCmdSetColorWriteEnableEXT` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_color_write_enable<I>(&mut self, enables: I)
where
I: IntoIterator<Item = bool>,
{
debug_assert!(self.device().enabled_extensions().ext_color_write_enable);
debug_assert!(self.device().enabled_features().color_write_enable);
let enables = enables
.into_iter()
.map(|v| v as ash::vk::Bool32)
.collect::<SmallVec<[_; 4]>>();
if enables.is_empty() {
return;
}
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_color_write_enable.cmd_set_color_write_enable_ext(
cmd,
enables.len() as u32,
enables.as_ptr(),
);
}
/// Calls `vkCmdSetCullModeEXT` on the builder.
#[inline]
pub unsafe fn set_cull_mode(&mut self, cull_mode: CullMode) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_cull_mode_ext(cmd, cull_mode.into());
}
/// Calls `vkCmdSetDepthBias` on the builder.
#[inline]
pub unsafe fn set_depth_bias(&mut self, constant_factor: f32, clamp: f32, slope_factor: f32) {
debug_assert!(clamp == 0.0 || self.device().enabled_features().depth_bias_clamp);
let fns = self.device().fns();
let cmd = self.internal_object();
debug_assert!(clamp == 0.0 || self.device().enabled_features().depth_bias_clamp);
fns.v1_0
.cmd_set_depth_bias(cmd, constant_factor, clamp, slope_factor);
}
/// Calls `vkCmdSetDepthBiasEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_bias_enable(&mut self, enable: bool) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state2
);
debug_assert!(self.device().enabled_features().extended_dynamic_state2);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state2
.cmd_set_depth_bias_enable_ext(cmd, enable.into());
}
/// Calls `vkCmdSetDepthBounds` on the builder.
#[inline]
pub unsafe fn set_depth_bounds(&mut self, min: f32, max: f32) {
let fns = self.device().fns();
let cmd = self.internal_object();
debug_assert!(min >= 0.0 && min <= 1.0);
debug_assert!(max >= 0.0 && max <= 1.0);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.v1_0.cmd_set_depth_bounds(cmd, min, max);
}
/// Calls `vkCmdSetDepthBoundsTestEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_bounds_test_enable(&mut self, enable: bool) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_depth_bounds_test_enable_ext(cmd, enable.into());
}
/// Calls `vkCmdSetDepthCompareOpEXT` on the builder.
#[inline]
pub unsafe fn set_depth_compare_op(&mut self, compare_op: CompareOp) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_depth_compare_op_ext(cmd, compare_op.into());
}
/// Calls `vkCmdSetDepthTestEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_test_enable(&mut self, enable: bool) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_depth_test_enable_ext(cmd, enable.into());
}
/// Calls `vkCmdSetDepthWriteEnableEXT` on the builder.
#[inline]
pub unsafe fn set_depth_write_enable(&mut self, enable: bool) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_depth_write_enable_ext(cmd, enable.into());
}
/// Calls `vkCmdSetDiscardRectangleEXT` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_discard_rectangle<I>(&mut self, first_rectangle: u32, rectangles: I)
where
I: IntoIterator<Item = Scissor>,
{
debug_assert!(self.device().enabled_extensions().ext_discard_rectangles);
let rectangles = rectangles
.into_iter()
.map(|v| v.clone().into())
.collect::<SmallVec<[_; 2]>>();
if rectangles.is_empty() {
return;
}
debug_assert!(
first_rectangle + rectangles.len() as u32
<= self
.device()
.physical_device()
.properties()
.max_discard_rectangles
.unwrap()
);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_discard_rectangles.cmd_set_discard_rectangle_ext(
cmd,
first_rectangle,
rectangles.len() as u32,
rectangles.as_ptr(),
);
}
/// Calls `vkCmdSetEvent` on the builder.
#[inline]
pub unsafe fn set_event(&mut self, event: &Event, stages: PipelineStages) {
let fns = self.device().fns();
let cmd = self.internal_object();
debug_assert!(!stages.host);
debug_assert_ne!(stages, PipelineStages::none());
let fns = self.device().fns();
let cmd = self.internal_object();
fns.v1_0
.cmd_set_event(cmd, event.internal_object(), stages.into());
}
/// Calls `vkCmdSetFrontFaceEXT` on the builder.
#[inline]
pub unsafe fn set_front_face(&mut self, face: FrontFace) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_front_face_ext(cmd, face.into());
}
/// Calls `vkCmdSetLineStippleEXT` on the builder.
#[inline]
pub unsafe fn set_line_stipple(&mut self, factor: u32, pattern: u16) {
debug_assert!(self.device().enabled_extensions().ext_line_rasterization);
debug_assert!(factor >= 1 && factor <= 256);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_line_rasterization
.cmd_set_line_stipple_ext(cmd, factor, pattern);
}
/// Calls `vkCmdSetLineWidth` on the builder.
#[inline]
pub unsafe fn set_line_width(&mut self, line_width: f32) {
debug_assert!(line_width == 1.0 || self.device().enabled_features().wide_lines);
let fns = self.device().fns();
let cmd = self.internal_object();
debug_assert!(line_width == 1.0 || self.device().enabled_features().wide_lines);
fns.v1_0.cmd_set_line_width(cmd, line_width);
}
/// Calls `vkCmdSetLogicOpEXT` on the builder.
#[inline]
pub unsafe fn set_logic_op(&mut self, logic_op: LogicOp) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state2
);
debug_assert!(
self.device()
.enabled_features()
.extended_dynamic_state2_logic_op
);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state2
.cmd_set_logic_op_ext(cmd, logic_op.into());
}
/// Calls `vkCmdSetPatchControlPointsEXT` on the builder.
#[inline]
pub unsafe fn set_patch_control_points(&mut self, num: u32) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state2
);
debug_assert!(
self.device()
.enabled_features()
.extended_dynamic_state2_patch_control_points
);
debug_assert!(num > 0);
debug_assert!(
num as u32
<= self
.device()
.physical_device()
.properties()
.max_tessellation_patch_size
);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state2
.cmd_set_patch_control_points_ext(cmd, num);
}
/// Calls `vkCmdSetPrimitiveRestartEnableEXT` on the builder.
#[inline]
pub unsafe fn set_primitive_restart_enable(&mut self, enable: bool) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state2
);
debug_assert!(self.device().enabled_features().extended_dynamic_state2);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state2
.cmd_set_primitive_restart_enable_ext(cmd, enable.into());
}
/// Calls `vkCmdSetPrimitiveTopologyEXT` on the builder.
#[inline]
pub unsafe fn set_primitive_topology(&mut self, topology: PrimitiveTopology) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_primitive_topology_ext(cmd, topology.into());
}
/// Calls `vkCmdSetRasterizerDiscardEnableEXT` on the builder.
#[inline]
pub unsafe fn set_rasterizer_discard_enable(&mut self, enable: bool) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state2
);
debug_assert!(self.device().enabled_features().extended_dynamic_state2);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state2
.cmd_set_rasterizer_discard_enable_ext(cmd, enable.into());
}
/// Calls `vkCmdSetStencilCompareMask` on the builder.
#[inline]
pub unsafe fn set_stencil_compare_mask(&mut self, face_mask: StencilFaces, compare_mask: u32) {
@ -1433,6 +1712,34 @@ impl UnsafeCommandBufferBuilder {
.cmd_set_stencil_compare_mask(cmd, face_mask.into(), compare_mask);
}
/// Calls `vkCmdSetStencilOpEXT` on the builder.
#[inline]
pub unsafe fn set_stencil_op(
&mut self,
face_mask: StencilFaces,
fail_op: StencilOp,
pass_op: StencilOp,
depth_fail_op: StencilOp,
compare_op: CompareOp,
) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state.cmd_set_stencil_op_ext(
cmd,
face_mask.into(),
fail_op.into(),
pass_op.into(),
depth_fail_op.into(),
compare_op.into(),
);
}
/// Calls `vkCmdSetStencilReference` on the builder.
#[inline]
pub unsafe fn set_stencil_reference(&mut self, face_mask: StencilFaces, reference: u32) {
@ -1442,6 +1749,21 @@ impl UnsafeCommandBufferBuilder {
.cmd_set_stencil_reference(cmd, face_mask.into(), reference);
}
/// Calls `vkCmdSetStencilTestEnableEXT` on the builder.
#[inline]
pub unsafe fn set_stencil_test_enable(&mut self, enable: bool) {
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_stencil_test_enable_ext(cmd, enable.into());
}
/// Calls `vkCmdSetStencilWriteMask` on the builder.
#[inline]
pub unsafe fn set_stencil_write_mask(&mut self, face_mask: StencilFaces, write_mask: u32) {
@ -1462,7 +1784,7 @@ impl UnsafeCommandBufferBuilder {
let scissors = scissors
.into_iter()
.map(|v| ash::vk::Rect2D::from(v.clone()))
.collect::<SmallVec<[_; 16]>>();
.collect::<SmallVec<[_; 2]>>();
if scissors.is_empty() {
return;
}
@ -1478,10 +1800,10 @@ impl UnsafeCommandBufferBuilder {
(first_scissor == 0 && scissors.len() == 1)
|| self.device().enabled_features().multi_viewport
);
debug_assert!({
let max = self.device().physical_device().properties().max_viewports;
first_scissor + scissors.len() as u32 <= max
});
debug_assert!(
first_scissor + scissors.len() as u32
<= self.device().physical_device().properties().max_viewports
);
let fns = self.device().fns();
let cmd = self.internal_object();
@ -1489,6 +1811,47 @@ impl UnsafeCommandBufferBuilder {
.cmd_set_scissor(cmd, first_scissor, scissors.len() as u32, scissors.as_ptr());
}
/// Calls `vkCmdSetScissorWithCountEXT` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_scissor_with_count<I>(&mut self, scissors: I)
where
I: IntoIterator<Item = Scissor>,
{
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let scissors = scissors
.into_iter()
.map(|v| ash::vk::Rect2D::from(v.clone()))
.collect::<SmallVec<[_; 2]>>();
if scissors.is_empty() {
return;
}
debug_assert!(scissors.iter().all(|s| s.offset.x >= 0 && s.offset.y >= 0));
debug_assert!(scissors.iter().all(|s| {
s.extent.width < i32::MAX as u32
&& s.extent.height < i32::MAX as u32
&& s.offset.x.checked_add(s.extent.width as i32).is_some()
&& s.offset.y.checked_add(s.extent.height as i32).is_some()
}));
debug_assert!(scissors.len() == 1 || self.device().enabled_features().multi_viewport);
debug_assert!(
scissors.len() as u32 <= self.device().physical_device().properties().max_viewports
);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_scissor_with_count_ext(cmd, scissors.len() as u32, scissors.as_ptr());
}
/// Calls `vkCmdSetViewport` on the builder.
///
/// If the list is empty then the command is automatically ignored.
@ -1500,7 +1863,7 @@ impl UnsafeCommandBufferBuilder {
let viewports = viewports
.into_iter()
.map(|v| v.clone().into())
.collect::<SmallVec<[_; 16]>>();
.collect::<SmallVec<[_; 2]>>();
if viewports.is_empty() {
return;
}
@ -1509,10 +1872,10 @@ impl UnsafeCommandBufferBuilder {
(first_viewport == 0 && viewports.len() == 1)
|| self.device().enabled_features().multi_viewport
);
debug_assert!({
let max = self.device().physical_device().properties().max_viewports;
first_viewport + viewports.len() as u32 <= max
});
debug_assert!(
first_viewport + viewports.len() as u32
<= self.device().physical_device().properties().max_viewports
);
let fns = self.device().fns();
let cmd = self.internal_object();
@ -1524,6 +1887,40 @@ impl UnsafeCommandBufferBuilder {
);
}
/// Calls `vkCmdSetViewportWithCountEXT` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_viewport_with_count<I>(&mut self, viewports: I)
where
I: IntoIterator<Item = Viewport>,
{
debug_assert!(
self.device()
.enabled_extensions()
.ext_extended_dynamic_state
);
debug_assert!(self.device().enabled_features().extended_dynamic_state);
let viewports = viewports
.into_iter()
.map(|v| v.clone().into())
.collect::<SmallVec<[_; 2]>>();
if viewports.is_empty() {
return;
}
debug_assert!(viewports.len() == 1 || self.device().enabled_features().multi_viewport);
debug_assert!(
viewports.len() as u32 <= self.device().physical_device().properties().max_viewports
);
let fns = self.device().fns();
let cmd = self.internal_object();
fns.ext_extended_dynamic_state
.cmd_set_viewport_with_count_ext(cmd, viewports.len() as u32, viewports.as_ptr());
}
/// Calls `vkCmdUpdateBuffer` on the builder.
#[inline]
pub unsafe fn update_buffer<B, D>(&mut self, buffer: &B, data: &D)

View File

@ -8,9 +8,11 @@
// according to those terms.
use crate::command_buffer::synced::CommandBufferState;
use crate::pipeline::input_assembly::PrimitiveTopology;
use crate::pipeline::shader::ShaderStage;
use crate::pipeline::DynamicState;
use crate::pipeline::DynamicStateMode;
use crate::pipeline::GraphicsPipeline;
use crate::pipeline::PartialStateMode;
use std::error;
use std::fmt;
@ -21,94 +23,322 @@ pub(in super::super) fn check_dynamic_state_validity(
) -> Result<(), CheckDynamicStateValidityError> {
let device = pipeline.device();
for state in pipeline.dynamic_states().filter_map(|(state, mode)| {
if matches!(mode, DynamicStateMode::Dynamic) {
Some(state)
} else {
None
}
}) {
match state {
for dynamic_state in pipeline
.dynamic_states()
.filter(|(_, d)| *d)
.map(|(s, _)| s)
{
match dynamic_state {
DynamicState::BlendConstants => {
if current_state.blend_constants().is_none() {
return Err(CheckDynamicStateValidityError::BlendConstantsNotSet);
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::ColorWriteEnable => {
let enables = if let Some(enables) = current_state.color_write_enable() {
enables
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
if enables.len() < pipeline.color_blend_state().unwrap().attachments.len() {
return Err(CheckDynamicStateValidityError::NotEnoughColorWriteEnable {
color_write_enable_count: enables.len() as u32,
attachment_count: pipeline.color_blend_state().unwrap().attachments.len()
as u32,
});
}
}
DynamicState::CullMode => {
if current_state.cull_mode().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthBias => {
if current_state.depth_bias().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthBiasEnable => {
if current_state.depth_bias_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::ColorWriteEnable => todo!(),
DynamicState::CullMode => todo!(),
DynamicState::DepthBias => todo!(),
DynamicState::DepthBiasEnable => todo!(),
DynamicState::DepthBounds => {
if current_state.depth_bounds().is_none() {
return Err(CheckDynamicStateValidityError::DepthBoundsNotSet);
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthBoundsTestEnable => {
if current_state.depth_bounds_test_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthCompareOp => {
if current_state.depth_compare_op().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthTestEnable => {
if current_state.depth_test_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::DepthWriteEnable => {
if current_state.depth_write_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
// TODO: Check if the depth buffer is writable
}
DynamicState::DiscardRectangle => {
let discard_rectangle_count =
match pipeline.discard_rectangle_state().unwrap().rectangles {
PartialStateMode::Dynamic(count) => count,
_ => unreachable!(),
};
for num in 0..discard_rectangle_count {
if current_state.discard_rectangle(num).is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
}
DynamicState::DepthBoundsTestEnable => todo!(),
DynamicState::DepthCompareOp => todo!(),
DynamicState::DepthTestEnable => todo!(),
DynamicState::DepthWriteEnable => todo!(),
DynamicState::DiscardRectangle => todo!(),
DynamicState::ExclusiveScissor => todo!(),
DynamicState::FragmentShadingRate => todo!(),
DynamicState::FrontFace => todo!(),
DynamicState::LineStipple => todo!(),
DynamicState::LineWidth => {
if current_state.line_width().is_none() {
return Err(CheckDynamicStateValidityError::LineWidthNotSet);
DynamicState::FrontFace => {
if current_state.front_face().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::LineStipple => {
if current_state.line_stipple().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::LineWidth => {
if current_state.line_width().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::LogicOp => {
if current_state.logic_op().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::PatchControlPoints => {
if current_state.patch_control_points().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::PrimitiveRestartEnable => {
let primitive_restart_enable =
if let Some(enable) = current_state.primitive_restart_enable() {
enable
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
if primitive_restart_enable {
let topology = match pipeline.input_assembly_state().topology {
PartialStateMode::Fixed(topology) => topology,
PartialStateMode::Dynamic(_) => {
if let Some(topology) = current_state.primitive_topology() {
topology
} else {
return Err(CheckDynamicStateValidityError::NotSet {
dynamic_state: DynamicState::PrimitiveTopology,
});
}
}
};
match topology {
PrimitiveTopology::PointList
| PrimitiveTopology::LineList
| PrimitiveTopology::TriangleList
| PrimitiveTopology::LineListWithAdjacency
| PrimitiveTopology::TriangleListWithAdjacency => {
if !device.enabled_features().primitive_topology_list_restart {
return Err(CheckDynamicStateValidityError::FeatureNotEnabled {
feature: "primitive_topology_list_restart",
reason: "the PrimitiveRestartEnable dynamic state was true in combination with a List PrimitiveTopology",
});
}
}
PrimitiveTopology::PatchList => {
if !device
.enabled_features()
.primitive_topology_patch_list_restart
{
return Err(CheckDynamicStateValidityError::FeatureNotEnabled {
feature: "primitive_topology_patch_list_restart",
reason: "the PrimitiveRestartEnable dynamic state was true in combination with PrimitiveTopology::PatchList",
});
}
}
_ => (),
}
}
}
DynamicState::PrimitiveTopology => {
let topology = if let Some(topology) = current_state.primitive_topology() {
topology
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
if pipeline.shader(ShaderStage::TessellationControl).is_some() {
if !matches!(topology, PrimitiveTopology::PatchList) {
return Err(CheckDynamicStateValidityError::InvalidPrimitiveTopology {
topology,
reason: "the graphics pipeline includes tessellation shaders, so the topology must be PatchList",
});
}
} else {
if matches!(topology, PrimitiveTopology::PatchList) {
return Err(CheckDynamicStateValidityError::InvalidPrimitiveTopology {
topology,
reason: "the graphics pipeline doesn't include tessellation shaders",
});
}
}
let topology_class = match pipeline.input_assembly_state().topology {
PartialStateMode::Dynamic(topology_class) => topology_class,
_ => unreachable!(),
};
if topology.class() != topology_class {
return Err(CheckDynamicStateValidityError::InvalidPrimitiveTopology {
topology,
reason: "the topology class does not match the class the pipeline was created for",
});
}
// TODO: check that the topology matches the geometry shader
}
DynamicState::RasterizerDiscardEnable => {
if current_state.rasterizer_discard_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::LogicOp => todo!(),
DynamicState::PatchControlPoints => todo!(),
DynamicState::PrimitiveRestartEnable => todo!(),
DynamicState::PrimitiveTopology => todo!(),
DynamicState::RasterizerDiscardEnable => todo!(),
DynamicState::RayTracingPipelineStackSize => unreachable!(
"RayTracingPipelineStackSize dynamic state should not occur on a graphics pipeline"
),
DynamicState::SampleLocations => todo!(),
DynamicState::Scissor => {
for num in 0..pipeline.num_viewports() {
for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
if current_state.scissor(num).is_none() {
return Err(CheckDynamicStateValidityError::ScissorNotSet { num });
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
}
DynamicState::ScissorWithCount => {
let scissor_count = if let Some(scissors) = current_state.scissor_with_count() {
scissors.len() as u32
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
// Check if the counts match, but only if the viewport count is fixed.
// If the viewport count is also dynamic, then the DynamicState::ViewportWithCount
// match arm will handle it.
if let Some(viewport_count) = pipeline.viewport_state().unwrap().count() {
if viewport_count != scissor_count {
return Err(
CheckDynamicStateValidityError::ViewportScissorCountMismatch {
viewport_count,
scissor_count,
},
);
}
}
}
DynamicState::ScissorWithCount => todo!(),
DynamicState::StencilCompareMask => {
let state = current_state.stencil_compare_mask();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilCompareMaskNotSet);
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::StencilOp => {
let state = current_state.stencil_op();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::StencilOp => todo!(),
DynamicState::StencilReference => {
let state = current_state.stencil_reference();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilReferenceNotSet);
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::StencilTestEnable => todo!(),
DynamicState::StencilTestEnable => {
if current_state.stencil_test_enable().is_none() {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
// TODO: Check if the stencil buffer is writable
}
DynamicState::StencilWriteMask => {
let state = current_state.stencil_write_mask();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilWriteMaskNotSet);
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
DynamicState::VertexInput => todo!(),
DynamicState::VertexInputBindingStride => todo!(),
DynamicState::Viewport => {
for num in 0..pipeline.num_viewports() {
for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
if current_state.viewport(num).is_none() {
return Err(CheckDynamicStateValidityError::ViewportNotSet { num });
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
}
}
DynamicState::ViewportCoarseSampleOrder => todo!(),
DynamicState::ViewportShadingRatePalette => todo!(),
DynamicState::ViewportWithCount => todo!(),
DynamicState::ViewportWithCount => {
let viewport_count = if let Some(viewports) = current_state.viewport_with_count() {
viewports.len() as u32
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
};
let scissor_count =
if let Some(scissor_count) = pipeline.viewport_state().unwrap().count() {
// The scissor count is fixed.
scissor_count
} else {
// The scissor count is also dynamic.
if let Some(scissors) = current_state.scissor_with_count() {
scissors.len() as u32
} else {
return Err(CheckDynamicStateValidityError::NotSet { dynamic_state });
}
};
if viewport_count != scissor_count {
return Err(
CheckDynamicStateValidityError::ViewportScissorCountMismatch {
viewport_count,
scissor_count,
},
);
}
// TODO: VUID-vkCmdDrawIndexed-primitiveFragmentShadingRateWithMultipleViewports-04552
// If the primitiveFragmentShadingRateWithMultipleViewports limit is not supported,
// the bound graphics pipeline was created with the
// VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT dynamic state enabled, and any of the
// shader stages of the bound graphics pipeline write to the PrimitiveShadingRateKHR
// built-in, then vkCmdSetViewportWithCountEXT must have been called in the current
// command buffer prior to this drawing command, and the viewportCount parameter of
// vkCmdSetViewportWithCountEXT must be 1
}
DynamicState::ViewportWScaling => todo!(),
}
}
@ -119,22 +349,34 @@ pub(in super::super) fn check_dynamic_state_validity(
/// Error that can happen when validating dynamic states.
#[derive(Debug, Copy, Clone)]
pub enum CheckDynamicStateValidityError {
/// The pipeline has dynamic blend constants, but no blend constants value was set.
BlendConstantsNotSet,
/// The pipeline has dynamic depth bounds, but no depth bounds value was set.
DepthBoundsNotSet,
/// The pipeline has a dynamic line width, but no line width value was set.
LineWidthNotSet,
/// The pipeline has a dynamic scissor, but the scissor for a slot used by the pipeline was not set.
ScissorNotSet { num: u32 },
/// The pipeline has dynamic stencil compare mask, but no compare mask was set for the front or back face.
StencilCompareMaskNotSet,
/// The pipeline has dynamic stencil reference, but no reference was set for the front or back face.
StencilReferenceNotSet,
/// The pipeline has dynamic stencil write mask, but no write mask was set for the front or back face.
StencilWriteMaskNotSet,
/// The pipeline has a dynamic viewport, but the viewport for a slot used by the pipeline was not set.
ViewportNotSet { num: u32 },
/// A device feature that was required for a particular dynamic state value was not enabled.
FeatureNotEnabled {
feature: &'static str,
reason: &'static str,
},
/// The provided dynamic primitive topology is not compatible with the pipeline.
InvalidPrimitiveTopology {
topology: PrimitiveTopology,
reason: &'static str,
},
/// The number of ColorWriteEnable values was less than the number of attachments in the
/// color blend state of the pipeline.
NotEnoughColorWriteEnable {
color_write_enable_count: u32,
attachment_count: u32,
},
/// The pipeline requires a particular state to be set dynamically, but the value was not or
/// only partially set.
NotSet { dynamic_state: DynamicState },
/// The viewport count and scissor count do not match.
ViewportScissorCountMismatch {
viewport_count: u32,
scissor_count: u32,
},
}
impl error::Error for CheckDynamicStateValidityError {}
@ -142,35 +384,32 @@ impl error::Error for CheckDynamicStateValidityError {}
impl fmt::Display for CheckDynamicStateValidityError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
CheckDynamicStateValidityError::BlendConstantsNotSet => {
"the pipeline has dynamic blend constants, but no blend constants value was set"
}
CheckDynamicStateValidityError::DepthBoundsNotSet => {
"the pipeline has dynamic depth bounds, but no depth bounds value was set"
}
CheckDynamicStateValidityError::LineWidthNotSet => {
"the pipeline has a dynamic line width, but no line width value was set"
}
CheckDynamicStateValidityError::ScissorNotSet { .. } => {
"The pipeline has a dynamic scissor, but the scissor for a slot used by the pipeline was not set"
}
CheckDynamicStateValidityError::StencilCompareMaskNotSet => {
"the pipeline has dynamic stencil compare mask, but no compare mask was set for the front or back face"
}
CheckDynamicStateValidityError::StencilReferenceNotSet => {
"the pipeline has dynamic stencil reference, but no reference was set for the front or back face"
}
CheckDynamicStateValidityError::StencilWriteMaskNotSet => {
"the pipeline has dynamic stencil write mask, but no write mask was set for the front or back face"
}
CheckDynamicStateValidityError::ViewportNotSet { .. } => {
"the pipeline has a dynamic viewport, but the viewport for a slot used by the pipeline was not set"
}
match *self {
Self::FeatureNotEnabled { feature, reason } => {
write!(fmt, "the feature {} must be enabled: {}", feature, reason)
}
)
Self::InvalidPrimitiveTopology { topology, reason } => {
write!(
fmt,
"invalid dynamic PrimitiveTypology::{:?}: {}",
topology, reason
)
}
Self::NotEnoughColorWriteEnable {
color_write_enable_count,
attachment_count,
} => {
write!(fmt, "the number of ColorWriteEnable values ({}) was less than the number of attachments ({}) in the color blend state of the pipeline", color_write_enable_count, attachment_count)
}
Self::NotSet { dynamic_state } => {
write!(fmt, "the pipeline requires the dynamic state {:?} to be set, but the value was not or only partially set", dynamic_state)
}
Self::ViewportScissorCountMismatch {
viewport_count,
scissor_count,
} => {
write!(fmt, "the viewport count and scissor count do not match; viewport count is {}, scissor count is {}", viewport_count, scissor_count)
}
}
}
}

View File

@ -1,291 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Defines how the color output of the fragment shader is written to the attachment.
//!
//! # Blending in details
//!
//! There are three kinds of color attachments for the purpose of blending:
//!
//! - Attachments with a floating-point, fixed point format.
//! - Attachments with a (non-normalized) integer format.
//! - Attachments with a normalized integer format.
//!
//! For floating-point and fixed-point formats, the blending operation is applied. For integer
//! formats, the logic operation is applied. For normalized integer formats, the logic operation
//! will take precedence if it is activated, otherwise the blending operation is applied.
//!
/// Describes how the color output of the fragment shader is written to the attachment. See the
/// documentation of the `blend` module for more info.
#[derive(Debug, Clone, PartialEq)]
pub struct Blend {
pub logic_op: Option<LogicOp>,
pub attachments: AttachmentsBlend,
/// The constant color to use for the `Constant*` blending operation.
///
/// If you pass `None`, then this state will be considered as dynamic and the blend constants
/// will need to be set when you build the command buffer.
pub blend_constants: Option<[f32; 4]>,
}
impl Blend {
/// Returns a `Blend` object that directly writes colors and alpha on the surface.
#[inline]
pub fn pass_through() -> Blend {
Blend {
logic_op: None,
attachments: AttachmentsBlend::Collective(AttachmentBlend::pass_through()),
blend_constants: Some([0.0, 0.0, 0.0, 0.0]),
}
}
/// Returns a `Blend` object that adds transparent objects over others.
#[inline]
pub fn alpha_blending() -> Blend {
Blend {
logic_op: None,
attachments: AttachmentsBlend::Collective(AttachmentBlend::alpha_blending()),
blend_constants: Some([0.0, 0.0, 0.0, 0.0]),
}
}
}
/// Describes how the blending system should behave.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttachmentsBlend {
/// All the framebuffer attachments will use the same blending.
Collective(AttachmentBlend),
/// Each attachment will behave differently. Note that this requires enabling the
/// `independent_blend` feature.
Individual(Vec<AttachmentBlend>),
}
/// Describes how the blending system should behave for an individual attachment.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AttachmentBlend {
// TODO: could be automatically determined from the other params
/// If false, blending is ignored and the output is directly written to the attachment.
pub enabled: bool,
pub color_op: BlendOp,
pub color_source: BlendFactor,
pub color_destination: BlendFactor,
pub alpha_op: BlendOp,
pub alpha_source: BlendFactor,
pub alpha_destination: BlendFactor,
pub mask_red: bool,
pub mask_green: bool,
pub mask_blue: bool,
pub mask_alpha: bool,
}
impl AttachmentBlend {
/// Builds an `AttachmentBlend` where blending is disabled.
#[inline]
pub fn pass_through() -> AttachmentBlend {
AttachmentBlend {
enabled: false,
color_op: BlendOp::Add,
color_source: BlendFactor::Zero,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Add,
alpha_source: BlendFactor::Zero,
alpha_destination: BlendFactor::One,
mask_red: true,
mask_green: true,
mask_blue: true,
mask_alpha: true,
}
}
/// Builds an `AttachmentBlend` where the output of the fragment shader is ignored and the
/// destination is untouched.
#[inline]
pub fn ignore_source() -> AttachmentBlend {
AttachmentBlend {
enabled: true,
color_op: BlendOp::Add,
color_source: BlendFactor::Zero,
color_destination: BlendFactor::DstColor,
alpha_op: BlendOp::Add,
alpha_source: BlendFactor::Zero,
alpha_destination: BlendFactor::DstColor,
mask_red: true,
mask_green: true,
mask_blue: true,
mask_alpha: true,
}
}
/// Builds an `AttachmentBlend` where the output will be merged with the existing value
/// based on the alpha of the source.
#[inline]
pub fn alpha_blending() -> AttachmentBlend {
AttachmentBlend {
enabled: true,
color_op: BlendOp::Add,
color_source: BlendFactor::SrcAlpha,
color_destination: BlendFactor::OneMinusSrcAlpha,
alpha_op: BlendOp::Add,
alpha_source: BlendFactor::SrcAlpha,
alpha_destination: BlendFactor::OneMinusSrcAlpha,
mask_red: true,
mask_green: true,
mask_blue: true,
mask_alpha: true,
}
}
}
impl From<AttachmentBlend> for ash::vk::PipelineColorBlendAttachmentState {
#[inline]
fn from(val: AttachmentBlend) -> Self {
ash::vk::PipelineColorBlendAttachmentState {
blend_enable: if val.enabled {
ash::vk::TRUE
} else {
ash::vk::FALSE
},
src_color_blend_factor: val.color_source.into(),
dst_color_blend_factor: val.color_destination.into(),
color_blend_op: val.color_op.into(),
src_alpha_blend_factor: val.alpha_source.into(),
dst_alpha_blend_factor: val.alpha_destination.into(),
alpha_blend_op: val.alpha_op.into(),
color_write_mask: {
let mut mask = ash::vk::ColorComponentFlags::empty();
if val.mask_red {
mask |= ash::vk::ColorComponentFlags::R;
}
if val.mask_green {
mask |= ash::vk::ColorComponentFlags::G;
}
if val.mask_blue {
mask |= ash::vk::ColorComponentFlags::B;
}
if val.mask_alpha {
mask |= ash::vk::ColorComponentFlags::A;
}
mask
},
}
}
}
/// Which logical operation to apply to the output values.
///
/// The operation is applied individually for each channel (red, green, blue and alpha).
///
/// Only relevant for integer or unsigned attachments.
///
/// Also note that some implementations don't support logic operations.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum LogicOp {
/// Returns `0`.
Clear = ash::vk::LogicOp::CLEAR.as_raw(),
/// Returns `source & destination`.
And = ash::vk::LogicOp::AND.as_raw(),
/// Returns `source & !destination`.
AndReverse = ash::vk::LogicOp::AND_REVERSE.as_raw(),
/// Returns `source`.
Copy = ash::vk::LogicOp::COPY.as_raw(),
/// Returns `!source & destination`.
AndInverted = ash::vk::LogicOp::AND_INVERTED.as_raw(),
/// Returns `destination`.
Noop = ash::vk::LogicOp::NO_OP.as_raw(),
/// Returns `source ^ destination`.
Xor = ash::vk::LogicOp::XOR.as_raw(),
/// Returns `source | destination`.
Or = ash::vk::LogicOp::OR.as_raw(),
/// Returns `!(source | destination)`.
Nor = ash::vk::LogicOp::NOR.as_raw(),
/// Returns `!(source ^ destination)`.
Equivalent = ash::vk::LogicOp::EQUIVALENT.as_raw(),
/// Returns `!destination`.
Invert = ash::vk::LogicOp::INVERT.as_raw(),
/// Returns `source | !destination.
OrReverse = ash::vk::LogicOp::OR_REVERSE.as_raw(),
/// Returns `!source`.
CopyInverted = ash::vk::LogicOp::COPY_INVERTED.as_raw(),
/// Returns `!source | destination`.
OrInverted = ash::vk::LogicOp::OR_INVERTED.as_raw(),
/// Returns `!(source & destination)`.
Nand = ash::vk::LogicOp::NAND.as_raw(),
/// Returns `!0` (all bits set to 1).
Set = ash::vk::LogicOp::SET.as_raw(),
}
impl From<LogicOp> for ash::vk::LogicOp {
#[inline]
fn from(val: LogicOp) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for LogicOp {
#[inline]
fn default() -> LogicOp {
LogicOp::Noop
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum BlendOp {
Add = ash::vk::BlendOp::ADD.as_raw(),
Subtract = ash::vk::BlendOp::SUBTRACT.as_raw(),
ReverseSubtract = ash::vk::BlendOp::REVERSE_SUBTRACT.as_raw(),
Min = ash::vk::BlendOp::MIN.as_raw(),
Max = ash::vk::BlendOp::MAX.as_raw(),
}
impl From<BlendOp> for ash::vk::BlendOp {
#[inline]
fn from(val: BlendOp) -> Self {
Self::from_raw(val as i32)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum BlendFactor {
Zero = ash::vk::BlendFactor::ZERO.as_raw(),
One = ash::vk::BlendFactor::ONE.as_raw(),
SrcColor = ash::vk::BlendFactor::SRC_COLOR.as_raw(),
OneMinusSrcColor = ash::vk::BlendFactor::ONE_MINUS_SRC_COLOR.as_raw(),
DstColor = ash::vk::BlendFactor::DST_COLOR.as_raw(),
OneMinusDstColor = ash::vk::BlendFactor::ONE_MINUS_DST_COLOR.as_raw(),
SrcAlpha = ash::vk::BlendFactor::SRC_ALPHA.as_raw(),
OneMinusSrcAlpha = ash::vk::BlendFactor::ONE_MINUS_SRC_ALPHA.as_raw(),
DstAlpha = ash::vk::BlendFactor::DST_ALPHA.as_raw(),
OneMinusDstAlpha = ash::vk::BlendFactor::ONE_MINUS_DST_ALPHA.as_raw(),
ConstantColor = ash::vk::BlendFactor::CONSTANT_COLOR.as_raw(),
OneMinusConstantColor = ash::vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR.as_raw(),
ConstantAlpha = ash::vk::BlendFactor::CONSTANT_ALPHA.as_raw(),
OneMinusConstantAlpha = ash::vk::BlendFactor::ONE_MINUS_CONSTANT_ALPHA.as_raw(),
SrcAlphaSaturate = ash::vk::BlendFactor::SRC_ALPHA_SATURATE.as_raw(),
Src1Color = ash::vk::BlendFactor::SRC1_COLOR.as_raw(),
OneMinusSrc1Color = ash::vk::BlendFactor::ONE_MINUS_SRC1_COLOR.as_raw(),
Src1Alpha = ash::vk::BlendFactor::SRC1_ALPHA.as_raw(),
OneMinusSrc1Alpha = ash::vk::BlendFactor::ONE_MINUS_SRC1_ALPHA.as_raw(),
}
impl From<BlendFactor> for ash::vk::BlendFactor {
#[inline]
fn from(val: BlendFactor) -> Self {
Self::from_raw(val as i32)
}
}

View File

@ -32,6 +32,7 @@ use std::sync::Arc;
/// Opaque cache that contains pipeline objects.
///
/// See [the documentation of the module](crate::pipeline::cache) for more info.
#[derive(Debug)]
pub struct PipelineCache {
device: Arc<Device>,
cache: ash::vk::PipelineCache,

View File

@ -0,0 +1,668 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Defines how the color output of the fragment shader is written to the attachment.
//!
//! # Blending in details
//!
//! There are three kinds of color attachments for the purpose of blending:
//!
//! - Attachments with a floating-point or fixed point format.
//! - Attachments with a (non-normalized) integer format.
//! - Attachments with a normalized integer format.
//!
//! For floating-point and fixed-point formats, the blending operation is applied. For integer
//! formats, the logic operation is applied. For normalized integer formats, the logic operation
//! will take precedence if it is activated, otherwise the blending operation is applied.
use super::{DynamicState, GraphicsPipelineCreationError};
use crate::{device::Device, pipeline::StateMode, render_pass::Subpass};
use fnv::FnvHashMap;
use smallvec::SmallVec;
/// Describes how the color output of the fragment shader is written to the attachment. See the
/// documentation of the `blend` module for more info.
#[derive(Clone, Debug)]
pub struct ColorBlendState {
/// Sets the logical operation to perform between the incoming fragment color and the existing
/// fragment in the framebuffer attachment.
///
/// If set to `Some`, the [`logic_op`](crate::device::Features::logic_op) feature must be
/// enabled on the device. If set to `Some(Dynamic)`, then the
/// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2_logic_op)
/// feature must also be enabled on the device.
pub logic_op: Option<StateMode<LogicOp>>,
/// Sets the blend and output state for each color attachment. The number of elements must match
/// the number of color attachments in the framebuffer.
///
/// If there are multiple elements, and the `blend` and `color_write_mask` members of each
/// element differ, then the [`independent_blend`](crate::device::Features::independent_blend)
/// feature must be enabled on the device.
pub attachments: Vec<ColorBlendAttachmentState>,
/// The constant color to use for some of the `BlendFactor` variants.
pub blend_constants: StateMode<[f32; 4]>,
}
impl ColorBlendState {
/// Creates a `ColorBlendState` with logical operations disabled, blend constants set to zero,
/// and `num` attachment entries that have blending disabled, and color write and all color
/// components enabled.
#[inline]
pub fn new(num: u32) -> Self {
Self {
logic_op: None,
attachments: (0..num)
.map(|_| ColorBlendAttachmentState {
blend: None,
color_write_mask: ColorComponents::all(),
color_write_enable: StateMode::Fixed(true),
})
.collect(),
blend_constants: StateMode::Fixed([0.0, 0.0, 0.0, 0.0]),
}
}
/// Enables logical operations with the given logical operation.
#[inline]
pub fn logic_op(mut self, logic_op: LogicOp) -> Self {
self.logic_op = Some(StateMode::Fixed(logic_op));
self
}
/// Enables logical operations with a dynamic logical operation.
#[inline]
pub fn logic_op_dynamic(mut self) -> Self {
self.logic_op = Some(StateMode::Dynamic);
self
}
/// Enables blending for all attachments, with the given parameters.
#[inline]
pub fn blend(mut self, blend: AttachmentBlend) -> Self {
self.attachments
.iter_mut()
.for_each(|attachment_state| attachment_state.blend = Some(blend));
self
}
/// Enables blending for all attachments, with alpha blending.
#[inline]
pub fn blend_alpha(mut self) -> Self {
self.attachments
.iter_mut()
.for_each(|attachment_state| attachment_state.blend = Some(AttachmentBlend::alpha()));
self
}
/// Enables blending for all attachments, with additive blending.
#[inline]
pub fn blend_additive(mut self) -> Self {
self.attachments.iter_mut().for_each(|attachment_state| {
attachment_state.blend = Some(AttachmentBlend::additive())
});
self
}
/// Sets the color write mask for all attachments.
#[inline]
pub fn color_write_mask(mut self, color_write_mask: ColorComponents) -> Self {
self.attachments
.iter_mut()
.for_each(|attachment_state| attachment_state.color_write_mask = color_write_mask);
self
}
/// Sets the blend constants.
#[inline]
pub fn blend_constants(mut self, constants: [f32; 4]) -> Self {
self.blend_constants = StateMode::Fixed(constants);
self
}
/// Sets the blend constants as dynamic.
#[inline]
pub fn blend_constants_dynamic(mut self) -> Self {
self.blend_constants = StateMode::Dynamic;
self
}
pub(crate) fn to_vulkan_attachments(
&mut self, // TODO: make non-mut
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
subpass: &Subpass,
) -> Result<
(
SmallVec<[ash::vk::PipelineColorBlendAttachmentState; 4]>,
SmallVec<[ash::vk::Bool32; 4]>,
),
GraphicsPipelineCreationError,
> {
let num_atch = subpass.num_color_attachments();
// If there is one element, duplicate it for all attachments.
// TODO: this is undocumented and only exists for compatibility with some of the
// deprecated builder methods. Remove it when those methods are gone.
if self.attachments.len() == 1 {
let element = self.attachments.pop().unwrap();
self.attachments
.extend(std::iter::repeat(element).take(num_atch as usize));
} else {
if self.attachments.len() != num_atch as usize {
return Err(GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount);
}
if self.attachments.len() > 1 && !device.enabled_features().independent_blend {
// Ensure that all `blend` and `color_write_mask` are identical.
let mut iter = self
.attachments
.iter()
.map(|state| (&state.blend, &state.color_write_mask));
let first = iter.next().unwrap();
if !iter.all(|state| state == first) {
return Err(
GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "independent_blend",
reason: "The blend and color_write_mask members of all elements of ColorBlendState::attachments were not identical",
},
);
}
}
}
let mut color_blend_attachments = SmallVec::with_capacity(self.attachments.len());
let mut color_write_enables = SmallVec::with_capacity(self.attachments.len());
for state in self.attachments.iter() {
let blend = if let Some(blend) = &state.blend {
if !device.enabled_features().dual_src_blend
&& std::array::IntoIter::new([
blend.color_source,
blend.color_destination,
blend.alpha_source,
blend.alpha_destination,
])
.any(|blend_factor| {
matches!(
blend_factor,
BlendFactor::Src1Color
| BlendFactor::OneMinusSrc1Color
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha
)
})
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "dual_src_blend",
reason: "One of the BlendFactor members of AttachmentBlend was set to Src1",
});
}
blend.clone().into()
} else {
Default::default()
};
let color_blend_attachment_state = ash::vk::PipelineColorBlendAttachmentState {
color_write_mask: state.color_write_mask.into(),
..blend
};
let color_write_enable = match state.color_write_enable {
StateMode::Fixed(enable) => {
if !enable && !device.enabled_features().color_write_enable {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "color_write_enable",
reason:
"ColorBlendAttachmentState::color_blend_enable was set to Fixed(false)",
});
}
dynamic_state_modes.insert(DynamicState::ColorWriteEnable, false);
enable as ash::vk::Bool32
}
StateMode::Dynamic => {
if !device.enabled_features().color_write_enable {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "color_write_enable",
reason:
"ColorBlendAttachmentState::color_blend_enable was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::ColorWriteEnable, true);
ash::vk::TRUE
}
};
color_blend_attachments.push(color_blend_attachment_state);
color_write_enables.push(color_write_enable);
}
debug_assert_eq!(color_blend_attachments.len(), color_write_enables.len());
Ok((color_blend_attachments, color_write_enables))
}
pub(crate) fn to_vulkan_color_write(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
color_write_enables: &[ash::vk::Bool32],
) -> Result<Option<ash::vk::PipelineColorWriteCreateInfoEXT>, GraphicsPipelineCreationError>
{
Ok(if device.enabled_extensions().ext_color_write_enable {
Some(ash::vk::PipelineColorWriteCreateInfoEXT {
attachment_count: color_write_enables.len() as u32,
p_color_write_enables: color_write_enables.as_ptr(),
..Default::default()
})
} else {
None
})
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
color_blend_attachments: &[ash::vk::PipelineColorBlendAttachmentState],
color_write: Option<&mut ash::vk::PipelineColorWriteCreateInfoEXT>,
) -> Result<ash::vk::PipelineColorBlendStateCreateInfo, GraphicsPipelineCreationError> {
let (logic_op_enable, logic_op) = if let Some(logic_op) = self.logic_op {
if !device.enabled_features().logic_op {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "logic_op",
reason: "ColorBlendState::logic_op was set to Some",
});
}
let logic_op = match logic_op {
StateMode::Fixed(logic_op) => {
dynamic_state_modes.insert(DynamicState::LogicOp, false);
logic_op.into()
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state2_logic_op {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state2_logic_op",
reason: "ColorBlendState::logic_op was set to Some(Dynamic)",
});
}
dynamic_state_modes.insert(DynamicState::LogicOp, true);
Default::default()
}
};
(ash::vk::TRUE, logic_op)
} else {
(ash::vk::FALSE, Default::default())
};
let blend_constants = match self.blend_constants {
StateMode::Fixed(blend_constants) => {
dynamic_state_modes.insert(DynamicState::BlendConstants, false);
blend_constants
}
StateMode::Dynamic => {
dynamic_state_modes.insert(DynamicState::BlendConstants, true);
Default::default()
}
};
let mut color_blend_state = ash::vk::PipelineColorBlendStateCreateInfo {
flags: ash::vk::PipelineColorBlendStateCreateFlags::empty(),
logic_op_enable,
logic_op,
attachment_count: color_blend_attachments.len() as u32,
p_attachments: color_blend_attachments.as_ptr(),
blend_constants,
..Default::default()
};
if let Some(color_write) = color_write {
color_write.p_next = color_blend_state.p_next;
color_blend_state.p_next = color_write as *const _ as *const _;
}
Ok(color_blend_state)
}
}
impl Default for ColorBlendState {
/// Returns [`ColorBlendState::new(1)`].
#[inline]
fn default() -> Self {
Self::new(1)
}
}
/// Which logical operation to apply to the output values.
///
/// The operation is applied individually for each channel (red, green, blue and alpha).
///
/// Only relevant for integer or unsigned attachments.
///
/// Also note that some implementations don't support logic operations.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum LogicOp {
/// Returns `0`.
Clear = ash::vk::LogicOp::CLEAR.as_raw(),
/// Returns `source & destination`.
And = ash::vk::LogicOp::AND.as_raw(),
/// Returns `source & !destination`.
AndReverse = ash::vk::LogicOp::AND_REVERSE.as_raw(),
/// Returns `source`.
Copy = ash::vk::LogicOp::COPY.as_raw(),
/// Returns `!source & destination`.
AndInverted = ash::vk::LogicOp::AND_INVERTED.as_raw(),
/// Returns `destination`.
Noop = ash::vk::LogicOp::NO_OP.as_raw(),
/// Returns `source ^ destination`.
Xor = ash::vk::LogicOp::XOR.as_raw(),
/// Returns `source | destination`.
Or = ash::vk::LogicOp::OR.as_raw(),
/// Returns `!(source | destination)`.
Nor = ash::vk::LogicOp::NOR.as_raw(),
/// Returns `!(source ^ destination)`.
Equivalent = ash::vk::LogicOp::EQUIVALENT.as_raw(),
/// Returns `!destination`.
Invert = ash::vk::LogicOp::INVERT.as_raw(),
/// Returns `source | !destination.
OrReverse = ash::vk::LogicOp::OR_REVERSE.as_raw(),
/// Returns `!source`.
CopyInverted = ash::vk::LogicOp::COPY_INVERTED.as_raw(),
/// Returns `!source | destination`.
OrInverted = ash::vk::LogicOp::OR_INVERTED.as_raw(),
/// Returns `!(source & destination)`.
Nand = ash::vk::LogicOp::NAND.as_raw(),
/// Returns `!0` (all bits set to 1).
Set = ash::vk::LogicOp::SET.as_raw(),
}
impl From<LogicOp> for ash::vk::LogicOp {
#[inline]
fn from(val: LogicOp) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for LogicOp {
#[inline]
fn default() -> LogicOp {
LogicOp::Noop
}
}
/// Describes how a framebuffer color attachment is handled in the pipeline during the color
/// blend stage.
#[derive(Clone, Debug)]
pub struct ColorBlendAttachmentState {
/// The blend parameters for the attachment.
///
/// If set to `None`, blending is disabled, and all incoming pixels will be used directly.
pub blend: Option<AttachmentBlend>,
/// Sets which components of the final pixel value are written to the attachment.
pub color_write_mask: ColorComponents,
/// Sets whether anything at all is written to the attachment. If enabled, the pixel data
/// that is written is determined by the `color_write_mask`. If disabled, the mask is ignored
/// and nothing is written.
///
/// If set to anything other than `Fixed(true)`, the
/// [`color_write_enable`](crate::device::Features::color_write_enable) feature must be enabled
/// on the device.
pub color_write_enable: StateMode<bool>,
}
/// Describes how the blending system should behave for an attachment.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AttachmentBlend {
/// The operation to apply between the color components of the source and destination pixels,
/// to produce the final pixel value.
pub color_op: BlendOp,
/// The operation to apply to the source color component before applying `color_op`.
pub color_source: BlendFactor,
/// The operation to apply to the destination color component before applying `color_op`.
pub color_destination: BlendFactor,
/// The operation to apply between the alpha component of the source and destination pixels,
/// to produce the final pixel value.
pub alpha_op: BlendOp,
/// The operation to apply to the source alpha component before applying `alpha_op`.
pub alpha_source: BlendFactor,
/// The operation to apply to the destination alpha component before applying `alpha_op`.
pub alpha_destination: BlendFactor,
}
impl AttachmentBlend {
/// Builds an `AttachmentBlend` where the output of the fragment shader is ignored and the
/// destination is untouched.
#[inline]
pub fn ignore_source() -> Self {
Self {
color_op: BlendOp::Add,
color_source: BlendFactor::Zero,
color_destination: BlendFactor::DstColor,
alpha_op: BlendOp::Add,
alpha_source: BlendFactor::Zero,
alpha_destination: BlendFactor::DstColor,
}
}
/// Builds an `AttachmentBlend` where the output will be merged with the existing value
/// based on the alpha of the source.
#[inline]
pub fn alpha() -> Self {
Self {
color_op: BlendOp::Add,
color_source: BlendFactor::SrcAlpha,
color_destination: BlendFactor::OneMinusSrcAlpha,
alpha_op: BlendOp::Add,
alpha_source: BlendFactor::SrcAlpha,
alpha_destination: BlendFactor::OneMinusSrcAlpha,
}
}
/// Builds an `AttachmentBlend` where the colors are added, and alpha is set to the maximum of
/// the two.
#[inline]
pub fn additive() -> Self {
Self {
color_op: BlendOp::Add,
color_source: BlendFactor::One,
color_destination: BlendFactor::One,
alpha_op: BlendOp::Max,
alpha_source: BlendFactor::One,
alpha_destination: BlendFactor::One,
}
}
}
impl From<AttachmentBlend> for ash::vk::PipelineColorBlendAttachmentState {
#[inline]
fn from(val: AttachmentBlend) -> Self {
ash::vk::PipelineColorBlendAttachmentState {
blend_enable: ash::vk::TRUE,
src_color_blend_factor: val.color_source.into(),
dst_color_blend_factor: val.color_destination.into(),
color_blend_op: val.color_op.into(),
src_alpha_blend_factor: val.alpha_source.into(),
dst_alpha_blend_factor: val.alpha_destination.into(),
alpha_blend_op: val.alpha_op.into(),
color_write_mask: ash::vk::ColorComponentFlags::empty(), // Overwritten by GraphicsPipelineBuilder
}
}
}
/// The operation that takes `source` (output from the fragment shader), `destination` (value
/// currently in the framebuffer attachment) and `blend_constant` input values,
/// and produces new inputs to be fed to `BlendOp`.
///
/// Some operations take `source1` as an input, representing the second source value. The
/// [`dual_src_blend`](crate::device::Features::dual_src_blend) feature must be enabled on the
/// device when these are used.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum BlendFactor {
/// Always `0`.
Zero = ash::vk::BlendFactor::ZERO.as_raw(),
/// Always `1`.
One = ash::vk::BlendFactor::ONE.as_raw(),
/// `source` component-wise.
SrcColor = ash::vk::BlendFactor::SRC_COLOR.as_raw(),
/// `1 - source` component-wise.
OneMinusSrcColor = ash::vk::BlendFactor::ONE_MINUS_SRC_COLOR.as_raw(),
/// `destination` component-wise.
DstColor = ash::vk::BlendFactor::DST_COLOR.as_raw(),
/// `1 - destination` component-wise.
OneMinusDstColor = ash::vk::BlendFactor::ONE_MINUS_DST_COLOR.as_raw(),
/// `source.a` for all components.
SrcAlpha = ash::vk::BlendFactor::SRC_ALPHA.as_raw(),
/// `1 - source.a` for all components.
OneMinusSrcAlpha = ash::vk::BlendFactor::ONE_MINUS_SRC_ALPHA.as_raw(),
/// `destination.a` for all components.
DstAlpha = ash::vk::BlendFactor::DST_ALPHA.as_raw(),
/// `1 - destination.a` for all components.
OneMinusDstAlpha = ash::vk::BlendFactor::ONE_MINUS_DST_ALPHA.as_raw(),
/// `blend_constants` component-wise.
ConstantColor = ash::vk::BlendFactor::CONSTANT_COLOR.as_raw(),
/// `1 - blend_constants` component-wise.
OneMinusConstantColor = ash::vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR.as_raw(),
/// `blend_constants.a` for all components.
ConstantAlpha = ash::vk::BlendFactor::CONSTANT_ALPHA.as_raw(),
/// `1 - blend_constants.a` for all components.
OneMinusConstantAlpha = ash::vk::BlendFactor::ONE_MINUS_CONSTANT_ALPHA.as_raw(),
/// For the alpha component, always `1`. For the color components,
/// `min(source.a, 1 - destination.a)` for all components.
SrcAlphaSaturate = ash::vk::BlendFactor::SRC_ALPHA_SATURATE.as_raw(),
/// `source1` component-wise.
Src1Color = ash::vk::BlendFactor::SRC1_COLOR.as_raw(),
/// `1 - source1` component-wise.
OneMinusSrc1Color = ash::vk::BlendFactor::ONE_MINUS_SRC1_COLOR.as_raw(),
/// `source1.a` for all components.
Src1Alpha = ash::vk::BlendFactor::SRC1_ALPHA.as_raw(),
/// `1 - source1.a` for all components.
OneMinusSrc1Alpha = ash::vk::BlendFactor::ONE_MINUS_SRC1_ALPHA.as_raw(),
}
impl From<BlendFactor> for ash::vk::BlendFactor {
#[inline]
fn from(val: BlendFactor) -> Self {
Self::from_raw(val as i32)
}
}
/// The arithmetic operation that is applied between the `source` and `destination` component
/// values, after the appropriate `BlendFactor` is applied to both.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum BlendOp {
/// `source + destination`.
Add = ash::vk::BlendOp::ADD.as_raw(),
/// `source - destination`.
Subtract = ash::vk::BlendOp::SUBTRACT.as_raw(),
/// `destination - source`.
ReverseSubtract = ash::vk::BlendOp::REVERSE_SUBTRACT.as_raw(),
/// `min(source, destination)`.
Min = ash::vk::BlendOp::MIN.as_raw(),
/// `max(source, destination)`.
Max = ash::vk::BlendOp::MAX.as_raw(),
}
impl From<BlendOp> for ash::vk::BlendOp {
#[inline]
fn from(val: BlendOp) -> Self {
Self::from_raw(val as i32)
}
}
/// A mask specifying color components that can be written to a framebuffer attachment.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ColorComponents {
#[allow(missing_docs)]
pub r: bool,
#[allow(missing_docs)]
pub g: bool,
#[allow(missing_docs)]
pub b: bool,
#[allow(missing_docs)]
pub a: bool,
}
impl ColorComponents {
/// Returns a mask that specifies no components.
#[inline]
pub fn none() -> Self {
Self {
r: false,
g: false,
b: false,
a: false,
}
}
/// Returns a mask that specifies all components.
#[inline]
pub fn all() -> Self {
Self {
r: true,
g: true,
b: true,
a: true,
}
}
}
impl From<ColorComponents> for ash::vk::ColorComponentFlags {
fn from(val: ColorComponents) -> Self {
let mut result = Self::empty();
if val.r {
result |= ash::vk::ColorComponentFlags::R;
}
if val.g {
result |= ash::vk::ColorComponentFlags::G;
}
if val.b {
result |= ash::vk::ColorComponentFlags::B;
}
if val.a {
result |= ash::vk::ColorComponentFlags::A;
}
result
}
}

View File

@ -9,154 +9,479 @@
//! Depth and stencil operations description.
//!
//! After the fragment shader has finished running, each fragment goes through the depth
//! and stencil tests.
//! After the fragment shader has finished running, each fragment goes through the depth, depth
//! bounds and stencil tests.
//!
//! The depth test passes of fails depending on how the depth value of each fragment compares
//! to the existing depth value in the depth buffer at that fragment's location. Depth values
//! are always between 0.0 and 1.0.
//!
//! The depth bounds test allows you to ask the GPU to exclude fragments that are outside of a
//! certain range. This is done in addition to the regular depth test.
//!
//! The stencil test passes or fails depending on how a reference value compares to the existing
//! value in the stencil buffer at each fragment's location. Depending on the outcome of the
//! depth and stencil tests, the value of the stencil buffer at that location can be updated.
use std::ops::Range;
use crate::device::Device;
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError, StateMode};
use crate::render_pass::Subpass;
use fnv::FnvHashMap;
use std::ops::RangeInclusive;
use std::u32;
/// Configuration of the depth and stencil tests.
#[derive(Debug, Clone)]
pub struct DepthStencil {
/// Comparison to use between the depth value of each fragment and the depth value currently
/// in the depth buffer.
pub depth_compare: CompareOp,
/// The state in a graphics pipeline describing how the depth, depth bounds and stencil tests
/// should behave.
#[derive(Clone, Debug)]
pub struct DepthStencilState {
/// The state of the depth test.
///
/// If set to `None`, the depth test is disabled, all fragments will pass and no depth writes
/// are performed.
pub depth: Option<DepthState>,
/// If `true`, then the value in the depth buffer will be updated when the depth test succeeds.
pub depth_write: bool,
/// The state of the depth bounds test.
///
/// If set to `None`, the depth bounds test is disabled, all fragments will pass.
pub depth_bounds: Option<DepthBoundsState>,
/// Allows you to ask the GPU to exclude fragments that are outside of a certain range. This is
/// done in addition to the regular depth test.
pub depth_bounds_test: DepthBounds,
/// Stencil operations to use for points, lines and triangles whose front is facing the user.
pub stencil_front: Stencil,
/// Stencil operations to use for triangles whose back is facing the user.
pub stencil_back: Stencil,
/// The state of the stencil test.
///
/// If set to `None`, the stencil test is disabled, all fragments will pass and no stencil
/// writes are performed.
pub stencil: Option<StencilState>,
}
impl DepthStencil {
/// Creates a `DepthStencil` where both the depth and stencil tests are disabled and have
/// no effect.
impl DepthStencilState {
/// Creates a `DepthStencilState` where all tests are disabled and have no effect.
#[inline]
pub fn disabled() -> DepthStencil {
DepthStencil {
depth_write: false,
depth_compare: CompareOp::Always,
depth_bounds_test: DepthBounds::Disabled,
stencil_front: Default::default(),
stencil_back: Default::default(),
pub fn disabled() -> Self {
Self {
depth: Default::default(),
depth_bounds: Default::default(),
stencil: Default::default(),
}
}
/// Creates a `DepthStencil` with a `Less` depth test, `depth_write` set to true, and stencil
/// testing disabled.
/// Creates a `DepthStencilState` with a `Less` depth test, `depth_write` set to true, and other
/// tests disabled.
#[inline]
pub fn simple_depth_test() -> DepthStencil {
DepthStencil {
depth_write: true,
depth_compare: CompareOp::Less,
depth_bounds_test: DepthBounds::Disabled,
stencil_front: Default::default(),
stencil_back: Default::default(),
pub fn simple_depth_test() -> Self {
Self {
depth: Some(DepthState {
enable_dynamic: false,
compare_op: StateMode::Fixed(CompareOp::Less),
write_enable: StateMode::Fixed(true),
}),
depth_bounds: Default::default(),
stencil: Default::default(),
}
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
subpass: &Subpass,
) -> Result<ash::vk::PipelineDepthStencilStateCreateInfo, GraphicsPipelineCreationError> {
let (depth_test_enable, depth_write_enable, depth_compare_op) =
if let Some(depth_state) = &self.depth {
if !subpass.has_depth() {
return Err(GraphicsPipelineCreationError::NoDepthAttachment);
}
if depth_state.enable_dynamic {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "DepthState::enable_dynamic was true",
});
}
dynamic_state_modes.insert(DynamicState::DepthTestEnable, true);
} else {
dynamic_state_modes.insert(DynamicState::DepthTestEnable, false);
}
let write_enable = match depth_state.write_enable {
StateMode::Fixed(write_enable) => {
if write_enable && !subpass.has_writable_depth() {
return Err(GraphicsPipelineCreationError::NoDepthAttachment);
}
dynamic_state_modes.insert(DynamicState::DepthWriteEnable, false);
write_enable as ash::vk::Bool32
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "DepthState::write_enable was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::DepthWriteEnable, true);
ash::vk::TRUE
}
};
let compare_op = match depth_state.compare_op {
StateMode::Fixed(compare_op) => {
dynamic_state_modes.insert(DynamicState::DepthCompareOp, false);
compare_op.into()
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "DepthState::compare_op was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::DepthCompareOp, true);
ash::vk::CompareOp::ALWAYS
}
};
(ash::vk::TRUE, write_enable, compare_op)
} else {
(ash::vk::FALSE, ash::vk::FALSE, ash::vk::CompareOp::ALWAYS)
};
let (depth_bounds_test_enable, min_depth_bounds, max_depth_bounds) = if let Some(
depth_bounds_state,
) =
&self.depth_bounds
{
if !device.enabled_features().depth_bounds {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "depth_bounds",
reason: "DepthStencilState::depth_bounds was Some",
});
}
if depth_bounds_state.enable_dynamic {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "DepthBoundsState::enable_dynamic was true",
});
}
dynamic_state_modes.insert(DynamicState::DepthBoundsTestEnable, true);
} else {
dynamic_state_modes.insert(DynamicState::DepthBoundsTestEnable, false);
}
let (min_bounds, max_bounds) = match depth_bounds_state.bounds.clone() {
StateMode::Fixed(bounds) => {
if !device.enabled_extensions().ext_depth_range_unrestricted
&& !(0.0..1.0).contains(bounds.start())
&& !(0.0..1.0).contains(bounds.end())
{
return Err(GraphicsPipelineCreationError::ExtensionNotEnabled {
extension: "ext_depth_range_unrestricted",
reason: "DepthBoundsState::bounds were not both between 0.0 and 1.0 inclusive",
});
}
dynamic_state_modes.insert(DynamicState::DepthBounds, false);
bounds.into_inner()
}
StateMode::Dynamic => {
dynamic_state_modes.insert(DynamicState::DepthBounds, true);
(0.0, 1.0)
}
};
(ash::vk::TRUE, min_bounds, max_bounds)
} else {
(ash::vk::FALSE, 0.0, 1.0)
};
let (stencil_test_enable, front, back) = if let Some(stencil_state) = &self.stencil {
if !subpass.has_stencil() {
return Err(GraphicsPipelineCreationError::NoStencilAttachment);
}
// TODO: if stencil buffer can potentially be written, check if it is writable
if stencil_state.enable_dynamic {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "StencilState::enable_dynamic was true",
});
}
dynamic_state_modes.insert(DynamicState::StencilTestEnable, true);
} else {
dynamic_state_modes.insert(DynamicState::StencilTestEnable, false);
}
match (stencil_state.front.ops, stencil_state.back.ops) {
(StateMode::Fixed(_), StateMode::Fixed(_)) => {
dynamic_state_modes.insert(DynamicState::StencilOp, false);
}
(StateMode::Dynamic, StateMode::Dynamic) => {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "StencilState::ops was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::StencilOp, true);
}
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
match (
stencil_state.front.compare_mask,
stencil_state.back.compare_mask,
) {
(StateMode::Fixed(_), StateMode::Fixed(_)) => {
dynamic_state_modes.insert(DynamicState::StencilCompareMask, false);
}
(StateMode::Dynamic, StateMode::Dynamic) => {
dynamic_state_modes.insert(DynamicState::StencilCompareMask, true);
}
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
match (
stencil_state.front.write_mask,
stencil_state.back.write_mask,
) {
(StateMode::Fixed(_), StateMode::Fixed(_)) => {
dynamic_state_modes.insert(DynamicState::StencilWriteMask, false);
}
(StateMode::Dynamic, StateMode::Dynamic) => {
dynamic_state_modes.insert(DynamicState::StencilWriteMask, true);
}
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
match (stencil_state.front.reference, stencil_state.back.reference) {
(StateMode::Fixed(_), StateMode::Fixed(_)) => {
dynamic_state_modes.insert(DynamicState::StencilReference, false);
}
(StateMode::Dynamic, StateMode::Dynamic) => {
dynamic_state_modes.insert(DynamicState::StencilReference, true);
}
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
let [front, back] = [&stencil_state.front, &stencil_state.back].map(|ops_state| {
let ops = match ops_state.ops {
StateMode::Fixed(x) => x,
StateMode::Dynamic => Default::default(),
};
let compare_mask = match ops_state.compare_mask {
StateMode::Fixed(x) => x,
StateMode::Dynamic => Default::default(),
};
let write_mask = match ops_state.write_mask {
StateMode::Fixed(x) => x,
StateMode::Dynamic => Default::default(),
};
let reference = match ops_state.reference {
StateMode::Fixed(x) => x,
StateMode::Dynamic => Default::default(),
};
ash::vk::StencilOpState {
fail_op: ops.fail_op.into(),
pass_op: ops.pass_op.into(),
depth_fail_op: ops.depth_fail_op.into(),
compare_op: ops.compare_op.into(),
compare_mask,
write_mask,
reference,
}
});
(ash::vk::TRUE, front, back)
} else {
(ash::vk::FALSE, Default::default(), Default::default())
};
Ok(ash::vk::PipelineDepthStencilStateCreateInfo {
flags: ash::vk::PipelineDepthStencilStateCreateFlags::empty(),
depth_test_enable,
depth_write_enable,
depth_compare_op,
depth_bounds_test_enable,
stencil_test_enable,
front,
back,
min_depth_bounds,
max_depth_bounds,
..Default::default()
})
}
}
impl Default for DepthStencilState {
/// Returns [`DepthStencilState::disabled()`].
#[inline]
fn default() -> Self {
DepthStencilState::disabled()
}
}
/// The state in a graphics pipeline describing how the depth test should behave when enabled.
#[derive(Clone, Copy, Debug)]
pub struct DepthState {
/// Sets whether depth testing should be enabled and disabled dynamically. If set to `false`,
/// depth testing is always enabled.
///
/// If set to `true`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub enable_dynamic: bool,
/// Sets whether the value in the depth buffer will be updated when the depth test succeeds.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub write_enable: StateMode<bool>,
/// Comparison operation to use between the depth value of each incoming fragment and the depth
/// value currently in the depth buffer.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub compare_op: StateMode<CompareOp>,
}
impl Default for DepthState {
/// Creates a `DepthState` with no dynamic state, depth writes disabled and `compare_op` set
/// to always pass.
#[inline]
fn default() -> Self {
Self {
enable_dynamic: false,
write_enable: StateMode::Fixed(false),
compare_op: StateMode::Fixed(CompareOp::Always),
}
}
}
impl Default for DepthStencil {
/// The state in a graphics pipeline describing how the depth bounds test should behave when
/// enabled.
#[derive(Clone, Debug)]
pub struct DepthBoundsState {
/// Sets whether depth bounds testing should be enabled and disabled dynamically. If set to
/// `false`, depth bounds testing is always enabled.
///
/// If set to `true`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub enable_dynamic: bool,
/// The minimum and maximum depth values to use for the test. Fragments with values outside this
/// range are discarded.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub bounds: StateMode<RangeInclusive<f32>>,
}
impl Default for DepthBoundsState {
/// Creates a `DepthBoundsState` with no dynamic state and the bounds set to `0.0..=1.0`.
#[inline]
fn default() -> DepthStencil {
DepthStencil::disabled()
fn default() -> Self {
Self {
enable_dynamic: false,
bounds: StateMode::Fixed(0.0..=1.0),
}
}
}
/// Configuration of a stencil test.
#[derive(Debug, Copy, Clone)]
pub struct Stencil {
/// The comparison to perform between the existing stencil value in the stencil buffer, and
/// the reference value (given by `reference`).
pub compare: CompareOp,
/// The state in a graphics pipeline describing how the stencil test should behave when enabled.
///
/// Dynamic state can only be enabled or disabled for both faces at once. Therefore, the dynamic
/// state values in `StencilOpState`, must match: the values for `front` and `back` must either both
/// be `Fixed` or both be `Dynamic`.
#[derive(Clone, Debug)]
pub struct StencilState {
/// Sets whether stencil testing should be enabled and disabled dynamically. If set to
/// `false`, stencil testing is always enabled.
///
/// If set to `true`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub enable_dynamic: bool,
/// The stencil operation state to use for points and lines, and for triangles whose front is
/// facing the user.
pub front: StencilOpState,
/// The stencil operation state to use for triangles whose back is facing the user.
pub back: StencilOpState,
}
/// Stencil test operations for a single face.
#[derive(Clone, Copy, Debug)]
pub struct StencilOpState {
/// The stencil operations to perform.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub ops: StateMode<StencilOps>,
/// A bitmask that selects the bits of the unsigned integer stencil values participating in the
/// stencil test. Ignored if `compare_op` is `Never` or `Always`.
pub compare_mask: StateMode<u32>,
/// A bitmask that selects the bits of the unsigned integer stencil values updated by the
/// stencil test in the stencil framebuffer attachment. Ignored if the relevant operation is
/// `Keep`.
pub write_mask: StateMode<u32>,
/// Reference value that is used in the unsigned stencil comparison. The stencil test is
/// considered to pass if the `compare_op` between the stencil buffer value and this reference
/// value yields true.
pub reference: StateMode<u32>,
}
impl Default for StencilOpState {
/// Creates a `StencilOpState` with no dynamic state, `compare_op` set to `Never`, the stencil
/// operations set to `Keep`, and the masks and reference values set to `u32::MAX`.
#[inline]
fn default() -> StencilOpState {
StencilOpState {
ops: StateMode::Fixed(Default::default()),
compare_mask: StateMode::Fixed(u32::MAX),
write_mask: StateMode::Fixed(u32::MAX),
reference: StateMode::Fixed(u32::MAX),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct StencilOps {
/// The operation to perform when the stencil test failed.
pub fail_op: StencilOp,
/// The operation to perform when both the depth test and the stencil test passed.
pub pass_op: StencilOp,
/// The operation to perform when the stencil test failed.
pub fail_op: StencilOp,
/// The operation to perform when the stencil test passed but the depth test failed.
pub depth_fail_op: StencilOp,
/// Selects the bits of the unsigned integer stencil values participating in the stencil test.
///
/// Ignored if `compare` is `Never` or `Always`.
///
/// If `None`, then this value is dynamic and will need to be set when drawing. Doesn't apply
/// if `compare` is `Never` or `Always`.
///
/// Note that if this value is `Some` in `stencil_front`, it must also be `Some` in
/// `stencil_back` (but the content can be different). If this value is `None` in
/// `stencil_front`, then it must also be `None` in `stencil_back`. This rule doesn't apply
/// if `compare` is `Never` or `Always`.
pub compare_mask: Option<u32>,
/// Selects the bits of the unsigned integer stencil values updated by the stencil test in the
/// stencil framebuffer attachment.
///
/// If `None`, then this value is dynamic and will need to be set when drawing.
///
/// Note that if this value is `Some` in `stencil_front`, it must also be `Some` in
/// `stencil_back` (but the content can be different). If this value is `None` in
/// `stencil_front`, then it must also be `None` in `stencil_back`.
pub write_mask: Option<u32>,
/// Reference value that is used in the unsigned stencil comparison.
///
/// If `None`, then this value is dynamic and will need to be set when drawing.
///
/// Note that if this value is `Some` in `stencil_front`, it must also be `Some` in
/// `stencil_back` (but the content can be different). If this value is `None` in
/// `stencil_front`, then it must also be `None` in `stencil_back`.
pub reference: Option<u32>,
/// The comparison to perform between the existing stencil value in the stencil buffer, and
/// the reference value (given by `reference`).
pub compare_op: CompareOp,
}
impl Stencil {
/// Returns true if the stencil operation will always result in `Keep`.
impl Default for StencilOps {
/// Creates a `StencilOps` with no dynamic state, `compare_op` set to `Never` and the stencil
/// operations set to `Keep`.
#[inline]
pub fn always_keep(&self) -> bool {
match self.compare {
CompareOp::Always => {
self.pass_op == StencilOp::Keep && self.depth_fail_op == StencilOp::Keep
}
CompareOp::Never => self.fail_op == StencilOp::Keep,
_ => {
self.pass_op == StencilOp::Keep
&& self.fail_op == StencilOp::Keep
&& self.depth_fail_op == StencilOp::Keep
}
}
}
}
impl Default for Stencil {
#[inline]
fn default() -> Stencil {
Stencil {
compare: CompareOp::Never,
fn default() -> Self {
Self {
pass_op: StencilOp::Keep,
fail_op: StencilOp::Keep,
depth_fail_op: StencilOp::Keep,
compare_mask: Some(u32::MAX),
write_mask: Some(u32::MAX),
reference: Some(u32::MAX),
compare_op: CompareOp::Never,
}
}
}
@ -205,32 +530,6 @@ pub struct DynamicStencilValue {
pub back: u32,
}
/// Allows you to ask the GPU to exclude fragments that are outside of a certain range.
#[derive(Debug, Clone, PartialEq)]
pub enum DepthBounds {
/// The test is disabled. All fragments pass the depth bounds test.
Disabled,
/// Fragments that are within the given range do pass the test. Values are depth values
/// between 0.0 and 1.0.
Fixed(Range<f32>),
/// The depth bounds test is enabled, but the range will need to specified when you submit
/// a draw command.
Dynamic,
}
impl DepthBounds {
/// Returns true if equal to `DepthBounds::Dynamic`.
#[inline]
pub fn is_dynamic(&self) -> bool {
match self {
&DepthBounds::Dynamic => true,
_ => false,
}
}
}
/// Specifies how two values should be compared to decide whether a test passes or fails.
///
/// Used for both depth testing and stencil testing.

View File

@ -0,0 +1,146 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! A test to discard pixels that would be written to certain areas of a framebuffer.
//!
//! The discard rectangle test is similar to, but separate from the scissor test.
use crate::device::Device;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError, PartialStateMode};
use fnv::FnvHashMap;
use smallvec::SmallVec;
/// The state in a graphics pipeline describing how the discard rectangle test should behave.
#[derive(Clone, Debug)]
pub struct DiscardRectangleState {
/// Sets whether the discard rectangle test operates inclusively or exclusively.
pub mode: DiscardRectangleMode,
/// Specifies the discard rectangles. If set to `Dynamic`, it specifies only the number of
/// rectangles used from the dynamic state.
///
/// If set to `Dynamic` or to `Fixed` with a non-empty list, the
/// [`ext_discard_rectangles`](crate::device::DeviceExtensions::ext_discard_rectangles)
/// extension must be enabled on the device.
pub rectangles: PartialStateMode<Vec<Scissor>, u32>,
}
impl DiscardRectangleState {
/// Creates a `DiscardRectangleState` in exclusive mode with zero rectangles.
#[inline]
pub fn new() -> Self {
Self {
mode: DiscardRectangleMode::Exclusive,
rectangles: PartialStateMode::Fixed(Vec::new()),
}
}
pub(crate) fn to_vulkan_rectangles(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
) -> Result<SmallVec<[ash::vk::Rect2D; 2]>, GraphicsPipelineCreationError> {
Ok(match &self.rectangles {
PartialStateMode::Fixed(rectangles) => {
dynamic_state_modes.insert(DynamicState::DiscardRectangle, false);
rectangles.iter().map(|&rect| rect.into()).collect()
}
PartialStateMode::Dynamic(_) => {
dynamic_state_modes.insert(DynamicState::DiscardRectangle, true);
Default::default()
}
})
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
discard_rectangles: &[ash::vk::Rect2D],
) -> Result<
Option<ash::vk::PipelineDiscardRectangleStateCreateInfoEXT>,
GraphicsPipelineCreationError,
> {
Ok(if device.enabled_extensions().ext_discard_rectangles {
if discard_rectangles.len()
> device
.physical_device()
.properties()
.max_discard_rectangles
.unwrap() as usize
{
return Err(
GraphicsPipelineCreationError::MaxDiscardRectanglesExceeded {
max: device
.physical_device()
.properties()
.max_discard_rectangles
.unwrap(),
obtained: discard_rectangles.len() as u32,
},
);
}
let discard_rectangle_count = match &self.rectangles {
PartialStateMode::Dynamic(count) => *count,
PartialStateMode::Fixed(_) => discard_rectangles.len() as u32,
};
Some(ash::vk::PipelineDiscardRectangleStateCreateInfoEXT {
flags: ash::vk::PipelineDiscardRectangleStateCreateFlagsEXT::empty(),
discard_rectangle_mode: self.mode.into(),
discard_rectangle_count,
p_discard_rectangles: discard_rectangles.as_ptr(),
..Default::default()
})
} else {
let error = match &self.rectangles {
PartialStateMode::Dynamic(_) => true,
PartialStateMode::Fixed(rectangles) => !rectangles.is_empty(),
};
if error {
return Err(GraphicsPipelineCreationError::ExtensionNotEnabled {
extension: "ext_discard_rectangles",
reason: "DiscardRectangleState::rectangles was not Fixed with an empty list",
});
}
None
})
}
}
impl Default for DiscardRectangleState {
/// Returns [`DiscardRectangleState::new`].
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum DiscardRectangleMode {
/// Samples that are inside a rectangle are kept, samples that are outside all rectangles
/// are discarded.
Inclusive = ash::vk::DiscardRectangleModeEXT::INCLUSIVE.as_raw(),
/// Samples that are inside a rectangle are discarded, samples that are outside all rectangles
/// are kept.
Exclusive = ash::vk::DiscardRectangleModeEXT::EXCLUSIVE.as_raw(),
}
impl From<DiscardRectangleMode> for ash::vk::DiscardRectangleModeEXT {
#[inline]
fn from(val: DiscardRectangleMode) -> Self {
Self::from_raw(val as i32)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,6 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::pipeline::input_assembly::PrimitiveTopology;
use crate::pipeline::layout::PipelineLayoutCreationError;
use crate::pipeline::layout::PipelineLayoutSupersetError;
use crate::pipeline::shader::ShaderInterfaceMismatchError;
@ -21,11 +20,21 @@ use std::u32;
/// Error that can happen when creating a graphics pipeline.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GraphicsPipelineCreationError {
/// Not enough memory.
OomError(OomError),
/// A device extension that was required for a particular setting on the graphics pipeline was not enabled.
ExtensionNotEnabled {
extension: &'static str,
reason: &'static str,
},
/// Error while creating the pipeline layout object.
PipelineLayoutCreationError(PipelineLayoutCreationError),
/// A device feature that was required for a particular setting on the graphics pipeline was not enabled.
FeatureNotEnabled {
feature: &'static str,
reason: &'static str,
},
/// The output of the fragment shader is not compatible with what the render pass subpass
/// expects.
FragmentShaderRenderPassIncompatible,
/// The pipeline layout is not compatible with what the shaders expect.
IncompatiblePipelineLayout(PipelineLayoutSupersetError),
@ -33,19 +42,26 @@ pub enum GraphicsPipelineCreationError {
/// The provided specialization constants are not compatible with what the shader expects.
IncompatibleSpecializationConstants,
/// The output interface of one shader and the input interface of the next shader does not match.
ShaderStagesMismatch(ShaderInterfaceMismatchError),
/// The output of the fragment shader is not compatible with what the render pass subpass
/// expects.
FragmentShaderRenderPassIncompatible,
/// The vertex definition is not compatible with the input of the vertex shader.
IncompatibleVertexDefinition(IncompatibleVertexDefinitionError),
/// The maximum stride value for vertex input (ie. the distance between two vertex elements)
/// has been exceeded.
MaxVertexInputBindingStrideExceeded {
/// Tried to use a patch list without a tessellation shader, or a non-patch-list with a
/// tessellation shader.
InvalidPrimitiveTopology,
/// `patch_control_points` was not greater than 0 and less than or equal to the `max_tessellation_patch_size` limit.
InvalidNumPatchControlPoints,
/// The maximum number of discard rectangles has been exceeded.
MaxDiscardRectanglesExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// The maximum value for the instance rate divisor has been exceeded.
MaxVertexAttribDivisorExceeded {
/// Index of the faulty binding.
binding: u32,
/// Maximum allowed value.
@ -54,23 +70,6 @@ pub enum GraphicsPipelineCreationError {
obtained: u32,
},
/// The maximum number of vertex sources has been exceeded.
MaxVertexInputBindingsExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// The maximum offset for a vertex attribute has been exceeded. This means that your vertex
/// struct is too large.
MaxVertexInputAttributeOffsetExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// The maximum number of vertex attributes has been exceeded.
MaxVertexInputAttributesExceeded {
/// Maximum allowed value.
@ -79,16 +78,26 @@ pub enum GraphicsPipelineCreationError {
obtained: usize,
},
/// The `vertex_attribute_instance_rate_divisor` feature must be enabled in order to use
/// instance rate divisors.
VertexAttributeInstanceRateDivisorFeatureNotEnabled,
/// The maximum offset for a vertex attribute has been exceeded. This means that your vertex
/// struct is too large.
MaxVertexInputAttributeOffsetExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// The `vertex_attribute_instance_rate_zero_divisor` feature must be enabled in order to use
/// an instance rate divisor of zero.
VertexAttributeInstanceRateZeroDivisorFeatureNotEnabled,
/// The maximum number of vertex sources has been exceeded.
MaxVertexInputBindingsExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// The maximum value for the instance rate divisor has been exceeded.
MaxVertexAttribDivisorExceeded {
/// The maximum stride value for vertex input (ie. the distance between two vertex elements)
/// has been exceeded.
MaxVertexInputBindingStrideExceeded {
/// Index of the faulty binding.
binding: u32,
/// Maximum allowed value.
@ -97,15 +106,6 @@ pub enum GraphicsPipelineCreationError {
obtained: u32,
},
/// The user requested to use primitive restart, but the primitive topology doesn't support it.
PrimitiveDoesntSupportPrimitiveRestart {
/// The topology that doesn't support primitive restart.
primitive: PrimitiveTopology,
},
/// The `multi_viewport` feature must be enabled in order to use multiple viewports at once.
MultiViewportFeatureNotEnabled,
/// The maximum number of viewports has been exceeded.
MaxViewportsExceeded {
/// Maximum allowed value.
@ -117,48 +117,15 @@ pub enum GraphicsPipelineCreationError {
/// The maximum dimensions of viewports has been exceeded.
MaxViewportDimensionsExceeded,
/// The minimum or maximum bounds of viewports have been exceeded.
ViewportBoundsExceeded,
/// The `wide_lines` feature must be enabled in order to use a line width greater than 1.0.
WideLinesFeatureNotEnabled,
/// The `depth_clamp` feature must be enabled in order to use depth clamping.
DepthClampFeatureNotEnabled,
/// The `depth_bias_clamp` feature must be enabled in order to use a depth bias clamp different
/// from 0.0.
DepthBiasClampFeatureNotEnabled,
/// The `fill_mode_non_solid` feature must be enabled in order to use a polygon mode different
/// from `Fill`.
FillModeNonSolidFeatureNotEnabled,
/// The `depth_bounds` feature must be enabled in order to use depth bounds testing.
DepthBoundsFeatureNotEnabled,
/// The requested stencil test is invalid.
WrongStencilState,
/// The primitives topology does not match what the geometry shader expects.
TopologyNotMatchingGeometryShader,
/// The `geometry_shader` feature must be enabled in order to use geometry shaders.
GeometryShaderFeatureNotEnabled,
/// The `tessellation_shader` feature must be enabled in order to use tessellation shaders.
TessellationShaderFeatureNotEnabled,
/// The number of attachments specified in the blending does not match the number of
/// attachments in the subpass.
MismatchBlendingAttachmentsCount,
/// The `independent_blend` feature must be enabled in order to use different blending
/// operations per attachment.
IndependentBlendFeatureNotEnabled,
/// The device doesn't support using the `multiview´ feature with geometry shaders.
MultiviewGeometryShaderNotSupported,
/// The `logic_op` feature must be enabled in order to use logic operations.
LogicOpFeatureNotEnabled,
/// The device doesn't support using the `multiview´ feature with tessellation shaders.
MultiviewTessellationShaderNotSupported,
/// The depth test requires a depth attachment but render pass has no depth attachment, or
/// depth writing is enabled and the depth attachment is read-only.
@ -168,29 +135,31 @@ pub enum GraphicsPipelineCreationError {
/// stencil writing is enabled and the stencil attachment is read-only.
NoStencilAttachment,
/// Tried to use a patch list without a tessellation shader, or a non-patch-list with a
/// tessellation shader.
InvalidPrimitiveTopology,
/// Not enough memory.
OomError(OomError),
/// The `maxTessellationPatchSize` limit was exceeded.
MaxTessellationPatchSizeExceeded,
/// Error while creating the pipeline layout object.
PipelineLayoutCreationError(PipelineLayoutCreationError),
/// The output interface of one shader and the input interface of the next shader do not match.
ShaderStagesMismatch(ShaderInterfaceMismatchError),
/// The [`strict_lines`](crate::device::Properties::strict_lines) device property was `false`.
StrictLinesNotSupported,
/// The primitives topology does not match what the geometry shader expects.
TopologyNotMatchingGeometryShader,
/// The minimum or maximum bounds of viewports have been exceeded.
ViewportBoundsExceeded,
/// The wrong type of shader has been passed.
///
/// For example you passed a vertex shader as the fragment shader.
WrongShaderType,
/// The `sample_rate_shading` feature must be enabled in order to use sample shading.
SampleRateShadingFeatureNotEnabled,
/// The `alpha_to_one` feature must be enabled in order to use alpha-to-one.
AlphaToOneFeatureNotEnabled,
/// The device doesn't support using the `multiview´ feature with geometry shaders.
MultiviewGeometryShaderNotSupported,
/// The device doesn't support using the `multiview´ feature with tessellation shaders.
MultiviewTessellationShaderNotSupported,
/// The requested stencil test is invalid.
WrongStencilState,
}
impl error::Error for GraphicsPipelineCreationError {
@ -211,143 +180,132 @@ impl fmt::Display for GraphicsPipelineCreationError {
// TODO: finish
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
GraphicsPipelineCreationError::OomError(_) => "not enough memory available",
GraphicsPipelineCreationError::ShaderStagesMismatch(_) => {
"the output interface of one shader and the input interface of the next shader does not match"
}
GraphicsPipelineCreationError::PipelineLayoutCreationError(_) => {
"error while creating the pipeline layout object"
}
GraphicsPipelineCreationError::IncompatiblePipelineLayout(_) => {
"the pipeline layout is not compatible with what the shaders expect"
}
GraphicsPipelineCreationError::IncompatibleSpecializationConstants => {
"the provided specialization constants are not compatible with what the shader expects"
}
GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible => {
"the output of the fragment shader is not compatible with what the render pass \
subpass expects"
}
GraphicsPipelineCreationError::IncompatibleVertexDefinition(_) => {
"the vertex definition is not compatible with the input of the vertex shader"
}
GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded { .. } => {
"the maximum stride value for vertex input (ie. the distance between two vertex \
elements) has been exceeded"
}
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded { .. } => {
"the maximum number of vertex sources has been exceeded"
}
GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded { .. } => {
"the maximum offset for a vertex attribute has been exceeded"
}
GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded { .. } => {
"the maximum number of vertex attributes has been exceeded"
}
GraphicsPipelineCreationError::VertexAttributeInstanceRateDivisorFeatureNotEnabled => {
"the `vertex_attribute_instance_rate_divisor` feature must be enabled in order to use instance rate divisors"
}
GraphicsPipelineCreationError::VertexAttributeInstanceRateZeroDivisorFeatureNotEnabled => {
"the `vertex_attribute_instance_rate_zero_divisor` feature must be enabled in order to use an instance rate divisor of zero"
}
GraphicsPipelineCreationError::MaxVertexAttribDivisorExceeded { .. } => {
"the maximum value for the instance rate divisor has been exceeded"
}
GraphicsPipelineCreationError::PrimitiveDoesntSupportPrimitiveRestart {
..
} => {
"the user requested to use primitive restart, but the primitive topology \
doesn't support it"
}
GraphicsPipelineCreationError::MultiViewportFeatureNotEnabled => {
"the `multi_viewport` feature must be enabled in order to use multiple viewports \
at once"
}
GraphicsPipelineCreationError::MaxViewportsExceeded { .. } => {
"the maximum number of viewports has been exceeded"
}
GraphicsPipelineCreationError::MaxViewportDimensionsExceeded => {
"the maximum dimensions of viewports has been exceeded"
}
GraphicsPipelineCreationError::ViewportBoundsExceeded => {
"the minimum or maximum bounds of viewports have been exceeded"
}
GraphicsPipelineCreationError::WideLinesFeatureNotEnabled => {
"the `wide_lines` feature must be enabled in order to use a line width \
greater than 1.0"
}
GraphicsPipelineCreationError::DepthClampFeatureNotEnabled => {
"the `depth_clamp` feature must be enabled in order to use depth clamping"
}
GraphicsPipelineCreationError::DepthBiasClampFeatureNotEnabled => {
"the `depth_bias_clamp` feature must be enabled in order to use a depth bias \
clamp different from 0.0."
}
GraphicsPipelineCreationError::FillModeNonSolidFeatureNotEnabled => {
"the `fill_mode_non_solid` feature must be enabled in order to use a polygon mode \
different from `Fill`"
}
GraphicsPipelineCreationError::DepthBoundsFeatureNotEnabled => {
"the `depth_bounds` feature must be enabled in order to use depth bounds testing"
}
GraphicsPipelineCreationError::WrongStencilState => {
"the requested stencil test is invalid"
}
GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader => {
"the primitives topology does not match what the geometry shader expects"
}
GraphicsPipelineCreationError::GeometryShaderFeatureNotEnabled => {
"the `geometry_shader` feature must be enabled in order to use geometry shaders"
}
GraphicsPipelineCreationError::TessellationShaderFeatureNotEnabled => {
"the `tessellation_shader` feature must be enabled in order to use tessellation \
shaders"
}
GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount => {
"the number of attachments specified in the blending does not match the number of \
attachments in the subpass"
}
GraphicsPipelineCreationError::IndependentBlendFeatureNotEnabled => {
"the `independent_blend` feature must be enabled in order to use different \
blending operations per attachment"
}
GraphicsPipelineCreationError::LogicOpFeatureNotEnabled => {
"the `logic_op` feature must be enabled in order to use logic operations"
}
GraphicsPipelineCreationError::NoDepthAttachment => {
"the depth attachment of the render pass does not match the depth test"
}
GraphicsPipelineCreationError::NoStencilAttachment => {
"the stencil attachment of the render pass does not match the stencil test"
}
GraphicsPipelineCreationError::InvalidPrimitiveTopology => {
"trying to use a patch list without a tessellation shader, or a non-patch-list \
with a tessellation shader"
}
GraphicsPipelineCreationError::MaxTessellationPatchSizeExceeded => {
"the maximum tessellation patch size was exceeded"
}
GraphicsPipelineCreationError::WrongShaderType => {
"the wrong type of shader has been passed"
}
GraphicsPipelineCreationError::SampleRateShadingFeatureNotEnabled => {
"the `sample_rate_shading` feature must be enabled in order to use sample shading"
}
GraphicsPipelineCreationError::AlphaToOneFeatureNotEnabled => {
"the `alpha_to_one` feature must be enabled in order to use alpha-to-one"
}
GraphicsPipelineCreationError::MultiviewGeometryShaderNotSupported => {
"the device doesn't support using the `multiview´ feature with geometry shaders"
}
GraphicsPipelineCreationError::MultiviewTessellationShaderNotSupported => {
"the device doesn't support using the `multiview´ feature with tessellation shaders"
}
match *self {
GraphicsPipelineCreationError::ExtensionNotEnabled { extension, reason } => {
write!(
fmt,
"the extension {} must be enabled: {}",
extension, reason
)
}
)
GraphicsPipelineCreationError::FeatureNotEnabled { feature, reason } => {
write!(fmt, "the feature {} must be enabled: {}", feature, reason)
}
GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible => {
write!(fmt, "the output of the fragment shader is not compatible with what the render pass subpass expects")
}
GraphicsPipelineCreationError::IncompatiblePipelineLayout(_) => {
write!(
fmt,
"the pipeline layout is not compatible with what the shaders expect"
)
}
GraphicsPipelineCreationError::IncompatibleSpecializationConstants => {
write!(fmt, "the provided specialization constants are not compatible with what the shader expects")
}
GraphicsPipelineCreationError::IncompatibleVertexDefinition(_) => {
write!(
fmt,
"the vertex definition is not compatible with the input of the vertex shader"
)
}
GraphicsPipelineCreationError::InvalidPrimitiveTopology => {
write!(fmt, "trying to use a patch list without a tessellation shader, or a non-patch-list with a tessellation shader")
}
GraphicsPipelineCreationError::InvalidNumPatchControlPoints => {
write!(fmt, "patch_control_points was not greater than 0 and less than or equal to the max_tessellation_patch_size limit")
}
GraphicsPipelineCreationError::MaxDiscardRectanglesExceeded { .. } => {
write!(
fmt,
"the maximum number of discard rectangles has been exceeded"
)
}
GraphicsPipelineCreationError::MaxVertexAttribDivisorExceeded { .. } => {
write!(
fmt,
"the maximum value for the instance rate divisor has been exceeded"
)
}
GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded { .. } => {
write!(
fmt,
"the maximum number of vertex attributes has been exceeded"
)
}
GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded { .. } => {
write!(
fmt,
"the maximum offset for a vertex attribute has been exceeded"
)
}
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded { .. } => {
write!(
fmt,
"the maximum number of vertex sources has been exceeded"
)
}
GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded { .. } => {
write!(fmt, "the maximum stride value for vertex input (ie. the distance between two vertex elements) has been exceeded")
}
GraphicsPipelineCreationError::MaxViewportsExceeded { .. } => {
write!(fmt, "the maximum number of viewports has been exceeded")
}
GraphicsPipelineCreationError::MaxViewportDimensionsExceeded => {
write!(fmt, "the maximum dimensions of viewports has been exceeded")
}
GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount => {
write!(fmt, "the number of attachments specified in the blending does not match the number of attachments in the subpass")
}
GraphicsPipelineCreationError::MultiviewGeometryShaderNotSupported => {
write!(fmt, "the device doesn't support using the `multiview´ feature with geometry shaders")
}
GraphicsPipelineCreationError::MultiviewTessellationShaderNotSupported => {
write!(fmt, "the device doesn't support using the `multiview´ feature with tessellation shaders")
}
GraphicsPipelineCreationError::NoDepthAttachment => {
write!(
fmt,
"the depth attachment of the render pass does not match the depth test"
)
}
GraphicsPipelineCreationError::NoStencilAttachment => {
write!(
fmt,
"the stencil attachment of the render pass does not match the stencil test"
)
}
GraphicsPipelineCreationError::OomError(_) => {
write!(fmt, "not enough memory available")
}
GraphicsPipelineCreationError::PipelineLayoutCreationError(_) => {
write!(fmt, "error while creating the pipeline layout object")
}
GraphicsPipelineCreationError::ShaderStagesMismatch(_) => {
write!(fmt, "the output interface of one shader and the input interface of the next shader do not match")
}
GraphicsPipelineCreationError::StrictLinesNotSupported => {
write!(fmt, "the strict_lines device property was false")
}
GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader => {
write!(
fmt,
"the primitives topology does not match what the geometry shader expects"
)
}
GraphicsPipelineCreationError::ViewportBoundsExceeded => {
write!(
fmt,
"the minimum or maximum bounds of viewports have been exceeded"
)
}
GraphicsPipelineCreationError::WrongShaderType => {
write!(fmt, "the wrong type of shader has been passed")
}
GraphicsPipelineCreationError::WrongStencilState => {
write!(fmt, "the requested stencil test is invalid")
}
}
}
}

View File

@ -9,11 +9,19 @@
pub use self::builder::GraphicsPipelineBuilder;
pub use self::creation_error::GraphicsPipelineCreationError;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::device::{Device, DeviceOwned};
use crate::pipeline::color_blend::ColorBlendState;
use crate::pipeline::depth_stencil::DepthStencilState;
use crate::pipeline::discard_rectangle::DiscardRectangleState;
use crate::pipeline::input_assembly::InputAssemblyState;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::vertex::BuffersDefinition;
use crate::pipeline::vertex::VertexInput;
use crate::pipeline::multisample::MultisampleState;
use crate::pipeline::rasterization::RasterizationState;
use crate::pipeline::shader::ShaderStage;
use crate::pipeline::tessellation::TessellationState;
use crate::pipeline::vertex::{BuffersDefinition, VertexInput};
use crate::pipeline::viewport::ViewportState;
use crate::pipeline::DynamicState;
use crate::render_pass::Subpass;
use crate::VulkanObject;
use fnv::FnvHashMap;
@ -23,7 +31,6 @@ use std::hash::Hasher;
use std::marker::PhantomData;
use std::ptr;
use std::sync::Arc;
use std::u32;
mod builder;
mod creation_error;
@ -38,9 +45,19 @@ pub struct GraphicsPipeline {
inner: Inner,
layout: Arc<PipelineLayout>,
subpass: Subpass,
// TODO: replace () with an object that describes the shaders in some way.
shaders: FnvHashMap<ShaderStage, ()>,
vertex_input: VertexInput,
dynamic_state: FnvHashMap<DynamicState, DynamicStateMode>,
num_viewports: u32,
input_assembly_state: InputAssemblyState,
tessellation_state: Option<TessellationState>,
viewport_state: Option<ViewportState>,
discard_rectangle_state: Option<DiscardRectangleState>,
rasterization_state: RasterizationState,
multisample_state: Option<MultisampleState>,
depth_stencil_state: Option<DepthStencilState>,
color_blend_state: Option<ColorBlendState>,
dynamic_state: FnvHashMap<DynamicState, bool>,
}
#[derive(PartialEq, Eq, Hash)]
@ -86,30 +103,81 @@ impl GraphicsPipeline {
&self.subpass
}
/// Returns the vertex input description of the graphics pipeline.
/// Returns information about a particular shader.
///
/// `None` is returned if the pipeline does not contain this shader.
///
/// Compatibility note: `()` is temporary, it will be replaced with something else in the future.
// TODO: ^ implement and make this public
#[inline]
pub(crate) fn shader(&self, stage: ShaderStage) -> Option<()> {
self.shaders.get(&stage).copied()
}
/// Returns the vertex input state used to create this pipeline.
#[inline]
pub fn vertex_input(&self) -> &VertexInput {
&self.vertex_input
}
/// Returns the number of viewports and scissors of this pipeline.
/// Returns the input assembly state used to create this pipeline.
#[inline]
pub fn num_viewports(&self) -> u32 {
self.num_viewports
pub fn input_assembly_state(&self) -> &InputAssemblyState {
&self.input_assembly_state
}
/// Returns the mode of a particular dynamic state.
/// Returns the tessellation state used to create this pipeline.
#[inline]
pub fn tessellation_state(&self) -> Option<&TessellationState> {
self.tessellation_state.as_ref()
}
/// Returns the viewport state used to create this pipeline.
#[inline]
pub fn viewport_state(&self) -> Option<&ViewportState> {
self.viewport_state.as_ref()
}
/// Returns the discard rectangle state used to create this pipeline.
#[inline]
pub fn discard_rectangle_state(&self) -> Option<&DiscardRectangleState> {
self.discard_rectangle_state.as_ref()
}
/// Returns the rasterization state used to create this pipeline.
#[inline]
pub fn rasterization_state(&self) -> &RasterizationState {
&self.rasterization_state
}
/// Returns the multisample state used to create this pipeline.
#[inline]
pub fn multisample_state(&self) -> Option<&MultisampleState> {
self.multisample_state.as_ref()
}
/// Returns the depth/stencil state used to create this pipeline.
#[inline]
pub fn depth_stencil_state(&self) -> Option<&DepthStencilState> {
self.depth_stencil_state.as_ref()
}
/// Returns the color blend state used to create this pipeline.
#[inline]
pub fn color_blend_state(&self) -> Option<&ColorBlendState> {
self.color_blend_state.as_ref()
}
/// Returns whether a particular state is must be dynamically set.
///
/// `None` is returned if the pipeline does not contain this state. Previously set dynamic
/// state is not disturbed when binding it.
pub fn dynamic_state(&self, state: DynamicState) -> Option<DynamicStateMode> {
pub fn dynamic_state(&self, state: DynamicState) -> Option<bool> {
self.dynamic_state.get(&state).copied()
}
/// Returns all dynamic states and their modes.
pub fn dynamic_states(
&self,
) -> impl ExactSizeIterator<Item = (DynamicState, DynamicStateMode)> + '_ {
/// Returns all potentially dynamic states in the pipeline, and whether they are dynamic or not.
pub fn dynamic_states(&self) -> impl ExactSizeIterator<Item = (DynamicState, bool)> + '_ {
self.dynamic_state.iter().map(|(k, v)| (*k, *v))
}
}
@ -176,66 +244,3 @@ unsafe impl<'a> VulkanObject for GraphicsPipelineSys<'a> {
self.0
}
}
/// A particular state value within a graphics pipeline that can be dynamically set by a command
/// buffer.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum DynamicState {
Viewport = ash::vk::DynamicState::VIEWPORT.as_raw(),
Scissor = ash::vk::DynamicState::SCISSOR.as_raw(),
LineWidth = ash::vk::DynamicState::LINE_WIDTH.as_raw(),
DepthBias = ash::vk::DynamicState::DEPTH_BIAS.as_raw(),
BlendConstants = ash::vk::DynamicState::BLEND_CONSTANTS.as_raw(),
DepthBounds = ash::vk::DynamicState::DEPTH_BOUNDS.as_raw(),
StencilCompareMask = ash::vk::DynamicState::STENCIL_COMPARE_MASK.as_raw(),
StencilWriteMask = ash::vk::DynamicState::STENCIL_WRITE_MASK.as_raw(),
StencilReference = ash::vk::DynamicState::STENCIL_REFERENCE.as_raw(),
ViewportWScaling = ash::vk::DynamicState::VIEWPORT_W_SCALING_NV.as_raw(),
DiscardRectangle = ash::vk::DynamicState::DISCARD_RECTANGLE_EXT.as_raw(),
SampleLocations = ash::vk::DynamicState::SAMPLE_LOCATIONS_EXT.as_raw(),
RayTracingPipelineStackSize =
ash::vk::DynamicState::RAY_TRACING_PIPELINE_STACK_SIZE_KHR.as_raw(),
ViewportShadingRatePalette = ash::vk::DynamicState::VIEWPORT_SHADING_RATE_PALETTE_NV.as_raw(),
ViewportCoarseSampleOrder = ash::vk::DynamicState::VIEWPORT_COARSE_SAMPLE_ORDER_NV.as_raw(),
ExclusiveScissor = ash::vk::DynamicState::EXCLUSIVE_SCISSOR_NV.as_raw(),
FragmentShadingRate = ash::vk::DynamicState::FRAGMENT_SHADING_RATE_KHR.as_raw(),
LineStipple = ash::vk::DynamicState::LINE_STIPPLE_EXT.as_raw(),
CullMode = ash::vk::DynamicState::CULL_MODE_EXT.as_raw(),
FrontFace = ash::vk::DynamicState::FRONT_FACE_EXT.as_raw(),
PrimitiveTopology = ash::vk::DynamicState::PRIMITIVE_TOPOLOGY_EXT.as_raw(),
ViewportWithCount = ash::vk::DynamicState::VIEWPORT_WITH_COUNT_EXT.as_raw(),
ScissorWithCount = ash::vk::DynamicState::SCISSOR_WITH_COUNT_EXT.as_raw(),
VertexInputBindingStride = ash::vk::DynamicState::VERTEX_INPUT_BINDING_STRIDE_EXT.as_raw(),
DepthTestEnable = ash::vk::DynamicState::DEPTH_TEST_ENABLE_EXT.as_raw(),
DepthWriteEnable = ash::vk::DynamicState::DEPTH_WRITE_ENABLE_EXT.as_raw(),
DepthCompareOp = ash::vk::DynamicState::DEPTH_COMPARE_OP_EXT.as_raw(),
DepthBoundsTestEnable = ash::vk::DynamicState::DEPTH_BOUNDS_TEST_ENABLE_EXT.as_raw(),
StencilTestEnable = ash::vk::DynamicState::STENCIL_TEST_ENABLE_EXT.as_raw(),
StencilOp = ash::vk::DynamicState::STENCIL_OP_EXT.as_raw(),
VertexInput = ash::vk::DynamicState::VERTEX_INPUT_EXT.as_raw(),
PatchControlPoints = ash::vk::DynamicState::PATCH_CONTROL_POINTS_EXT.as_raw(),
RasterizerDiscardEnable = ash::vk::DynamicState::RASTERIZER_DISCARD_ENABLE_EXT.as_raw(),
DepthBiasEnable = ash::vk::DynamicState::DEPTH_BIAS_ENABLE_EXT.as_raw(),
LogicOp = ash::vk::DynamicState::LOGIC_OP_EXT.as_raw(),
PrimitiveRestartEnable = ash::vk::DynamicState::PRIMITIVE_RESTART_ENABLE_EXT.as_raw(),
ColorWriteEnable = ash::vk::DynamicState::COLOR_WRITE_ENABLE_EXT.as_raw(),
}
impl From<DynamicState> for ash::vk::DynamicState {
#[inline]
fn from(val: DynamicState) -> Self {
Self::from_raw(val as i32)
}
}
/// Specifies how a dynamic state is handled by a graphics pipeline.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DynamicStateMode {
/// The pipeline has a fixed value for this state. Previously set dynamic state will be lost
/// when binding it, and will have to be re-set after binding a pipeline that uses it.
Fixed,
/// The pipeline expects a dynamic value to be set by a command buffer. Previously set dynamic
/// state is not disturbed when binding it.
Dynamic,
}

View File

@ -130,7 +130,7 @@ fn bad_primitive_restart() {
);
match result {
Err(GraphicsPipelineCreationError::PrimitiveDoesntSupportPrimitiveRestart { .. }) => (),
Err(GraphicsPipelineCreationError::FeatureNotEnabled { .. }) => (),
_ => panic!(),
}
}

View File

@ -10,93 +10,281 @@
//! Assembling vertices into primitives.
//!
//! The input assembly is the stage where lists of vertices are turned into primitives.
//!
use crate::device::Device;
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError, PartialStateMode, StateMode};
use crate::DeviceSize;
use fnv::FnvHashMap;
/// How the input assembly stage should behave.
#[derive(Copy, Clone, Debug)]
#[deprecated]
pub struct InputAssembly {
/// The state in a graphics pipeline describing how the input assembly stage should behave.
#[derive(Clone, Copy, Debug)]
pub struct InputAssemblyState {
/// The type of primitives.
///
/// Note that some topologies don't support primitive restart.
pub topology: PrimitiveTopology,
/// If true, then the special index value `0xffff` or `0xffffffff` will tell the GPU that it is
/// the end of the current primitive. A new primitive will restart at the next index.
/// Note that some topologies require a feature to be enabled on the device.
///
/// Note that some topologies don't support primitive restart.
pub primitive_restart_enable: bool,
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub topology: PartialStateMode<PrimitiveTopology, PrimitiveTopologyClass>,
/// If true, then when drawing with an index buffer, the special index value consisting of the
/// maximum unsigned value (`0xff`, `0xffff` or `0xffffffff`) will tell the GPU that it is the
/// end of the current primitive. A new primitive will restart at the next index.
///
/// Primitive restart is mostly useful in combination with "strip" and "fan" topologies. "List"
/// topologies require a feature to be enabled on the device when combined with primitive
/// restart.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub primitive_restart_enable: StateMode<bool>,
}
impl InputAssembly {
/// Builds an `InputAssembly` struct with the `TriangleList` topology.
impl InputAssemblyState {
/// Creates an `InputAssemblyState` with the `TriangleList` topology and primitive restart
/// disabled.
#[inline]
pub fn triangle_list() -> InputAssembly {
InputAssembly {
topology: PrimitiveTopology::TriangleList,
primitive_restart_enable: false,
pub fn new() -> Self {
Self {
topology: PartialStateMode::Fixed(PrimitiveTopology::TriangleList),
primitive_restart_enable: StateMode::Fixed(false),
}
}
/// Sets the primitive topology.
#[inline]
pub fn topology(mut self, topology: PrimitiveTopology) -> Self {
self.topology = PartialStateMode::Fixed(topology);
self
}
/// Sets the primitive topology to dynamic.
#[inline]
pub fn topology_dynamic(mut self, topology_class: PrimitiveTopologyClass) -> Self {
self.topology = PartialStateMode::Dynamic(topology_class);
self
}
/// Enables primitive restart.
#[inline]
pub fn primitive_restart_enable(mut self) -> Self {
self.primitive_restart_enable = StateMode::Fixed(true);
self
}
/// Sets primitive restart enable to dynmamic.
#[inline]
pub fn primitive_restart_enable_dynamic(mut self) -> Self {
self.primitive_restart_enable = StateMode::Dynamic;
self
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
) -> Result<ash::vk::PipelineInputAssemblyStateCreateInfo, GraphicsPipelineCreationError> {
let topology = match self.topology {
PartialStateMode::Fixed(topology) => {
match topology {
PrimitiveTopology::LineListWithAdjacency
| PrimitiveTopology::LineStripWithAdjacency
| PrimitiveTopology::TriangleListWithAdjacency
| PrimitiveTopology::TriangleStripWithAdjacency => {
if !device.enabled_features().geometry_shader {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "geometry_shader",
reason: "InputAssemblyState::topology was set to a WithAdjacency PrimitiveTopology",
});
}
}
PrimitiveTopology::PatchList => {
if !device.enabled_features().tessellation_shader {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "tessellation_shader",
reason: "InputAssemblyState::topology was set to PrimitiveTopology::PatchList",
});
}
}
_ => (),
}
dynamic_state_modes.insert(DynamicState::PrimitiveTopology, false);
topology.into()
}
PartialStateMode::Dynamic(topology_class) => {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "InputAssemblyState::topology was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::PrimitiveTopology, true);
topology_class.example().into()
}
};
let primitive_restart_enable = match self.primitive_restart_enable {
StateMode::Fixed(primitive_restart_enable) => {
if primitive_restart_enable {
match self.topology {
PartialStateMode::Fixed(
PrimitiveTopology::PointList
| PrimitiveTopology::LineList
| PrimitiveTopology::TriangleList
| PrimitiveTopology::LineListWithAdjacency
| PrimitiveTopology::TriangleListWithAdjacency,
) => {
if !device.enabled_features().primitive_topology_list_restart {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "primitive_topology_list_restart",
reason: "InputAssemblyState::primitive_restart_enable was set to true in combination with a List PrimitiveTopology",
});
}
}
PartialStateMode::Fixed(PrimitiveTopology::PatchList) => {
if !device
.enabled_features()
.primitive_topology_patch_list_restart
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "primitive_topology_patch_list_restart",
reason: "InputAssemblyState::primitive_restart_enable was set to true in combination with PrimitiveTopology::PatchList",
});
}
}
_ => (),
}
}
dynamic_state_modes.insert(DynamicState::PrimitiveRestartEnable, false);
primitive_restart_enable as ash::vk::Bool32
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state2 {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state2",
reason: "InputAssemblyState::primitive_restart_enable was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::PrimitiveRestartEnable, true);
Default::default()
}
};
Ok(ash::vk::PipelineInputAssemblyStateCreateInfo {
flags: ash::vk::PipelineInputAssemblyStateCreateFlags::empty(),
topology,
primitive_restart_enable,
..Default::default()
})
}
}
impl Default for InputAssemblyState {
/// Returns [`InputAssemblyState::new()`].
#[inline]
fn default() -> Self {
Self::new()
}
}
/// Describes how vertices must be grouped together to form primitives.
///
/// Note that some topologies don't support primitive restart.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// When enabling primitive restart, "list" topologies require a feature to be enabled on the
/// device:
/// - The `PatchList` topology requires the
/// [`primitive_topology_patch_list_restart`](crate::device::Features::primitive_topology_patch_list_restart)
/// feature.
/// - All other "list" topologies require the
/// [`primitive_topology_list_restart`](crate::device::Features::primitive_topology_list_restart)
/// feature.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum PrimitiveTopology {
PointList,
LineList,
LineStrip,
TriangleList,
TriangleStrip,
TriangleFan,
LineListWithAdjacency,
LineStripWithAdjacency,
TriangleListWithAdjacency,
TriangleStripWithAdjacency,
PatchList { vertices_per_patch: u32 },
/// A series of separate point primitives.
PointList = ash::vk::PrimitiveTopology::POINT_LIST.as_raw(),
/// A series of separate line primitives.
LineList = ash::vk::PrimitiveTopology::LINE_LIST.as_raw(),
/// A series of consecutive line primitives, with consecutive lines sharing a vertex.
LineStrip = ash::vk::PrimitiveTopology::LINE_STRIP.as_raw(),
/// A series of separate triangle primitives.
TriangleList = ash::vk::PrimitiveTopology::TRIANGLE_LIST.as_raw(),
/// A series of consecutive triangle primitives, with consecutive triangles sharing an edge (two vertices).
TriangleStrip = ash::vk::PrimitiveTopology::TRIANGLE_STRIP.as_raw(),
/// A series of consecutive triangle primitives, with all triangles sharing a common vertex (the first).
TriangleFan = ash::vk::PrimitiveTopology::TRIANGLE_FAN.as_raw(),
/// As `LineList, but with adjacency, used in combination with geometry shaders. Requires the
/// [`geometry_shader`](crate::device::Features::geometry_shader) feature.
LineListWithAdjacency = ash::vk::PrimitiveTopology::LINE_LIST_WITH_ADJACENCY.as_raw(),
/// As `LineStrip`, but with adjacency, used in combination with geometry shaders. Requires the
/// [`geometry_shader`](crate::device::Features::geometry_shader) feature.
LineStripWithAdjacency = ash::vk::PrimitiveTopology::LINE_STRIP_WITH_ADJACENCY.as_raw(),
/// As `TriangleList`, but with adjacency, used in combination with geometry shaders. Requires
/// the [`geometry_shader`](crate::device::Features::geometry_shader) feature.
TriangleListWithAdjacency = ash::vk::PrimitiveTopology::TRIANGLE_LIST_WITH_ADJACENCY.as_raw(),
/// As `TriangleStrip`, but with adjacency, used in combination with geometry shaders. Requires
/// the [`geometry_shader`](crate::device::Features::geometry_shader) feature.
TriangleStripWithAdjacency = ash::vk::PrimitiveTopology::TRIANGLE_STRIP_WITH_ADJACENCY.as_raw(),
/// Separate patch primitives, used in combination with tessellation shaders. Requires the
/// [`tessellation_shader`](crate::device::Features::tessellation_shader) feature.
PatchList = ash::vk::PrimitiveTopology::PATCH_LIST.as_raw(),
}
// TODO: use the #[default] attribute once it's stable.
// See: https://github.com/rust-lang/rust/issues/87517
impl Default for PrimitiveTopology {
#[inline]
fn default() -> Self {
PrimitiveTopology::TriangleList
}
}
impl From<PrimitiveTopology> for ash::vk::PrimitiveTopology {
#[inline]
fn from(val: PrimitiveTopology) -> ash::vk::PrimitiveTopology {
match val {
PrimitiveTopology::PointList => ash::vk::PrimitiveTopology::POINT_LIST,
PrimitiveTopology::LineList => ash::vk::PrimitiveTopology::LINE_LIST,
PrimitiveTopology::LineStrip => ash::vk::PrimitiveTopology::LINE_STRIP,
PrimitiveTopology::TriangleList => ash::vk::PrimitiveTopology::TRIANGLE_LIST,
PrimitiveTopology::TriangleStrip => ash::vk::PrimitiveTopology::TRIANGLE_STRIP,
PrimitiveTopology::TriangleFan => ash::vk::PrimitiveTopology::TRIANGLE_FAN,
PrimitiveTopology::LineListWithAdjacency => {
ash::vk::PrimitiveTopology::LINE_LIST_WITH_ADJACENCY
}
PrimitiveTopology::LineStripWithAdjacency => {
ash::vk::PrimitiveTopology::LINE_STRIP_WITH_ADJACENCY
}
PrimitiveTopology::TriangleListWithAdjacency => {
ash::vk::PrimitiveTopology::TRIANGLE_LIST_WITH_ADJACENCY
}
PrimitiveTopology::TriangleStripWithAdjacency => {
ash::vk::PrimitiveTopology::TRIANGLE_STRIP_WITH_ADJACENCY
}
PrimitiveTopology::PatchList { .. } => ash::vk::PrimitiveTopology::PATCH_LIST,
}
Self::from_raw(val as i32)
}
}
impl PrimitiveTopology {
/// Returns true if this primitive topology supports using primitives restart.
/// Returns the topology class of this topology.
#[inline]
pub fn supports_primitive_restart(&self) -> bool {
match *self {
PrimitiveTopology::LineStrip => true,
PrimitiveTopology::TriangleStrip => true,
PrimitiveTopology::TriangleFan => true,
PrimitiveTopology::LineStripWithAdjacency => true,
PrimitiveTopology::TriangleStripWithAdjacency => true,
_ => false,
pub fn class(&self) -> PrimitiveTopologyClass {
match self {
Self::PointList => PrimitiveTopologyClass::Point,
Self::LineList
| Self::LineStrip
| Self::LineListWithAdjacency
| Self::LineStripWithAdjacency => PrimitiveTopologyClass::Line,
Self::TriangleList
| Self::TriangleStrip
| Self::TriangleFan
| Self::TriangleListWithAdjacency
| Self::TriangleStripWithAdjacency => PrimitiveTopologyClass::Triangle,
Self::PatchList => PrimitiveTopologyClass::Patch,
}
}
}
/// Describes the shape of a primitive topology.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PrimitiveTopologyClass {
Point,
Line,
Triangle,
Patch,
}
impl PrimitiveTopologyClass {
/// Returns a representative example of this topology class.
#[inline]
pub(crate) fn example(&self) -> PrimitiveTopology {
match self {
Self::Point => PrimitiveTopology::PointList,
Self::Line => PrimitiveTopology::LineList,
Self::Triangle => PrimitiveTopology::TriangleList,
Self::Patch => PrimitiveTopology::PatchList,
}
}
}

View File

@ -78,23 +78,23 @@
pub use self::compute_pipeline::ComputePipeline;
pub use self::compute_pipeline::ComputePipelineCreationError;
pub use self::compute_pipeline::ComputePipelineSys;
pub use self::graphics_pipeline::DynamicState;
pub use self::graphics_pipeline::DynamicStateMode;
pub use self::graphics_pipeline::GraphicsPipeline;
pub use self::graphics_pipeline::GraphicsPipelineBuilder;
pub use self::graphics_pipeline::GraphicsPipelineCreationError;
pub use self::graphics_pipeline::GraphicsPipelineSys;
pub mod blend;
pub mod cache;
pub mod color_blend;
mod compute_pipeline;
pub mod depth_stencil;
pub mod discard_rectangle;
mod graphics_pipeline;
pub mod input_assembly;
pub mod layout;
pub mod multisample;
pub mod raster;
pub mod rasterization;
pub mod shader;
pub mod tessellation;
pub mod vertex;
pub mod viewport;
@ -111,3 +111,94 @@ impl From<PipelineBindPoint> for ash::vk::PipelineBindPoint {
Self::from_raw(val as i32)
}
}
/// A particular state value within a graphics pipeline that can be dynamically set by a command
/// buffer.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum DynamicState {
Viewport = ash::vk::DynamicState::VIEWPORT.as_raw(),
Scissor = ash::vk::DynamicState::SCISSOR.as_raw(),
LineWidth = ash::vk::DynamicState::LINE_WIDTH.as_raw(),
DepthBias = ash::vk::DynamicState::DEPTH_BIAS.as_raw(),
BlendConstants = ash::vk::DynamicState::BLEND_CONSTANTS.as_raw(),
DepthBounds = ash::vk::DynamicState::DEPTH_BOUNDS.as_raw(),
StencilCompareMask = ash::vk::DynamicState::STENCIL_COMPARE_MASK.as_raw(),
StencilWriteMask = ash::vk::DynamicState::STENCIL_WRITE_MASK.as_raw(),
StencilReference = ash::vk::DynamicState::STENCIL_REFERENCE.as_raw(),
ViewportWScaling = ash::vk::DynamicState::VIEWPORT_W_SCALING_NV.as_raw(),
DiscardRectangle = ash::vk::DynamicState::DISCARD_RECTANGLE_EXT.as_raw(),
SampleLocations = ash::vk::DynamicState::SAMPLE_LOCATIONS_EXT.as_raw(),
RayTracingPipelineStackSize =
ash::vk::DynamicState::RAY_TRACING_PIPELINE_STACK_SIZE_KHR.as_raw(),
ViewportShadingRatePalette = ash::vk::DynamicState::VIEWPORT_SHADING_RATE_PALETTE_NV.as_raw(),
ViewportCoarseSampleOrder = ash::vk::DynamicState::VIEWPORT_COARSE_SAMPLE_ORDER_NV.as_raw(),
ExclusiveScissor = ash::vk::DynamicState::EXCLUSIVE_SCISSOR_NV.as_raw(),
FragmentShadingRate = ash::vk::DynamicState::FRAGMENT_SHADING_RATE_KHR.as_raw(),
LineStipple = ash::vk::DynamicState::LINE_STIPPLE_EXT.as_raw(),
CullMode = ash::vk::DynamicState::CULL_MODE_EXT.as_raw(),
FrontFace = ash::vk::DynamicState::FRONT_FACE_EXT.as_raw(),
PrimitiveTopology = ash::vk::DynamicState::PRIMITIVE_TOPOLOGY_EXT.as_raw(),
ViewportWithCount = ash::vk::DynamicState::VIEWPORT_WITH_COUNT_EXT.as_raw(),
ScissorWithCount = ash::vk::DynamicState::SCISSOR_WITH_COUNT_EXT.as_raw(),
VertexInputBindingStride = ash::vk::DynamicState::VERTEX_INPUT_BINDING_STRIDE_EXT.as_raw(),
DepthTestEnable = ash::vk::DynamicState::DEPTH_TEST_ENABLE_EXT.as_raw(),
DepthWriteEnable = ash::vk::DynamicState::DEPTH_WRITE_ENABLE_EXT.as_raw(),
DepthCompareOp = ash::vk::DynamicState::DEPTH_COMPARE_OP_EXT.as_raw(),
DepthBoundsTestEnable = ash::vk::DynamicState::DEPTH_BOUNDS_TEST_ENABLE_EXT.as_raw(),
StencilTestEnable = ash::vk::DynamicState::STENCIL_TEST_ENABLE_EXT.as_raw(),
StencilOp = ash::vk::DynamicState::STENCIL_OP_EXT.as_raw(),
VertexInput = ash::vk::DynamicState::VERTEX_INPUT_EXT.as_raw(),
PatchControlPoints = ash::vk::DynamicState::PATCH_CONTROL_POINTS_EXT.as_raw(),
RasterizerDiscardEnable = ash::vk::DynamicState::RASTERIZER_DISCARD_ENABLE_EXT.as_raw(),
DepthBiasEnable = ash::vk::DynamicState::DEPTH_BIAS_ENABLE_EXT.as_raw(),
LogicOp = ash::vk::DynamicState::LOGIC_OP_EXT.as_raw(),
PrimitiveRestartEnable = ash::vk::DynamicState::PRIMITIVE_RESTART_ENABLE_EXT.as_raw(),
ColorWriteEnable = ash::vk::DynamicState::COLOR_WRITE_ENABLE_EXT.as_raw(),
}
impl From<DynamicState> for ash::vk::DynamicState {
#[inline]
fn from(val: DynamicState) -> Self {
Self::from_raw(val as i32)
}
}
/// Specifies how a dynamic state is handled by a graphics pipeline.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StateMode<F> {
/// The pipeline has a fixed value for this state. Previously set dynamic state will be lost
/// when binding it, and will have to be re-set after binding a pipeline that uses it.
Fixed(F),
/// The pipeline expects a dynamic value to be set by a command buffer. Previously set dynamic
/// state is not disturbed when binding it.
Dynamic,
}
impl<T> From<Option<T>> for StateMode<T> {
#[inline]
fn from(val: Option<T>) -> Self {
match val {
Some(x) => StateMode::Fixed(x),
None => StateMode::Dynamic,
}
}
}
impl<T> From<StateMode<T>> for Option<T> {
#[inline]
fn from(val: StateMode<T>) -> Self {
match val {
StateMode::Fixed(x) => Some(x),
StateMode::Dynamic => None,
}
}
}
/// A variant of `StateMode` that is used for cases where some value is still needed when the state
/// is dynamic.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PartialStateMode<F, D> {
Fixed(F),
Dynamic(D),
}

View File

@ -11,51 +11,121 @@
//!
//! Multisampling allows you to ask the GPU to run the rasterizer to generate more than one
//! sample per pixel.
//!
//! For example, if `rasterization_samples` is 1 then the fragment shader, depth test and stencil
//! test will be run once for each pixel. However if `rasterization_samples` is `n`, then the
//! GPU will pick `n` different locations within each pixel and assign to each of these locations
//! a different depth value. Depth and stencil test will then be run `n` times.
//!
//! In addition to this, the `sample_shading` parameter is the proportion (between 0.0 and 1.0) or
//! the samples that will be run through the fragment shader. For example if you set this to 1.0,
//! then all the sub-pixel samples will run through the shader and get a different value. If you
//! set this to 0.5, about half of the samples will run through the shader and the other half will
//! get their values from the ones which went through the shader.
//!
//! If `alpha_to_coverage` is true, then the alpha value of the fragment will be used in
//! an implementation-defined way to determine which samples get disabled or not. For example if
//! the alpha value is 0.5, then about half of the samples will be discarded. If you render to a
//! multisample image, this means that the color will end up being mixed with whatever color was
//! underneath, which gives the same effect as alpha blending.
//!
//! If `alpha_to_one` is true, the alpha value of all the samples will be forced to 1.0 (or the
//! maximum possible value) after the effects of `alpha_to_coverage` have been applied.
use crate::device::Device;
use crate::image::SampleCount;
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError};
use crate::render_pass::Subpass;
use fnv::FnvHashMap;
use std::ptr;
// TODO: handle some weird behaviors with non-floating-point targets
/// State of the multisampling.
///
/// See the documentation in this module.
#[deprecated(note = "No longer needed")]
#[derive(Debug, Copy, Clone)]
pub struct Multisample {
pub rasterization_samples: u32,
pub sample_mask: [u32; 4],
#[derive(Copy, Clone, Debug)]
pub struct MultisampleState {
/*
TODO: enable
/// The number of rasterization samples to take per pixel. The GPU will pick this many different
/// locations within each pixel and assign to each of these locations a different depth value.
/// The depth and stencil test will then be run for each sample.
pub rasterization_samples: SampleCount,
*/
/// Controls the proportion (between 0.0 and 1.0) of the samples that will be run through the
/// fragment shader.
///
/// If the value is 1.0, then all sub-pixel samples will run
/// through the shader and get a different value. If the value is 0.5, about half of the samples
/// will run through the shader and the other half will get their values from the ones which
/// went through the shader.
///
/// If set to `Some`, the [`sample_rate_shading`](crate::device::Features::sample_rate_shading)
/// feature must be enabled on the device.
pub sample_shading: Option<f32>,
pub alpha_to_coverage: bool,
pub alpha_to_one: bool,
/*
TODO: enable
pub sample_mask: Option<[u32; 2]>, // 64 bits for needed for 64 SampleCount
*/
/// Controls whether the alpha value of the fragment will be used in an implementation-defined
/// way to determine which samples get disabled or not. For example if the alpha value is 0.5,
/// then about half of the samples will be discarded. If you render to a multisample image, this
/// means that the color will end up being mixed with whatever color was underneath, which gives
/// the same effect as alpha blending.
pub alpha_to_coverage_enable: bool,
/// Controls whether the alpha value of all the samples will be forced to 1.0 (or the
/// maximum possible value) after the effects of `alpha_to_coverage` have been applied.
///
/// If set to `true`, the [`alpha_to_one`](crate::device::Features::alpha_to_one)
/// feature must be enabled on the device.
pub alpha_to_one_enable: bool,
}
impl Multisample {
impl MultisampleState {
/// Creates a `MultisampleState` with multisampling disabled.
#[inline]
pub fn disabled() -> Multisample {
Multisample {
rasterization_samples: 1,
sample_mask: [0xffffffff; 4],
pub fn new() -> MultisampleState {
MultisampleState {
//rasterization_samples: 1,
sample_shading: None,
alpha_to_coverage: false,
alpha_to_one: false,
//sample_mask: [0xffffffff; 4],
alpha_to_coverage_enable: false,
alpha_to_one_enable: false,
}
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
subpass: &Subpass,
) -> Result<ash::vk::PipelineMultisampleStateCreateInfo, GraphicsPipelineCreationError> {
let rasterization_samples = subpass.num_samples().unwrap_or(SampleCount::Sample1).into();
let (sample_shading_enable, min_sample_shading) =
if let Some(min_sample_shading) = self.sample_shading {
if !device.enabled_features().sample_rate_shading {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "sample_rate_shading",
reason: "MultisampleState::sample_shading was Some",
});
}
assert!(min_sample_shading >= 0.0 && min_sample_shading <= 1.0); // TODO: return error?
(ash::vk::TRUE, min_sample_shading)
} else {
(ash::vk::FALSE, 0.0)
};
let alpha_to_one_enable = {
if self.alpha_to_one_enable {
if !device.enabled_features().alpha_to_one {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "alpha_to_one",
reason: "MultisampleState::alpha_to_one was true",
});
}
}
self.alpha_to_one_enable as ash::vk::Bool32
};
Ok(ash::vk::PipelineMultisampleStateCreateInfo {
flags: ash::vk::PipelineMultisampleStateCreateFlags::empty(),
rasterization_samples,
sample_shading_enable,
min_sample_shading,
p_sample_mask: ptr::null(),
alpha_to_coverage_enable: self.alpha_to_coverage_enable as ash::vk::Bool32,
alpha_to_one_enable,
..Default::default()
})
}
}
impl Default for MultisampleState {
/// Returns [`MultisampleState::new()`].
#[inline]
fn default() -> Self {
Self::new()
}
}

View File

@ -1,166 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Stage when triangles are turned into pixels.
//!
//! The rasterization is the stage when collections of triangles are turned into collections
//! of pixels or samples.
//!
/// State of the rasterizer.
#[derive(Clone, Debug)]
pub struct Rasterization {
/// If true, then the depth value of the vertices will be clamped to [0.0 ; 1.0]. If false,
/// fragments whose depth is outside of this range will be discarded.
pub depth_clamp: bool,
/// If true, all the fragments will be discarded. This is usually used when your vertex shader
/// has some side effects and you don't need to run the fragment shader.
pub rasterizer_discard: bool,
/// This setting can ask the rasterizer to downgrade triangles into lines or points, or lines
/// into points.
pub polygon_mode: PolygonMode,
/// Specifies whether front faces or back faces should be discarded, or none, or both.
pub cull_mode: CullMode,
/// Specifies which triangle orientation corresponds to the front or the triangle.
pub front_face: FrontFace,
/// Width, in pixels, of lines when drawing lines.
///
/// If you pass `None`, then this state will be considered as dynamic and the line width will
/// need to be set when you build the command buffer.
pub line_width: Option<f32>,
pub depth_bias: DepthBiasControl,
}
impl Default for Rasterization {
#[inline]
fn default() -> Rasterization {
Rasterization {
depth_clamp: false,
rasterizer_discard: false,
polygon_mode: Default::default(),
cull_mode: Default::default(),
front_face: Default::default(),
line_width: Some(1.0),
depth_bias: DepthBiasControl::Disabled,
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum DepthBiasControl {
Disabled,
Dynamic,
Static(DepthBias),
}
impl DepthBiasControl {
#[inline]
pub fn is_dynamic(&self) -> bool {
match *self {
DepthBiasControl::Dynamic => true,
_ => false,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct DepthBias {
pub constant_factor: f32,
/// Requires the `depth_bias_clamp` feature to be enabled.
pub clamp: f32,
pub slope_factor: f32,
}
/// Specifies the culling mode.
///
/// This setting works in pair with `front_face`. The `front_face` setting tells the GPU whether
/// clockwise or counter-clockwise correspond to the front and the back of each triangle. Then
/// `cull_mode` lets you specify whether front faces should be discarded, back faces should be
/// discarded, or none, or both.
#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum CullMode {
/// No culling.
None = ash::vk::CullModeFlags::NONE.as_raw(),
/// The faces facing the front of the screen (ie. facing the user) will be removed.
Front = ash::vk::CullModeFlags::FRONT.as_raw(),
/// The faces facing the back of the screen will be removed.
Back = ash::vk::CullModeFlags::BACK.as_raw(),
/// All faces will be removed.
FrontAndBack = ash::vk::CullModeFlags::FRONT_AND_BACK.as_raw(),
}
impl From<CullMode> for ash::vk::CullModeFlags {
#[inline]
fn from(val: CullMode) -> Self {
Self::from_raw(val as u32)
}
}
impl Default for CullMode {
#[inline]
fn default() -> CullMode {
CullMode::None
}
}
/// Specifies which triangle orientation corresponds to the front or the triangle.
#[derive(Copy, Clone, Debug)]
#[repr(i32)]
pub enum FrontFace {
/// Triangles whose vertices are oriented counter-clockwise on the screen will be considered
/// as facing their front. Otherwise they will be considered as facing their back.
CounterClockwise = ash::vk::FrontFace::COUNTER_CLOCKWISE.as_raw(),
/// Triangles whose vertices are oriented clockwise on the screen will be considered
/// as facing their front. Otherwise they will be considered as facing their back.
Clockwise = ash::vk::FrontFace::CLOCKWISE.as_raw(),
}
impl From<FrontFace> for ash::vk::FrontFace {
#[inline]
fn from(val: FrontFace) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for FrontFace {
#[inline]
fn default() -> FrontFace {
FrontFace::CounterClockwise
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum PolygonMode {
Fill = ash::vk::PolygonMode::FILL.as_raw(),
Line = ash::vk::PolygonMode::LINE.as_raw(),
Point = ash::vk::PolygonMode::POINT.as_raw(),
}
impl From<PolygonMode> for ash::vk::PolygonMode {
#[inline]
fn from(val: PolygonMode) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for PolygonMode {
#[inline]
fn default() -> PolygonMode {
PolygonMode::Fill
}
}

View File

@ -0,0 +1,610 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Stage when triangles are turned into pixels.
//!
//! The rasterization is the stage when collections of triangles are turned into collections
//! of pixels or samples.
use crate::{
device::Device,
pipeline::{DynamicState, GraphicsPipelineCreationError, StateMode},
};
use fnv::FnvHashMap;
/// The state in a graphics pipeline describing how the rasterization stage should behave.
#[derive(Clone, Debug)]
pub struct RasterizationState {
/// If true, then the depth value of the vertices will be clamped to the range [0.0, 1.0]. If
/// false, fragments whose depth is outside of this range will be discarded.
///
/// If enabled, the [`depth_clamp`](crate::device::Features::depth_clamp) feature must be
/// enabled on the device.
pub depth_clamp_enable: bool,
/// If true, all the fragments will be discarded, and the fragment shader will not be run. This
/// is usually used when your vertex shader has some side effects and you don't need to run the
/// fragment shader.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature must
/// be enabled on the device.
pub rasterizer_discard_enable: StateMode<bool>,
/// This setting can ask the rasterizer to downgrade triangles into lines or points, or lines
/// into points.
///
/// If set to a value other than `Fill`, the
/// [`fill_mode_non_solid`](crate::device::Features::fill_mode_non_solid) feature must be
/// enabled on the device.
pub polygon_mode: PolygonMode,
/// Specifies whether front faces or back faces should be discarded, or none, or both.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub cull_mode: StateMode<CullMode>,
/// Specifies which triangle orientation is considered to be the front of the triangle.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must be
/// enabled on the device.
pub front_face: StateMode<FrontFace>,
/// Sets how to modify depth values in the rasterization stage.
///
/// If set to `None`, depth biasing is disabled, the depth values will pass to the fragment
/// shader unmodified.
pub depth_bias: Option<DepthBiasState>,
/// Width, in pixels, of lines when drawing lines.
///
/// Setting this to a value other than 1.0 requires the
/// [`wide_lines`](crate::device::Features::wide_lines) feature to be enabled on
/// the device.
pub line_width: StateMode<f32>,
/// The rasterization mode for lines.
///
/// If this is not set to `Default`, the
/// [`ext_line_rasterization`](crate::device::DeviceExtensions::ext_line_rasterization)
/// extension and an additional feature must be enabled on the device.
pub line_rasterization_mode: LineRasterizationMode,
/// Enables and sets the parameters for line stippling.
///
/// If this is set to `Some`, the
/// [`ext_line_rasterization`](crate::device::DeviceExtensions::ext_line_rasterization)
/// extension and an additional feature must be enabled on the device.
pub line_stipple: Option<StateMode<LineStipple>>,
}
impl RasterizationState {
/// Creates a `RasterizationState` with depth clamping, discard, depth biasing and line
/// stippling disabled, filled polygons, no culling, counterclockwise front face, and the
/// default line width and line rasterization mode.
#[inline]
pub fn new() -> Self {
Self {
depth_clamp_enable: false,
rasterizer_discard_enable: StateMode::Fixed(false),
polygon_mode: Default::default(),
cull_mode: StateMode::Fixed(Default::default()),
front_face: StateMode::Fixed(Default::default()),
depth_bias: None,
line_width: StateMode::Fixed(1.0),
line_rasterization_mode: Default::default(),
line_stipple: None,
}
}
/// Sets the polygon mode.
#[inline]
pub fn polygon_mode(mut self, polygon_mode: PolygonMode) -> Self {
self.polygon_mode = polygon_mode;
self
}
/// Sets the cull mode.
#[inline]
pub fn cull_mode(mut self, cull_mode: CullMode) -> Self {
self.cull_mode = StateMode::Fixed(cull_mode);
self
}
/// Sets the cull mode to dynamic.
#[inline]
pub fn cull_mode_dynamic(mut self) -> Self {
self.cull_mode = StateMode::Dynamic;
self
}
/// Sets the front face.
#[inline]
pub fn front_face(mut self, front_face: FrontFace) -> Self {
self.front_face = StateMode::Fixed(front_face);
self
}
/// Sets the front face to dynamic.
#[inline]
pub fn front_face_dynamic(mut self) -> Self {
self.front_face = StateMode::Dynamic;
self
}
pub(crate) fn to_vulkan_line_state(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
) -> Result<
Option<ash::vk::PipelineRasterizationLineStateCreateInfoEXT>,
GraphicsPipelineCreationError,
> {
Ok(if device.enabled_extensions().ext_line_rasterization {
let line_rasterization_mode = {
match self.line_rasterization_mode {
LineRasterizationMode::Default => (),
LineRasterizationMode::Rectangular => {
if !device.enabled_features().rectangular_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "rectangular_lines",
reason:
"RasterizationState::line_rasterization_mode was Rectangular",
});
}
}
LineRasterizationMode::Bresenham => {
if !device.enabled_features().bresenham_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "bresenham_lines",
reason: "RasterizationState::line_rasterization_mode was Bresenham",
});
}
}
LineRasterizationMode::RectangularSmooth => {
if !device.enabled_features().smooth_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "smooth_lines",
reason:
"RasterizationState::line_rasterization_mode was RectangularSmooth",
});
}
}
}
self.line_rasterization_mode.into()
};
let (stippled_line_enable, line_stipple_factor, line_stipple_pattern) = if let Some(
line_stipple,
) =
self.line_stipple
{
match self.line_rasterization_mode {
LineRasterizationMode::Default => {
if !device.enabled_features().stippled_rectangular_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "stippled_rectangular_lines",
reason:
"RasterizationState::line_stipple was Some and line_rasterization_mode was Default",
});
}
if !device.physical_device().properties().strict_lines {
return Err(GraphicsPipelineCreationError::StrictLinesNotSupported);
}
}
LineRasterizationMode::Rectangular => {
if !device.enabled_features().stippled_rectangular_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "stippled_rectangular_lines",
reason:
"RasterizationState::line_stipple was Some and line_rasterization_mode was Rectangular",
});
}
}
LineRasterizationMode::Bresenham => {
if !device.enabled_features().stippled_bresenham_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "stippled_bresenham_lines",
reason: "RasterizationState::line_stipple was Some and line_rasterization_mode was Bresenham",
});
}
}
LineRasterizationMode::RectangularSmooth => {
if !device.enabled_features().stippled_smooth_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "stippled_smooth_lines",
reason:
"RasterizationState::line_stipple was Some and line_rasterization_mode was RectangularSmooth",
});
}
}
}
let (factor, pattern) = match line_stipple {
StateMode::Fixed(line_stipple) => {
assert!(line_stipple.factor >= 1 && line_stipple.factor <= 256); // TODO: return error?
dynamic_state_modes.insert(DynamicState::LineStipple, false);
(line_stipple.factor, line_stipple.pattern)
}
StateMode::Dynamic => {
dynamic_state_modes.insert(DynamicState::LineStipple, true);
(1, 0)
}
};
(ash::vk::TRUE, factor, pattern)
} else {
(ash::vk::FALSE, 1, 0)
};
Some(ash::vk::PipelineRasterizationLineStateCreateInfoEXT {
line_rasterization_mode,
stippled_line_enable,
line_stipple_factor,
line_stipple_pattern,
..Default::default()
})
} else {
if self.line_rasterization_mode != LineRasterizationMode::Default {
return Err(GraphicsPipelineCreationError::ExtensionNotEnabled {
extension: "ext_line_rasterization",
reason: "RasterizationState::line_rasterization_mode was not Default",
});
}
if self.line_stipple.is_some() {
return Err(GraphicsPipelineCreationError::ExtensionNotEnabled {
extension: "ext_line_rasterization",
reason: "RasterizationState::line_stipple was not None",
});
}
None
})
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
rasterization_line_state: Option<&mut ash::vk::PipelineRasterizationLineStateCreateInfoEXT>,
) -> Result<ash::vk::PipelineRasterizationStateCreateInfo, GraphicsPipelineCreationError> {
if self.depth_clamp_enable && !device.enabled_features().depth_clamp {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "depth_clamp",
reason: "RasterizationState::depth_clamp_enable was true",
});
}
let rasterizer_discard_enable = match self.rasterizer_discard_enable {
StateMode::Fixed(rasterizer_discard_enable) => {
dynamic_state_modes.insert(DynamicState::RasterizerDiscardEnable, false);
rasterizer_discard_enable as ash::vk::Bool32
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state2 {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state2",
reason: "RasterizationState::rasterizer_discard_enable was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::RasterizerDiscardEnable, true);
ash::vk::FALSE
}
};
if self.polygon_mode != PolygonMode::Fill && !device.enabled_features().fill_mode_non_solid
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "fill_mode_non_solid",
reason: "RasterizationState::polygon_mode was not Fill",
});
}
let cull_mode = match self.cull_mode {
StateMode::Fixed(cull_mode) => {
dynamic_state_modes.insert(DynamicState::CullMode, false);
cull_mode.into()
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "RasterizationState::cull_mode was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::CullMode, true);
CullMode::default().into()
}
};
let front_face = match self.front_face {
StateMode::Fixed(front_face) => {
dynamic_state_modes.insert(DynamicState::FrontFace, false);
front_face.into()
}
StateMode::Dynamic => {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "RasterizationState::front_face was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::FrontFace, true);
FrontFace::default().into()
}
};
let (
depth_bias_enable,
depth_bias_constant_factor,
depth_bias_clamp,
depth_bias_slope_factor,
) = if let Some(depth_bias_state) = self.depth_bias {
if depth_bias_state.enable_dynamic {
if !device.enabled_features().extended_dynamic_state2 {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state2",
reason: "DepthBiasState::enable_dynamic was true",
});
}
dynamic_state_modes.insert(DynamicState::DepthTestEnable, true);
} else {
dynamic_state_modes.insert(DynamicState::DepthBiasEnable, false);
}
let (constant_factor, clamp, slope_factor) = match depth_bias_state.bias {
StateMode::Fixed(bias) => {
if bias.clamp != 0.0 && !device.enabled_features().depth_bias_clamp {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "depth_bias_clamp",
reason: "DepthBias::clamp was not 0.0",
});
}
dynamic_state_modes.insert(DynamicState::DepthBias, false);
(bias.constant_factor, bias.clamp, bias.slope_factor)
}
StateMode::Dynamic => {
dynamic_state_modes.insert(DynamicState::DepthBias, true);
(0.0, 0.0, 0.0)
}
};
(ash::vk::TRUE, constant_factor, clamp, slope_factor)
} else {
(ash::vk::FALSE, 0.0, 0.0, 0.0)
};
let line_width = match self.line_width {
StateMode::Fixed(line_width) => {
if line_width != 1.0 && !device.enabled_features().wide_lines {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "wide_lines",
reason: "RasterizationState::line_width was not 1.0",
});
}
dynamic_state_modes.insert(DynamicState::LineWidth, false);
line_width
}
StateMode::Dynamic => {
dynamic_state_modes.insert(DynamicState::LineWidth, true);
1.0
}
};
let mut rasterization_state = ash::vk::PipelineRasterizationStateCreateInfo {
flags: ash::vk::PipelineRasterizationStateCreateFlags::empty(),
depth_clamp_enable: self.depth_clamp_enable as ash::vk::Bool32,
rasterizer_discard_enable,
polygon_mode: self.polygon_mode.into(),
cull_mode,
front_face,
depth_bias_enable,
depth_bias_constant_factor,
depth_bias_clamp,
depth_bias_slope_factor,
line_width,
..Default::default()
};
if let Some(rasterization_line_state) = rasterization_line_state {
rasterization_line_state.p_next = rasterization_state.p_next;
rasterization_state.p_next = rasterization_line_state as *const _ as *const _;
}
Ok(rasterization_state)
}
}
impl Default for RasterizationState {
/// Returns [`RasterizationState::new()`].
#[inline]
fn default() -> Self {
Self::new()
}
}
/// The state in a graphics pipeline describing how depth biasing should behave when enabled.
#[derive(Clone, Copy, Debug)]
pub struct DepthBiasState {
/// Sets whether depth biasing should be enabled and disabled dynamically. If set to `false`,
/// depth biasing is always enabled.
///
/// If set to `true`, the
/// [`extended_dynamic_state2`](crate::device::Features::extended_dynamic_state2) feature must
/// be enabled on the device.
pub enable_dynamic: bool,
/// The values to use when depth biasing is enabled.
pub bias: StateMode<DepthBias>,
}
/// The values to use for depth biasing.
#[derive(Clone, Copy, Debug)]
pub struct DepthBias {
/// Specifies a constant factor to be added to every depth value.
pub constant_factor: f32,
/// The maximum (or minimum) depth bias of a fragment.
///
/// Setting this to a value other than 0.0 requires the
/// [`depth_bias_clamp`](crate::device::Features::depth_bias_clamp) feature to be enabled on
/// the device.
pub clamp: f32,
/// A scalar factor applied to a fragment's slope in depth bias calculations.
pub slope_factor: f32,
}
/// Specifies the culling mode.
///
/// This setting works in pair with `front_face`. The `front_face` setting tells the GPU whether
/// clockwise or counter-clockwise correspond to the front and the back of each triangle. Then
/// `cull_mode` lets you specify whether front faces should be discarded, back faces should be
/// discarded, or none, or both.
#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum CullMode {
/// No culling.
None = ash::vk::CullModeFlags::NONE.as_raw(),
/// The faces facing the front of the screen (ie. facing the user) will be removed.
Front = ash::vk::CullModeFlags::FRONT.as_raw(),
/// The faces facing the back of the screen will be removed.
Back = ash::vk::CullModeFlags::BACK.as_raw(),
/// All faces will be removed.
FrontAndBack = ash::vk::CullModeFlags::FRONT_AND_BACK.as_raw(),
}
impl From<CullMode> for ash::vk::CullModeFlags {
#[inline]
fn from(val: CullMode) -> Self {
Self::from_raw(val as u32)
}
}
impl Default for CullMode {
#[inline]
fn default() -> CullMode {
CullMode::None
}
}
/// Specifies which triangle orientation corresponds to the front or the triangle.
#[derive(Copy, Clone, Debug)]
#[repr(i32)]
pub enum FrontFace {
/// Triangles whose vertices are oriented counter-clockwise on the screen will be considered
/// as facing their front. Otherwise they will be considered as facing their back.
CounterClockwise = ash::vk::FrontFace::COUNTER_CLOCKWISE.as_raw(),
/// Triangles whose vertices are oriented clockwise on the screen will be considered
/// as facing their front. Otherwise they will be considered as facing their back.
Clockwise = ash::vk::FrontFace::CLOCKWISE.as_raw(),
}
impl From<FrontFace> for ash::vk::FrontFace {
#[inline]
fn from(val: FrontFace) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for FrontFace {
#[inline]
fn default() -> FrontFace {
FrontFace::CounterClockwise
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum PolygonMode {
Fill = ash::vk::PolygonMode::FILL.as_raw(),
Line = ash::vk::PolygonMode::LINE.as_raw(),
Point = ash::vk::PolygonMode::POINT.as_raw(),
}
impl From<PolygonMode> for ash::vk::PolygonMode {
#[inline]
fn from(val: PolygonMode) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for PolygonMode {
#[inline]
fn default() -> PolygonMode {
PolygonMode::Fill
}
}
/// The rasterization mode to use for lines.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum LineRasterizationMode {
/// If the [`strict_lines`](crate::device::Properties::strict_lines) device property is `true`,
/// then this is the same as `Rectangular`. Otherwise, lines are drawn as parallelograms.
///
/// If [`RasterizationState::line_stipple`] is `Some`, then the
/// [`strict_lines`](crate::device::Properties::strict_lines) property must be `true` and the
/// [`stippled_rectangular_lines`](crate::device::Features::stippled_rectangular_lines) feature
/// must be enabled on the device.
Default = ash::vk::LineRasterizationModeEXT::DEFAULT.as_raw(),
/// Lines are drawn as if they were rectangles extruded from the line.
///
/// The [`rectangular_lines`](crate::device::Features::rectangular_lines) feature must be
/// enabled on the device. If [`RasterizationState::line_stipple`] is `Some`, then the
/// [`stippled_rectangular_lines`](crate::device::Features::stippled_rectangular_lines) must
/// also be enabled.
Rectangular = ash::vk::LineRasterizationModeEXT::RECTANGULAR.as_raw(),
/// Lines are drawn by determining which pixel diamonds the line intersects and exits.
///
/// The [`bresenham_lines`](crate::device::Features::bresenham_lines) feature must be
/// enabled on the device. If [`RasterizationState::line_stipple`] is `Some`, then the
/// [`stippled_bresenham_lines`](crate::device::Features::stippled_bresenham_lines) must
/// also be enabled.
Bresenham = ash::vk::LineRasterizationModeEXT::BRESENHAM.as_raw(),
/// As `Rectangular`, but with alpha falloff.
///
/// The [`smooth_lines`](crate::device::Features::smooth_lines) feature must be
/// enabled on the device. If [`RasterizationState::line_stipple`] is `Some`, then the
/// [`stippled_smooth_lines`](crate::device::Features::stippled_smooth_lines) must
/// also be enabled.
RectangularSmooth = ash::vk::LineRasterizationModeEXT::RECTANGULAR_SMOOTH.as_raw(),
}
impl Default for LineRasterizationMode {
/// Returns `LineRasterizationMode::Default`.
fn default() -> Self {
Self::Default
}
}
impl From<LineRasterizationMode> for ash::vk::LineRasterizationModeEXT {
fn from(val: LineRasterizationMode) -> Self {
Self::from_raw(val as i32)
}
}
/// The parameters of a stippled line.
#[derive(Clone, Copy, Debug)]
pub struct LineStipple {
/// The repeat factor used in stippled line rasterization. Must be between 1 and 256 inclusive.
pub factor: u32,
/// The bit pattern used in stippled line rasterization.
pub pattern: u16,
}

View File

@ -611,7 +611,31 @@ pub struct SpecializationMapEntry {
pub size: usize,
}
/// Describes a set of shader stages.
/// A single shader stage.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum ShaderStage {
Vertex = ash::vk::ShaderStageFlags::VERTEX.as_raw(),
TessellationControl = ash::vk::ShaderStageFlags::TESSELLATION_CONTROL.as_raw(),
TessellationEvaluation = ash::vk::ShaderStageFlags::TESSELLATION_EVALUATION.as_raw(),
Geometry = ash::vk::ShaderStageFlags::GEOMETRY.as_raw(),
Fragment = ash::vk::ShaderStageFlags::FRAGMENT.as_raw(),
Compute = ash::vk::ShaderStageFlags::COMPUTE.as_raw(),
Raygen = ash::vk::ShaderStageFlags::RAYGEN_KHR.as_raw(),
AnyHit = ash::vk::ShaderStageFlags::ANY_HIT_KHR.as_raw(),
ClosestHit = ash::vk::ShaderStageFlags::CLOSEST_HIT_KHR.as_raw(),
Miss = ash::vk::ShaderStageFlags::MISS_KHR.as_raw(),
Intersection = ash::vk::ShaderStageFlags::INTERSECTION_KHR.as_raw(),
Callable = ash::vk::ShaderStageFlags::CALLABLE_KHR.as_raw(),
}
impl From<ShaderStage> for ash::vk::ShaderStageFlags {
fn from(val: ShaderStage) -> Self {
Self::from_raw(val as u32)
}
}
/// A set of shader stages.
// TODO: add example with BitOr
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ShaderStages {

View File

@ -0,0 +1,112 @@
// Copyright (c) 2021 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Subdividing primitives.
//!
//! A tessellation shader divides primitives into smaller primitives.
use crate::device::Device;
use crate::pipeline::input_assembly::{
InputAssemblyState, PrimitiveTopology, PrimitiveTopologyClass,
};
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError, PartialStateMode, StateMode};
use fnv::FnvHashMap;
/// The state in a graphics pipeline describing the tessellation shader execution of a graphics
/// pipeline.
#[derive(Clone, Copy, Debug)]
pub struct TessellationState {
/// The number of patch control points to use.
///
/// If set to `Dynamic`, the
/// [`extended_dynamic_state2_patch_control_points`](crate::device::Features::extended_dynamic_state2_patch_control_points)
/// feature must be enabled on the device.
pub patch_control_points: StateMode<u32>,
}
impl TessellationState {
/// Creates a new `TessellationState` with 3 patch control points.
#[inline]
pub fn new() -> Self {
Self {
patch_control_points: StateMode::Fixed(3),
}
}
/// Sets the number of patch control points.
#[inline]
pub fn patch_control_points(mut self, num: u32) -> Self {
self.patch_control_points = StateMode::Fixed(num);
self
}
/// Sets the number of patch control points to dynamic.
pub fn patch_control_points_dynamic(mut self) -> Self {
self.patch_control_points = StateMode::Dynamic;
self
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
input_assembly_state: &InputAssemblyState,
) -> Result<ash::vk::PipelineTessellationStateCreateInfo, GraphicsPipelineCreationError> {
if !matches!(
input_assembly_state.topology,
PartialStateMode::Dynamic(PrimitiveTopologyClass::Patch)
| PartialStateMode::Fixed(PrimitiveTopology::PatchList)
) {
return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology);
}
let patch_control_points = match self.patch_control_points {
StateMode::Fixed(patch_control_points) => {
if patch_control_points <= 0
|| patch_control_points
> device
.physical_device()
.properties()
.max_tessellation_patch_size
{
return Err(GraphicsPipelineCreationError::InvalidNumPatchControlPoints);
}
dynamic_state_modes.insert(DynamicState::PatchControlPoints, false);
patch_control_points
}
StateMode::Dynamic => {
if !device
.enabled_features()
.extended_dynamic_state2_patch_control_points
{
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state2_patch_control_points",
reason: "TessellationState::patch_control_points was set to Dynamic",
});
}
dynamic_state_modes.insert(DynamicState::PatchControlPoints, true);
Default::default()
}
};
Ok(ash::vk::PipelineTessellationStateCreateInfo {
flags: ash::vk::PipelineTessellationStateCreateFlags::empty(),
patch_control_points,
..Default::default()
})
}
}
impl Default for TessellationState {
/// Returns [`TessellationState::new()`].
#[inline]
fn default() -> Self {
Self::new()
}
}

View File

@ -20,7 +20,7 @@ use crate::DeviceSize;
use std::mem;
/// A vertex definition for any number of vertex and instance buffers.
#[derive(Clone, Default)]
#[derive(Clone, Debug, Default)]
pub struct BuffersDefinition(Vec<VertexBuffer>);
#[derive(Clone, Copy)]
@ -30,6 +30,15 @@ struct VertexBuffer {
input_rate: VertexInputRate,
}
impl std::fmt::Debug for VertexBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VertexBuffer")
.field("stride", &self.stride)
.field("input_rate", &self.input_rate)
.finish()
}
}
impl From<VertexBuffer> for VertexInputBinding {
#[inline]
fn from(val: VertexBuffer) -> Self {

View File

@ -48,73 +48,372 @@
//! In all cases the number of viewports and scissor boxes must be the same.
//!
use std::ops::Range;
use crate::{
device::Device,
pipeline::{DynamicState, GraphicsPipelineCreationError},
};
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::{ops::Range, ptr};
/// List of viewports and scissors that are used when creating a graphics pipeline object.
///
/// Note that the number of viewports and scissors must be the same.
#[derive(Debug, Clone)]
pub enum ViewportsState {
pub enum ViewportState {
/// The state is known in advance.
Fixed {
/// State of the viewports and scissors.
data: Vec<(Viewport, Scissor)>,
},
/// The state of scissors is known in advance, but the state of viewports is dynamic and will
/// bet set when drawing.
///
/// Note that the number of viewports and scissors must be the same.
DynamicViewports {
/// State of the scissors.
scissors: Vec<Scissor>,
/// The state of viewports is known in advance, but the state of scissors is dynamic and will
/// be set when drawing.
FixedViewport {
/// State of the viewports.
viewports: Vec<Viewport>,
/// Sets whether the scissor count is also dynamic, or only the scissors themselves.
///
/// If set to `true`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must
/// be enabled on the device.
scissor_count_dynamic: bool,
},
/// The state of viewports is known in advance, but the state of scissors is dynamic and will
/// bet set when drawing.
///
/// Note that the number of viewports and scissors must be the same.
DynamicScissors {
/// State of the viewports
viewports: Vec<Viewport>,
/// The state of scissors is known in advance, but the state of viewports is dynamic and will
/// be set when drawing.
FixedScissor {
/// State of the scissors.
scissors: Vec<Scissor>,
/// Sets whether the viewport count is also dynamic, or only the viewports themselves.
///
/// If set to `true`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must
/// be enabled on the device.
viewport_count_dynamic: bool,
},
/// The state of both the viewports and scissors is dynamic and will be set when drawing.
Dynamic {
/// Number of viewports and scissors.
num: u32,
///
/// This is ignored if both `viewport_count_dynamic` and `scissor_count_dynamic` are `true`.
count: u32,
/// Sets whether the viewport count is also dynamic, or only the viewports themselves.
///
/// If set to `true`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must
/// be enabled on the device.
viewport_count_dynamic: bool,
/// Sets whether the scissor count is also dynamic, or only the scissors themselves.
///
/// If set to `true`, the
/// [`extended_dynamic_state`](crate::device::Features::extended_dynamic_state) feature must
/// be enabled on the device.
scissor_count_dynamic: bool,
},
}
impl ViewportsState {
/// Returns true if the state of the viewports is dynamic.
pub fn dynamic_viewports(&self) -> bool {
match *self {
ViewportsState::Fixed { .. } => false,
ViewportsState::DynamicViewports { .. } => true,
ViewportsState::DynamicScissors { .. } => false,
ViewportsState::Dynamic { .. } => true,
impl ViewportState {
/// Creates a `ViewportState` with fixed state and no viewports or scissors.
#[inline]
pub fn new() -> Self {
Self::Fixed { data: Vec::new() }
}
/// Creates a `ViewportState` with fixed state from the given viewports and scissors.
#[inline]
pub fn viewport_fixed_scissor_fixed(
data: impl IntoIterator<Item = (Viewport, Scissor)>,
) -> Self {
Self::Fixed {
data: data.into_iter().collect(),
}
}
/// Returns true if the state of the scissors is dynamic.
pub fn dynamic_scissors(&self) -> bool {
match *self {
ViewportsState::Fixed { .. } => false,
ViewportsState::DynamicViewports { .. } => false,
ViewportsState::DynamicScissors { .. } => true,
ViewportsState::Dynamic { .. } => true,
/// Creates a `ViewportState` with fixed state from the given viewports, and matching scissors
/// that cover the whole viewport.
#[inline]
pub fn viewport_fixed_scissor_irrelevant(data: impl IntoIterator<Item = Viewport>) -> Self {
Self::Fixed {
data: data
.into_iter()
.map(|viewport| (viewport, Scissor::irrelevant()))
.collect(),
}
}
/// Creates a `ViewportState` with dynamic viewport, and a single scissor that always covers
/// the whole viewport.
#[inline]
pub fn viewport_dynamic_scissor_irrelevant() -> Self {
Self::FixedScissor {
scissors: vec![Scissor::irrelevant()],
viewport_count_dynamic: false,
}
}
/// Creates a `ViewportState` with dynamic viewports and scissors, but a fixed count.
#[inline]
pub fn viewport_dynamic_scissor_dynamic(count: u32) -> Self {
Self::Dynamic {
count,
viewport_count_dynamic: false,
scissor_count_dynamic: false,
}
}
/// Creates a `ViewportState` with dynamic viewport count and scissor count.
#[inline]
pub fn viewport_count_dynamic_scissor_count_dynamic() -> Self {
Self::Dynamic {
count: 0,
viewport_count_dynamic: true,
scissor_count_dynamic: true,
}
}
/// Returns the number of viewports and scissors.
pub fn num_viewports(&self) -> u32 {
match *self {
ViewportsState::Fixed { ref data } => data.len() as u32,
ViewportsState::DynamicViewports { ref scissors } => scissors.len() as u32,
ViewportsState::DynamicScissors { ref viewports } => viewports.len() as u32,
ViewportsState::Dynamic { num } => num,
///
/// `None` is returned if both `viewport_count_dynamic` and `scissor_count_dynamic` are `true`.
pub fn count(&self) -> Option<u32> {
Some(match *self {
ViewportState::Fixed { ref data } => data.len() as u32,
ViewportState::FixedViewport { ref viewports, .. } => viewports.len() as u32,
ViewportState::FixedScissor { ref scissors, .. } => scissors.len() as u32,
ViewportState::Dynamic {
viewport_count_dynamic: true,
scissor_count_dynamic: true,
..
} => return None,
ViewportState::Dynamic { count, .. } => count,
})
}
pub(crate) fn to_vulkan_viewports_scissors(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
) -> Result<
(
u32,
SmallVec<[ash::vk::Viewport; 2]>,
u32,
SmallVec<[ash::vk::Rect2D; 2]>,
),
GraphicsPipelineCreationError,
> {
Ok(match self {
ViewportState::Fixed { data } => {
let count = data.len() as u32;
assert!(count != 0); // TODO: return error?
let viewports = data
.iter()
.map(|e| e.0.clone().into())
.collect::<SmallVec<[ash::vk::Viewport; 2]>>();
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
let scissors = data
.iter()
.map(|e| e.1.clone().into())
.collect::<SmallVec<[ash::vk::Rect2D; 2]>>();
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
(count, viewports, count, scissors)
}
ViewportState::FixedViewport {
viewports,
scissor_count_dynamic,
} => {
let viewport_count = viewports.len() as u32;
assert!(viewport_count != 0); // TODO: return error?
let viewports = viewports
.iter()
.map(|e| e.clone().into())
.collect::<SmallVec<[ash::vk::Viewport; 2]>>();
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
let scissor_count = if *scissor_count_dynamic {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "ViewportState::FixedViewport::scissor_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Scissor, true);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
viewport_count
};
(viewport_count, viewports, scissor_count, SmallVec::new())
}
ViewportState::FixedScissor {
scissors,
viewport_count_dynamic,
} => {
let scissor_count = scissors.len() as u32;
assert!(scissor_count != 0); // TODO: return error?
let scissors = scissors
.iter()
.map(|e| e.clone().into())
.collect::<SmallVec<[ash::vk::Rect2D; 2]>>();
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
let viewport_count = if *viewport_count_dynamic {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "ViewportState::FixedScissor::viewport_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Viewport, true);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
scissor_count
};
(viewport_count, SmallVec::new(), scissor_count, scissors)
}
ViewportState::Dynamic {
count,
viewport_count_dynamic,
scissor_count_dynamic,
} => {
if !(*viewport_count_dynamic && *scissor_count_dynamic) {
assert!(*count != 0); // TODO: return error?
}
let viewport_count = if *viewport_count_dynamic {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason:
"ViewportState::Dynamic::viewport_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Viewport, false);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Viewport, true);
dynamic_state_modes.insert(DynamicState::ViewportWithCount, false);
*count
};
let scissor_count = if *scissor_count_dynamic {
if !device.enabled_features().extended_dynamic_state {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "extended_dynamic_state",
reason: "ViewportState::Dynamic::scissor_count_dynamic was set to true",
});
}
dynamic_state_modes.insert(DynamicState::Scissor, false);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, true);
0
} else {
dynamic_state_modes.insert(DynamicState::Scissor, true);
dynamic_state_modes.insert(DynamicState::ScissorWithCount, false);
*count
};
(
viewport_count,
SmallVec::new(),
scissor_count,
SmallVec::new(),
)
}
})
}
pub(crate) fn to_vulkan(
&self,
device: &Device,
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
viewport_count: u32,
viewports: &[ash::vk::Viewport],
scissor_count: u32,
scissors: &[ash::vk::Rect2D],
) -> Result<ash::vk::PipelineViewportStateCreateInfo, GraphicsPipelineCreationError> {
let viewport_scissor_count = u32::max(viewport_count, scissor_count);
if viewport_scissor_count > 1 && !device.enabled_features().multi_viewport {
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
feature: "multi_viewport",
reason: "ViewportState viewport/scissor count was greater than 1",
});
}
if viewport_scissor_count > device.physical_device().properties().max_viewports {
return Err(GraphicsPipelineCreationError::MaxViewportsExceeded {
obtained: viewport_scissor_count,
max: device.physical_device().properties().max_viewports,
});
}
for vp in viewports.iter() {
if vp.width
> device
.physical_device()
.properties()
.max_viewport_dimensions[0] as f32
|| vp.height
> device
.physical_device()
.properties()
.max_viewport_dimensions[1] as f32
{
return Err(GraphicsPipelineCreationError::MaxViewportDimensionsExceeded);
}
if vp.x < device.physical_device().properties().viewport_bounds_range[0]
|| vp.x + vp.width > device.physical_device().properties().viewport_bounds_range[1]
|| vp.y < device.physical_device().properties().viewport_bounds_range[0]
|| vp.y + vp.height > device.physical_device().properties().viewport_bounds_range[1]
{
return Err(GraphicsPipelineCreationError::ViewportBoundsExceeded);
}
}
Ok(ash::vk::PipelineViewportStateCreateInfo {
flags: ash::vk::PipelineViewportStateCreateFlags::empty(),
viewport_count,
p_viewports: if viewports.is_empty() {
ptr::null()
} else {
viewports.as_ptr()
}, // validation layer crashes if you just pass the pointer
scissor_count,
p_scissors: if scissors.is_empty() {
ptr::null()
} else {
scissors.as_ptr()
}, // validation layer crashes if you just pass the pointer
..Default::default()
})
}
}
impl Default for ViewportState {
/// Returns [`ViewportState::new()`].
#[inline]
fn default() -> Self {
Self::new()
}
}
@ -168,7 +467,8 @@ pub struct Scissor {
}
impl Scissor {
/// Returns a scissor that, when used, will instruct the pipeline to draw to the entire framebuffer.
/// Returns a scissor that, when used, will instruct the pipeline to draw to the entire
/// framebuffer no matter its size.
#[inline]
pub fn irrelevant() -> Scissor {
Scissor {

View File

@ -550,6 +550,15 @@ impl RenderPass {
pub fn desc(&self) -> &RenderPassDesc {
&self.desc
}
/// Returns the first subpass of the render pass.
#[inline]
pub fn first_subpass(self: Arc<Self>) -> Subpass {
Subpass {
render_pass: self,
subpass_id: 0, // Guaranteed to exist
}
}
}
unsafe impl DeviceOwned for RenderPass {
@ -677,11 +686,32 @@ impl Subpass {
}
}
/// Returns the subpass description for this subpass.
#[inline]
fn subpass_desc(&self) -> &SubpassDesc {
pub fn subpass_desc(&self) -> &SubpassDesc {
&self.render_pass.desc().subpasses()[self.subpass_id as usize]
}
/// Returns whether this subpass is the last one in the render pass. If `true` is returned,
/// `next_subpass` will return `None`.
#[inline]
pub fn is_last_subpass(&self) -> bool {
self.subpass_id as usize == self.render_pass.desc().subpasses().len() - 1
}
/// Tries to advance to the next subpass after this one, and returns `true` if successful.
#[inline]
pub fn try_next_subpass(&mut self) -> bool {
let next_id = self.subpass_id + 1;
if (next_id as usize) < self.render_pass.desc().subpasses().len() {
self.subpass_id = next_id;
true
} else {
false
}
}
#[inline]
fn attachment_desc(&self, atch_num: usize) -> &AttachmentDesc {
&self.render_pass.desc().attachments()[atch_num]

File diff suppressed because it is too large Load Diff