mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-25 16:25:31 +00:00
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:
parent
14f7ad57ac
commit
64ca6f78e1
@ -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())
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
);
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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(),
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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(),
|
||||
);
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
);
|
||||
|
@ -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(),
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
668
vulkano/src/pipeline/color_blend.rs
Normal file
668
vulkano/src/pipeline/color_blend.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
146
vulkano/src/pipeline/discard_rectangle.rs
Normal file
146
vulkano/src/pipeline/discard_rectangle.rs
Normal 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
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ fn bad_primitive_restart() {
|
||||
);
|
||||
|
||||
match result {
|
||||
Err(GraphicsPipelineCreationError::PrimitiveDoesntSupportPrimitiveRestart { .. }) => (),
|
||||
Err(GraphicsPipelineCreationError::FeatureNotEnabled { .. }) => (),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
610
vulkano/src/pipeline/rasterization.rs
Normal file
610
vulkano/src/pipeline/rasterization.rs
Normal 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,
|
||||
}
|
@ -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 {
|
||||
|
112
vulkano/src/pipeline/tessellation.rs
Normal file
112
vulkano/src/pipeline/tessellation.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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]
|
||||
|
570
vulkano/vk.xml
570
vulkano/vk.xml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user