mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-28 17:54:45 +00:00
Bring vertex input in line with other pipeline state, reorganise modules (#1758)
* Bring vertex input in line with other pipeline state, reorganise modules * Documentation improvement * Use ShaderInterfaceEntryType in shader interface instead of format * Example fix
This commit is contained in:
parent
3845925655
commit
0ebf05f35d
@ -28,8 +28,9 @@ use vulkano::device::{Device, DeviceExtensions, Features};
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
|
||||
@ -165,7 +166,7 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -15,9 +15,12 @@ use vulkano::command_buffer::{
|
||||
use vulkano::descriptor_set::PersistentDescriptorSet;
|
||||
use vulkano::device::Queue;
|
||||
use vulkano::image::ImageViewAbstract;
|
||||
use vulkano::pipeline::color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState};
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::color_blend::{
|
||||
AttachmentBlend, BlendFactor, BlendOp, ColorBlendState,
|
||||
};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::Subpass;
|
||||
|
||||
@ -60,7 +63,7 @@ impl AmbientLightingSystem {
|
||||
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
|
||||
|
||||
GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -16,9 +16,12 @@ use vulkano::command_buffer::{
|
||||
use vulkano::descriptor_set::PersistentDescriptorSet;
|
||||
use vulkano::device::Queue;
|
||||
use vulkano::image::ImageViewAbstract;
|
||||
use vulkano::pipeline::color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState};
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::color_blend::{
|
||||
AttachmentBlend, BlendFactor, BlendOp, ColorBlendState,
|
||||
};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::Subpass;
|
||||
|
||||
@ -61,7 +64,7 @@ impl DirectionalLightingSystem {
|
||||
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
|
||||
|
||||
GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -16,9 +16,12 @@ use vulkano::command_buffer::{
|
||||
use vulkano::descriptor_set::PersistentDescriptorSet;
|
||||
use vulkano::device::Queue;
|
||||
use vulkano::image::ImageViewAbstract;
|
||||
use vulkano::pipeline::color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState};
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::color_blend::{
|
||||
AttachmentBlend, BlendFactor, BlendOp, ColorBlendState,
|
||||
};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::Subpass;
|
||||
|
||||
@ -60,7 +63,7 @@ impl PointLightingSystem {
|
||||
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
|
||||
|
||||
GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -13,9 +13,10 @@ use vulkano::command_buffer::{
|
||||
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
|
||||
};
|
||||
use vulkano::device::Queue;
|
||||
use vulkano::pipeline::depth_stencil::DepthStencilState;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::depth_stencil::DepthStencilState;
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::Subpass;
|
||||
|
||||
@ -55,7 +56,7 @@ impl TriangleDrawSystem {
|
||||
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
|
||||
|
||||
GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -27,9 +27,10 @@ use vulkano::{
|
||||
image::{view::ImageView, ImageCreateFlags, ImageUsage, StorageImage, SwapchainImage},
|
||||
instance::{debug::DebugCallback, Instance, InstanceExtensions},
|
||||
pipeline::{
|
||||
color_blend::ColorBlendState,
|
||||
input_assembly::{InputAssemblyState, PrimitiveTopology},
|
||||
viewport::{Scissor, Viewport, ViewportState},
|
||||
graphics::color_blend::ColorBlendState,
|
||||
graphics::input_assembly::{InputAssemblyState, PrimitiveTopology},
|
||||
graphics::vertex_input::BuffersDefinition,
|
||||
graphics::viewport::{Scissor, Viewport, ViewportState},
|
||||
GraphicsPipeline, Pipeline, PipelineBindPoint,
|
||||
},
|
||||
render_pass::{Framebuffer, RenderPass, Subpass},
|
||||
@ -324,7 +325,7 @@ fn vk_setup() -> (
|
||||
Arc<Swapchain<winit::window::Window>>,
|
||||
Arc<vulkano::swapchain::Surface<winit::window::Window>>,
|
||||
winit::event_loop::EventLoop<()>,
|
||||
vulkano::pipeline::viewport::Viewport,
|
||||
vulkano::pipeline::graphics::viewport::Viewport,
|
||||
Arc<Queue>,
|
||||
Arc<RenderPass>,
|
||||
Vec<Arc<Framebuffer>>,
|
||||
@ -486,7 +487,7 @@ fn vk_setup() -> (
|
||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
|
||||
.viewport_state(ViewportState::FixedScissor {
|
||||
|
@ -21,9 +21,10 @@ use vulkano::image::{
|
||||
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
|
||||
};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology};
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology};
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
|
||||
@ -193,7 +194,7 @@ fn main() {
|
||||
|
||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -30,9 +30,10 @@ use vulkano::image::{
|
||||
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
|
||||
};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology};
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology};
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
|
||||
@ -199,7 +200,7 @@ fn main() {
|
||||
|
||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -42,8 +42,9 @@ use vulkano::device::{Device, DeviceExtensions, Features};
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
|
||||
@ -237,7 +238,7 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let render_pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -26,9 +26,9 @@ use vulkano::device::{Device, DeviceExtensions, Features};
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::vertex::BuffersDefinition;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
|
||||
@ -238,7 +238,7 @@ fn main() {
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
// Use the `BuffersDefinition` to describe to vulkano how the two vertex types
|
||||
// are expected to be used.
|
||||
.vertex_input(
|
||||
.vertex_input_state(
|
||||
BuffersDefinition::new()
|
||||
.vertex::<Vertex>()
|
||||
.instance::<InstanceData>(),
|
||||
|
@ -15,8 +15,9 @@ use vulkano::command_buffer::{
|
||||
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::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::Subpass;
|
||||
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
|
||||
@ -84,7 +85,7 @@ impl PixelsDrawPipeline {
|
||||
let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module");
|
||||
let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module");
|
||||
GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<TexturedVertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<TexturedVertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.fragment_shader(fs.entry_point("main").unwrap(), ())
|
||||
|
@ -80,7 +80,8 @@ use vulkano::image::{
|
||||
view::ImageView, AttachmentImage, ImageDimensions, SampleCount, StorageImage,
|
||||
};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{Framebuffer, Subpass};
|
||||
use vulkano::sync::GpuFuture;
|
||||
@ -266,7 +267,7 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
.fragment_shader(fs.entry_point("main").unwrap(), ())
|
||||
|
@ -25,8 +25,9 @@ use vulkano::device::{Device, DeviceExtensions, Features};
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::swapchain::{self, AcquireError, Surface, Swapchain, SwapchainCreationError};
|
||||
@ -208,7 +209,7 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -28,8 +28,9 @@ use vulkano::image::{
|
||||
StorageImage,
|
||||
};
|
||||
use vulkano::instance::{Instance, InstanceExtensions};
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{
|
||||
AttachmentDesc, Framebuffer, LoadOp, MultiviewDesc, RenderPass, RenderPassDesc, StoreOp,
|
||||
@ -237,7 +238,7 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([
|
||||
|
@ -20,9 +20,10 @@ use vulkano::format::Format;
|
||||
use vulkano::image::ImageAccess;
|
||||
use vulkano::image::{view::ImageView, AttachmentImage, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::depth_stencil::DepthStencilState;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::depth_stencil::DepthStencilState;
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::query::{QueryControlFlags, QueryPool, QueryResultFlags, QueryType};
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
@ -237,7 +238,7 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -21,9 +21,10 @@ use vulkano::image::{
|
||||
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
|
||||
};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology};
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology};
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
|
||||
@ -191,7 +192,7 @@ fn main() {
|
||||
|
||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip))
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -30,9 +30,10 @@ use vulkano::device::{Device, DeviceExtensions, Features};
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::rasterization::{CullMode, FrontFace, RasterizationState};
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::rasterization::{CullMode, FrontFace, RasterizationState};
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::shader::ShaderModule;
|
||||
@ -152,7 +153,7 @@ fn main() {
|
||||
};
|
||||
|
||||
let graphics_pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
|
@ -24,9 +24,10 @@ use vulkano::image::{
|
||||
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
|
||||
};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::graphics::color_blend::ColorBlendState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::layout::PipelineLayout;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
|
||||
@ -307,7 +308,7 @@ fn main() {
|
||||
|
||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
.fragment_shader(fs.entry_point("main").unwrap(), ())
|
||||
|
@ -22,10 +22,10 @@ use vulkano::image::attachment::AttachmentImage;
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, 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, ViewportState};
|
||||
use vulkano::pipeline::graphics::depth_stencil::DepthStencilState;
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint};
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::shader::ShaderModule;
|
||||
@ -333,7 +333,7 @@ fn window_size_dependent_setup(
|
||||
// This allows the driver to optimize things, at the cost of slower window resizes.
|
||||
// https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input(
|
||||
.vertex_input_state(
|
||||
BuffersDefinition::new()
|
||||
.vertex::<Vertex>()
|
||||
.vertex::<Normal>(),
|
||||
|
@ -26,10 +26,11 @@ use vulkano::device::{Device, DeviceExtensions, Features};
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
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::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology};
|
||||
use vulkano::pipeline::graphics::rasterization::{PolygonMode, RasterizationState};
|
||||
use vulkano::pipeline::graphics::tessellation::TessellationState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
|
||||
@ -272,7 +273,7 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
// Actually use the tessellation shaders.
|
||||
.tessellation_shaders(
|
||||
|
@ -24,8 +24,9 @@ use vulkano::device::{Device, DeviceExtensions, Features};
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::image::{ImageAccess, ImageUsage, SwapchainImage};
|
||||
use vulkano::instance::Instance;
|
||||
use vulkano::pipeline::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
|
||||
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::render_pass::{Framebuffer, RenderPass, Subpass};
|
||||
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
|
||||
@ -318,12 +319,9 @@ fn main() {
|
||||
// program, but much more specific.
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
// We need to indicate the layout of the vertices.
|
||||
// The type `SingleBufferDefinition` actually contains a template parameter corresponding
|
||||
// to the type of each vertex. But in this code it is automatically inferred.
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||
// which one. The `main` word of `main_entry_point` actually corresponds to the name of
|
||||
// the entry point.
|
||||
// which one.
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
// The content of the vertex buffer describes a list of triangles.
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
|
@ -12,7 +12,7 @@ use proc_macro2::TokenStream;
|
||||
use vulkano::pipeline::layout::PipelineLayoutPcRange;
|
||||
use vulkano::shader::{
|
||||
DescriptorRequirements, GeometryShaderExecution, ShaderExecution, ShaderInterfaceEntry,
|
||||
SpecializationConstantRequirements,
|
||||
ShaderInterfaceEntryType, SpecializationConstantRequirements,
|
||||
};
|
||||
use vulkano::shader::{EntryPointInfo, ShaderInterface, ShaderStages};
|
||||
use vulkano::shader::spirv::ExecutionModel;
|
||||
@ -239,17 +239,28 @@ fn write_interface(interface: &ShaderInterface) -> TokenStream {
|
||||
let items = interface.elements().iter().map(
|
||||
|ShaderInterfaceEntry {
|
||||
location,
|
||||
format,
|
||||
component,
|
||||
ty:
|
||||
ShaderInterfaceEntryType {
|
||||
base_type,
|
||||
num_components,
|
||||
num_elements,
|
||||
is_64bit,
|
||||
},
|
||||
name,
|
||||
}| {
|
||||
let start = location.start;
|
||||
let end = location.end;
|
||||
let format = format_ident!("{}", format!("{:?}", format));
|
||||
let base_type = format_ident!("{}", format!("{:?}", base_type));
|
||||
|
||||
quote! {
|
||||
::vulkano::shader::ShaderInterfaceEntry {
|
||||
location: #start .. #end,
|
||||
format: ::vulkano::format::Format::#format,
|
||||
location: #location,
|
||||
component: #component,
|
||||
ty: ::vulkano::shader::ShaderInterfaceEntryType {
|
||||
base_type: ::vulkano::shader::ShaderScalarType::#base_type,
|
||||
num_components: #num_components,
|
||||
num_elements: #num_elements,
|
||||
is_64bit: #is_64bit,
|
||||
},
|
||||
name: Some(::std::borrow::Cow::Borrowed(#name))
|
||||
},
|
||||
}
|
||||
|
@ -50,19 +50,19 @@ 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::graphics::color_blend::LogicOp;
|
||||
use crate::pipeline::graphics::depth_stencil::CompareOp;
|
||||
use crate::pipeline::graphics::depth_stencil::StencilFaces;
|
||||
use crate::pipeline::graphics::depth_stencil::StencilOp;
|
||||
use crate::pipeline::graphics::input_assembly::Index;
|
||||
use crate::pipeline::graphics::input_assembly::IndexType;
|
||||
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
|
||||
use crate::pipeline::graphics::rasterization::CullMode;
|
||||
use crate::pipeline::graphics::rasterization::FrontFace;
|
||||
use crate::pipeline::graphics::vertex_input::VertexBuffersCollection;
|
||||
use crate::pipeline::graphics::viewport::Scissor;
|
||||
use crate::pipeline::graphics::viewport::Viewport;
|
||||
use crate::pipeline::layout::PipelineLayout;
|
||||
use crate::pipeline::rasterization::CullMode;
|
||||
use crate::pipeline::rasterization::FrontFace;
|
||||
use crate::pipeline::vertex::VertexBuffersCollection;
|
||||
use crate::pipeline::viewport::Scissor;
|
||||
use crate::pipeline::viewport::Viewport;
|
||||
use crate::pipeline::ComputePipeline;
|
||||
use crate::pipeline::DynamicState;
|
||||
use crate::pipeline::GraphicsPipeline;
|
||||
@ -1845,7 +1845,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
|
||||
/// 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).
|
||||
/// - [`attachments.len()`](crate::pipeline::graphics::color_blend::ColorBlendState::attachments).
|
||||
#[inline]
|
||||
pub fn set_color_write_enable<I>(&mut self, enables: I) -> &mut Self
|
||||
where
|
||||
|
@ -109,9 +109,9 @@ pub use self::traits::PrimaryCommandBuffer;
|
||||
pub use self::traits::SecondaryCommandBuffer;
|
||||
use crate::query::QueryControlFlags;
|
||||
use crate::query::QueryPipelineStatisticFlags;
|
||||
use crate::render_pass::Framebuffer;
|
||||
use crate::render_pass::Subpass;
|
||||
use std::sync::Arc;
|
||||
use crate::render_pass::Framebuffer;
|
||||
|
||||
mod auto;
|
||||
pub mod pool;
|
||||
|
@ -25,19 +25,19 @@ use crate::device::Device;
|
||||
use crate::device::DeviceOwned;
|
||||
use crate::image::ImageAccess;
|
||||
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::graphics::color_blend::LogicOp;
|
||||
use crate::pipeline::graphics::depth_stencil::CompareOp;
|
||||
use crate::pipeline::graphics::depth_stencil::StencilOp;
|
||||
use crate::pipeline::graphics::depth_stencil::StencilOps;
|
||||
use crate::pipeline::graphics::input_assembly::IndexType;
|
||||
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
|
||||
use crate::pipeline::graphics::rasterization::CullMode;
|
||||
use crate::pipeline::graphics::rasterization::DepthBias;
|
||||
use crate::pipeline::graphics::rasterization::FrontFace;
|
||||
use crate::pipeline::graphics::rasterization::LineStipple;
|
||||
use crate::pipeline::graphics::viewport::Scissor;
|
||||
use crate::pipeline::graphics::viewport::Viewport;
|
||||
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;
|
||||
use crate::pipeline::DynamicState;
|
||||
use crate::pipeline::GraphicsPipeline;
|
||||
|
@ -32,12 +32,12 @@ use crate::descriptor_set::DescriptorSetWithOffsets;
|
||||
use crate::format::ClearValue;
|
||||
use crate::image::ImageAccess;
|
||||
use crate::image::ImageLayout;
|
||||
use crate::pipeline::depth_stencil::StencilFaces;
|
||||
use crate::pipeline::input_assembly::IndexType;
|
||||
use crate::pipeline::graphics::depth_stencil::StencilFaces;
|
||||
use crate::pipeline::graphics::input_assembly::IndexType;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputState;
|
||||
use crate::pipeline::graphics::viewport::Scissor;
|
||||
use crate::pipeline::graphics::viewport::Viewport;
|
||||
use crate::pipeline::layout::PipelineLayout;
|
||||
use crate::pipeline::vertex::VertexInput;
|
||||
use crate::pipeline::viewport::Scissor;
|
||||
use crate::pipeline::viewport::Viewport;
|
||||
use crate::pipeline::ComputePipeline;
|
||||
use crate::pipeline::GraphicsPipeline;
|
||||
use crate::pipeline::PipelineBindPoint;
|
||||
@ -1167,7 +1167,7 @@ impl SyncCommandBufferBuilder {
|
||||
PipelineBindPoint::Graphics,
|
||||
pipeline.descriptor_requirements(),
|
||||
);
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input());
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
|
||||
|
||||
self.append_command(
|
||||
Cmd {
|
||||
@ -1223,7 +1223,7 @@ impl SyncCommandBufferBuilder {
|
||||
PipelineBindPoint::Graphics,
|
||||
pipeline.descriptor_requirements(),
|
||||
);
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input());
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
|
||||
self.add_index_buffer_resources(&mut resources);
|
||||
|
||||
self.append_command(
|
||||
@ -1271,7 +1271,7 @@ impl SyncCommandBufferBuilder {
|
||||
PipelineBindPoint::Graphics,
|
||||
pipeline.descriptor_requirements(),
|
||||
);
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input());
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
|
||||
self.add_indirect_buffer_resources(&mut resources, indirect_buffer.clone());
|
||||
|
||||
self.append_command(
|
||||
@ -1322,7 +1322,7 @@ impl SyncCommandBufferBuilder {
|
||||
PipelineBindPoint::Graphics,
|
||||
pipeline.descriptor_requirements(),
|
||||
);
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input());
|
||||
self.add_vertex_buffer_resources(&mut resources, pipeline.vertex_input_state());
|
||||
self.add_index_buffer_resources(&mut resources);
|
||||
self.add_indirect_buffer_resources(&mut resources, indirect_buffer.clone());
|
||||
|
||||
@ -2680,9 +2680,9 @@ impl SyncCommandBufferBuilder {
|
||||
ImageUninitializedSafe,
|
||||
)>,
|
||||
)>,
|
||||
vertex_input: &VertexInput,
|
||||
vertex_input: &VertexInputState,
|
||||
) {
|
||||
resources.extend(vertex_input.bindings().map(|(binding_num, _)| {
|
||||
resources.extend(vertex_input.bindings.iter().map(|(&binding_num, _)| {
|
||||
let buffer = self.current_state.vertex_buffers[&binding_num].clone();
|
||||
(
|
||||
KeyTy::Buffer(buffer),
|
||||
|
@ -29,17 +29,17 @@ 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::graphics::color_blend::LogicOp;
|
||||
use crate::pipeline::graphics::depth_stencil::CompareOp;
|
||||
use crate::pipeline::graphics::depth_stencil::StencilFaces;
|
||||
use crate::pipeline::graphics::depth_stencil::StencilOp;
|
||||
use crate::pipeline::graphics::input_assembly::IndexType;
|
||||
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
|
||||
use crate::pipeline::graphics::rasterization::CullMode;
|
||||
use crate::pipeline::graphics::rasterization::FrontFace;
|
||||
use crate::pipeline::graphics::viewport::Scissor;
|
||||
use crate::pipeline::graphics::viewport::Viewport;
|
||||
use crate::pipeline::layout::PipelineLayout;
|
||||
use crate::pipeline::rasterization::CullMode;
|
||||
use crate::pipeline::rasterization::FrontFace;
|
||||
use crate::pipeline::viewport::Scissor;
|
||||
use crate::pipeline::viewport::Viewport;
|
||||
use crate::pipeline::ComputePipeline;
|
||||
use crate::pipeline::GraphicsPipeline;
|
||||
use crate::pipeline::PipelineBindPoint;
|
||||
|
@ -8,7 +8,7 @@
|
||||
// according to those terms.
|
||||
|
||||
use crate::command_buffer::synced::CommandBufferState;
|
||||
use crate::pipeline::input_assembly::PrimitiveTopology;
|
||||
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
|
||||
use crate::pipeline::DynamicState;
|
||||
use crate::pipeline::GraphicsPipeline;
|
||||
use crate::pipeline::PartialStateMode;
|
||||
|
@ -8,7 +8,7 @@
|
||||
// according to those terms.
|
||||
|
||||
use crate::command_buffer::synced::CommandBufferState;
|
||||
use crate::pipeline::vertex::VertexInputRate;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputRate;
|
||||
use crate::pipeline::GraphicsPipeline;
|
||||
use crate::DeviceSize;
|
||||
use std::convert::TryInto;
|
||||
@ -21,11 +21,11 @@ pub(in super::super) fn check_vertex_buffers(
|
||||
vertices: Option<(u32, u32)>,
|
||||
instances: Option<(u32, u32)>,
|
||||
) -> Result<(), CheckVertexBufferError> {
|
||||
let vertex_input = pipeline.vertex_input();
|
||||
let vertex_input = pipeline.vertex_input_state();
|
||||
let mut max_vertex_count: Option<u32> = None;
|
||||
let mut max_instance_count: Option<u32> = None;
|
||||
|
||||
for (binding_num, binding_desc) in vertex_input.bindings() {
|
||||
for (&binding_num, binding_desc) in &vertex_input.bindings {
|
||||
let vertex_buffer = match current_state.vertex_buffer(binding_num) {
|
||||
Some(x) => x,
|
||||
None => return Err(CheckVertexBufferError::BufferNotBound { binding_num }),
|
||||
|
@ -247,8 +247,8 @@ impl Drop for PipelineCache {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::pipeline::cache::PipelineCache;
|
||||
use crate::shader::ShaderModule;
|
||||
use crate::pipeline::ComputePipeline;
|
||||
use crate::shader::ShaderModule;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
|
@ -7,6 +7,21 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! A pipeline that performs general-purpose operations.
|
||||
//!
|
||||
//! A compute pipeline takes buffers and/or images as both inputs and outputs. It operates
|
||||
//! "standalone", with no additional infrastructure such as render passes or vertex input. Compute
|
||||
//! pipelines can be used by themselves for performing work on the Vulkan device, but they can also
|
||||
//! assist graphics operations by precalculating or postprocessing the operations from another kind
|
||||
//! of pipeline. While it theoretically possible to perform graphics operations entirely in a
|
||||
//! compute pipeline, a graphics pipeline is better suited to that task.
|
||||
//!
|
||||
//! A compute pipeline is relatively simple to create, requiring only a pipeline layout and a single
|
||||
//! shader, the *compute shader*. The compute shader is the actual program that performs the work.
|
||||
//! Once created, you can execute a compute pipeline by *binding* it in a command buffer, binding
|
||||
//! any descriptor sets and/or push constants that the pipeline needs, and then issuing a `dispatch`
|
||||
//! command on the command buffer.
|
||||
|
||||
use crate::check_errors;
|
||||
use crate::descriptor_set::layout::{DescriptorSetDesc, DescriptorSetLayout};
|
||||
use crate::device::{Device, DeviceOwned};
|
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
// 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.
|
||||
//! Configures how the color output of the fragment shader is written to the attachment.
|
||||
//!
|
||||
//! # Blending in details
|
||||
//!
|
374
vulkano/src/pipeline/graphics/creation_error.rs
Normal file
374
vulkano/src/pipeline/graphics/creation_error.rs
Normal file
@ -0,0 +1,374 @@
|
||||
// Copyright (c) 2017 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.
|
||||
|
||||
use crate::format::Format;
|
||||
use crate::format::NumericType;
|
||||
use crate::pipeline::graphics::vertex_input::IncompatibleVertexDefinitionError;
|
||||
use crate::pipeline::layout::PipelineLayoutCreationError;
|
||||
use crate::pipeline::layout::PipelineLayoutSupersetError;
|
||||
use crate::shader::ShaderInterfaceMismatchError;
|
||||
use crate::Error;
|
||||
use crate::OomError;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::u32;
|
||||
|
||||
/// Error that can happen when creating a graphics pipeline.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum GraphicsPipelineCreationError {
|
||||
/// A device extension that was required for a particular setting on the graphics pipeline was not enabled.
|
||||
ExtensionNotEnabled {
|
||||
extension: &'static str,
|
||||
reason: &'static str,
|
||||
},
|
||||
|
||||
/// 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),
|
||||
|
||||
/// The provided specialization constants are not compatible with what the shader expects.
|
||||
IncompatibleSpecializationConstants,
|
||||
|
||||
/// The vertex definition is not compatible with the input of the vertex shader.
|
||||
IncompatibleVertexDefinition(IncompatibleVertexDefinitionError),
|
||||
|
||||
/// 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.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// The maximum number of vertex attributes has been exceeded.
|
||||
MaxVertexInputAttributesExceeded {
|
||||
/// Maximum allowed value.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: usize,
|
||||
},
|
||||
|
||||
/// 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 sources has been exceeded.
|
||||
MaxVertexInputBindingsExceeded {
|
||||
/// Maximum allowed value.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// 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.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// The maximum number of viewports has been exceeded.
|
||||
MaxViewportsExceeded {
|
||||
/// Maximum allowed value.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// The maximum dimensions of viewports has been exceeded.
|
||||
MaxViewportDimensionsExceeded,
|
||||
|
||||
/// The number of attachments specified in the blending does not match the number of
|
||||
/// attachments in the subpass.
|
||||
MismatchBlendingAttachmentsCount,
|
||||
|
||||
/// 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.
|
||||
NoDepthAttachment,
|
||||
|
||||
/// The stencil test requires a stencil attachment but render pass has no stencil attachment, or
|
||||
/// stencil writing is enabled and the stencil attachment is read-only.
|
||||
NoStencilAttachment,
|
||||
|
||||
/// Not enough memory.
|
||||
OomError(OomError),
|
||||
|
||||
/// 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 type of the shader input variable at the given location is not compatible with the
|
||||
/// format of the corresponding vertex input attribute.
|
||||
VertexInputAttributeIncompatibleFormat {
|
||||
location: u32,
|
||||
shader_type: NumericType,
|
||||
attribute_type: NumericType,
|
||||
},
|
||||
|
||||
/// The binding number specified by a vertex input attribute does not exist in the provided list
|
||||
/// of binding descriptions.
|
||||
VertexInputAttributeInvalidBinding { location: u32, binding: u32 },
|
||||
|
||||
/// The vertex shader expects an input variable at the given location, but no vertex input
|
||||
/// attribute exists for that location.
|
||||
VertexInputAttributeMissing { location: u32 },
|
||||
|
||||
/// The format specified by a vertex input attribute is not supported for vertex buffers.
|
||||
VertexInputAttributeUnsupportedFormat { location: u32, format: Format },
|
||||
|
||||
/// 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 requested stencil test is invalid.
|
||||
WrongStencilState,
|
||||
}
|
||||
|
||||
impl error::Error for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match *self {
|
||||
Self::OomError(ref err) => Some(err),
|
||||
Self::PipelineLayoutCreationError(ref err) => Some(err),
|
||||
Self::IncompatiblePipelineLayout(ref err) => Some(err),
|
||||
Self::ShaderStagesMismatch(ref err) => Some(err),
|
||||
Self::IncompatibleVertexDefinition(ref err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GraphicsPipelineCreationError {
|
||||
// TODO: finish
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Self::ExtensionNotEnabled { extension, reason } => write!(
|
||||
fmt,
|
||||
"the extension {} must be enabled: {}",
|
||||
extension, reason
|
||||
),
|
||||
Self::FeatureNotEnabled { feature, reason } => write!(
|
||||
fmt,
|
||||
"the feature {} must be enabled: {}",
|
||||
feature, reason
|
||||
),
|
||||
Self::FragmentShaderRenderPassIncompatible => write!(
|
||||
fmt,
|
||||
"the output of the fragment shader is not compatible with what the render pass subpass expects",
|
||||
),
|
||||
Self::IncompatiblePipelineLayout(_) => write!(
|
||||
fmt,
|
||||
"the pipeline layout is not compatible with what the shaders expect",
|
||||
),
|
||||
Self::IncompatibleSpecializationConstants => write!(
|
||||
fmt,
|
||||
"the provided specialization constants are not compatible with what the shader expects",
|
||||
),
|
||||
Self::IncompatibleVertexDefinition(_) => write!(
|
||||
fmt,
|
||||
"the vertex definition is not compatible with the input of the vertex shader",
|
||||
),
|
||||
Self::InvalidPrimitiveTopology => write!(
|
||||
fmt,
|
||||
"trying to use a patch list without a tessellation shader, or a non-patch-list with a tessellation shader",
|
||||
),
|
||||
Self::InvalidNumPatchControlPoints => write!(
|
||||
fmt,
|
||||
"patch_control_points was not greater than 0 and less than or equal to the max_tessellation_patch_size limit",
|
||||
),
|
||||
Self::MaxDiscardRectanglesExceeded { .. } => write!(
|
||||
fmt,
|
||||
"the maximum number of discard rectangles has been exceeded",
|
||||
),
|
||||
Self::MaxVertexAttribDivisorExceeded { .. } => write!(
|
||||
fmt,
|
||||
"the maximum value for the instance rate divisor has been exceeded",
|
||||
),
|
||||
Self::MaxVertexInputAttributesExceeded { .. } => write!(
|
||||
fmt,
|
||||
"the maximum number of vertex attributes has been exceeded",
|
||||
),
|
||||
Self::MaxVertexInputAttributeOffsetExceeded { .. } => write!(
|
||||
fmt,
|
||||
"the maximum offset for a vertex attribute has been exceeded",
|
||||
),
|
||||
Self::MaxVertexInputBindingsExceeded { .. } => write!(
|
||||
fmt,
|
||||
"the maximum number of vertex sources has been exceeded",
|
||||
),
|
||||
Self::MaxVertexInputBindingStrideExceeded { .. } => write!(
|
||||
fmt,
|
||||
"the maximum stride value for vertex input (ie. the distance between two vertex elements) has been exceeded",
|
||||
),
|
||||
Self::MaxViewportsExceeded { .. } => write!(
|
||||
fmt,
|
||||
"the maximum number of viewports has been exceeded",
|
||||
),
|
||||
Self::MaxViewportDimensionsExceeded => write!(
|
||||
fmt,
|
||||
"the maximum dimensions of viewports has been exceeded",
|
||||
),
|
||||
Self::MismatchBlendingAttachmentsCount => write!(
|
||||
fmt,
|
||||
"the number of attachments specified in the blending does not match the number of attachments in the subpass",
|
||||
),
|
||||
Self::NoDepthAttachment => write!(
|
||||
fmt,
|
||||
"the depth attachment of the render pass does not match the depth test",
|
||||
),
|
||||
Self::NoStencilAttachment => write!(
|
||||
fmt,
|
||||
"the stencil attachment of the render pass does not match the stencil test",
|
||||
),
|
||||
Self::OomError(_) => write!(
|
||||
fmt,
|
||||
"not enough memory available",
|
||||
),
|
||||
Self::PipelineLayoutCreationError(_) => write!(
|
||||
fmt,
|
||||
"error while creating the pipeline layout object",
|
||||
),
|
||||
Self::ShaderStagesMismatch(_) => write!(
|
||||
fmt,
|
||||
"the output interface of one shader and the input interface of the next shader do not match",
|
||||
),
|
||||
Self::StrictLinesNotSupported => write!(
|
||||
fmt,
|
||||
"the strict_lines device property was false",
|
||||
),
|
||||
Self::TopologyNotMatchingGeometryShader => write!(
|
||||
fmt,
|
||||
"the primitives topology does not match what the geometry shader expects",
|
||||
),
|
||||
Self::VertexInputAttributeIncompatibleFormat {
|
||||
location,
|
||||
shader_type,
|
||||
attribute_type,
|
||||
} => write!(
|
||||
fmt,
|
||||
"the type of the shader input variable at location {} ({:?}) is not compatible with the format of the corresponding vertex input attribute ({:?})",
|
||||
location, shader_type, attribute_type,
|
||||
),
|
||||
Self::VertexInputAttributeInvalidBinding { location, binding } => write!(
|
||||
fmt,
|
||||
"the binding number {} specified by vertex input attribute location {} does not exist in the provided list of binding descriptions",
|
||||
binding, location,
|
||||
),
|
||||
Self::VertexInputAttributeMissing { location } => write!(
|
||||
fmt,
|
||||
"the vertex shader expects an input variable at location {}, but no vertex input attribute exists for that location",
|
||||
location,
|
||||
),
|
||||
Self::VertexInputAttributeUnsupportedFormat { location, format } => write!(
|
||||
fmt,
|
||||
"the format {:?} specified by vertex input attribute location {} is not supported for vertex buffers",
|
||||
format, location,
|
||||
),
|
||||
Self::ViewportBoundsExceeded => write!(
|
||||
fmt,
|
||||
"the minimum or maximum bounds of viewports have been exceeded",
|
||||
),
|
||||
Self::WrongShaderType => write!(
|
||||
fmt,
|
||||
"the wrong type of shader has been passed",
|
||||
),
|
||||
Self::WrongStencilState => write!(
|
||||
fmt,
|
||||
"the requested stencil test is invalid",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OomError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: OomError) -> GraphicsPipelineCreationError {
|
||||
Self::OomError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PipelineLayoutCreationError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: PipelineLayoutCreationError) -> GraphicsPipelineCreationError {
|
||||
Self::PipelineLayoutCreationError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PipelineLayoutSupersetError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: PipelineLayoutSupersetError) -> GraphicsPipelineCreationError {
|
||||
Self::IncompatiblePipelineLayout(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IncompatibleVertexDefinitionError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: IncompatibleVertexDefinitionError) -> GraphicsPipelineCreationError {
|
||||
Self::IncompatibleVertexDefinition(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: Error) -> GraphicsPipelineCreationError {
|
||||
match err {
|
||||
err @ Error::OutOfHostMemory => Self::OomError(OomError::from(err)),
|
||||
err @ Error::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
|
||||
_ => panic!("unexpected error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
@ -7,10 +7,7 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! Depth and stencil operations description.
|
||||
//!
|
||||
//! After the fragment shader has finished running, each fragment goes through the depth, depth
|
||||
//! bounds and stencil tests.
|
||||
//! Configures the operation of the depth, stencil and depth bounds 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
|
||||
@ -24,7 +21,8 @@
|
||||
//! depth and stencil tests, the value of the stencil buffer at that location can be updated.
|
||||
|
||||
use crate::device::Device;
|
||||
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError, StateMode};
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::{DynamicState, StateMode};
|
||||
use crate::render_pass::Subpass;
|
||||
use fnv::FnvHashMap;
|
||||
use std::ops::RangeInclusive;
|
@ -12,8 +12,9 @@
|
||||
//! 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 crate::pipeline::graphics::viewport::Scissor;
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::{DynamicState, PartialStateMode};
|
||||
use fnv::FnvHashMap;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
@ -126,6 +127,7 @@ impl Default for DiscardRectangleState {
|
||||
}
|
||||
}
|
||||
|
||||
/// The mode in which the discard rectangle test operates.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i32)]
|
||||
pub enum DiscardRectangleMode {
|
@ -7,12 +7,11 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! Assembling vertices into primitives.
|
||||
//!
|
||||
//! The input assembly is the stage where lists of vertices are turned into primitives.
|
||||
//! Configures how input vertices are assembled into primitives.
|
||||
|
||||
use crate::device::Device;
|
||||
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError, PartialStateMode, StateMode};
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::{DynamicState, PartialStateMode, StateMode};
|
||||
use crate::DeviceSize;
|
||||
use fnv::FnvHashMap;
|
||||
|
@ -7,19 +7,68 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! A pipeline that performs graphics processing operations.
|
||||
//!
|
||||
//! Unlike a compute pipeline, which performs general-purpose work, a graphics pipeline is geared
|
||||
//! specifically towards doing graphical processing. To that end, it consists of several shaders,
|
||||
//! with additional state and glue logic in between.
|
||||
//!
|
||||
//! A graphics pipeline performs many separate steps, that execute more or less in sequence.
|
||||
//! Due to the parallel nature of a GPU, no strict ordering guarantees may exist.
|
||||
//!
|
||||
//! 1. Vertex input and assembly: vertex input data is read from data buffers and then assembled
|
||||
//! into primitives (points, lines, triangles etc.).
|
||||
//! 2. Vertex shader invocations: the vertex data of each primitive is fed as input to the vertex
|
||||
//! shader, which performs transformations on the data and generates new data as output.
|
||||
//! 3. (Optional) Tessellation: primitives are subdivided by the operations of two shaders, the
|
||||
//! tessellation control and tessellation evaluation shaders. The control shader produces the
|
||||
//! tessellation level to apply for the primitive, while the evaluation shader postprocesses the
|
||||
//! newly created vertices.
|
||||
//! 4. (Optional) Geometry shading: whole primitives are fed as input and processed into a new set
|
||||
//! of output primitives.
|
||||
//! 5. Vertex post-processing, including:
|
||||
//! - Clipping primitives to the view frustum and user-defined clipping planes.
|
||||
//! - Perspective division.
|
||||
//! - Viewport mapping.
|
||||
//! 6. Rasterization: converting primitives into a two-dimensional representation. Primitives may be
|
||||
//! discarded depending on their orientation, and are then converted into a collection of
|
||||
//! fragments that are processed further.
|
||||
//! 7. Fragment operations. These include invocations of the fragment shader, which generates the
|
||||
//! values to be written to the color attachment. Various testing and discarding operations can
|
||||
//! be performed both before and after the fragment shader ("early" and "late" fragment tests),
|
||||
//! including:
|
||||
//! - Discard rectangle test
|
||||
//! - Scissor test
|
||||
//! - Sample mask test
|
||||
//! - Depth bounds test
|
||||
//! - Stencil test
|
||||
//! - Depth test
|
||||
//! 8. Color attachment output: the final pixel data is written to a framebuffer. Blending and
|
||||
//! logical operations can be applied to combine incoming pixel data with data already present
|
||||
//! in the framebuffer.
|
||||
//!
|
||||
//! A graphics pipeline contains many configuration options, which are grouped into collections of
|
||||
//! "state". Often, these directly correspond to one or more steps in the graphics pipeline. Each
|
||||
//! state collection has a dedicated submodule.
|
||||
//!
|
||||
//! Once a graphics pipeline has been created, you can execute it by first *binding* it in a command
|
||||
//! buffer, binding the necessary vertex buffers, binding any descriptor sets, setting push
|
||||
//! constants, and setting any dynamic state that the pipeline may need. Then you issue a `draw`
|
||||
//! command.
|
||||
|
||||
pub use self::builder::GraphicsPipelineBuilder;
|
||||
pub use self::creation_error::GraphicsPipelineCreationError;
|
||||
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::graphics::color_blend::ColorBlendState;
|
||||
use crate::pipeline::graphics::depth_stencil::DepthStencilState;
|
||||
use crate::pipeline::graphics::discard_rectangle::DiscardRectangleState;
|
||||
use crate::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||
use crate::pipeline::graphics::multisample::MultisampleState;
|
||||
use crate::pipeline::graphics::rasterization::RasterizationState;
|
||||
use crate::pipeline::graphics::tessellation::TessellationState;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputState;
|
||||
use crate::pipeline::graphics::viewport::ViewportState;
|
||||
use crate::pipeline::layout::PipelineLayout;
|
||||
use crate::pipeline::multisample::MultisampleState;
|
||||
use crate::pipeline::rasterization::RasterizationState;
|
||||
use crate::pipeline::tessellation::TessellationState;
|
||||
use crate::pipeline::vertex::{BuffersDefinition, VertexInput};
|
||||
use crate::pipeline::viewport::ViewportState;
|
||||
use crate::pipeline::{DynamicState, Pipeline, PipelineBindPoint};
|
||||
use crate::render_pass::Subpass;
|
||||
use crate::shader::{DescriptorRequirements, ShaderStage};
|
||||
@ -32,7 +81,16 @@ use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod builder;
|
||||
pub mod color_blend;
|
||||
mod creation_error;
|
||||
pub mod depth_stencil;
|
||||
pub mod discard_rectangle;
|
||||
pub mod input_assembly;
|
||||
pub mod multisample;
|
||||
pub mod rasterization;
|
||||
pub mod tessellation;
|
||||
pub mod vertex_input;
|
||||
pub mod viewport;
|
||||
// FIXME: restore
|
||||
//mod tests;
|
||||
|
||||
@ -50,7 +108,7 @@ pub struct GraphicsPipeline {
|
||||
descriptor_requirements: FnvHashMap<(u32, u32), DescriptorRequirements>,
|
||||
num_used_descriptor_sets: u32,
|
||||
|
||||
vertex_input: VertexInput,
|
||||
vertex_input_state: VertexInputState,
|
||||
input_assembly_state: InputAssemblyState,
|
||||
tessellation_state: Option<TessellationState>,
|
||||
viewport_state: Option<ViewportState>,
|
||||
@ -71,7 +129,7 @@ impl GraphicsPipeline {
|
||||
'static,
|
||||
'static,
|
||||
'static,
|
||||
BuffersDefinition,
|
||||
VertexInputState,
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
@ -116,8 +174,8 @@ impl GraphicsPipeline {
|
||||
|
||||
/// Returns the vertex input state used to create this pipeline.
|
||||
#[inline]
|
||||
pub fn vertex_input(&self) -> &VertexInput {
|
||||
&self.vertex_input
|
||||
pub fn vertex_input_state(&self) -> &VertexInputState {
|
||||
&self.vertex_input_state
|
||||
}
|
||||
|
||||
/// Returns the input assembly state used to create this pipeline.
|
@ -7,14 +7,13 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! State of multisampling.
|
||||
//!
|
||||
//! Multisampling allows you to ask the GPU to run the rasterizer to generate more than one
|
||||
//! sample per pixel.
|
||||
//! Generates multiple fragments per framebuffer pixel when rasterizing. This can be used for
|
||||
//! anti-aliasing.
|
||||
|
||||
use crate::device::Device;
|
||||
use crate::image::SampleCount;
|
||||
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError};
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::DynamicState;
|
||||
use crate::render_pass::Subpass;
|
||||
use fnv::FnvHashMap;
|
||||
use std::ptr;
|
@ -7,15 +7,11 @@
|
||||
// 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.
|
||||
//! Configures how primitives should be converted into collections of fragments.
|
||||
|
||||
use crate::{
|
||||
device::Device,
|
||||
pipeline::{DynamicState, GraphicsPipelineCreationError, StateMode},
|
||||
};
|
||||
use crate::device::Device;
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::{DynamicState, StateMode};
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
/// The state in a graphics pipeline describing how the rasterization stage should behave.
|
@ -7,15 +7,14 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! Subdividing primitives.
|
||||
//!
|
||||
//! A tessellation shader divides primitives into smaller primitives.
|
||||
//! Subdivides primitives into smaller primitives.
|
||||
|
||||
use crate::device::Device;
|
||||
use crate::pipeline::input_assembly::{
|
||||
use crate::pipeline::graphics::input_assembly::{
|
||||
InputAssemblyState, PrimitiveTopology, PrimitiveTopologyClass,
|
||||
};
|
||||
use crate::pipeline::{DynamicState, GraphicsPipelineCreationError, PartialStateMode, StateMode};
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::{DynamicState, PartialStateMode, StateMode};
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
/// The state in a graphics pipeline describing the tessellation shader execution of a graphics
|
@ -8,13 +8,13 @@
|
||||
// according to those terms.
|
||||
|
||||
use super::VertexMemberInfo;
|
||||
use crate::pipeline::vertex::IncompatibleVertexDefinitionError;
|
||||
use crate::pipeline::vertex::Vertex;
|
||||
use crate::pipeline::vertex::VertexDefinition;
|
||||
use crate::pipeline::vertex::VertexInput;
|
||||
use crate::pipeline::vertex::VertexInputAttribute;
|
||||
use crate::pipeline::vertex::VertexInputBinding;
|
||||
use crate::pipeline::vertex::VertexInputRate;
|
||||
use crate::pipeline::graphics::vertex_input::IncompatibleVertexDefinitionError;
|
||||
use crate::pipeline::graphics::vertex_input::Vertex;
|
||||
use crate::pipeline::graphics::vertex_input::VertexDefinition;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputAttributeDescription;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputBindingDescription;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputRate;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputState;
|
||||
use crate::shader::ShaderInterface;
|
||||
use crate::DeviceSize;
|
||||
use std::mem;
|
||||
@ -39,7 +39,7 @@ impl std::fmt::Debug for VertexBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VertexBuffer> for VertexInputBinding {
|
||||
impl From<VertexBuffer> for VertexInputBindingDescription {
|
||||
#[inline]
|
||||
fn from(val: VertexBuffer) -> Self {
|
||||
Self {
|
||||
@ -100,13 +100,13 @@ unsafe impl VertexDefinition for BuffersDefinition {
|
||||
fn definition(
|
||||
&self,
|
||||
interface: &ShaderInterface,
|
||||
) -> Result<VertexInput, IncompatibleVertexDefinitionError> {
|
||||
) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
|
||||
let bindings = self
|
||||
.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(binding, &buffer)| (binding as u32, buffer.into()));
|
||||
let mut attributes: Vec<(u32, VertexInputAttribute)> = Vec::new();
|
||||
let mut attributes: Vec<(u32, VertexInputAttributeDescription)> = Vec::new();
|
||||
|
||||
for element in interface.elements() {
|
||||
let name = element.name.as_ref().unwrap();
|
||||
@ -126,34 +126,38 @@ unsafe impl VertexDefinition for BuffersDefinition {
|
||||
|
||||
if !infos.ty.matches(
|
||||
infos.array_size,
|
||||
element.format,
|
||||
element.location.end - element.location.start,
|
||||
element.ty.to_format(),
|
||||
element.ty.num_locations(),
|
||||
) {
|
||||
// TODO: move this check to GraphicsPipelineBuilder
|
||||
return Err(IncompatibleVertexDefinitionError::FormatMismatch {
|
||||
attribute: name.clone().into_owned(),
|
||||
shader: (
|
||||
element.format,
|
||||
(element.location.end - element.location.start) as usize,
|
||||
element.ty.to_format(),
|
||||
(element.ty.num_locations()) as usize,
|
||||
),
|
||||
definition: (infos.ty, infos.array_size),
|
||||
});
|
||||
}
|
||||
|
||||
let mut offset = infos.offset as DeviceSize;
|
||||
for location in element.location.clone() {
|
||||
let location_range = element.location..element.location + element.ty.num_locations();
|
||||
|
||||
for location in location_range {
|
||||
attributes.push((
|
||||
location,
|
||||
VertexInputAttribute {
|
||||
VertexInputAttributeDescription {
|
||||
binding,
|
||||
format: element.format,
|
||||
format: element.ty.to_format(),
|
||||
offset: offset as u32,
|
||||
},
|
||||
));
|
||||
offset += element.format.size().unwrap();
|
||||
offset += element.ty.to_format().size().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(VertexInput::new(bindings, attributes))
|
||||
Ok(VertexInputState::new()
|
||||
.bindings(bindings)
|
||||
.attributes(attributes))
|
||||
}
|
||||
}
|
114
vulkano/src/pipeline/graphics/vertex_input/definition.rs
Normal file
114
vulkano/src/pipeline/graphics/vertex_input/definition.rs
Normal file
@ -0,0 +1,114 @@
|
||||
// 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.
|
||||
|
||||
//! Definition for creating a [`VertexInputState`] based on a [`ShaderInterface`].
|
||||
//!
|
||||
//! # Implementing `Vertex`
|
||||
//!
|
||||
//! The implementations of the `VertexDefinition` trait that are provided by vulkano require you to
|
||||
//! use a buffer whose content is `[V]` where `V` implements the `Vertex` trait.
|
||||
//!
|
||||
//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!`
|
||||
//! macro.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```ignore // TODO:
|
||||
//! # #[macro_use] extern crate vulkano
|
||||
//! # fn main() {
|
||||
//! # use std::sync::Arc;
|
||||
//! # use vulkano::device::Device;
|
||||
//! # use vulkano::device::Queue;
|
||||
//! use vulkano::buffer::BufferAccess;
|
||||
//! use vulkano::buffer::BufferUsage;
|
||||
//! use vulkano::memory::HostVisible;
|
||||
//! use vulkano::pipeline::vertex::;
|
||||
//! # let device: Arc<Device> = return;
|
||||
//! # let queue: Arc<Queue> = return;
|
||||
//!
|
||||
//! struct Vertex {
|
||||
//! position: [f32; 2]
|
||||
//! }
|
||||
//!
|
||||
//! impl_vertex!(Vertex, position);
|
||||
//!
|
||||
//! let usage = BufferUsage {
|
||||
//! vertex_buffer: true,
|
||||
//! .. BufferUsage::none()
|
||||
//! };
|
||||
//!
|
||||
//! let vertex_buffer = BufferAccess::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue)
|
||||
//! .expect("failed to create buffer");
|
||||
//!
|
||||
//! // TODO: finish example
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use crate::format::Format;
|
||||
use crate::pipeline::graphics::vertex_input::VertexInputState;
|
||||
use crate::pipeline::graphics::vertex_input::VertexMemberTy;
|
||||
use crate::shader::ShaderInterface;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
/// Trait for types that can create a [`VertexInputState`] from a [`ShaderInterface`].
|
||||
pub unsafe trait VertexDefinition {
|
||||
/// Builds the vertex definition to use to link this definition to a vertex shader's input
|
||||
/// interface.
|
||||
// TODO: remove error return, move checks to GraphicsPipelineBuilder
|
||||
fn definition(
|
||||
&self,
|
||||
interface: &ShaderInterface,
|
||||
) -> Result<VertexInputState, IncompatibleVertexDefinitionError>;
|
||||
}
|
||||
|
||||
unsafe impl VertexDefinition for VertexInputState {
|
||||
fn definition(
|
||||
&self,
|
||||
interface: &ShaderInterface,
|
||||
) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when the vertex definition doesn't match the input of the vertex shader.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum IncompatibleVertexDefinitionError {
|
||||
/// An attribute of the vertex shader is missing in the vertex source.
|
||||
MissingAttribute {
|
||||
/// Name of the missing attribute.
|
||||
attribute: String,
|
||||
},
|
||||
|
||||
/// The format of an attribute does not match.
|
||||
FormatMismatch {
|
||||
/// Name of the attribute.
|
||||
attribute: String,
|
||||
/// The format in the vertex shader.
|
||||
shader: (Format, usize),
|
||||
/// The format in the vertex definition.
|
||||
definition: (VertexMemberTy, usize),
|
||||
},
|
||||
}
|
||||
|
||||
impl error::Error for IncompatibleVertexDefinitionError {}
|
||||
|
||||
impl fmt::Display for IncompatibleVertexDefinitionError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
IncompatibleVertexDefinitionError::MissingAttribute { .. } => {
|
||||
write!(fmt, "an attribute is missing",)
|
||||
}
|
||||
IncompatibleVertexDefinitionError::FormatMismatch { .. } => {
|
||||
write!(fmt, "the format of an attribute does not match")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use crate::pipeline::vertex::VertexMemberTy;
|
||||
use crate::pipeline::graphics::vertex_input::VertexMemberTy;
|
||||
|
||||
/// Implements the `Vertex` trait on a struct.
|
||||
///# Example
|
||||
@ -26,15 +26,15 @@ use crate::pipeline::vertex::VertexMemberTy;
|
||||
macro_rules! impl_vertex {
|
||||
($out:ty $(, $member:ident)*) => (
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl $crate::pipeline::vertex::Vertex for $out {
|
||||
unsafe impl $crate::pipeline::graphics::vertex_input::Vertex for $out {
|
||||
#[inline(always)]
|
||||
fn member(name: &str) -> Option<$crate::pipeline::vertex::VertexMemberInfo> {
|
||||
fn member(name: &str) -> Option<$crate::pipeline::graphics::vertex_input::VertexMemberInfo> {
|
||||
use std::ptr;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::format::Format;
|
||||
use $crate::pipeline::vertex::VertexMemberInfo;
|
||||
use $crate::pipeline::vertex::VertexMemberTy;
|
||||
use $crate::pipeline::vertex::VertexMember;
|
||||
use $crate::pipeline::graphics::vertex_input::VertexMemberInfo;
|
||||
use $crate::pipeline::graphics::vertex_input::VertexMemberTy;
|
||||
use $crate::pipeline::graphics::vertex_input::VertexMember;
|
||||
|
||||
$(
|
||||
if name == stringify!($member) {
|
488
vulkano/src/pipeline/graphics/vertex_input/mod.rs
Normal file
488
vulkano/src/pipeline/graphics/vertex_input/mod.rs
Normal file
@ -0,0 +1,488 @@
|
||||
// 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.
|
||||
|
||||
//! Configures how data from vertex buffers is read into vertex shader input locations.
|
||||
//!
|
||||
//! The vertex input stage is the stage where data is read from a buffer and fed into the vertex
|
||||
//! shader. After each invocation of the vertex shader, the pipeline then proceeds to the input
|
||||
//! assembly stage.
|
||||
//!
|
||||
//! # Input locations and components
|
||||
//!
|
||||
//! Input data is assigned per shader input location. Locations are set by adding the `location`
|
||||
//! layout qualifier to an input variable in GLSL. A single location contains four data elements,
|
||||
//! named "components", which are each 32 bits in size. These correspond to the `x`, `y`, `z` and
|
||||
//! `w` (or equivalently `r`, `g`, `b`, `a`) components of a `vec4` inside the shader.
|
||||
//! A component can contain at most one value, and data types that are smaller than 32 bits will
|
||||
//! still take up a whole component, so a single `i8vec4` variable will still take up all four
|
||||
//! components in a location, even if not all bits are actually used.
|
||||
//!
|
||||
//! A variable may take up fewer than four components. For example, a single `float` takes up only
|
||||
//! one component, a `vec2` takes up two, and so on. Using the `component` layout qualifier in GLSL,
|
||||
//! it is possible to fit multiple variables into a single four-component location slot, as long
|
||||
//! as the components of each variable don't overlap.
|
||||
//!
|
||||
//! If the input variable is an array, then it takes up a series of consecutive locations. Each
|
||||
//! element of the array always starts at a new location, regardless of whether there is still room
|
||||
//! in the previous one. So, for example, an array of three `vec2` takes three locations, since
|
||||
//! `vec2` alone needs one location. An array can be decorated with the `component` qualifier as
|
||||
//! well; this is equivalent to applying the qualifier to every element of the array. If elements do
|
||||
//! not use all components in their locations, those free components can be filled with additional
|
||||
//! variables, just like for non-array types.
|
||||
//!
|
||||
//! Matrices are laid out as if they were an array of column vectors. Thus, a `mat4x3` is laid out
|
||||
//! as an array of four `vec3`s, `mat2x4` as two `vec4`s. As with individual vectors, each column of
|
||||
//! the matrix uses up as many components of its location as there are rows in the matrix, and the
|
||||
//! remaining components are available for additional variables as described above. However, it is
|
||||
//! not possible to use the `component` qualifier on a matrix.
|
||||
//!
|
||||
//! If a 64-bit value is to be passed to a shader, it will take up two adjacent components. Vectors
|
||||
//! of 64-bit values are correspondingly twice as large: `dvec2` takes up all four components of a
|
||||
//! location, `dvec4` takes two full locations, while `dvec3` takes one full location and the first
|
||||
//! two components of the next. An array or matrix of a 64-bit type is made up of multiple adjacent
|
||||
//! 64-bit elements, just like for smaller types: each new element starts at a fresh location.
|
||||
//!
|
||||
//! # Input attributes
|
||||
//!
|
||||
//! An input attribute is a mapping between data in a vertex buffer and the locations and components
|
||||
//! of the vertex shader.
|
||||
//!
|
||||
//! Input attributes are assigned on a per-location basis; it is not possible to assign attributes
|
||||
//! to individual components. Instead, each attribute specifies up to four values to be read from
|
||||
//! the vertex buffer at once, which are then mapped to the four components of the given location.
|
||||
//! Like the texels in an image, each attribute's data format in a vertex buffer is described by a
|
||||
//! [`Format`]. The input data doesn't have to be an actual color, the format simply describes the
|
||||
//! type, size and layout of the data for the four input components. For example,
|
||||
//! `Format::R32G32B32A32_SFLOAT` will read four `f32` values from the vertex buffer and assigns
|
||||
//! them to the four components of the attribute's location.
|
||||
//!
|
||||
//! It is possible to specify a `Format` that contains less than four components. In this case, the
|
||||
//! missing components are given default values: the first three components default to 0, while the
|
||||
//! fourth defaults to 1. This means that you can, for example, store only the `x`, `y` and `z`
|
||||
//! components of a vertex position in a vertex buffer, and have the vertex input state
|
||||
//! automatically set the `w` value to 1 for you. An exception to this are 64-bit values: these do
|
||||
//! *not* receive default values, meaning that components that are missing from the format are
|
||||
//! assigned no value and must not be used in the shader at all.
|
||||
//!
|
||||
//! When matching attribute formats to shader input types, the following rules apply:
|
||||
//! - Signed integers in the shader must have an attribute format with a `SINT` type.
|
||||
//! - Unsigned integers in the shader must have an attribute format with a `UINT` type.
|
||||
//! - Floating point values in the shader must have an attribute format with a type other than
|
||||
//! `SINT` or `UINT`. This includes `SFLOAT`, `UFLOAT` and `SRGB`, but also `SNORM`, `UNORM`,
|
||||
//! `SSCALED` and `USCALED`.
|
||||
//! - 64-bit values in the shader must have a 64-bit attribute format.
|
||||
//! - 32-bit and smaller values in the shader must have a 32-bit or smaller attribute format, but
|
||||
//! the exact number of bits doesn't matter. For example, `Format::R8G8B8A8_UNORM` can be used
|
||||
//! with a `vec4` in the shader.
|
||||
//!
|
||||
//! # Input bindings
|
||||
//!
|
||||
//! An input binding is a definition of a Vulkan buffer that contains the actual data from which
|
||||
//! each input attribute is to be read. The buffer itself is referred to as a "vertex buffer", and
|
||||
//! is set during drawing with the
|
||||
//! [`bind_vertex_buffers`](crate::command_buffer::AutoCommandBufferBuilder::bind_vertex_buffers)
|
||||
//! command.
|
||||
//!
|
||||
//! The data in a vertex buffer is typically arranged into an array, where each array element
|
||||
//! contains the data for a single vertex shader invocation. When deciding which element read from
|
||||
//! the vertex buffer for a given vertex and instance number, each binding has an "input rate".
|
||||
//! If the input rate is `Vertex`, then the vertex input state advances to the next element of that
|
||||
//! buffer each time a new vertex number is processed. Likewise, if the input rate is `Instance`,
|
||||
//! it advances to the next element for each new instance number. Different bindings can have
|
||||
//! different input rates, and it's also possible to have multiple bindings with the same input
|
||||
//! rate.
|
||||
|
||||
pub use self::buffers::BuffersDefinition;
|
||||
pub use self::collection::VertexBuffersCollection;
|
||||
pub use self::definition::IncompatibleVertexDefinitionError;
|
||||
pub use self::definition::VertexDefinition;
|
||||
pub use self::impl_vertex::VertexMember;
|
||||
pub use self::vertex::Vertex;
|
||||
pub use self::vertex::VertexMemberInfo;
|
||||
pub use self::vertex::VertexMemberTy;
|
||||
use crate::device::Device;
|
||||
use crate::format::Format;
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::DynamicState;
|
||||
use fnv::FnvHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use std::ptr;
|
||||
|
||||
mod buffers;
|
||||
mod collection;
|
||||
mod definition;
|
||||
mod impl_vertex;
|
||||
mod vertex;
|
||||
|
||||
/// The state in a graphics pipeline describing how the vertex input stage should behave.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct VertexInputState {
|
||||
/// A description of the vertex buffers that the vertex input stage will read from.
|
||||
pub bindings: FnvHashMap<u32, VertexInputBindingDescription>,
|
||||
|
||||
/// Describes, for each shader input location, the mapping between elements in a vertex buffer
|
||||
/// and the components of that location in the shader.
|
||||
pub attributes: FnvHashMap<u32, VertexInputAttributeDescription>,
|
||||
}
|
||||
|
||||
impl VertexInputState {
|
||||
/// Constructs a new `VertexInputState` with no bindings or attributes.
|
||||
#[inline]
|
||||
pub fn new() -> VertexInputState {
|
||||
VertexInputState {
|
||||
bindings: Default::default(),
|
||||
attributes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a single binding.
|
||||
#[inline]
|
||||
pub fn binding(mut self, binding: u32, description: VertexInputBindingDescription) -> Self {
|
||||
self.bindings.insert(binding, description);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets all bindings.
|
||||
#[inline]
|
||||
pub fn bindings(
|
||||
mut self,
|
||||
bindings: impl IntoIterator<Item = (u32, VertexInputBindingDescription)>,
|
||||
) -> Self {
|
||||
self.bindings = bindings.into_iter().collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a single attribute.
|
||||
#[inline]
|
||||
pub fn attribute(
|
||||
mut self,
|
||||
location: u32,
|
||||
description: VertexInputAttributeDescription,
|
||||
) -> Self {
|
||||
self.attributes.insert(location, description);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets all attributes.
|
||||
#[inline]
|
||||
pub fn attributes(
|
||||
mut self,
|
||||
attributes: impl IntoIterator<Item = (u32, VertexInputAttributeDescription)>,
|
||||
) -> Self {
|
||||
self.attributes = attributes.into_iter().collect();
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn to_vulkan(
|
||||
&self,
|
||||
device: &Device,
|
||||
dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
|
||||
binding_descriptions: &[ash::vk::VertexInputBindingDescription],
|
||||
attribute_descriptions: &[ash::vk::VertexInputAttributeDescription],
|
||||
binding_divisor_state: Option<&ash::vk::PipelineVertexInputDivisorStateCreateInfoEXT>,
|
||||
) -> ash::vk::PipelineVertexInputStateCreateInfo {
|
||||
dynamic_state_modes.insert(DynamicState::VertexInput, false);
|
||||
|
||||
ash::vk::PipelineVertexInputStateCreateInfo {
|
||||
p_next: if let Some(next) = binding_divisor_state {
|
||||
next as *const _ as *const _
|
||||
} else {
|
||||
ptr::null()
|
||||
},
|
||||
flags: ash::vk::PipelineVertexInputStateCreateFlags::empty(),
|
||||
vertex_binding_description_count: binding_descriptions.len() as u32,
|
||||
p_vertex_binding_descriptions: binding_descriptions.as_ptr(),
|
||||
vertex_attribute_description_count: attribute_descriptions.len() as u32,
|
||||
p_vertex_attribute_descriptions: attribute_descriptions.as_ptr(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_vulkan_bindings(
|
||||
&self,
|
||||
device: &Device,
|
||||
) -> Result<SmallVec<[ash::vk::VertexInputBindingDescription; 8]>, GraphicsPipelineCreationError>
|
||||
{
|
||||
let binding_descriptions: SmallVec<[_; 8]> = self
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|(&binding, binding_desc)| {
|
||||
if binding
|
||||
>= device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_bindings
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded {
|
||||
max: device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_bindings,
|
||||
obtained: binding,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if binding_desc.stride
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_binding_stride
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded {
|
||||
binding,
|
||||
max: device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_binding_stride,
|
||||
obtained: binding_desc.stride,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(ash::vk::VertexInputBindingDescription {
|
||||
binding,
|
||||
stride: binding_desc.stride,
|
||||
input_rate: binding_desc.input_rate.into(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
if binding_descriptions.len()
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_bindings as usize
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded {
|
||||
max: device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_bindings,
|
||||
obtained: binding_descriptions.len() as u32,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(binding_descriptions)
|
||||
}
|
||||
|
||||
pub(crate) fn to_vulkan_attributes(
|
||||
&self,
|
||||
device: &Device,
|
||||
) -> Result<
|
||||
SmallVec<[ash::vk::VertexInputAttributeDescription; 8]>,
|
||||
GraphicsPipelineCreationError,
|
||||
> {
|
||||
let attribute_descriptions: SmallVec<[_; 8]> = self
|
||||
.attributes
|
||||
.iter()
|
||||
.map(|(&location, attribute_desc)| {
|
||||
if !self.bindings.contains_key(&attribute_desc.binding) {
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::VertexInputAttributeInvalidBinding {
|
||||
location,
|
||||
binding: attribute_desc.binding,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if attribute_desc.offset
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_attribute_offset
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded {
|
||||
max: device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_attribute_offset,
|
||||
obtained: attribute_desc.offset,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if !attribute_desc
|
||||
.format
|
||||
.properties(device.physical_device())
|
||||
.buffer_features
|
||||
.vertex_buffer
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::VertexInputAttributeUnsupportedFormat {
|
||||
location,
|
||||
format: attribute_desc.format,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(ash::vk::VertexInputAttributeDescription {
|
||||
location,
|
||||
binding: attribute_desc.binding,
|
||||
format: attribute_desc.format.into(),
|
||||
offset: attribute_desc.offset,
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
if attribute_descriptions.len()
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_attributes as usize
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded {
|
||||
max: device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_input_attributes,
|
||||
obtained: attribute_descriptions.len(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(attribute_descriptions)
|
||||
}
|
||||
|
||||
pub(crate) fn to_vulkan_binding_divisor_state(
|
||||
&self,
|
||||
binding_divisor_descriptions: &[ash::vk::VertexInputBindingDivisorDescriptionEXT],
|
||||
) -> Option<ash::vk::PipelineVertexInputDivisorStateCreateInfoEXT> {
|
||||
if !binding_divisor_descriptions.is_empty() {
|
||||
Some(ash::vk::PipelineVertexInputDivisorStateCreateInfoEXT {
|
||||
vertex_binding_divisor_count: binding_divisor_descriptions.len() as u32,
|
||||
p_vertex_binding_divisors: binding_divisor_descriptions.as_ptr(),
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_vulkan_binding_divisors(
|
||||
&self,
|
||||
device: &Device,
|
||||
) -> Result<
|
||||
SmallVec<[ash::vk::VertexInputBindingDivisorDescriptionEXT; 8]>,
|
||||
GraphicsPipelineCreationError,
|
||||
> {
|
||||
self.bindings
|
||||
.iter()
|
||||
.filter_map(|(&binding, binding_desc)| match binding_desc.input_rate {
|
||||
VertexInputRate::Instance { divisor } if divisor != 1 => Some((binding, divisor)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|(binding, divisor)| {
|
||||
if !device
|
||||
.enabled_features()
|
||||
.vertex_attribute_instance_rate_divisor
|
||||
{
|
||||
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
|
||||
feature: "vertex_attribute_instance_rate_divisor",
|
||||
reason: "VertexInputRate::Instance::divisor was not 1",
|
||||
});
|
||||
}
|
||||
|
||||
if divisor == 0
|
||||
&& !device
|
||||
.enabled_features()
|
||||
.vertex_attribute_instance_rate_zero_divisor
|
||||
{
|
||||
return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
|
||||
feature: "vertex_attribute_instance_rate_zero_divisor",
|
||||
reason: "VertexInputRate::Instance::divisor was 0",
|
||||
});
|
||||
}
|
||||
|
||||
if divisor
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_attrib_divisor
|
||||
.unwrap()
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexAttribDivisorExceeded {
|
||||
binding,
|
||||
max: device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_vertex_attrib_divisor
|
||||
.unwrap(),
|
||||
obtained: divisor,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(ash::vk::VertexInputBindingDivisorDescriptionEXT { binding, divisor })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a single vertex buffer binding.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VertexInputBindingDescription {
|
||||
/// The number of bytes from the start of one element in the vertex buffer to the start of the
|
||||
/// next element. This can be simply the size of the data in each element, but larger strides
|
||||
/// are possible.
|
||||
pub stride: u32,
|
||||
|
||||
/// How often the vertex input should advance to the next element.
|
||||
pub input_rate: VertexInputRate,
|
||||
}
|
||||
|
||||
/// Describes a single vertex buffer attribute mapping.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VertexInputAttributeDescription {
|
||||
/// The vertex buffer binding number that this attribute should take its data from.
|
||||
pub binding: u32,
|
||||
|
||||
/// The size and type of the vertex data.
|
||||
pub format: Format,
|
||||
|
||||
/// Number of bytes between the start of a vertex buffer element and the location of attribute.
|
||||
pub offset: u32,
|
||||
}
|
||||
|
||||
/// How the vertex source should be unrolled.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum VertexInputRate {
|
||||
/// Each element of the source corresponds to a vertex.
|
||||
Vertex,
|
||||
|
||||
/// Each element of the source corresponds to an instance.
|
||||
///
|
||||
/// `divisor` indicates how many consecutive instances will use the same instance buffer data.
|
||||
/// This value must be 1, unless the
|
||||
/// [`vertex_attribute_instance_rate_divisor`](crate::device::Features::vertex_attribute_instance_rate_divisor)
|
||||
/// feature has been enabled on the device.
|
||||
///
|
||||
/// `divisor` can be 0 if the
|
||||
/// [`vertex_attribute_instance_rate_zero_divisor`](crate::device::Features::vertex_attribute_instance_rate_zero_divisor)
|
||||
/// feature is also enabled. This means that every vertex will use the same vertex and instance
|
||||
/// data.
|
||||
Instance { divisor: u32 },
|
||||
}
|
||||
|
||||
impl From<VertexInputRate> for ash::vk::VertexInputRate {
|
||||
#[inline]
|
||||
fn from(val: VertexInputRate) -> Self {
|
||||
match val {
|
||||
VertexInputRate::Vertex => ash::vk::VertexInputRate::VERTEX,
|
||||
VertexInputRate::Instance { .. } => ash::vk::VertexInputRate::INSTANCE,
|
||||
}
|
||||
}
|
||||
}
|
@ -7,15 +7,15 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! Viewports and scissor boxes.
|
||||
//! Configures the area of the framebuffer that pixels will be written to.
|
||||
//!
|
||||
//! There are two different concepts to determine where things will be drawn:
|
||||
//!
|
||||
//! - The viewport is the region of the image which corresponds to the
|
||||
//! vertex coordinates `-1.0` to `1.0`.
|
||||
//! - The viewport is the region of the image which corresponds to the vertex coordinates `-1.0` to
|
||||
//! `1.0`.
|
||||
//! - Any pixel outside of the scissor box will be discarded.
|
||||
//!
|
||||
//! In other words modifying the viewport will stretch the image, while modifying the scissor
|
||||
//! In other words, modifying the viewport will stretch the image, while modifying the scissor
|
||||
//! box acts like a filter.
|
||||
//!
|
||||
//! It is legal and sensible to use a viewport that is larger than the target image or that
|
||||
@ -48,10 +48,9 @@
|
||||
//! In all cases the number of viewports and scissor boxes must be the same.
|
||||
//!
|
||||
|
||||
use crate::{
|
||||
device::Device,
|
||||
pipeline::{DynamicState, GraphicsPipelineCreationError},
|
||||
};
|
||||
use crate::device::Device;
|
||||
use crate::pipeline::graphics::GraphicsPipelineCreationError;
|
||||
use crate::pipeline::DynamicState;
|
||||
use fnv::FnvHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use std::{ops::Range, ptr};
|
@ -1,353 +0,0 @@
|
||||
// Copyright (c) 2017 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.
|
||||
|
||||
use crate::pipeline::layout::PipelineLayoutCreationError;
|
||||
use crate::pipeline::layout::PipelineLayoutSupersetError;
|
||||
use crate::pipeline::vertex::IncompatibleVertexDefinitionError;
|
||||
use crate::shader::ShaderInterfaceMismatchError;
|
||||
use crate::Error;
|
||||
use crate::OomError;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::u32;
|
||||
|
||||
/// Error that can happen when creating a graphics pipeline.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum GraphicsPipelineCreationError {
|
||||
/// A device extension that was required for a particular setting on the graphics pipeline was not enabled.
|
||||
ExtensionNotEnabled {
|
||||
extension: &'static str,
|
||||
reason: &'static str,
|
||||
},
|
||||
|
||||
/// 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),
|
||||
|
||||
/// The provided specialization constants are not compatible with what the shader expects.
|
||||
IncompatibleSpecializationConstants,
|
||||
|
||||
/// The vertex definition is not compatible with the input of the vertex shader.
|
||||
IncompatibleVertexDefinition(IncompatibleVertexDefinitionError),
|
||||
|
||||
/// 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.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// The maximum number of vertex attributes has been exceeded.
|
||||
MaxVertexInputAttributesExceeded {
|
||||
/// Maximum allowed value.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: usize,
|
||||
},
|
||||
|
||||
/// 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 sources has been exceeded.
|
||||
MaxVertexInputBindingsExceeded {
|
||||
/// Maximum allowed value.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// 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.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// The maximum number of viewports has been exceeded.
|
||||
MaxViewportsExceeded {
|
||||
/// Maximum allowed value.
|
||||
max: u32,
|
||||
/// Value that was passed.
|
||||
obtained: u32,
|
||||
},
|
||||
|
||||
/// The maximum dimensions of viewports has been exceeded.
|
||||
MaxViewportDimensionsExceeded,
|
||||
|
||||
/// The number of attachments specified in the blending does not match the number of
|
||||
/// attachments in the subpass.
|
||||
MismatchBlendingAttachmentsCount,
|
||||
|
||||
/// 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 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.
|
||||
NoDepthAttachment,
|
||||
|
||||
/// The stencil test requires a stencil attachment but render pass has no stencil attachment, or
|
||||
/// stencil writing is enabled and the stencil attachment is read-only.
|
||||
NoStencilAttachment,
|
||||
|
||||
/// Not enough memory.
|
||||
OomError(OomError),
|
||||
|
||||
/// 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 requested stencil test is invalid.
|
||||
WrongStencilState,
|
||||
}
|
||||
|
||||
impl error::Error for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match *self {
|
||||
GraphicsPipelineCreationError::OomError(ref err) => Some(err),
|
||||
GraphicsPipelineCreationError::PipelineLayoutCreationError(ref err) => Some(err),
|
||||
GraphicsPipelineCreationError::IncompatiblePipelineLayout(ref err) => Some(err),
|
||||
GraphicsPipelineCreationError::ShaderStagesMismatch(ref err) => Some(err),
|
||||
GraphicsPipelineCreationError::IncompatibleVertexDefinition(ref err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GraphicsPipelineCreationError {
|
||||
// TODO: finish
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OomError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: OomError) -> GraphicsPipelineCreationError {
|
||||
GraphicsPipelineCreationError::OomError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PipelineLayoutCreationError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: PipelineLayoutCreationError) -> GraphicsPipelineCreationError {
|
||||
GraphicsPipelineCreationError::PipelineLayoutCreationError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PipelineLayoutSupersetError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: PipelineLayoutSupersetError) -> GraphicsPipelineCreationError {
|
||||
GraphicsPipelineCreationError::IncompatiblePipelineLayout(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IncompatibleVertexDefinitionError> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: IncompatibleVertexDefinitionError) -> GraphicsPipelineCreationError {
|
||||
GraphicsPipelineCreationError::IncompatibleVertexDefinition(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for GraphicsPipelineCreationError {
|
||||
#[inline]
|
||||
fn from(err: Error) -> GraphicsPipelineCreationError {
|
||||
match err {
|
||||
err @ Error::OutOfHostMemory => {
|
||||
GraphicsPipelineCreationError::OomError(OomError::from(err))
|
||||
}
|
||||
err @ Error::OutOfDeviceMemory => {
|
||||
GraphicsPipelineCreationError::OomError(OomError::from(err))
|
||||
}
|
||||
_ => panic!("unexpected error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
1093
vulkano/src/pipeline/layout.rs
Normal file
1093
vulkano/src/pipeline/layout.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,495 +0,0 @@
|
||||
// Copyright (c) 2017 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.
|
||||
|
||||
//! Contains the `check_desc_against_limits` function and the `PipelineLayoutLimitsError` error.
|
||||
|
||||
use crate::descriptor_set::layout::DescriptorSetLayout;
|
||||
use crate::descriptor_set::layout::DescriptorType;
|
||||
use crate::device::Properties;
|
||||
use crate::pipeline::layout::PipelineLayoutPcRange;
|
||||
use crate::shader::ShaderStages;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Checks whether the pipeline layout description fulfills the device limits requirements.
|
||||
pub fn check_desc_against_limits(
|
||||
properties: &Properties,
|
||||
descriptor_set_layouts: &[Arc<DescriptorSetLayout>],
|
||||
push_constants_ranges: &[PipelineLayoutPcRange],
|
||||
) -> Result<(), PipelineLayoutLimitsError> {
|
||||
let mut num_resources = Counter::default();
|
||||
let mut num_samplers = Counter::default();
|
||||
let mut num_uniform_buffers = Counter::default();
|
||||
let mut num_uniform_buffers_dynamic = 0;
|
||||
let mut num_storage_buffers = Counter::default();
|
||||
let mut num_storage_buffers_dynamic = 0;
|
||||
let mut num_sampled_images = Counter::default();
|
||||
let mut num_storage_images = Counter::default();
|
||||
let mut num_input_attachments = Counter::default();
|
||||
|
||||
for set in descriptor_set_layouts {
|
||||
for descriptor in (0..set.num_bindings()).filter_map(|i| set.descriptor(i).map(|d| d)) {
|
||||
num_resources.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
|
||||
match descriptor.ty {
|
||||
// TODO:
|
||||
DescriptorType::Sampler => {
|
||||
num_samplers.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
}
|
||||
DescriptorType::CombinedImageSampler => {
|
||||
num_samplers.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
num_sampled_images.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
}
|
||||
DescriptorType::SampledImage | DescriptorType::UniformTexelBuffer => {
|
||||
num_sampled_images.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
}
|
||||
DescriptorType::StorageImage | DescriptorType::StorageTexelBuffer => {
|
||||
num_storage_images.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
}
|
||||
DescriptorType::UniformBuffer => {
|
||||
num_uniform_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
}
|
||||
DescriptorType::UniformBufferDynamic => {
|
||||
num_uniform_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
num_uniform_buffers_dynamic += 1;
|
||||
}
|
||||
DescriptorType::StorageBuffer => {
|
||||
num_storage_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
}
|
||||
DescriptorType::StorageBufferDynamic => {
|
||||
num_storage_buffers.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
num_storage_buffers_dynamic += 1;
|
||||
}
|
||||
DescriptorType::InputAttachment => {
|
||||
num_input_attachments
|
||||
.increment(descriptor.descriptor_count, &descriptor.stages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if descriptor_set_layouts.len() > properties.max_bound_descriptor_sets as usize {
|
||||
return Err(PipelineLayoutLimitsError::MaxDescriptorSetsLimitExceeded {
|
||||
limit: properties.max_bound_descriptor_sets as usize,
|
||||
requested: descriptor_set_layouts.len(),
|
||||
});
|
||||
}
|
||||
|
||||
if num_resources.max_per_stage() > properties.max_per_stage_resources {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageResourcesLimitExceeded {
|
||||
limit: properties.max_per_stage_resources,
|
||||
requested: num_resources.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if num_samplers.max_per_stage() > properties.max_per_stage_descriptor_samplers {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorSamplersLimitExceeded {
|
||||
limit: properties.max_per_stage_descriptor_samplers,
|
||||
requested: num_samplers.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_uniform_buffers.max_per_stage() > properties.max_per_stage_descriptor_uniform_buffers {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorUniformBuffersLimitExceeded {
|
||||
limit: properties.max_per_stage_descriptor_uniform_buffers,
|
||||
requested: num_uniform_buffers.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_buffers.max_per_stage() > properties.max_per_stage_descriptor_storage_buffers {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageBuffersLimitExceeded {
|
||||
limit: properties.max_per_stage_descriptor_storage_buffers,
|
||||
requested: num_storage_buffers.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_sampled_images.max_per_stage() > properties.max_per_stage_descriptor_sampled_images {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorSampledImagesLimitExceeded {
|
||||
limit: properties.max_per_stage_descriptor_sampled_images,
|
||||
requested: num_sampled_images.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_images.max_per_stage() > properties.max_per_stage_descriptor_storage_images {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageImagesLimitExceeded {
|
||||
limit: properties.max_per_stage_descriptor_storage_images,
|
||||
requested: num_storage_images.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_input_attachments.max_per_stage() > properties.max_per_stage_descriptor_input_attachments
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorInputAttachmentsLimitExceeded {
|
||||
limit: properties.max_per_stage_descriptor_input_attachments,
|
||||
requested: num_input_attachments.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if num_samplers.total > properties.max_descriptor_set_samplers {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetSamplersLimitExceeded {
|
||||
limit: properties.max_descriptor_set_samplers,
|
||||
requested: num_samplers.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_uniform_buffers.total > properties.max_descriptor_set_uniform_buffers {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersLimitExceeded {
|
||||
limit: properties.max_descriptor_set_uniform_buffers,
|
||||
requested: num_uniform_buffers.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_uniform_buffers_dynamic > properties.max_descriptor_set_uniform_buffers_dynamic {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
|
||||
limit: properties.max_descriptor_set_uniform_buffers_dynamic,
|
||||
requested: num_uniform_buffers_dynamic,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_buffers.total > properties.max_descriptor_set_storage_buffers {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersLimitExceeded {
|
||||
limit: properties.max_descriptor_set_storage_buffers,
|
||||
requested: num_storage_buffers.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_buffers_dynamic > properties.max_descriptor_set_storage_buffers_dynamic {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
|
||||
limit: properties.max_descriptor_set_storage_buffers_dynamic,
|
||||
requested: num_storage_buffers_dynamic,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_sampled_images.total > properties.max_descriptor_set_sampled_images {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetSampledImagesLimitExceeded {
|
||||
limit: properties.max_descriptor_set_sampled_images,
|
||||
requested: num_sampled_images.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_images.total > properties.max_descriptor_set_storage_images {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageImagesLimitExceeded {
|
||||
limit: properties.max_descriptor_set_storage_images,
|
||||
requested: num_storage_images.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_input_attachments.total > properties.max_descriptor_set_input_attachments {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetInputAttachmentsLimitExceeded {
|
||||
limit: properties.max_descriptor_set_input_attachments,
|
||||
requested: num_input_attachments.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
for &PipelineLayoutPcRange { offset, size, .. } in push_constants_ranges {
|
||||
if offset + size > properties.max_push_constants_size {
|
||||
return Err(PipelineLayoutLimitsError::MaxPushConstantsSizeExceeded {
|
||||
limit: properties.max_push_constants_size,
|
||||
requested: offset + size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The pipeline layout description isn't compatible with the hardware limits.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PipelineLayoutLimitsError {
|
||||
/// The maximum number of descriptor sets has been exceeded.
|
||||
MaxDescriptorSetsLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: usize,
|
||||
/// What was requested.
|
||||
requested: usize,
|
||||
},
|
||||
|
||||
/// The maximum size of push constants has been exceeded.
|
||||
MaxPushConstantsSizeExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_per_stage_resources()` limit has been exceeded.
|
||||
MaxPerStageResourcesLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_per_stage_descriptor_samplers()` limit has been exceeded.
|
||||
MaxPerStageDescriptorSamplersLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_per_stage_descriptor_uniform_buffers()` limit has been exceeded.
|
||||
MaxPerStageDescriptorUniformBuffersLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_per_stage_descriptor_storage_buffers()` limit has been exceeded.
|
||||
MaxPerStageDescriptorStorageBuffersLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_per_stage_descriptor_sampled_images()` limit has been exceeded.
|
||||
MaxPerStageDescriptorSampledImagesLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_per_stage_descriptor_storage_images()` limit has been exceeded.
|
||||
MaxPerStageDescriptorStorageImagesLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_per_stage_descriptor_input_attachments()` limit has been exceeded.
|
||||
MaxPerStageDescriptorInputAttachmentsLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_samplers()` limit has been exceeded.
|
||||
MaxDescriptorSetSamplersLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_uniform_buffers()` limit has been exceeded.
|
||||
MaxDescriptorSetUniformBuffersLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_uniform_buffers_dynamic()` limit has been exceeded.
|
||||
MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_storage_buffers()` limit has been exceeded.
|
||||
MaxDescriptorSetStorageBuffersLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_storage_buffers_dynamic()` limit has been exceeded.
|
||||
MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_sampled_images()` limit has been exceeded.
|
||||
MaxDescriptorSetSampledImagesLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_storage_images()` limit has been exceeded.
|
||||
MaxDescriptorSetStorageImagesLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
|
||||
/// The `max_descriptor_set_input_attachments()` limit has been exceeded.
|
||||
MaxDescriptorSetInputAttachmentsLimitExceeded {
|
||||
/// The limit that must be fulfilled.
|
||||
limit: u32,
|
||||
/// What was requested.
|
||||
requested: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl error::Error for PipelineLayoutLimitsError {}
|
||||
|
||||
impl fmt::Display for PipelineLayoutLimitsError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"{}",
|
||||
match *self {
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetsLimitExceeded { .. } => {
|
||||
"the maximum number of descriptor sets has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxPushConstantsSizeExceeded { .. } => {
|
||||
"the maximum size of push constants has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxPerStageResourcesLimitExceeded { .. } => {
|
||||
"the `max_per_stage_resources()` limit has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorSamplersLimitExceeded {
|
||||
..
|
||||
} => {
|
||||
"the `max_per_stage_descriptor_samplers()` limit has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorUniformBuffersLimitExceeded {
|
||||
..
|
||||
} => "the `max_per_stage_descriptor_uniform_buffers()` limit has been exceeded",
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageBuffersLimitExceeded {
|
||||
..
|
||||
} => "the `max_per_stage_descriptor_storage_buffers()` limit has been exceeded",
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorSampledImagesLimitExceeded {
|
||||
..
|
||||
} => "the `max_per_stage_descriptor_sampled_images()` limit has been exceeded",
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageImagesLimitExceeded {
|
||||
..
|
||||
} => "the `max_per_stage_descriptor_storage_images()` limit has been exceeded",
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorInputAttachmentsLimitExceeded {
|
||||
..
|
||||
} => "the `max_per_stage_descriptor_input_attachments()` limit has been exceeded",
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetSamplersLimitExceeded { .. } => {
|
||||
"the `max_descriptor_set_samplers()` limit has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersLimitExceeded {
|
||||
..
|
||||
} => {
|
||||
"the `max_descriptor_set_uniform_buffers()` limit has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
|
||||
..
|
||||
} => "the `max_descriptor_set_uniform_buffers_dynamic()` limit has been exceeded",
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersLimitExceeded {
|
||||
..
|
||||
} => {
|
||||
"the `max_descriptor_set_storage_buffers()` limit has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
|
||||
..
|
||||
} => "the `max_descriptor_set_storage_buffers_dynamic()` limit has been exceeded",
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetSampledImagesLimitExceeded {
|
||||
..
|
||||
} => {
|
||||
"the `max_descriptor_set_sampled_images()` limit has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageImagesLimitExceeded {
|
||||
..
|
||||
} => {
|
||||
"the `max_descriptor_set_storage_images()` limit has been exceeded"
|
||||
}
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetInputAttachmentsLimitExceeded {
|
||||
..
|
||||
} => {
|
||||
"the `max_descriptor_set_input_attachments()` limit has been exceeded"
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper struct for the main function.
|
||||
#[derive(Default)]
|
||||
struct Counter {
|
||||
total: u32,
|
||||
compute: u32,
|
||||
vertex: u32,
|
||||
geometry: u32,
|
||||
tess_ctl: u32,
|
||||
tess_eval: u32,
|
||||
frag: u32,
|
||||
}
|
||||
|
||||
impl Counter {
|
||||
fn increment(&mut self, num: u32, stages: &ShaderStages) {
|
||||
self.total += num;
|
||||
if stages.compute {
|
||||
self.compute += num;
|
||||
}
|
||||
if stages.vertex {
|
||||
self.vertex += num;
|
||||
}
|
||||
if stages.tessellation_control {
|
||||
self.tess_ctl += num;
|
||||
}
|
||||
if stages.tessellation_evaluation {
|
||||
self.tess_eval += num;
|
||||
}
|
||||
if stages.geometry {
|
||||
self.geometry += num;
|
||||
}
|
||||
if stages.fragment {
|
||||
self.frag += num;
|
||||
}
|
||||
}
|
||||
|
||||
fn max_per_stage(&self) -> u32 {
|
||||
let mut max = 0;
|
||||
if self.compute > max {
|
||||
max = self.compute;
|
||||
}
|
||||
if self.vertex > max {
|
||||
max = self.vertex;
|
||||
}
|
||||
if self.geometry > max {
|
||||
max = self.geometry;
|
||||
}
|
||||
if self.tess_ctl > max {
|
||||
max = self.tess_ctl;
|
||||
}
|
||||
if self.tess_eval > max {
|
||||
max = self.tess_eval;
|
||||
}
|
||||
if self.frag > max {
|
||||
max = self.frag;
|
||||
}
|
||||
max
|
||||
}
|
||||
}
|
@ -1,79 +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.
|
||||
|
||||
//! A pipeline layout describes the layout of descriptors and push constants used by a pipeline.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! The layout itself only *describes* the descriptors and push constants, and does not contain
|
||||
//! their content itself. Instead, you can think of it as a `struct` definition that states which
|
||||
//! members there are, what types they have, and in what order.
|
||||
//! One could imagine a Rust definition somewhat like this:
|
||||
//!
|
||||
//! ```text
|
||||
//! #[repr(C)]
|
||||
//! struct MyPipelineLayout {
|
||||
//! push_constants: Pc,
|
||||
//! descriptor_set0: Ds0,
|
||||
//! descriptor_set1: Ds1,
|
||||
//! descriptor_set2: Ds2,
|
||||
//! descriptor_set3: Ds3,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Of course, a pipeline layout is created at runtime, unlike a Rust type.
|
||||
//!
|
||||
//! # Layout compatibility
|
||||
//!
|
||||
//! When binding descriptor sets or setting push constants, you must provide a pipeline layout.
|
||||
//! This pipeline is used to decide where in memory Vulkan should write the new data. The
|
||||
//! descriptor sets and push constants can later be read by dispatch or draw calls, but only if
|
||||
//! the bound pipeline being used for the command has a layout that is *compatible* with the layout
|
||||
//! that was used to bind the resources.
|
||||
//!
|
||||
//! *Compatible* means that the pipeline layout must be the same object, or a different layout in
|
||||
//! which the push constant ranges and descriptor set layouts were be identically defined.
|
||||
//! However, Vulkan allows for partial compatibility as well. In the `struct` analogy used above,
|
||||
//! one could imagine that using a different definition would leave some members with the same
|
||||
//! offset and size within the struct as in the old definition, while others are no longer
|
||||
//! positioned correctly. For example, if a new, incompatible type were used for `Ds1`, then the
|
||||
//! `descriptor_set1`, `descriptor_set2` and `descriptor_set3` members would no longer be correct,
|
||||
//! but `descriptor_set0` and `push_constants` would remain accessible in the new layout.
|
||||
//! Because of this behaviour, the following rules apply to compatibility between the layouts used
|
||||
//! in subsequent descriptor set binding calls:
|
||||
//!
|
||||
//! - An incompatible definition of `Pc` invalidates all bound descriptor sets.
|
||||
//! - An incompatible definition of `DsN` invalidates all bound descriptor sets *N* and higher.
|
||||
//! - If *N* is the highest set being assigned in a bind command, and it and all lower sets
|
||||
//! have compatible definitions, including the push constants, then descriptor sets above *N*
|
||||
//! remain valid.
|
||||
//!
|
||||
//! [`SyncCommandBufferBuilder`](crate::command_buffer::synced::SyncCommandBufferBuilder) keeps
|
||||
//! track of this state and will automatically remove descriptor sets that have been invalidated
|
||||
//! by incompatible layouts in subsequent binding commands.
|
||||
//!
|
||||
//! # Creating pipeline layouts
|
||||
//!
|
||||
//! A pipeline layout is a Vulkan object type, represented in Vulkano with the `PipelineLayout`
|
||||
//! type. Each pipeline that you create holds a pipeline layout object.
|
||||
//!
|
||||
//! By default, creating a pipeline automatically builds a new pipeline layout object describing the
|
||||
//! union of all the descriptors and push constants of all the shaders used by the pipeline.
|
||||
//! However, it is also possible to create a pipeline layout separately, and provide that to the
|
||||
//! pipeline constructor. This can in some cases be more efficient than using the auto-generated
|
||||
//! pipeline layouts.
|
||||
|
||||
pub use self::limits_check::PipelineLayoutLimitsError;
|
||||
pub use self::sys::PipelineLayout;
|
||||
pub use self::sys::PipelineLayoutCreationError;
|
||||
pub use self::sys::PipelineLayoutPcRange;
|
||||
pub use self::sys::PipelineLayoutSupersetError;
|
||||
|
||||
mod limits_check;
|
||||
mod sys;
|
@ -1,555 +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.
|
||||
|
||||
use super::limits_check;
|
||||
use crate::check_errors;
|
||||
use crate::descriptor_set::layout::DescriptorRequirementsNotMet;
|
||||
use crate::descriptor_set::layout::DescriptorSetLayout;
|
||||
use crate::descriptor_set::layout::DescriptorSetLayoutError;
|
||||
use crate::device::Device;
|
||||
use crate::device::DeviceOwned;
|
||||
use crate::pipeline::layout::PipelineLayoutLimitsError;
|
||||
use crate::shader::DescriptorRequirements;
|
||||
use crate::shader::ShaderStages;
|
||||
use crate::Error;
|
||||
use crate::OomError;
|
||||
use crate::VulkanObject;
|
||||
use smallvec::SmallVec;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Wrapper around the `PipelineLayout` Vulkan object. Describes to the Vulkan implementation the
|
||||
/// descriptor sets and push constants available to your shaders.
|
||||
pub struct PipelineLayout {
|
||||
handle: ash::vk::PipelineLayout,
|
||||
device: Arc<Device>,
|
||||
descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 4]>,
|
||||
push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 4]>,
|
||||
}
|
||||
|
||||
impl PipelineLayout {
|
||||
/// Creates a new `PipelineLayout`.
|
||||
#[inline]
|
||||
pub fn new<D, P>(
|
||||
device: Arc<Device>,
|
||||
descriptor_set_layouts: D,
|
||||
push_constant_ranges: P,
|
||||
) -> Result<Arc<PipelineLayout>, PipelineLayoutCreationError>
|
||||
where
|
||||
D: IntoIterator<Item = Arc<DescriptorSetLayout>>,
|
||||
P: IntoIterator<Item = PipelineLayoutPcRange>,
|
||||
{
|
||||
let fns = device.fns();
|
||||
let descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 4]> =
|
||||
descriptor_set_layouts.into_iter().collect();
|
||||
|
||||
if descriptor_set_layouts
|
||||
.iter()
|
||||
.filter(|layout| layout.desc().is_push_descriptor())
|
||||
.count()
|
||||
> 1
|
||||
{
|
||||
return Err(PipelineLayoutCreationError::MultiplePushDescriptor);
|
||||
}
|
||||
|
||||
let mut push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 4]> =
|
||||
push_constant_ranges.into_iter().collect();
|
||||
|
||||
// Check for overlapping stages
|
||||
for (a_id, a) in push_constant_ranges.iter().enumerate() {
|
||||
for b in push_constant_ranges.iter().skip(a_id + 1) {
|
||||
if a.stages.intersects(&b.stages) {
|
||||
return Err(PipelineLayoutCreationError::PushConstantsConflict {
|
||||
first_range: *a,
|
||||
second_range: *b,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the ranges for the purpose of comparing for equality.
|
||||
// The stage mask is guaranteed to be unique by the above check, so it's a suitable
|
||||
// sorting key.
|
||||
push_constant_ranges.sort_unstable_by_key(|range| {
|
||||
(
|
||||
range.offset,
|
||||
range.size,
|
||||
ash::vk::ShaderStageFlags::from(range.stages),
|
||||
)
|
||||
});
|
||||
|
||||
// Check against device limits
|
||||
limits_check::check_desc_against_limits(
|
||||
device.physical_device().properties(),
|
||||
&descriptor_set_layouts,
|
||||
&push_constant_ranges,
|
||||
)?;
|
||||
|
||||
// Grab the list of `vkDescriptorSetLayout` objects from `layouts`.
|
||||
let layouts_ids = descriptor_set_layouts
|
||||
.iter()
|
||||
.map(|l| l.internal_object())
|
||||
.collect::<SmallVec<[_; 4]>>();
|
||||
|
||||
// Builds a list of `vkPushConstantRange` that describe the push constants.
|
||||
let push_constants = {
|
||||
let mut out: SmallVec<[_; 4]> = SmallVec::new();
|
||||
|
||||
for &PipelineLayoutPcRange {
|
||||
offset,
|
||||
size,
|
||||
stages,
|
||||
} in &push_constant_ranges
|
||||
{
|
||||
if stages == ShaderStages::none() || size == 0 || (size % 4) != 0 {
|
||||
return Err(PipelineLayoutCreationError::InvalidPushConstant);
|
||||
}
|
||||
|
||||
out.push(ash::vk::PushConstantRange {
|
||||
stage_flags: stages.into(),
|
||||
offset,
|
||||
size,
|
||||
});
|
||||
}
|
||||
|
||||
out
|
||||
};
|
||||
|
||||
// Each bit of `stageFlags` must only be present in a single push constants range.
|
||||
// We check that with a debug_assert because it's supposed to be enforced by the
|
||||
// `PipelineLayoutDesc`.
|
||||
debug_assert!({
|
||||
let mut stages = ash::vk::ShaderStageFlags::empty();
|
||||
let mut outcome = true;
|
||||
for pc in push_constants.iter() {
|
||||
if !(stages & pc.stage_flags).is_empty() {
|
||||
outcome = false;
|
||||
break;
|
||||
}
|
||||
stages &= pc.stage_flags;
|
||||
}
|
||||
outcome
|
||||
});
|
||||
|
||||
// FIXME: it is not legal to pass eg. the TESSELLATION_SHADER bit when the device doesn't
|
||||
// have tess shaders enabled
|
||||
|
||||
// Build the final object.
|
||||
let handle = unsafe {
|
||||
let infos = ash::vk::PipelineLayoutCreateInfo {
|
||||
flags: ash::vk::PipelineLayoutCreateFlags::empty(),
|
||||
set_layout_count: layouts_ids.len() as u32,
|
||||
p_set_layouts: layouts_ids.as_ptr(),
|
||||
push_constant_range_count: push_constants.len() as u32,
|
||||
p_push_constant_ranges: push_constants.as_ptr(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut output = MaybeUninit::uninit();
|
||||
check_errors(fns.v1_0.create_pipeline_layout(
|
||||
device.internal_object(),
|
||||
&infos,
|
||||
ptr::null(),
|
||||
output.as_mut_ptr(),
|
||||
))?;
|
||||
output.assume_init()
|
||||
};
|
||||
|
||||
Ok(Arc::new(PipelineLayout {
|
||||
handle,
|
||||
device: device.clone(),
|
||||
descriptor_set_layouts,
|
||||
push_constant_ranges,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns the descriptor set layouts this pipeline layout was created from.
|
||||
#[inline]
|
||||
pub fn descriptor_set_layouts(&self) -> &[Arc<DescriptorSetLayout>] {
|
||||
&self.descriptor_set_layouts
|
||||
}
|
||||
|
||||
/// Returns a slice containing the push constant ranges this pipeline layout was created from.
|
||||
///
|
||||
/// The ranges are guaranteed to be sorted deterministically by offset, size, then stages.
|
||||
/// This means that two slices containing the same elements will always have the same order.
|
||||
#[inline]
|
||||
pub fn push_constant_ranges(&self) -> &[PipelineLayoutPcRange] {
|
||||
&self.push_constant_ranges
|
||||
}
|
||||
|
||||
/// Returns whether `self` is compatible with `other` for the given number of sets.
|
||||
pub fn is_compatible_with(&self, other: &PipelineLayout, num_sets: u32) -> bool {
|
||||
let num_sets = num_sets as usize;
|
||||
assert!(num_sets >= self.descriptor_set_layouts.len());
|
||||
|
||||
if self.handle == other.handle {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.push_constant_ranges != other.push_constant_ranges {
|
||||
return false;
|
||||
}
|
||||
|
||||
let other_sets = match other.descriptor_set_layouts.get(0..num_sets) {
|
||||
Some(x) => x,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
self.descriptor_set_layouts.iter().zip(other_sets).all(
|
||||
|(self_set_layout, other_set_layout)| {
|
||||
self_set_layout.is_compatible_with(other_set_layout)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Makes sure that `self` is a superset of the provided descriptor set layouts and push
|
||||
/// constant ranges. Returns an `Err` if this is not the case.
|
||||
pub fn ensure_compatible_with_shader<'a>(
|
||||
&self,
|
||||
descriptor_requirements: impl IntoIterator<Item = ((u32, u32), &'a DescriptorRequirements)>,
|
||||
push_constant_range: Option<&PipelineLayoutPcRange>,
|
||||
) -> Result<(), PipelineLayoutSupersetError> {
|
||||
for ((set_num, binding_num), reqs) in descriptor_requirements.into_iter() {
|
||||
let descriptor_desc = self
|
||||
.descriptor_set_layouts
|
||||
.get(set_num as usize)
|
||||
.and_then(|set_desc| set_desc.descriptor(binding_num));
|
||||
|
||||
let descriptor_desc = match descriptor_desc {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(PipelineLayoutSupersetError::DescriptorMissing {
|
||||
set_num,
|
||||
binding_num,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(error) = descriptor_desc.ensure_compatible_with_shader(reqs) {
|
||||
return Err(PipelineLayoutSupersetError::DescriptorRequirementsNotMet {
|
||||
set_num,
|
||||
binding_num,
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: check push constants
|
||||
if let Some(range) = push_constant_range {
|
||||
for own_range in self.push_constant_ranges.as_ref().into_iter() {
|
||||
if range.stages.intersects(&own_range.stages) && // check if it shares any stages
|
||||
(range.offset < own_range.offset || // our range must start before and end after the given range
|
||||
own_range.offset + own_range.size < range.offset + range.size)
|
||||
{
|
||||
return Err(PipelineLayoutSupersetError::PushConstantRange {
|
||||
first_range: *own_range,
|
||||
second_range: *range,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl DeviceOwned for PipelineLayout {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
&self.device
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VulkanObject for PipelineLayout {
|
||||
type Object = ash::vk::PipelineLayout;
|
||||
|
||||
fn internal_object(&self) -> Self::Object {
|
||||
self.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PipelineLayout {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fmt.debug_struct("PipelineLayout")
|
||||
.field("raw", &self.handle)
|
||||
.field("device", &self.device)
|
||||
.field("descriptor_set_layouts", &self.descriptor_set_layouts)
|
||||
.field("push_constant_ranges", &self.push_constant_ranges)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PipelineLayout {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let fns = self.device.fns();
|
||||
fns.v1_0.destroy_pipeline_layout(
|
||||
self.device.internal_object(),
|
||||
self.handle,
|
||||
ptr::null(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when creating a pipeline layout.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PipelineLayoutCreationError {
|
||||
/// Not enough memory.
|
||||
OomError(OomError),
|
||||
/// The pipeline layout description doesn't fulfill the limit requirements.
|
||||
LimitsError(PipelineLayoutLimitsError),
|
||||
/// One of the push constants range didn't obey the rules. The list of stages must not be
|
||||
/// empty, the size must not be 0, and the size must be a multiple or 4.
|
||||
InvalidPushConstant,
|
||||
/// More than one descriptor set layout was set for push descriptors.
|
||||
MultiplePushDescriptor,
|
||||
/// Conflict between different push constants ranges.
|
||||
PushConstantsConflict {
|
||||
first_range: PipelineLayoutPcRange,
|
||||
second_range: PipelineLayoutPcRange,
|
||||
},
|
||||
/// One of the set layouts has an error.
|
||||
SetLayoutError(DescriptorSetLayoutError),
|
||||
}
|
||||
|
||||
impl error::Error for PipelineLayoutCreationError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match *self {
|
||||
Self::OomError(ref err) => Some(err),
|
||||
Self::LimitsError(ref err) => Some(err),
|
||||
Self::SetLayoutError(ref err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PipelineLayoutCreationError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Self::OomError(_) => write!(fmt, "not enough memory available"),
|
||||
Self::LimitsError(_) => {
|
||||
write!(
|
||||
fmt,
|
||||
"the pipeline layout description doesn't fulfill the limit requirements"
|
||||
)
|
||||
}
|
||||
Self::InvalidPushConstant => {
|
||||
write!(fmt, "one of the push constants range didn't obey the rules")
|
||||
}
|
||||
Self::MultiplePushDescriptor => {
|
||||
write!(
|
||||
fmt,
|
||||
"more than one descriptor set layout was set for push descriptors"
|
||||
)
|
||||
}
|
||||
Self::PushConstantsConflict { .. } => {
|
||||
write!(fmt, "conflict between different push constants ranges")
|
||||
}
|
||||
Self::SetLayoutError(_) => write!(fmt, "one of the sets has an error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OomError> for PipelineLayoutCreationError {
|
||||
#[inline]
|
||||
fn from(err: OomError) -> PipelineLayoutCreationError {
|
||||
PipelineLayoutCreationError::OomError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PipelineLayoutLimitsError> for PipelineLayoutCreationError {
|
||||
#[inline]
|
||||
fn from(err: PipelineLayoutLimitsError) -> PipelineLayoutCreationError {
|
||||
PipelineLayoutCreationError::LimitsError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DescriptorSetLayoutError> for PipelineLayoutCreationError {
|
||||
#[inline]
|
||||
fn from(err: DescriptorSetLayoutError) -> PipelineLayoutCreationError {
|
||||
PipelineLayoutCreationError::SetLayoutError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for PipelineLayoutCreationError {
|
||||
#[inline]
|
||||
fn from(err: Error) -> PipelineLayoutCreationError {
|
||||
match err {
|
||||
err @ Error::OutOfHostMemory => {
|
||||
PipelineLayoutCreationError::OomError(OomError::from(err))
|
||||
}
|
||||
err @ Error::OutOfDeviceMemory => {
|
||||
PipelineLayoutCreationError::OomError(OomError::from(err))
|
||||
}
|
||||
_ => panic!("unexpected error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error when checking whether a pipeline layout is a superset of another one.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PipelineLayoutSupersetError {
|
||||
DescriptorMissing {
|
||||
set_num: u32,
|
||||
binding_num: u32,
|
||||
},
|
||||
DescriptorRequirementsNotMet {
|
||||
set_num: u32,
|
||||
binding_num: u32,
|
||||
error: DescriptorRequirementsNotMet,
|
||||
},
|
||||
PushConstantRange {
|
||||
first_range: PipelineLayoutPcRange,
|
||||
second_range: PipelineLayoutPcRange,
|
||||
},
|
||||
}
|
||||
|
||||
impl error::Error for PipelineLayoutSupersetError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match *self {
|
||||
PipelineLayoutSupersetError::DescriptorRequirementsNotMet { ref error, .. } => {
|
||||
Some(error)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PipelineLayoutSupersetError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
PipelineLayoutSupersetError::DescriptorRequirementsNotMet { set_num, binding_num, .. } => write!(
|
||||
fmt,
|
||||
"the descriptor at set {} binding {} does not meet the requirements",
|
||||
set_num, binding_num
|
||||
),
|
||||
PipelineLayoutSupersetError::DescriptorMissing {
|
||||
set_num,
|
||||
binding_num,
|
||||
} => write!(
|
||||
fmt,
|
||||
"a descriptor at set {} binding {} is required by the shaders, but is missing from the pipeline layout",
|
||||
set_num, binding_num
|
||||
),
|
||||
PipelineLayoutSupersetError::PushConstantRange {
|
||||
first_range,
|
||||
second_range,
|
||||
} => {
|
||||
writeln!(
|
||||
fmt,
|
||||
"our range did not completely encompass the other range"
|
||||
)?;
|
||||
writeln!(fmt, " our stages: {:?}", first_range.stages)?;
|
||||
writeln!(
|
||||
fmt,
|
||||
" our range: {} - {}",
|
||||
first_range.offset,
|
||||
first_range.offset + first_range.size
|
||||
)?;
|
||||
writeln!(fmt, " other stages: {:?}", second_range.stages)?;
|
||||
write!(
|
||||
fmt,
|
||||
" other range: {} - {}",
|
||||
second_range.offset,
|
||||
second_range.offset + second_range.size
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a range of the push constants of a pipeline layout.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct PipelineLayoutPcRange {
|
||||
/// Offset in bytes from the start of the push constants to this range.
|
||||
pub offset: u32,
|
||||
/// Size in bytes of the range.
|
||||
pub size: u32,
|
||||
/// The stages which can access this range.
|
||||
/// A stage can access at most one push constant range.
|
||||
pub stages: ShaderStages,
|
||||
}
|
||||
|
||||
/* TODO: restore
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
use descriptor::descriptor::ShaderStages;
|
||||
use descriptor::descriptor_set::DescriptorSetLayout;
|
||||
use descriptor::pipeline_layout::sys::PipelineLayout;
|
||||
use descriptor::pipeline_layout::sys::PipelineLayoutCreationError;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
let _layout = PipelineLayout::new(&device, iter::empty(), iter::empty()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrong_device_panic() {
|
||||
let (device1, _) = gfx_dev_and_queue!();
|
||||
let (device2, _) = gfx_dev_and_queue!();
|
||||
|
||||
let set = match DescriptorSetLayout::raw(device1, iter::empty()) {
|
||||
Ok(s) => Arc::new(s),
|
||||
Err(_) => return
|
||||
};
|
||||
|
||||
assert_should_panic!({
|
||||
let _ = PipelineLayout::new(&device2, Some(&set), iter::empty());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_push_constant_stages() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let push_constant = (0, 8, ShaderStages::none());
|
||||
|
||||
match PipelineLayout::new(&device, iter::empty(), Some(push_constant)) {
|
||||
Err(PipelineLayoutCreationError::InvalidPushConstant) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_push_constant_size1() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let push_constant = (0, 0, ShaderStages::all_graphics());
|
||||
|
||||
match PipelineLayout::new(&device, iter::empty(), Some(push_constant)) {
|
||||
Err(PipelineLayoutCreationError::InvalidPushConstant) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_push_constant_size2() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
let push_constant = (0, 11, ShaderStages::all_graphics());
|
||||
|
||||
match PipelineLayout::new(&device, iter::empty(), Some(push_constant)) {
|
||||
Err(PipelineLayoutCreationError::InvalidPushConstant) => (),
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
}*/
|
@ -7,7 +7,7 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//! Describes a graphical or compute operation.
|
||||
//! Describes a processing operation that will execute on the Vulkan device.
|
||||
//!
|
||||
//! In Vulkan, before you can add a draw or a compute command to a command buffer you have to
|
||||
//! create a *pipeline object* that describes this command.
|
||||
@ -16,88 +16,18 @@
|
||||
//! code that will execute the operation (similar to a compiler that generates an executable for
|
||||
//! the CPU). Consequently it is a CPU-intensive operation that should be performed at
|
||||
//! initialization or during a loading screen.
|
||||
//!
|
||||
//! There are two kinds of pipelines:
|
||||
//!
|
||||
//! - `ComputePipeline`s, for compute operations (general-purpose operations that read/write data
|
||||
//! in buffers or raw pixels in images).
|
||||
//! - `GraphicsPipeline`s, for graphical operations (operations that take vertices as input and
|
||||
//! write pixels to a framebuffer).
|
||||
//!
|
||||
//! # Creating a compute pipeline.
|
||||
//!
|
||||
//! In order to create a compute pipeline, you first need a *shader entry point*.
|
||||
//!
|
||||
//! TODO: write the rest
|
||||
//! For now vulkano has no "clean" way to create shaders ; everything's a bit hacky
|
||||
//!
|
||||
//! # Creating a graphics pipeline
|
||||
//!
|
||||
//! A graphics operation takes vertices or vertices and indices as input, and writes pixels to a
|
||||
//! framebuffer. It consists of multiple steps:
|
||||
//!
|
||||
//! - A *shader* named the *vertex shader* is run once for each vertex of the input.
|
||||
//! - Vertices are assembled into primitives.
|
||||
//! - Optionally, a shader named the *tessellation control shader* is run once for each primitive
|
||||
//! and indicates the tessellation level to apply for this primitive.
|
||||
//! - Optionally, a shader named the *tessellation evaluation shader* is run once for each vertex,
|
||||
//! including the ones newly created by the tessellation.
|
||||
//! - Optionally, a shader named the *geometry shader* is run once for each line or triangle.
|
||||
//! - The vertex coordinates (as outputted by the geometry shader, or by the tessellation
|
||||
//! evaluation shader if there's no geometry shader, or by the vertex shader if there's no
|
||||
//! geometry shader nor tessellation evaluation shader) are turned into screen-space coordinates.
|
||||
//! - The list of pixels that cover each triangle are determined.
|
||||
//! - A shader named the fragment shader is run once for each pixel that covers one of the
|
||||
//! triangles.
|
||||
//! - The depth test and/or the stencil test are performed.
|
||||
//! - The output of the fragment shader is written to the framebuffer attachments, possibly by
|
||||
//! mixing it with the existing values.
|
||||
//!
|
||||
//! All the sub-modules of this module (with the exception of `cache`) correspond to the various
|
||||
//! stages of graphical pipelines.
|
||||
//!
|
||||
//! > **Note**: With the exception of the addition of the tessellation shaders and the geometry
|
||||
//! > shader, these steps haven't changed in the past decade. If you are familiar with shaders in
|
||||
//! > OpenGL 2 for example, don't worry as it works in the same in Vulkan.
|
||||
//!
|
||||
//! > **Note**: All the stages that consist in executing a shader are performed by a microprocessor
|
||||
//! > (unless you happen to use a software implementation of Vulkan). As for the other stages,
|
||||
//! > some hardware (usually desktop graphics cards) have dedicated chips that will execute them
|
||||
//! > while some other hardware (usually mobile) perform them with the microprocessor as well. In
|
||||
//! > the latter situation, the implementation will usually glue these steps to your shaders.
|
||||
//!
|
||||
//! Creating a graphics pipeline follows the same principle as a compute pipeline, except that
|
||||
//! you must pass multiple shaders alongside with configuration for the other steps.
|
||||
//!
|
||||
//! TODO: add an example
|
||||
|
||||
// TODO: graphics pipeline params are deprecated, but are still the primary implementation in order
|
||||
// to avoid duplicating code, so we hide the warnings for now
|
||||
#![allow(deprecated)]
|
||||
|
||||
pub use self::compute_pipeline::ComputePipeline;
|
||||
pub use self::compute_pipeline::ComputePipelineCreationError;
|
||||
pub use self::graphics_pipeline::GraphicsPipeline;
|
||||
pub use self::graphics_pipeline::GraphicsPipelineBuilder;
|
||||
pub use self::graphics_pipeline::GraphicsPipelineCreationError;
|
||||
use self::layout::PipelineLayout;
|
||||
pub use self::compute::ComputePipeline;
|
||||
pub use self::graphics::GraphicsPipeline;
|
||||
pub use self::layout::PipelineLayout;
|
||||
use std::sync::Arc;
|
||||
|
||||
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 compute;
|
||||
pub mod graphics;
|
||||
pub mod layout;
|
||||
pub mod multisample;
|
||||
pub mod rasterization;
|
||||
pub mod tessellation;
|
||||
pub mod vertex;
|
||||
pub mod viewport;
|
||||
|
||||
// A trait for operations shared between pipeline types.
|
||||
/// A trait for operations shared between pipeline types.
|
||||
pub trait Pipeline {
|
||||
/// Returns the bind point of this pipeline.
|
||||
fn bind_point(&self) -> PipelineBindPoint;
|
||||
@ -110,6 +40,12 @@ pub trait Pipeline {
|
||||
fn num_used_descriptor_sets(&self) -> u32;
|
||||
}
|
||||
|
||||
/// The type of a pipeline.
|
||||
///
|
||||
/// When binding a pipeline or descriptor sets in a command buffer, the state for each bind point
|
||||
/// is independent from the others. This means that it is possible, for example, to bind a graphics
|
||||
/// pipeline without disturbing any bound compute pipeline. Likewise, binding descriptor sets for
|
||||
/// the `Compute` bind point does not affect sets that were bound to the `Graphics` bind point.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i32)]
|
||||
pub enum PipelineBindPoint {
|
||||
|
@ -1,80 +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.
|
||||
|
||||
use crate::format::Format;
|
||||
use crate::pipeline::vertex::VertexInput;
|
||||
use crate::pipeline::vertex::VertexMemberTy;
|
||||
use crate::shader::ShaderInterface;
|
||||
use crate::SafeDeref;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
/// Trait for types that describe the definition of the vertex input used by a graphics pipeline.
|
||||
pub unsafe trait VertexDefinition {
|
||||
/// Builds the vertex definition to use to link this definition to a vertex shader's input
|
||||
/// interface.
|
||||
// TODO: remove error return, move checks to GraphicsPipelineBuilder
|
||||
fn definition(
|
||||
&self,
|
||||
interface: &ShaderInterface,
|
||||
) -> Result<VertexInput, IncompatibleVertexDefinitionError>;
|
||||
}
|
||||
|
||||
unsafe impl<T> VertexDefinition for T
|
||||
where
|
||||
T: SafeDeref,
|
||||
T::Target: VertexDefinition,
|
||||
{
|
||||
#[inline]
|
||||
fn definition(
|
||||
&self,
|
||||
interface: &ShaderInterface,
|
||||
) -> Result<VertexInput, IncompatibleVertexDefinitionError> {
|
||||
(**self).definition(interface)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when the vertex definition doesn't match the input of the vertex shader.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum IncompatibleVertexDefinitionError {
|
||||
/// An attribute of the vertex shader is missing in the vertex source.
|
||||
MissingAttribute {
|
||||
/// Name of the missing attribute.
|
||||
attribute: String,
|
||||
},
|
||||
|
||||
/// The format of an attribute does not match.
|
||||
FormatMismatch {
|
||||
/// Name of the attribute.
|
||||
attribute: String,
|
||||
/// The format in the vertex shader.
|
||||
shader: (Format, usize),
|
||||
/// The format in the vertex definition.
|
||||
definition: (VertexMemberTy, usize),
|
||||
},
|
||||
}
|
||||
|
||||
impl error::Error for IncompatibleVertexDefinitionError {}
|
||||
|
||||
impl fmt::Display for IncompatibleVertexDefinitionError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"{}",
|
||||
match *self {
|
||||
IncompatibleVertexDefinitionError::MissingAttribute { .. } =>
|
||||
"an attribute is missing",
|
||||
IncompatibleVertexDefinitionError::FormatMismatch { .. } => {
|
||||
"the format of an attribute does not match"
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -1,193 +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.
|
||||
|
||||
//! # Vertex sources definition
|
||||
//!
|
||||
//! When you create a graphics pipeline object, you need to pass an object which indicates the
|
||||
//! layout of the vertex buffer(s) that will serve as input for the vertex shader. This is done
|
||||
//! by passing an implementation of the `VertexDefinition` trait.
|
||||
//!
|
||||
//! In addition to this, the object that you pass when you create the graphics pipeline must also
|
||||
//! implement the `VertexSource` trait. This trait has a template parameter which corresponds to the
|
||||
//! list of vertex buffers.
|
||||
//!
|
||||
//! The vulkano library provides some structs that already implement these traits.
|
||||
//! The most common situation is a single vertex buffer and no instancing, in which case you can
|
||||
//! pass a `SingleBufferDefinition` when you create the pipeline.
|
||||
//!
|
||||
//! # Implementing `Vertex`
|
||||
//!
|
||||
//! The implementations of the `VertexDefinition` trait that are provided by vulkano (like
|
||||
//! `SingleBufferDefinition`) require you to use a buffer whose content is `[V]` where `V`
|
||||
//! implements the `Vertex` trait.
|
||||
//!
|
||||
//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!`
|
||||
//! macro.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```ignore // TODO:
|
||||
//! # #[macro_use] extern crate vulkano
|
||||
//! # fn main() {
|
||||
//! # use std::sync::Arc;
|
||||
//! # use vulkano::device::Device;
|
||||
//! # use vulkano::device::Queue;
|
||||
//! use vulkano::buffer::BufferAccess;
|
||||
//! use vulkano::buffer::BufferUsage;
|
||||
//! use vulkano::memory::HostVisible;
|
||||
//! use vulkano::pipeline::vertex::;
|
||||
//! # let device: Arc<Device> = return;
|
||||
//! # let queue: Arc<Queue> = return;
|
||||
//!
|
||||
//! struct Vertex {
|
||||
//! position: [f32; 2]
|
||||
//! }
|
||||
//!
|
||||
//! impl_vertex!(Vertex, position);
|
||||
//!
|
||||
//! let usage = BufferUsage {
|
||||
//! vertex_buffer: true,
|
||||
//! .. BufferUsage::none()
|
||||
//! };
|
||||
//!
|
||||
//! let vertex_buffer = BufferAccess::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue)
|
||||
//! .expect("failed to create buffer");
|
||||
//!
|
||||
//! // TODO: finish example
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
pub use self::buffers::BuffersDefinition;
|
||||
pub use self::collection::VertexBuffersCollection;
|
||||
pub use self::definition::IncompatibleVertexDefinitionError;
|
||||
pub use self::definition::VertexDefinition;
|
||||
pub use self::impl_vertex::VertexMember;
|
||||
pub use self::vertex::Vertex;
|
||||
pub use self::vertex::VertexMemberInfo;
|
||||
pub use self::vertex::VertexMemberTy;
|
||||
use crate::format::Format;
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
mod buffers;
|
||||
mod collection;
|
||||
mod definition;
|
||||
mod impl_vertex;
|
||||
mod vertex;
|
||||
|
||||
/// A description of the vertex input of a graphics pipeline.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct VertexInput {
|
||||
bindings: FnvHashMap<u32, VertexInputBinding>,
|
||||
attributes: FnvHashMap<u32, VertexInputAttribute>,
|
||||
}
|
||||
|
||||
impl VertexInput {
|
||||
/// Constructs a new `VertexInput` from the given bindings and attributes.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if any element of `attributes` refers to a binding number that is not provided in
|
||||
/// `bindings`.
|
||||
#[inline]
|
||||
pub fn new(
|
||||
bindings: impl IntoIterator<Item = (u32, VertexInputBinding)>,
|
||||
attributes: impl IntoIterator<Item = (u32, VertexInputAttribute)>,
|
||||
) -> VertexInput {
|
||||
let bindings: FnvHashMap<_, _> = bindings.into_iter().collect();
|
||||
let attributes: FnvHashMap<_, _> = attributes.into_iter().collect();
|
||||
|
||||
assert!(attributes
|
||||
.iter()
|
||||
.all(|(_, attr)| bindings.contains_key(&attr.binding)));
|
||||
|
||||
VertexInput {
|
||||
bindings,
|
||||
attributes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a new empty `VertexInput`.
|
||||
#[inline]
|
||||
pub fn empty() -> VertexInput {
|
||||
VertexInput {
|
||||
bindings: Default::default(),
|
||||
attributes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator of the binding numbers and their descriptions.
|
||||
#[inline]
|
||||
pub fn bindings(&self) -> impl ExactSizeIterator<Item = (u32, &VertexInputBinding)> {
|
||||
self.bindings.iter().map(|(&key, val)| (key, val))
|
||||
}
|
||||
|
||||
/// Returns an iterator of the attribute numbers and their descriptions.
|
||||
#[inline]
|
||||
pub fn attributes(&self) -> impl ExactSizeIterator<Item = (u32, &VertexInputAttribute)> {
|
||||
self.attributes.iter().map(|(&key, val)| (key, val))
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a single vertex buffer binding in a graphics pipeline.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VertexInputBinding {
|
||||
/// The size of each element in the vertex buffer.
|
||||
pub stride: u32,
|
||||
/// How often the vertex input should advance to the next element.
|
||||
pub input_rate: VertexInputRate,
|
||||
}
|
||||
|
||||
/// Describes how each vertex shader input attribute should be read from a vertex buffer.
|
||||
///
|
||||
/// An attribute can contain a maximum of four data elements, described by a particular `Format`.
|
||||
/// For shader inputs that are larger than this, such as matrices or arrays, multiple separate
|
||||
/// attributes should be used, with increasing offsets.
|
||||
///
|
||||
/// For example, to pass a `mat4` to the shader using sixteen `f32` as input, you would include four
|
||||
/// attributes with `Format::R32G32B32A32Sfloat`, using the offset of the matrix from the start of
|
||||
/// the vertex buffer element, plus 0, 16, 32, 48 respectively.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VertexInputAttribute {
|
||||
/// The vertex buffer binding number that this attribute should take its data from.
|
||||
pub binding: u32,
|
||||
/// The size and type of the vertex data.
|
||||
pub format: Format,
|
||||
/// Number of bytes between the start of a vertex buffer element and the location of attribute.
|
||||
pub offset: u32,
|
||||
}
|
||||
|
||||
/// How the vertex source should be unrolled.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum VertexInputRate {
|
||||
/// Each element of the source corresponds to a vertex.
|
||||
Vertex,
|
||||
|
||||
/// Each element of the source corresponds to an instance.
|
||||
///
|
||||
/// `divisor` indicates how many consecutive instances will use the same instance buffer data.
|
||||
/// This value must be 1, unless the
|
||||
/// [`vertex_attribute_instance_rate_divisor`](crate::device::Features::vertex_attribute_instance_rate_divisor)
|
||||
/// feature has been enabled on the device.
|
||||
///
|
||||
/// `divisor` can be 0 if the
|
||||
/// [`vertex_attribute_instance_rate_zero_divisor`](crate::device::Features::vertex_attribute_instance_rate_zero_divisor)
|
||||
/// feature is also enabled. This means that every vertex will use the same vertex and instance
|
||||
/// data.
|
||||
Instance { divisor: u32 },
|
||||
}
|
||||
|
||||
impl From<VertexInputRate> for ash::vk::VertexInputRate {
|
||||
#[inline]
|
||||
fn from(val: VertexInputRate) -> Self {
|
||||
match val {
|
||||
VertexInputRate::Vertex => ash::vk::VertexInputRate::VERTEX,
|
||||
VertexInputRate::Instance { .. } => ash::vk::VertexInputRate::INSTANCE,
|
||||
}
|
||||
}
|
||||
}
|
@ -126,7 +126,10 @@ impl RenderPassDesc {
|
||||
};
|
||||
|
||||
for element in shader_interface.elements() {
|
||||
for location in element.location.clone() {
|
||||
assert!(!element.ty.is_64bit); // TODO: implement
|
||||
let location_range = element.location..element.location + element.ty.num_locations();
|
||||
|
||||
for location in location_range {
|
||||
let attachment_id = match pass_descr.color_attachments.get(location as usize) {
|
||||
Some(a) => a.0,
|
||||
None => return false,
|
||||
|
@ -65,7 +65,7 @@
|
||||
use crate::check_errors;
|
||||
use crate::device::Device;
|
||||
use crate::device::DeviceOwned;
|
||||
use crate::pipeline::depth_stencil::CompareOp;
|
||||
use crate::pipeline::graphics::depth_stencil::CompareOp;
|
||||
use crate::Error;
|
||||
use crate::OomError;
|
||||
use crate::VulkanObject;
|
||||
|
@ -22,7 +22,7 @@ use crate::descriptor_set::layout::DescriptorType;
|
||||
use crate::device::Device;
|
||||
use crate::format::Format;
|
||||
use crate::image::view::ImageViewType;
|
||||
use crate::pipeline::input_assembly::PrimitiveTopology;
|
||||
use crate::pipeline::graphics::input_assembly::PrimitiveTopology;
|
||||
use crate::pipeline::layout::PipelineLayoutPcRange;
|
||||
use crate::shader::spirv::{Capability, Spirv, SpirvError};
|
||||
use crate::sync::PipelineStages;
|
||||
@ -42,7 +42,6 @@ use std::fmt::Display;
|
||||
use std::mem;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ops::BitOr;
|
||||
use std::ops::Range;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -755,6 +754,7 @@ impl ShaderInterface {
|
||||
///
|
||||
/// - Must only provide one entry per location.
|
||||
/// - The format of each element must not be larger than 128 bits.
|
||||
// TODO: 4x64 bit formats are possible, but they require special handling.
|
||||
// TODO: could this be made safe?
|
||||
#[inline]
|
||||
pub unsafe fn new_unchecked(elements: Vec<ShaderInterfaceEntry>) -> ShaderInterface {
|
||||
@ -786,11 +786,12 @@ impl ShaderInterface {
|
||||
}
|
||||
|
||||
for a in self.elements() {
|
||||
for loc in a.location.clone() {
|
||||
let location_range = a.location..a.location + a.ty.num_locations();
|
||||
for loc in location_range {
|
||||
let b = match other
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| loc >= e.location.start && loc < e.location.end)
|
||||
.find(|e| loc >= e.location && loc < e.location + e.ty.num_locations())
|
||||
{
|
||||
None => {
|
||||
return Err(ShaderInterfaceMismatchError::MissingElement { location: loc })
|
||||
@ -798,11 +799,11 @@ impl ShaderInterface {
|
||||
Some(b) => b,
|
||||
};
|
||||
|
||||
if a.format != b.format {
|
||||
return Err(ShaderInterfaceMismatchError::FormatMismatch {
|
||||
if a.ty != b.ty {
|
||||
return Err(ShaderInterfaceMismatchError::TypeMismatch {
|
||||
location: loc,
|
||||
self_format: a.format,
|
||||
other_format: b.format,
|
||||
self_ty: a.ty,
|
||||
other_ty: b.ty,
|
||||
});
|
||||
}
|
||||
|
||||
@ -824,12 +825,76 @@ impl ShaderInterface {
|
||||
/// Entry of a shader interface definition.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShaderInterfaceEntry {
|
||||
/// Range of locations covered by the element.
|
||||
pub location: Range<u32>,
|
||||
/// Format of a each location of the element.
|
||||
pub format: Format,
|
||||
/// The location slot that the variable starts at.
|
||||
pub location: u32,
|
||||
|
||||
/// The component slot that the variable starts at. Must be in the range 0..=3.
|
||||
pub component: u32,
|
||||
|
||||
/// Name of the element, or `None` if the name is unknown.
|
||||
pub name: Option<Cow<'static, str>>,
|
||||
|
||||
/// The type of the variable.
|
||||
pub ty: ShaderInterfaceEntryType,
|
||||
}
|
||||
|
||||
/// The type of a variable in a shader interface.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ShaderInterfaceEntryType {
|
||||
/// The base numeric type.
|
||||
pub base_type: ShaderScalarType,
|
||||
|
||||
/// The number of vector components. Must be in the range 1..=4.
|
||||
pub num_components: u32,
|
||||
|
||||
/// The number of array elements or matrix columns.
|
||||
pub num_elements: u32,
|
||||
|
||||
/// Whether the base type is 64 bits wide. If true, each item of the base type takes up two
|
||||
/// component slots instead of one.
|
||||
pub is_64bit: bool,
|
||||
}
|
||||
|
||||
impl ShaderInterfaceEntryType {
|
||||
pub(crate) fn to_format(&self) -> Format {
|
||||
assert!(!self.is_64bit); // TODO: implement
|
||||
match self.base_type {
|
||||
ShaderScalarType::Float => match self.num_components {
|
||||
1 => Format::R32_SFLOAT,
|
||||
2 => Format::R32G32_SFLOAT,
|
||||
3 => Format::R32G32B32_SFLOAT,
|
||||
4 => Format::R32G32B32A32_SFLOAT,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
ShaderScalarType::Sint => match self.num_components {
|
||||
1 => Format::R32_SINT,
|
||||
2 => Format::R32G32_SINT,
|
||||
3 => Format::R32G32B32_SINT,
|
||||
4 => Format::R32G32B32A32_SINT,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
ShaderScalarType::Uint => match self.num_components {
|
||||
1 => Format::R32_UINT,
|
||||
2 => Format::R32G32_UINT,
|
||||
3 => Format::R32G32B32_UINT,
|
||||
4 => Format::R32G32B32A32_UINT,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn num_locations(&self) -> u32 {
|
||||
assert!(!self.is_64bit); // TODO: implement
|
||||
self.num_elements
|
||||
}
|
||||
}
|
||||
|
||||
/// The numeric base type of a shader interface variable.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ShaderScalarType {
|
||||
Float,
|
||||
Sint,
|
||||
Uint,
|
||||
}
|
||||
|
||||
/// Error that can happen when the interface mismatches between two shader stages.
|
||||
@ -849,14 +914,14 @@ pub enum ShaderInterfaceMismatchError {
|
||||
location: u32,
|
||||
},
|
||||
|
||||
/// The format of an element does not match.
|
||||
FormatMismatch {
|
||||
/// The type of an element does not match.
|
||||
TypeMismatch {
|
||||
/// Location of the element that mismatches.
|
||||
location: u32,
|
||||
/// Format in the first interface.
|
||||
self_format: Format,
|
||||
/// Format in the second interface.
|
||||
other_format: Format,
|
||||
/// Type in the first interface.
|
||||
self_ty: ShaderInterfaceEntryType,
|
||||
/// Type in the second interface.
|
||||
other_ty: ShaderInterfaceEntryType,
|
||||
},
|
||||
}
|
||||
|
||||
@ -873,8 +938,8 @@ impl fmt::Display for ShaderInterfaceMismatchError {
|
||||
"the number of elements mismatches"
|
||||
}
|
||||
ShaderInterfaceMismatchError::MissingElement { .. } => "an element is missing",
|
||||
ShaderInterfaceMismatchError::FormatMismatch { .. } => {
|
||||
"the format of an element does not match"
|
||||
ShaderInterfaceMismatchError::TypeMismatch { .. } => {
|
||||
"the type of an element does not match"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
use crate::descriptor_set::layout::DescriptorType;
|
||||
use crate::image::view::ImageViewType;
|
||||
use crate::shader::ShaderScalarType;
|
||||
use crate::DeviceSize;
|
||||
use crate::{
|
||||
format::Format,
|
||||
@ -21,8 +22,8 @@ use crate::{
|
||||
Instruction, Spirv, StorageClass,
|
||||
},
|
||||
DescriptorRequirements, EntryPointInfo, GeometryShaderExecution, GeometryShaderInput,
|
||||
ShaderExecution, ShaderInterface, ShaderInterfaceEntry, ShaderStage,
|
||||
SpecializationConstantRequirements,
|
||||
ShaderExecution, ShaderInterface, ShaderInterfaceEntry, ShaderInterfaceEntryType,
|
||||
ShaderStage, SpecializationConstantRequirements,
|
||||
},
|
||||
};
|
||||
use fnv::FnvHashMap;
|
||||
@ -738,12 +739,23 @@ fn shader_interface(
|
||||
result_id, name,
|
||||
)
|
||||
});
|
||||
let component = id_info
|
||||
.iter_decoration()
|
||||
.find_map(|instruction| match instruction {
|
||||
Instruction::Decorate {
|
||||
decoration: Decoration::Component { component },
|
||||
..
|
||||
} => Some(*component),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
let (format, num_locations) = format_of_type(spirv, result_type_id, ignore_first_array);
|
||||
assert!(num_locations >= 1);
|
||||
let ty = shader_interface_type_of(spirv, result_type_id, ignore_first_array);
|
||||
assert!(ty.num_elements >= 1);
|
||||
Some(ShaderInterfaceEntry {
|
||||
location: location..location + num_locations,
|
||||
format,
|
||||
location,
|
||||
component,
|
||||
ty,
|
||||
name,
|
||||
})
|
||||
})
|
||||
@ -752,20 +764,20 @@ fn shader_interface(
|
||||
// Checking for overlapping elements.
|
||||
for (offset, element1) in elements.iter().enumerate() {
|
||||
for element2 in elements.iter().skip(offset + 1) {
|
||||
if element1.location.start == element2.location.start
|
||||
|| (element1.location.start < element2.location.start
|
||||
&& element1.location.end > element2.location.start)
|
||||
|| (element2.location.start < element1.location.start
|
||||
&& element2.location.end > element1.location.start)
|
||||
if element1.location == element2.location
|
||||
|| (element1.location < element2.location
|
||||
&& element1.location + element1.ty.num_locations() > element2.location)
|
||||
|| (element2.location < element1.location
|
||||
&& element2.location + element2.ty.num_locations() > element1.location)
|
||||
{
|
||||
panic!(
|
||||
"The locations of attributes `{:?}` ({}..{}) and `{:?}` ({}..{}) overlap",
|
||||
element1.name,
|
||||
element1.location.start,
|
||||
element1.location.end,
|
||||
element1.location,
|
||||
element1.location + element1.ty.num_locations(),
|
||||
element2.name,
|
||||
element2.location.start,
|
||||
element2.location.end,
|
||||
element2.location,
|
||||
element2.location + element2.ty.num_locations(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -886,39 +898,46 @@ fn offset_of_struct(spirv: &Spirv, id: Id) -> u32 {
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Returns the vulkano `Format` and number of occupied locations from an id.
|
||||
///
|
||||
/// If `ignore_first_array` is true, the function expects the outermost instruction to be
|
||||
/// `OpTypeArray`. If it's the case, the OpTypeArray will be ignored. If not, the function will
|
||||
/// panic.
|
||||
fn format_of_type(spirv: &Spirv, id: Id, ignore_first_array: bool) -> (Format, u32) {
|
||||
fn shader_interface_type_of(
|
||||
spirv: &Spirv,
|
||||
id: Id,
|
||||
ignore_first_array: bool,
|
||||
) -> ShaderInterfaceEntryType {
|
||||
match spirv.id(id).instruction() {
|
||||
&Instruction::TypeInt {
|
||||
width, signedness, ..
|
||||
} => {
|
||||
assert!(!ignore_first_array);
|
||||
let format = match (width, signedness) {
|
||||
(8, 1) => Format::R8_SINT,
|
||||
(8, 0) => Format::R8_UINT,
|
||||
(16, 1) => Format::R16_SINT,
|
||||
(16, 0) => Format::R16_UINT,
|
||||
(32, 1) => Format::R32_SINT,
|
||||
(32, 0) => Format::R32_UINT,
|
||||
(64, 1) => Format::R64_SINT,
|
||||
(64, 0) => Format::R64_UINT,
|
||||
_ => panic!(),
|
||||
};
|
||||
(format, 1)
|
||||
ShaderInterfaceEntryType {
|
||||
base_type: match signedness {
|
||||
0 => ShaderScalarType::Uint,
|
||||
1 => ShaderScalarType::Sint,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
num_components: 1,
|
||||
num_elements: 1,
|
||||
is_64bit: match width {
|
||||
8 | 16 | 32 => false,
|
||||
64 => true,
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
&Instruction::TypeFloat { width, .. } => {
|
||||
assert!(!ignore_first_array);
|
||||
let format = match width {
|
||||
16 => Format::R16_SFLOAT,
|
||||
32 => Format::R32_SFLOAT,
|
||||
64 => Format::R64_SFLOAT,
|
||||
_ => panic!(),
|
||||
};
|
||||
(format, 1)
|
||||
ShaderInterfaceEntryType {
|
||||
base_type: ShaderScalarType::Float,
|
||||
num_components: 1,
|
||||
num_elements: 1,
|
||||
is_64bit: match width {
|
||||
16 | 32 => false,
|
||||
64 => true,
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
&Instruction::TypeVector {
|
||||
component_type,
|
||||
@ -926,53 +945,10 @@ fn format_of_type(spirv: &Spirv, id: Id, ignore_first_array: bool) -> (Format, u
|
||||
..
|
||||
} => {
|
||||
assert!(!ignore_first_array);
|
||||
// TODO: Add handling of 64-bit types, which need special care. See the sections
|
||||
// "Attribute Location and Component Assignment" and "Vertex Input Extraction" in the spec:
|
||||
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap22.html#fxvertex-attrib-location
|
||||
let format = match spirv.id(component_type).instruction() {
|
||||
Instruction::TypeInt {
|
||||
width, signedness, ..
|
||||
} => match (component_count, width, signedness) {
|
||||
(1, 8, 1) => Format::R8_SINT,
|
||||
(1, 8, 0) => Format::R8_UINT,
|
||||
(1, 16, 1) => Format::R16_SINT,
|
||||
(1, 16, 0) => Format::R16_UINT,
|
||||
(1, 32, 1) => Format::R32_SINT,
|
||||
(1, 32, 0) => Format::R32_UINT,
|
||||
(2, 8, 1) => Format::R8G8_SINT,
|
||||
(2, 8, 0) => Format::R8G8_UINT,
|
||||
(2, 16, 1) => Format::R16G16_SINT,
|
||||
(2, 16, 0) => Format::R16G16_UINT,
|
||||
(2, 32, 1) => Format::R32G32_SINT,
|
||||
(2, 32, 0) => Format::R32G32_UINT,
|
||||
(3, 8, 1) => Format::R8G8B8_SINT,
|
||||
(3, 8, 0) => Format::R8G8B8_UINT,
|
||||
(3, 16, 1) => Format::R16G16B16_SINT,
|
||||
(3, 16, 0) => Format::R16G16B16_UINT,
|
||||
(3, 32, 1) => Format::R32G32B32_SINT,
|
||||
(3, 32, 0) => Format::R32G32B32_UINT,
|
||||
(4, 8, 1) => Format::R8G8B8A8_SINT,
|
||||
(4, 8, 0) => Format::R8G8B8A8_UINT,
|
||||
(4, 16, 1) => Format::R16G16B16A16_SINT,
|
||||
(4, 16, 0) => Format::R16G16B16A16_UINT,
|
||||
(4, 32, 1) => Format::R32G32B32A32_SINT,
|
||||
(4, 32, 0) => Format::R32G32B32A32_UINT,
|
||||
_ => panic!(),
|
||||
},
|
||||
&Instruction::TypeFloat { width, .. } => match (component_count, width) {
|
||||
(1, 16) => Format::R16_SFLOAT,
|
||||
(1, 32) => Format::R32_SFLOAT,
|
||||
(2, 16) => Format::R16G16_SFLOAT,
|
||||
(2, 32) => Format::R32G32_SFLOAT,
|
||||
(3, 16) => Format::R16G16B16_SFLOAT,
|
||||
(3, 32) => Format::R32G32B32_SFLOAT,
|
||||
(4, 16) => Format::R16G16B16A16_SFLOAT,
|
||||
(4, 32) => Format::R32G32B32A32_SFLOAT,
|
||||
_ => panic!(),
|
||||
},
|
||||
_ => panic!(),
|
||||
};
|
||||
(format, 1)
|
||||
ShaderInterfaceEntryType {
|
||||
num_components: component_count,
|
||||
..shader_interface_type_of(spirv, component_type, false)
|
||||
}
|
||||
}
|
||||
&Instruction::TypeMatrix {
|
||||
column_type,
|
||||
@ -980,8 +956,10 @@ fn format_of_type(spirv: &Spirv, id: Id, ignore_first_array: bool) -> (Format, u
|
||||
..
|
||||
} => {
|
||||
assert!(!ignore_first_array);
|
||||
let (format, num_locations) = format_of_type(spirv, column_type, false);
|
||||
(format, num_locations * column_count)
|
||||
ShaderInterfaceEntryType {
|
||||
num_elements: column_count,
|
||||
..shader_interface_type_of(spirv, column_type, false)
|
||||
}
|
||||
}
|
||||
&Instruction::TypeArray {
|
||||
element_type,
|
||||
@ -989,10 +967,10 @@ fn format_of_type(spirv: &Spirv, id: Id, ignore_first_array: bool) -> (Format, u
|
||||
..
|
||||
} => {
|
||||
if ignore_first_array {
|
||||
format_of_type(spirv, element_type, false)
|
||||
shader_interface_type_of(spirv, element_type, false)
|
||||
} else {
|
||||
let (format, num_locations) = format_of_type(spirv, element_type, false);
|
||||
let len = spirv
|
||||
let mut ty = shader_interface_type_of(spirv, element_type, false);
|
||||
let num_elements = spirv
|
||||
.instructions()
|
||||
.iter()
|
||||
.filter_map(|e| match e {
|
||||
@ -1004,12 +982,18 @@ fn format_of_type(spirv: &Spirv, id: Id, ignore_first_array: bool) -> (Format, u
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.expect("failed to find array length");
|
||||
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64) as u32;
|
||||
(format, num_locations * len)
|
||||
.expect("failed to find array length")
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(0u64, |a, &b| (a << 32) | b as u64)
|
||||
as u32;
|
||||
ty.num_elements *= num_elements;
|
||||
ty
|
||||
}
|
||||
}
|
||||
&Instruction::TypePointer { ty, .. } => format_of_type(spirv, ty, ignore_first_array),
|
||||
&Instruction::TypePointer { ty, .. } => {
|
||||
shader_interface_type_of(spirv, ty, ignore_first_array)
|
||||
}
|
||||
_ => panic!("Type {} not found or invalid", id),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user