diff --git a/examples/src/bin/buffer-pool.rs b/examples/src/bin/buffer-pool.rs index 37793bba..b0f6da3a 100644 --- a/examples/src/bin/buffer-pool.rs +++ b/examples/src/bin/buffer-pool.rs @@ -32,7 +32,7 @@ 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -338,11 +338,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/clear_attachments.rs b/examples/src/bin/clear_attachments.rs index e2138a78..9d2d8bec 100644 --- a/examples/src/bin/clear_attachments.rs +++ b/examples/src/bin/clear_attachments.rs @@ -17,7 +17,7 @@ use vulkano::image::{view::ImageView, ImageUsage, SwapchainImage}; use vulkano::instance::{Instance, InstanceCreateInfo}; use vulkano::pipeline::graphics::viewport::ViewportState; use vulkano::pipeline::GraphicsPipeline; -use vulkano::render_pass::{Framebuffer, RenderPass, Subpass}; +use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -295,11 +295,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/deferred/frame/system.rs b/examples/src/bin/deferred/frame/system.rs index a7097d7d..bb5ef075 100644 --- a/examples/src/bin/deferred/frame/system.rs +++ b/examples/src/bin/deferred/frame/system.rs @@ -26,6 +26,7 @@ use vulkano::image::ImageAccess; use vulkano::image::ImageUsage; use vulkano::image::ImageViewAbstract; use vulkano::render_pass::Framebuffer; +use vulkano::render_pass::FramebufferCreateInfo; use vulkano::render_pass::RenderPass; use vulkano::render_pass::Subpass; use vulkano::sync::GpuFuture; @@ -281,17 +282,19 @@ impl FrameSystem { // Build the framebuffer. The image must be attached in the same order as they were defined // with the `ordered_passes_renderpass!` macro. - let framebuffer = Framebuffer::start(self.render_pass.clone()) - .add(final_image.clone()) - .unwrap() - .add(self.diffuse_buffer.clone()) - .unwrap() - .add(self.normals_buffer.clone()) - .unwrap() - .add(self.depth_buffer.clone()) - .unwrap() - .build() - .unwrap(); + let framebuffer = Framebuffer::new( + self.render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![ + final_image.clone(), + self.diffuse_buffer.clone(), + self.normals_buffer.clone(), + self.depth_buffer.clone(), + ], + ..Default::default() + }, + ) + .unwrap(); // Start the command buffer builder that will be filled throughout the frame handling. let mut command_buffer_builder = AutoCommandBufferBuilder::primary( @@ -448,8 +451,7 @@ impl<'f, 's: 'f> DrawPass<'f, 's> { /// Returns the dimensions in pixels of the viewport. #[inline] pub fn viewport_dimensions(&self) -> [u32; 2] { - let dims = self.frame.framebuffer.dimensions(); - [dims[0], dims[1]] + self.frame.framebuffer.extent() } /// Returns the 4x4 matrix that turns world coordinates into 2D coordinates on the framebuffer. @@ -470,9 +472,8 @@ impl<'f, 's: 'f> LightingPass<'f, 's> { /// /// All the objects will be colored with an intensity of `color`. pub fn ambient_light(&mut self, color: [f32; 3]) { - let dims = self.frame.framebuffer.dimensions(); let command_buffer = self.frame.system.ambient_lighting_system.draw( - [dims[0], dims[1]], + self.frame.framebuffer.extent(), self.frame.system.diffuse_buffer.clone(), color, ); @@ -489,9 +490,8 @@ impl<'f, 's: 'f> LightingPass<'f, 's> { /// All the objects will be colored with an intensity varying between `[0, 0, 0]` and `color`, /// depending on the dot product of their normal and `direction`. pub fn directional_light(&mut self, direction: Vector3, color: [f32; 3]) { - let dims = self.frame.framebuffer.dimensions(); let command_buffer = self.frame.system.directional_lighting_system.draw( - [dims[0], dims[1]], + self.frame.framebuffer.extent(), self.frame.system.diffuse_buffer.clone(), self.frame.system.normals_buffer.clone(), direction, @@ -511,10 +511,9 @@ impl<'f, 's: 'f> LightingPass<'f, 's> { /// depending on their distance with `position`. Objects that aren't facing `position` won't /// receive any light. pub fn point_light(&mut self, position: Vector3, color: [f32; 3]) { - let dims = self.frame.framebuffer.dimensions(); let command_buffer = { self.frame.system.point_lighting_system.draw( - [dims[0], dims[1]], + self.frame.framebuffer.extent(), self.frame.system.diffuse_buffer.clone(), self.frame.system.normals_buffer.clone(), self.frame.system.depth_buffer.clone(), diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index 6c1b077b..2ad46f6b 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -557,7 +557,7 @@ fn window_size_dependent_setup( render_pass: Arc, viewport: &mut Viewport, ) -> Vec> { - use vulkano::image::ImageAccess; + use vulkano::{image::ImageAccess, render_pass::FramebufferCreateInfo}; let dimensions = images[0].dimensions().width_height(); viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32]; @@ -566,11 +566,14 @@ fn window_size_dependent_setup( .map(|image| -> Arc { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/image-self-copy-blit/main.rs b/examples/src/bin/image-self-copy-blit/main.rs index 8459b026..d43dcdc0 100644 --- a/examples/src/bin/image-self-copy-blit/main.rs +++ b/examples/src/bin/image-self-copy-blit/main.rs @@ -25,7 +25,7 @@ use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveT 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -401,11 +401,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index c51c1340..ca566559 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -26,7 +26,7 @@ use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveT 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -335,11 +335,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index 49663241..da9e1678 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -35,7 +35,7 @@ use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveT 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -343,11 +343,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/indirect.rs b/examples/src/bin/indirect.rs index 75afec92..7bcb7e8c 100644 --- a/examples/src/bin/indirect.rs +++ b/examples/src/bin/indirect.rs @@ -46,7 +46,7 @@ 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -421,11 +421,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/instancing.rs b/examples/src/bin/instancing.rs index 14fd8d3d..8213acf0 100644 --- a/examples/src/bin/instancing.rs +++ b/examples/src/bin/instancing.rs @@ -30,7 +30,7 @@ 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -388,11 +388,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/interactive_fractal/place_over_frame.rs b/examples/src/bin/interactive_fractal/place_over_frame.rs index a2b74b58..c68be8e0 100644 --- a/examples/src/bin/interactive_fractal/place_over_frame.rs +++ b/examples/src/bin/interactive_fractal/place_over_frame.rs @@ -14,7 +14,7 @@ use vulkano::{ device::Queue, format::Format, image::ImageAccess, - render_pass::{Framebuffer, RenderPass, Subpass}, + render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sync::GpuFuture, }; @@ -68,11 +68,14 @@ impl RenderPassPlaceOverFrame { // Get dimensions let img_dims = target.image().dimensions(); // Create framebuffer (must be in same order as render pass description in `new` - let framebuffer = Framebuffer::start(self.render_pass.clone()) - .add(target) - .unwrap() - .build() - .unwrap(); + let framebuffer = Framebuffer::new( + self.render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![target], + ..Default::default() + }, + ) + .unwrap(); // Create primary command buffer builder let mut command_buffer_builder = AutoCommandBufferBuilder::primary( self.gfx_queue.device().clone(), diff --git a/examples/src/bin/msaa-renderpass.rs b/examples/src/bin/msaa-renderpass.rs index ddf9c703..023cc99a 100644 --- a/examples/src/bin/msaa-renderpass.rs +++ b/examples/src/bin/msaa-renderpass.rs @@ -83,7 +83,7 @@ use vulkano::instance::{Instance, InstanceCreateInfo}; 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::render_pass::{Framebuffer, FramebufferCreateInfo, Subpass}; use vulkano::sync::GpuFuture; fn main() { @@ -200,13 +200,14 @@ fn main() { .unwrap(); // Creating the framebuffer, the calls to `add` match the list of attachments in order. - let framebuffer = Framebuffer::start(render_pass.clone()) - .add(intermediary.clone()) - .unwrap() - .add(view.clone()) - .unwrap() - .build() - .unwrap(); + let framebuffer = Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![intermediary.clone(), view.clone()], + ..Default::default() + }, + ) + .unwrap(); // Here is the "end" of the multisampling example, as starting from here everything is the same // as in any other example. diff --git a/examples/src/bin/multi-window.rs b/examples/src/bin/multi-window.rs index 178bf918..abe8aca8 100644 --- a/examples/src/bin/multi-window.rs +++ b/examples/src/bin/multi-window.rs @@ -29,7 +29,7 @@ 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Surface, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -418,11 +418,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/multi_window_game_of_life/render_pass.rs b/examples/src/bin/multi_window_game_of_life/render_pass.rs index 34fd5211..9947016b 100644 --- a/examples/src/bin/multi_window_game_of_life/render_pass.rs +++ b/examples/src/bin/multi_window_game_of_life/render_pass.rs @@ -14,7 +14,7 @@ use vulkano::{ device::Queue, format::Format, image::ImageAccess, - render_pass::{Framebuffer, RenderPass, Subpass}, + render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sync::GpuFuture, }; @@ -68,11 +68,14 @@ impl RenderPassPlaceOverFrame { // Get dimensions let img_dims = target.image().dimensions(); // Create framebuffer (must be in same order as render pass description in `new` - let framebuffer = Framebuffer::start(self.render_pass.clone()) - .add(target) - .unwrap() - .build() - .unwrap(); + let framebuffer = Framebuffer::new( + self.render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![target], + ..Default::default() + }, + ) + .unwrap(); // Create primary command buffer builder let mut command_buffer_builder = AutoCommandBufferBuilder::primary( self.gfx_queue.device().clone(), diff --git a/examples/src/bin/multiview.rs b/examples/src/bin/multiview.rs index 4f385ff3..11720af4 100644 --- a/examples/src/bin/multiview.rs +++ b/examples/src/bin/multiview.rs @@ -33,8 +33,8 @@ 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, - Subpass, SubpassDesc, + AttachmentDescription, AttachmentReference, Framebuffer, FramebufferCreateInfo, LoadOp, + RenderPass, RenderPassCreateInfo, StoreOp, Subpass, SubpassDescription, }; use vulkano::sync::{self, GpuFuture}; @@ -197,44 +197,45 @@ fn main() { let vs = vs::load(device.clone()).unwrap(); let fs = fs::load(device.clone()).unwrap(); - let render_pass_description = RenderPassDesc::with_multiview( - vec![AttachmentDesc { - format: image.format(), + let render_pass_description = RenderPassCreateInfo { + attachments: vec![AttachmentDescription { + format: Some(image.format()), samples: SampleCount::Sample1, - load: LoadOp::Clear, - store: StoreOp::Store, - stencil_load: LoadOp::Clear, - stencil_store: StoreOp::Store, + load_op: LoadOp::Clear, + store_op: StoreOp::Store, + stencil_load_op: LoadOp::Clear, + stencil_store_op: StoreOp::Store, initial_layout: ImageLayout::ColorAttachmentOptimal, final_layout: ImageLayout::ColorAttachmentOptimal, + ..Default::default() }], - vec![SubpassDesc { - color_attachments: vec![(0, ImageLayout::ColorAttachmentOptimal)], - depth_stencil: None, - input_attachments: vec![], - resolve_attachments: vec![], - preserve_attachments: vec![], + subpasses: vec![SubpassDescription { + // the view mask indicates which layers of the framebuffer should be rendered for each + // subpass + view_mask: 0b11, + color_attachments: vec![Some(AttachmentReference { + attachment: 0, + layout: ImageLayout::ColorAttachmentOptimal, + ..Default::default() + })], + ..Default::default() }], - vec![], - MultiviewDesc { - // the view masks indicate which layers of the framebuffer - // should be rendered for each subpass - view_masks: vec![0b11], - // the correlation masks indicate sets of views that may be more efficient to render concurrently - correlation_masks: vec![0b11], - // for each dependency the view offset controls which views in the source subpass - // the views in the destination subpass depend on - view_offsets: vec![], - }, - ); + // the correlated view masks indicate sets of views that may be more efficient to render + // concurrently + correlated_view_masks: vec![0b11], + ..Default::default() + }; let render_pass = RenderPass::new(device.clone(), render_pass_description).unwrap(); - let framebuffer = Framebuffer::start(render_pass.clone()) - .add(image_view) - .unwrap() - .build() - .unwrap(); + let framebuffer = Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![image_view], + ..Default::default() + }, + ) + .unwrap(); let pipeline = GraphicsPipeline::start() .vertex_input_state(BuffersDefinition::new().vertex::()) diff --git a/examples/src/bin/occlusion-query.rs b/examples/src/bin/occlusion-query.rs index c8c11c02..acdbd5d7 100644 --- a/examples/src/bin/occlusion-query.rs +++ b/examples/src/bin/occlusion-query.rs @@ -26,7 +26,7 @@ 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}; +use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -466,13 +466,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .add(depth_attachment.clone()) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view, depth_attachment.clone()], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index c715da46..f2c5c408 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -26,7 +26,7 @@ use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveT 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -325,11 +325,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/runtime-shader/main.rs b/examples/src/bin/runtime-shader/main.rs index 7c479d05..cfe068c3 100644 --- a/examples/src/bin/runtime-shader/main.rs +++ b/examples/src/bin/runtime-shader/main.rs @@ -35,7 +35,7 @@ use vulkano::pipeline::graphics::rasterization::{CullMode, FrontFace, Rasterizat 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::shader::ShaderModule; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -317,11 +317,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index 541d9aa5..741f3133 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -29,7 +29,7 @@ use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::layout::PipelineLayout; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use vulkano::render_pass::{Framebuffer, RenderPass, Subpass}; +use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::{Filter, Sampler, SamplerAddressMode}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -454,11 +454,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/teapot/main.rs b/examples/src/bin/teapot/main.rs index c30f13b5..8c9c075b 100644 --- a/examples/src/bin/teapot/main.rs +++ b/examples/src/bin/teapot/main.rs @@ -27,7 +27,7 @@ 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::shader::ShaderModule; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -323,13 +323,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .add(depth_buffer.clone()) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view, depth_buffer.clone()], + ..Default::default() + }, + ) + .unwrap() }) .collect::>(); diff --git a/examples/src/bin/tessellation.rs b/examples/src/bin/tessellation.rs index 994e4386..670cf323 100644 --- a/examples/src/bin/tessellation.rs +++ b/examples/src/bin/tessellation.rs @@ -32,7 +32,7 @@ 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -419,11 +419,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/texture_array/main.rs b/examples/src/bin/texture_array/main.rs index 51aa8463..49ce6908 100644 --- a/examples/src/bin/texture_array/main.rs +++ b/examples/src/bin/texture_array/main.rs @@ -31,7 +31,7 @@ use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveT 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::sampler::Sampler; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; @@ -343,11 +343,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 2b928055..8134edf2 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -28,7 +28,7 @@ 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::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError}; use vulkano::sync::{self, FlushError, GpuFuture}; use vulkano_win::VkSurfaceBuild; @@ -540,11 +540,14 @@ fn window_size_dependent_setup( .iter() .map(|image| { let view = ImageView::new(image.clone()).unwrap(); - Framebuffer::start(render_pass.clone()) - .add(view) - .unwrap() - .build() - .unwrap() + Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap() }) .collect::>() } diff --git a/vulkano/src/command_buffer/auto.rs b/vulkano/src/command_buffer/auto.rs index 2d4e2b19..977018ea 100644 --- a/vulkano/src/command_buffer/auto.rs +++ b/vulkano/src/command_buffer/auto.rs @@ -135,7 +135,7 @@ struct RenderPassState { subpass: Subpass, contents: SubpassContents, attached_layers_ranges: SmallVec<[Range; 4]>, - dimensions: [u32; 3], + extent: [u32; 2], framebuffer: ash::vk::Framebuffer, // Always null for secondary command buffers } @@ -296,10 +296,7 @@ impl AutoCommandBufferBuilder { }| RenderPassState { subpass: subpass.clone(), contents: SubpassContents::Inline, - dimensions: framebuffer - .as_ref() - .map(|f| f.dimensions()) - .unwrap_or_default(), + extent: framebuffer.as_ref().map(|f| f.extent()).unwrap_or_default(), attached_layers_ranges: framebuffer .as_ref() .map(|f| f.attached_layers_ranges()) @@ -486,8 +483,7 @@ impl AutoCommandBufferBuilder { if !pipeline .subpass() .render_pass() - .desc() - .is_compatible_with_desc(&render_pass_state.subpass.render_pass().desc()) + .is_compatible_with(&render_pass_state.subpass.render_pass()) { return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass); } @@ -933,10 +929,8 @@ impl AutoCommandBufferBuilder { let render_pass_state = self.render_pass_state.as_ref().unwrap(); let subpass = &render_pass_state.subpass; - let multiview = subpass.render_pass().desc().multiview().is_some(); let has_depth_stencil_attachment = subpass.has_depth_stencil_attachment(); let num_color_attachments = subpass.num_color_attachments(); - let dimensions = render_pass_state.dimensions; let attached_layers_ranges = &render_pass_state.attached_layers_ranges; let attachments: SmallVec<[ClearAttachment; 3]> = attachments.into_iter().collect(); @@ -965,8 +959,8 @@ impl AutoCommandBufferBuilder { if rect.rect_extent[0] == 0 || rect.rect_extent[1] == 0 { return Err(ClearAttachmentsError::ZeroRectExtent); } - if rect.rect_offset[0] + rect.rect_extent[0] > dimensions[0] - || rect.rect_offset[1] + rect.rect_extent[1] > dimensions[1] + if rect.rect_offset[0] + rect.rect_extent[0] > render_pass_state.extent[0] + || rect.rect_offset[1] + rect.rect_extent[1] > render_pass_state.extent[1] { return Err(ClearAttachmentsError::RectOutOfBounds); } @@ -974,7 +968,9 @@ impl AutoCommandBufferBuilder { if rect.layer_count == 0 { return Err(ClearAttachmentsError::ZeroLayerCount); } - if multiview && (rect.base_array_layer != 0 || rect.layer_count != 1) { + if subpass.render_pass().views_used() != 0 + && (rect.base_array_layer != 0 || rect.layer_count != 1) + { return Err(ClearAttachmentsError::InvalidMultiviewLayerRange); } @@ -3212,24 +3208,22 @@ where self.ensure_outside_render_pass()?; - let clear_values = framebuffer - .render_pass() - .desc() - .convert_clear_values(clear_values); + let clear_values = framebuffer.render_pass().convert_clear_values(clear_values); let clear_values = clear_values.collect::>().into_iter(); // TODO: necessary for Send + Sync ; needs an API rework of convert_clear_values let mut clear_values_copy = clear_values.clone().enumerate(); // TODO: Proper errors for clear value errors instead of panics for (atch_i, atch_desc) in framebuffer .render_pass() - .desc() .attachments() .into_iter() .enumerate() { match clear_values_copy.next() { Some((clear_i, clear_value)) => { - if atch_desc.load == LoadOp::Clear { - let aspects = atch_desc.format.aspects(); + if atch_desc.load_op == LoadOp::Clear { + let aspects = atch_desc + .format + .map_or(ImageAspects::none(), |f| f.aspects()); if aspects.depth && aspects.stencil { assert!( @@ -3255,7 +3249,9 @@ where atch_i, clear_value, ); - } else if let Some(numeric_type) = atch_desc.format.type_color() { + } else if let Some(numeric_type) = + atch_desc.format.and_then(|f| f.type_color()) + { match numeric_type { NumericType::SFLOAT | NumericType::UFLOAT @@ -3312,28 +3308,12 @@ where panic!("Too many clear values") } - if let Some(multiview_desc) = framebuffer.render_pass().desc().multiview() { - // When multiview is enabled, at the beginning of each subpass all non-render pass state is undefined - self.inner.reset_state(); - - // ensure that the framebuffer is compatible with the render pass multiview configuration - if multiview_desc - .view_masks - .iter() - .chain(multiview_desc.correlation_masks.iter()) - .map(|&mask| 32 - mask.leading_zeros()) // calculates the highest used layer index of the mask - .any(|highest_used_layer| highest_used_layer > framebuffer.layers()) - { - panic!("A multiview mask references more layers than exist in the framebuffer"); - } - } - let framebuffer_object = framebuffer.internal_object(); self.inner .begin_render_pass(framebuffer.clone(), contents, clear_values)?; self.render_pass_state = Some(RenderPassState { subpass: framebuffer.render_pass().clone().first_subpass(), - dimensions: framebuffer.dimensions(), + extent: framebuffer.extent(), attached_layers_ranges: framebuffer.attached_layers_ranges(), contents, framebuffer: framebuffer_object, @@ -3352,12 +3332,7 @@ where if let Some(render_pass_state) = self.render_pass_state.as_ref() { if !render_pass_state.subpass.is_last_subpass() { return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch { - actual: render_pass_state - .subpass - .render_pass() - .desc() - .subpasses() - .len() as u32, + actual: render_pass_state.subpass.render_pass().subpasses().len() as u32, current: render_pass_state.subpass.index(), }); } @@ -3510,8 +3485,7 @@ where if !render_pass .subpass .render_pass() - .desc() - .is_compatible_with_desc(render_pass_state.subpass.render_pass().desc()) + .is_compatible_with(render_pass_state.subpass.render_pass()) { return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass); } @@ -3539,18 +3513,12 @@ where render_pass_state.contents = contents; } else { return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch { - actual: render_pass_state - .subpass - .render_pass() - .desc() - .subpasses() - .len() as u32, + actual: render_pass_state.subpass.render_pass().subpasses().len() as u32, current: render_pass_state.subpass.index(), }); } - if let Some(multiview) = render_pass_state.subpass.render_pass().desc().multiview() - { + if render_pass_state.subpass.render_pass().views_used() != 0 { // When multiview is enabled, at the beginning of each subpass all non-render pass state is undefined self.inner.reset_state(); } diff --git a/vulkano/src/command_buffer/synced/builder.rs b/vulkano/src/command_buffer/synced/builder.rs index e9261c88..6f235cf8 100644 --- a/vulkano/src/command_buffer/synced/builder.rs +++ b/vulkano/src/command_buffer/synced/builder.rs @@ -251,7 +251,7 @@ impl SyncCommandBufferBuilder { if let Some((memory, start_layout, end_layout, image_uninitialized_safe)) = resource { // Anti-dumbness checks. debug_assert!(memory.exclusive || start_layout == end_layout); - debug_assert!(memory.access.is_compatible_with(&memory.stages)); + debug_assert!(memory.stages.supported_access().contains(&memory.access)); debug_assert!( !matches!(resource_ty, KeyTy::Image { .. }) || end_layout != ImageLayout::Undefined diff --git a/vulkano/src/command_buffer/synced/commands.rs b/vulkano/src/command_buffer/synced/commands.rs index d8423ad9..0cee78d5 100644 --- a/vulkano/src/command_buffer/synced/commands.rs +++ b/vulkano/src/command_buffer/synced/commands.rs @@ -143,13 +143,12 @@ impl SyncCommandBufferBuilder { let resources = framebuffer .render_pass() - .desc() .attachments() .iter() .enumerate() .map(|(num, desc)| { ( - KeyTy::Image(framebuffer.attached_image_view(num).unwrap().image()), + KeyTy::Image(framebuffer.attachments()[num].image()), format!("attachment {}", num).into(), Some(( PipelineMemoryAccess { @@ -170,7 +169,7 @@ impl SyncCommandBufferBuilder { desc.initial_layout, desc.final_layout, match desc.initial_layout != ImageLayout::Undefined - || desc.load == LoadOp::Clear + || desc.load_op == LoadOp::Clear { true => ImageUninitializedSafe::Safe, false => ImageUninitializedSafe::Unsafe, diff --git a/vulkano/src/command_buffer/sys.rs b/vulkano/src/command_buffer/sys.rs index a2a9b50b..352053cf 100644 --- a/vulkano/src/command_buffer/sys.rs +++ b/vulkano/src/command_buffer/sys.rs @@ -287,10 +287,7 @@ impl UnsafeCommandBufferBuilder { .collect(); // TODO: allow customizing - let rect = [ - 0..framebuffer.dimensions()[0], - 0..framebuffer.dimensions()[1], - ]; + let rect = framebuffer.extent().map(|x| 0..x); let begin = ash::vk::RenderPassBeginInfo { render_pass: raw_render_pass, @@ -2348,8 +2345,10 @@ impl UnsafeCommandBufferBuilderPipelineBarrier { destination_access: AccessFlags, by_region: bool, ) { - debug_assert!(source_access.is_compatible_with(&source_stage)); - debug_assert!(destination_access.is_compatible_with(&destination_stage)); + debug_assert!(source_stage.supported_access().contains(&source_access)); + debug_assert!(destination_stage + .supported_access() + .contains(&destination_access)); self.add_execution_dependency(source_stage, destination_stage, by_region); @@ -2389,8 +2388,10 @@ impl UnsafeCommandBufferBuilderPipelineBarrier { ) where B: ?Sized + BufferAccess, { - debug_assert!(source_access.is_compatible_with(&source_stage)); - debug_assert!(destination_access.is_compatible_with(&destination_stage)); + debug_assert!(source_stage.supported_access().contains(&source_access)); + debug_assert!(destination_stage + .supported_access() + .contains(&destination_access)); self.add_execution_dependency(source_stage, destination_stage, by_region); @@ -2452,8 +2453,10 @@ impl UnsafeCommandBufferBuilderPipelineBarrier { ) where I: ?Sized + ImageAccess, { - debug_assert!(source_access.is_compatible_with(&source_stage)); - debug_assert!(destination_access.is_compatible_with(&destination_stage)); + debug_assert!(source_stage.supported_access().contains(&source_access)); + debug_assert!(destination_stage + .supported_access() + .contains(&destination_access)); self.add_execution_dependency(source_stage, destination_stage, by_region); diff --git a/vulkano/src/command_buffer/validity/vertex_buffers.rs b/vulkano/src/command_buffer/validity/vertex_buffers.rs index e5124298..9465c5c0 100644 --- a/vulkano/src/command_buffer/validity/vertex_buffers.rs +++ b/vulkano/src/command_buffer/validity/vertex_buffers.rs @@ -87,13 +87,7 @@ pub(in super::super) fn check_vertex_buffers( } } - if pipeline - .subpass() - .render_pass() - .desc() - .multiview() - .is_some() - { + if pipeline.subpass().render_pass().views_used() != 0 { let max_instance_index = pipeline .device() .physical_device() diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index e02fb3a0..c1df7d19 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -1162,6 +1162,15 @@ pub struct FormatProperties { pub _ne: crate::NonExhaustive, } +impl FormatProperties { + /// Returns the potential format features, following the definition of + /// . + #[inline] + pub fn potential_format_features(&self) -> FormatFeatures { + &self.linear_tiling_features | &self.optimal_tiling_features + } +} + /// The features supported by a device for an image or buffer with a particular format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] #[allow(missing_docs)] diff --git a/vulkano/src/image/view.rs b/vulkano/src/image/view.rs index 9f9d1f93..ada53b20 100644 --- a/vulkano/src/image/view.rs +++ b/vulkano/src/image/view.rs @@ -26,6 +26,7 @@ use crate::VulkanObject; use crate::{check_errors, Error}; use std::error; use std::fmt; +use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::mem::MaybeUninit; use std::ops::Range; @@ -1005,7 +1006,7 @@ impl From for ash::vk::ImageViewType { /// Trait for types that represent the GPU can access an image view. pub unsafe trait ImageViewAbstract: - VulkanObject + DeviceOwned + Send + Sync + VulkanObject + DeviceOwned + Debug + Send + Sync { /// Returns the wrapped image that this image view was created from. fn image(&self) -> Arc; diff --git a/vulkano/src/pipeline/graphics/builder.rs b/vulkano/src/pipeline/graphics/builder.rs index fb512586..6fc49755 100644 --- a/vulkano/src/pipeline/graphics/builder.rs +++ b/vulkano/src/pipeline/graphics/builder.rs @@ -335,17 +335,15 @@ where _ => return Err(GraphicsPipelineCreationError::WrongShaderType), } - if let Some(multiview) = subpass.render_pass().desc().multiview().as_ref() { - if multiview.used_layer_count() > 0 - && !device.enabled_features().multiview_tessellation_shader - { - return Err( + if subpass.render_pass().views_used() != 0 + && !device.enabled_features().multiview_tessellation_shader + { + return Err( GraphicsPipelineCreationError::FeatureNotEnabled { feature: "multiview_tessellation_shader", reason: "a tessellation shader was provided, and the render pass has multiview enabled with more than zero layers used", }, ); - } } } @@ -368,17 +366,15 @@ where } } - if let Some(multiview) = subpass.render_pass().desc().multiview().as_ref() { - if multiview.used_layer_count() > 0 - && !device.enabled_features().multiview_geometry_shader - { - return Err( - GraphicsPipelineCreationError::FeatureNotEnabled { - feature: "multiview_geometry_shader", - reason: "a geometry shader was provided, and the render pass has multiview enabled with more than zero layers used", - }, - ); - } + if subpass.render_pass().views_used() != 0 + && !device.enabled_features().multiview_geometry_shader + { + return Err( + GraphicsPipelineCreationError::FeatureNotEnabled { + feature: "multiview_geometry_shader", + reason: "a geometry shader was provided, and the render pass has multiview enabled with more than zero layers used", + }, + ); } // TODO: VUID-VkGraphicsPipelineCreateInfo-pStages-00739 @@ -671,7 +667,7 @@ where // Depth/stencil state let depth_stencil_state = if has_fragment_shader_state - && subpass.subpass_desc().depth_stencil.is_some() + && subpass.subpass_desc().depth_stencil_attachment.is_some() { Some( self.depth_stencil_state diff --git a/vulkano/src/render_pass/compat_atch.rs b/vulkano/src/render_pass/compat_atch.rs deleted file mode 100644 index 7c68e6be..00000000 --- a/vulkano/src/render_pass/compat_atch.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2017 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -//! This module contains the `ensure_image_view_compatible` function, which verifies whether -//! an image view can be used as a render pass attachment. - -use crate::image::view::ImageViewAbstract; -use crate::render_pass::RenderPassDesc; -use crate::{format::Format, image::SampleCount}; -use std::error; -use std::fmt; - -/// Checks whether the given image view is allowed to be the nth attachment of the given render -/// pass. -/// -/// # Panic -/// -/// Panics if the attachment number is out of range. -// TODO: add a specializable trait instead, that uses this function -// TODO: ImageView instead of ImageViewAbstract? -pub fn ensure_image_view_compatible( - render_pass_desc: &RenderPassDesc, - attachment_num: usize, - image_view: &I, -) -> Result<(), IncompatibleRenderPassAttachmentError> -where - I: ?Sized + ImageViewAbstract, -{ - let attachment_desc = render_pass_desc - .attachments() - .get(attachment_num) - .expect("Attachment num out of range"); - - if image_view.format() != attachment_desc.format { - return Err(IncompatibleRenderPassAttachmentError::FormatMismatch { - expected: attachment_desc.format, - obtained: image_view.format(), - }); - } - - if image_view.image().samples() != attachment_desc.samples { - return Err(IncompatibleRenderPassAttachmentError::SamplesMismatch { - expected: attachment_desc.samples, - obtained: image_view.image().samples(), - }); - } - - if !image_view.component_mapping().is_identity() { - return Err(IncompatibleRenderPassAttachmentError::NotIdentitySwizzled); - } - - let aspects = image_view.image().format().aspects(); // TODO: should use view format? - - for subpass in render_pass_desc.subpasses() { - if subpass - .color_attachments - .iter() - .any(|&(n, _)| n == attachment_num) - { - debug_assert!(aspects.color); // Was normally checked by the render pass. - if !image_view.usage().color_attachment { - return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage); - } - } - - if let Some((ds, _)) = subpass.depth_stencil { - if ds == attachment_num { - // Was normally checked by the render pass. - debug_assert!(aspects.depth || aspects.stencil); - if !image_view.usage().depth_stencil_attachment { - return Err( - IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage, - ); - } - } - } - - if subpass - .input_attachments - .iter() - .any(|&(n, _)| n == attachment_num) - { - if !image_view.usage().input_attachment { - return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage); - } - } - } - - // TODO: consider forbidding LoadOp::Load if image is transient - - // TODO: are all image layouts allowed? check this - - Ok(()) -} - -/// Error that can happen when an image is not compatible with a render pass attachment slot. -#[derive(Copy, Clone, Debug)] -pub enum IncompatibleRenderPassAttachmentError { - /// The image format expected by the render pass doesn't match the actual format of - /// the image. - FormatMismatch { - /// Format expected by the render pass. - expected: Format, - /// Format of the image. - obtained: Format, - }, - - /// The number of samples expected by the render pass doesn't match the number of samples of - /// the image. - SamplesMismatch { - /// Number of samples expected by the render pass. - expected: SampleCount, - /// Number of samples of the image. - obtained: SampleCount, - }, - - /// The image view has a component swizzle that is different from identity. - NotIdentitySwizzled, - - /// The image is used as a color attachment but is missing the color attachment usage. - MissingColorAttachmentUsage, - - /// The image is used as a depth/stencil attachment but is missing the depth-stencil attachment - /// usage. - MissingDepthStencilAttachmentUsage, - - /// The image is used as an input attachment but is missing the input attachment usage. - MissingInputAttachmentUsage, -} - -impl error::Error for IncompatibleRenderPassAttachmentError {} - -impl fmt::Display for IncompatibleRenderPassAttachmentError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - IncompatibleRenderPassAttachmentError::FormatMismatch { .. } => { - "mismatch between the format expected by the render pass and the actual format" - } - IncompatibleRenderPassAttachmentError::SamplesMismatch { .. } => { - "mismatch between the number of samples expected by the render pass and the actual \ - number of samples" - } - IncompatibleRenderPassAttachmentError::NotIdentitySwizzled => { - "the image view's component mapping is not identity swizzled" - } - IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage => { - "the image is used as a color attachment but is missing the color attachment usage" - } - IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage => { - "the image is used as a depth/stencil attachment but is missing the depth-stencil \ - attachment usage" - } - IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage => { - "the image is used as an input attachment but is missing the input \ - attachment usage" - } - } - ) - } -} - -#[cfg(test)] -mod tests { - use super::ensure_image_view_compatible; - use super::IncompatibleRenderPassAttachmentError; - use crate::format::Format; - use crate::image::view::ImageView; - use crate::image::AttachmentImage; - use crate::render_pass::RenderPassDesc; - - #[test] - fn basic_ok() { - let (device, _) = gfx_dev_and_queue!(); - - let rp = single_pass_renderpass!(device.clone(), - attachments: { - color: { - load: Clear, - store: Store, - format: Format::R8G8B8A8_UNORM, - samples: 1, - } - }, - pass: { - color: [color], - depth_stencil: {} - } - ) - .unwrap(); - - let view = ImageView::new( - AttachmentImage::new(device, [128, 128], Format::R8G8B8A8_UNORM).unwrap(), - ) - .unwrap(); - - ensure_image_view_compatible(rp.desc(), 0, view.as_ref()).unwrap(); - } - - #[test] - fn format_mismatch() { - let (device, _) = gfx_dev_and_queue!(); - - let rp = single_pass_renderpass!(device.clone(), - attachments: { - color: { - load: Clear, - store: Store, - format: Format::R16G16_SFLOAT, - samples: 1, - } - }, - pass: { - color: [color], - depth_stencil: {} - } - ) - .unwrap(); - - let view = ImageView::new( - AttachmentImage::new(device, [128, 128], Format::R8G8B8A8_UNORM).unwrap(), - ) - .unwrap(); - - match ensure_image_view_compatible(rp.desc(), 0, view.as_ref()) { - Err(IncompatibleRenderPassAttachmentError::FormatMismatch { - expected: Format::R16G16_SFLOAT, - obtained: Format::R8G8B8A8_UNORM, - }) => (), - e => panic!("{:?}", e), - } - } - - #[test] - fn attachment_out_of_range() { - let (device, _) = gfx_dev_and_queue!(); - - let rp = RenderPassDesc::empty(); - let view = ImageView::new( - AttachmentImage::new(device, [128, 128], Format::R8G8B8A8_UNORM).unwrap(), - ) - .unwrap(); - - assert_should_panic!("Attachment num out of range", { - let _ = ensure_image_view_compatible(&rp, 0, view.as_ref()); - }); - } - - // TODO: more tests -} diff --git a/vulkano/src/render_pass/create.rs b/vulkano/src/render_pass/create.rs new file mode 100644 index 00000000..60622aab --- /dev/null +++ b/vulkano/src/render_pass/create.rs @@ -0,0 +1,1710 @@ +// Copyright (c) 2022 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// 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::{ + AttachmentDescription, AttachmentReference, LoadOp, RenderPass, RenderPassCreateInfo, + SubpassDependency, SubpassDescription, +}; +use crate::{ + check_errors, + device::Device, + image::{ImageAspects, ImageLayout, SampleCount}, + sync::PipelineStages, + Error, OomError, Version, VulkanObject, +}; +use smallvec::SmallVec; +use std::{error, fmt, mem::MaybeUninit, ptr}; + +impl RenderPass { + pub(super) fn validate( + device: &Device, + create_info: &mut RenderPassCreateInfo, + ) -> Result { + let RenderPassCreateInfo { + attachments, + subpasses, + dependencies, + correlated_view_masks, + _ne: _, + } = create_info; + + let mut views_used = 0; + + /* + Attachments + */ + + let mut attachment_potential_format_features = Vec::with_capacity(attachments.len()); + + for (atch_num, attachment) in attachments.iter().enumerate() { + let &AttachmentDescription { + format, + samples, + load_op, + store_op, + stencil_load_op, + stencil_store_op, + initial_layout, + final_layout, + _ne: _, + } = attachment; + let atch_num = atch_num as u32; + + // VUID-VkAttachmentDescription2-finalLayout-03061 + if matches!( + final_layout, + ImageLayout::Undefined | ImageLayout::Preinitialized + ) { + return Err(RenderPassCreationError::AttachmentLayoutInvalid { + attachment: atch_num, + }); + } + + let format = format.unwrap(); + let aspects = format.aspects(); + + attachment_potential_format_features.push( + device + .physical_device() + .format_properties(format) + .potential_format_features(), + ); + + if aspects.color { + // VUID-VkAttachmentDescription2-format-03294 + if matches!( + initial_layout, + ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + ) { + return Err(RenderPassCreationError::AttachmentLayoutInvalid { + attachment: atch_num, + }); + } + + // VUID-VkAttachmentDescription2-format-03296 + if matches!( + final_layout, + ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + ) { + return Err(RenderPassCreationError::AttachmentLayoutInvalid { + attachment: atch_num, + }); + } + } else if aspects.depth || aspects.stencil { + // VUID-VkAttachmentDescription2-format-03295 + if matches!(initial_layout, ImageLayout::ColorAttachmentOptimal) { + return Err(RenderPassCreationError::AttachmentLayoutInvalid { + attachment: atch_num, + }); + } + + // VUID-VkAttachmentDescription2-format-03297 + if matches!(final_layout, ImageLayout::ColorAttachmentOptimal) { + return Err(RenderPassCreationError::AttachmentLayoutInvalid { + attachment: atch_num, + }); + } + } + } + + /* + Subpasses + */ + + // VUID-VkRenderPassCreateInfo2-subpassCount-arraylength + assert!(!subpasses.is_empty()); + + let is_multiview = subpasses[0].view_mask != 0; + + if is_multiview && !device.enabled_features().multiview { + return Err(RenderPassCreationError::FeatureNotEnabled { + feature: "multiview", + reason: "the subpasses specified a nonzero `view_mask`", + }); + } + + let mut attachment_used = vec![false; attachments.len()]; + + for (subpass_num, subpass) in subpasses.iter_mut().enumerate() { + let &mut SubpassDescription { + view_mask, + ref mut input_attachments, + ref color_attachments, + ref resolve_attachments, + ref depth_stencil_attachment, + ref preserve_attachments, + _ne: _, + } = subpass; + let subpass_num = subpass_num as u32; + + // VUID-VkRenderPassCreateInfo2-viewMask-03058 + if (view_mask != 0) != is_multiview { + return Err(RenderPassCreationError::SubpassMultiviewMismatch { + subpass: subpass_num, + multiview: subpass.view_mask != 0, + first_subpass_multiview: is_multiview, + }); + } + + views_used = views_used.max(32 - view_mask.leading_zeros()); + + // VUID-VkSubpassDescription2-colorAttachmentCount-03063 + if color_attachments.len() as u32 + > device.physical_device().properties().max_color_attachments + { + return Err( + RenderPassCreationError::SubpassMaxColorAttachmentsExceeded { + subpass: subpass_num, + color_attachments: color_attachments.len() as u32, + max: device.physical_device().properties().max_color_attachments, + }, + ); + } + + // Track the layout of each attachment used in this subpass + let mut layouts = vec![None; attachments.len()]; + + // Common checks for all attachment types + let mut check_attachment = |atch_ref: &AttachmentReference| { + // VUID-VkRenderPassCreateInfo2-attachment-03051 + let atch = attachments + .get(atch_ref.attachment as usize) + .ok_or_else(|| RenderPassCreationError::SubpassAttachmentOutOfRange { + subpass: subpass_num, + attachment: atch_ref.attachment, + })?; + + // VUID-VkSubpassDescription2-layout-02528 + match &mut layouts[atch_ref.attachment as usize] { + Some(layout) if *layout == atch_ref.layout => (), + Some(layout) => { + return Err(RenderPassCreationError::SubpassAttachmentLayoutMismatch { + subpass: subpass_num, + attachment: atch_ref.attachment, + }) + } + layout @ None => *layout = Some(atch_ref.layout), + } + + let first_use = + !std::mem::replace(&mut attachment_used[atch_ref.attachment as usize], true); + + // VUID-VkRenderPassCreateInfo2-pAttachments-02522 + // VUID-VkRenderPassCreateInfo2-pAttachments-02523 + if first_use + && matches!( + atch_ref.layout, + ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + ) + && (atch.load_op == LoadOp::Clear || atch.stencil_load_op == LoadOp::Clear) + { + return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid { + attachment: atch_ref.attachment, + first_use_subpass: subpass_num, + }); + } + + let potential_format_features = + &attachment_potential_format_features[atch_ref.attachment as usize]; + + Ok((atch, potential_format_features, first_use)) + }; + + /* + Check color attachments + */ + + let mut color_samples = None; + + for atch_ref in color_attachments.iter().flatten() { + let (atch, features, _first_use) = check_attachment(atch_ref)?; + + // VUID-VkSubpassDescription2-pColorAttachments-02898 + if !features.color_attachment { + return Err( + RenderPassCreationError::SubpassAttachmentUsageNotSupported { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "color", + }, + ); + } + + // VUID-VkAttachmentReference2-layout-03077 + // VUID-VkSubpassDescription2-None-04439 + if !matches!( + atch_ref.layout, + ImageLayout::ColorAttachmentOptimal | ImageLayout::General + ) { + return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "color", + }); + } + + // Not required by spec, but enforced by Vulkano for sanity. + if atch_ref.aspects != ImageAspects::none() { + return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty { + subpass: subpass_num, + attachment: atch_ref.attachment, + }); + } + + // VUID-VkSubpassDescription2-pColorAttachments-03069 + match &mut color_samples { + Some(samples) if *samples == atch.samples => (), + Some(samples) => { + return Err( + RenderPassCreationError::SubpassColorDepthStencilAttachmentSamplesMismatch { + subpass: subpass_num, + attachment: atch_ref.attachment, + samples: atch.samples, + first_samples: *samples, + }, + ) + } + samples @ None => *samples = Some(atch.samples), + } + } + + /* + Check depth/stencil attachment + */ + + if let Some(atch_ref) = depth_stencil_attachment.as_ref() { + let (atch, features, _first_use) = check_attachment(atch_ref)?; + + // VUID-VkSubpassDescription2-pDepthStencilAttachment-02900 + if !features.depth_stencil_attachment { + return Err( + RenderPassCreationError::SubpassAttachmentUsageNotSupported { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "depth/stencil", + }, + ); + } + + // VUID-VkAttachmentReference2-layout-03077 + // VUID-VkSubpassDescription2-None-04439 + if !matches!( + atch_ref.layout, + ImageLayout::DepthStencilAttachmentOptimal + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::General + ) { + return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "depth/stencil", + }); + } + + // Not required by spec, but enforced by Vulkano for sanity. + if atch_ref.aspects != ImageAspects::none() { + return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty { + subpass: subpass_num, + attachment: atch_ref.attachment, + }); + } + + // VUID-VkSubpassDescription2-pDepthStencilAttachment-04440 + if color_attachments + .iter() + .flatten() + .any(|color_atch_ref| color_atch_ref.attachment == atch_ref.attachment) + { + return Err( + RenderPassCreationError::SubpassAttachmentUsageColorDepthStencil { + subpass: subpass_num, + attachment: atch_ref.attachment, + }, + ); + } + + // VUID-VkSubpassDescription2-pDepthStencilAttachment-03071 + if let Some(samples) = color_samples.filter(|samples| *samples != atch.samples) { + return Err( + RenderPassCreationError::SubpassColorDepthStencilAttachmentSamplesMismatch { + subpass: subpass_num, + attachment: atch_ref.attachment, + samples: atch.samples, + first_samples: samples, + }, + ); + } + } + + /* + Check input attachments + This must be placed after color and depth/stencil checks so that `first_use` + will be true for VUID-VkSubpassDescription2-loadOp-03064. + */ + + for atch_ref in input_attachments.iter_mut().flatten() { + let (atch, features, first_use) = check_attachment(atch_ref)?; + + // VUID-VkSubpassDescription2-pInputAttachments-02897 + if !(features.color_attachment || features.depth_stencil_attachment) { + return Err( + RenderPassCreationError::SubpassAttachmentUsageNotSupported { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "input", + }, + ); + } + + // VUID-VkAttachmentReference2-layout-03077 + // VUID-VkSubpassDescription2-None-04439 + if !matches!( + atch_ref.layout, + ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::General + ) { + return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "input", + }); + } + + let atch_aspects = atch.format.unwrap().aspects(); + + if atch_ref.aspects == ImageAspects::none() { + // VUID-VkSubpassDescription2-attachment-02800 + atch_ref.aspects = atch_aspects; + } else if atch_ref.aspects != atch_aspects { + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_create_renderpass2 + || device.enabled_extensions().khr_maintenance2) + { + if device + .physical_device() + .supported_extensions() + .khr_create_renderpass2 + { + return Err(RenderPassCreationError::ExtensionNotEnabled { + extension: "khr_create_renderpass2", + reason: "an attachment reference selected a subset of the `aspects` of the attachment's format", + }); + } else { + return Err(RenderPassCreationError::ExtensionNotEnabled { + extension: "khr_maintenance2", + reason: "an attachment reference selected a subset of the `aspects` of the attachment's format", + }); + } + } + + // VUID-VkSubpassDescription2-attachment-02801 + // VUID-VkSubpassDescription2-attachment-04563 + // VUID-VkRenderPassCreateInfo2-attachment-02525 + if !atch_aspects.contains(&atch_ref.aspects) { + return Err( + RenderPassCreationError::SubpassInputAttachmentAspectsNotCompatible { + subpass: subpass_num, + attachment: atch_ref.attachment, + }, + ); + } + } + + // VUID-VkSubpassDescription2-loadOp-03064 + if first_use && atch.load_op == LoadOp::Clear { + return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid { + attachment: atch_ref.attachment, + first_use_subpass: subpass_num, + }); + } + } + + /* + Check resolve attachments + */ + + // VUID-VkSubpassDescription2-pResolveAttachments-parameter + if !(resolve_attachments.is_empty() + || resolve_attachments.len() == color_attachments.len()) + { + return Err( + RenderPassCreationError::SubpassResolveAttachmentsColorAttachmentsLenMismatch { + subpass: subpass_num, + }, + ); + } + + for (atch_ref, color_atch_ref) in resolve_attachments + .iter() + .zip(subpass.color_attachments.iter()) + .filter_map(|(r, c)| r.as_ref().map(|r| (r, c.as_ref()))) + { + let (atch, features, _first_use) = check_attachment(atch_ref)?; + + // VUID-VkSubpassDescription2-pResolveAttachments-02899 + if !features.color_attachment { + return Err( + RenderPassCreationError::SubpassAttachmentUsageNotSupported { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "resolve", + }, + ); + } + + // VUID-VkAttachmentReference2-layout-03077 + // VUID-VkSubpassDescription2-None-04439 + // TODO: the spec doesn't mention anything about layouts for resolve attachments + // specifically, so this just does a general check. + if matches!( + atch_ref.layout, + ImageLayout::Undefined | ImageLayout::Preinitialized | ImageLayout::PresentSrc + ) { + return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid { + subpass: subpass_num, + attachment: atch_ref.attachment, + usage: "resolve", + }); + } + + // Not required by spec, but enforced by Vulkano for sanity. + if atch_ref.aspects != ImageAspects::none() { + return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty { + subpass: subpass_num, + attachment: atch_ref.attachment, + }); + } + + // VUID-VkSubpassDescription2-pResolveAttachments-03065 + let color_atch_ref = color_atch_ref.ok_or_else(|| { + RenderPassCreationError::SubpassResolveAttachmentWithoutColorAttachment { + subpass: subpass_num, + } + })?; + let color_atch = &attachments[color_atch_ref.attachment as usize]; + + // VUID-VkSubpassDescription2-pResolveAttachments-03067 + if atch.samples != SampleCount::Sample1 { + return Err( + RenderPassCreationError::SubpassResolveAttachmentMultisampled { + subpass: subpass_num, + attachment: atch_ref.attachment, + }, + ); + } + + // VUID-VkSubpassDescription2-pResolveAttachments-03066 + if color_atch.samples == SampleCount::Sample1 { + return Err( + RenderPassCreationError::SubpassColorAttachmentWithResolveNotMultisampled { + subpass: subpass_num, + attachment: atch_ref.attachment, + }, + ); + } + + // VUID-VkSubpassDescription2-pResolveAttachments-03068 + if atch.format != color_atch.format { + return Err( + RenderPassCreationError::SubpassResolveAttachmentFormatMismatch { + subpass: subpass_num, + resolve_attachment: atch_ref.attachment, + color_attachment: color_atch_ref.attachment, + }, + ); + } + } + + /* + Check preserve attachments + */ + + for &atch in preserve_attachments { + // VUID-VkRenderPassCreateInfo2-attachment-03051 + if atch as usize >= attachments.len() { + return Err(RenderPassCreationError::SubpassAttachmentOutOfRange { + subpass: subpass_num, + attachment: atch, + }); + } + + // VUID-VkSubpassDescription2-pPreserveAttachments-03074 + if layouts[atch as usize].is_some() { + return Err( + RenderPassCreationError::SubpassPreserveAttachmentUsedElsewhere { + subpass: subpass_num, + attachment: atch, + }, + ); + } + } + } + + /* + Dependencies + */ + + for (dependency_num, dependency) in dependencies.iter().enumerate() { + let &SubpassDependency { + source_subpass, + destination_subpass, + ref source_stages, + ref destination_stages, + ref source_access, + ref destination_access, + by_region, + view_local, + _ne: _, + } = dependency; + let dependency_num = dependency_num as u32; + + for (stages, access) in [ + (source_stages, source_access), + (destination_stages, destination_access), + ] { + // VUID-VkSubpassDependency2-srcStageMask-04090 + // VUID-VkSubpassDependency2-dstStageMask-04090 + if stages.geometry_shader && !device.enabled_features().geometry_shader { + return Err(RenderPassCreationError::FeatureNotEnabled { + feature: "geometry_shader", + reason: "a dependency specified the `geometry_shader` stage", + }); + } + + // VUID-VkSubpassDependency2-srcStageMask-04091 + // VUID-VkSubpassDependency2-dstStageMask-04091 + if (stages.tessellation_control_shader || stages.tessellation_evaluation_shader) + && !device.enabled_features().tessellation_shader + { + return Err(RenderPassCreationError::FeatureNotEnabled { + feature: "tessellation_shader", + reason: "a dependency specified the `tessellation_control_shader` or `tessellation_evaluation_shader` stage", + }); + } + + // VUID-VkSubpassDependency2-srcStageMask-03937 + // VUID-VkSubpassDependency2-dstStageMask-03937 + if *stages == PipelineStages::none() && !device.enabled_features().synchronization2 + { + return Err(RenderPassCreationError::FeatureNotEnabled { + feature: "synchronization2", + reason: "a dependency specified no shader stages", + }); + } + + // VUID-VkSubpassDependency2-srcAccessMask-03088 + // VUID-VkSubpassDependency2-dstAccessMask-03089 + if !stages.supported_access().contains(access) { + return Err( + RenderPassCreationError::DependencyAccessNotSupportedByStages { + dependency: dependency_num, + }, + ); + } + } + + // VUID-VkRenderPassCreateInfo2-viewMask-03059 + if view_local.is_some() && !is_multiview { + return Err( + RenderPassCreationError::DependencyViewLocalMultiviewNotEnabled { + dependency: dependency_num, + }, + ); + } + + // VUID-VkSubpassDependency2-srcSubpass-03085 + if source_subpass.is_none() && destination_subpass.is_none() { + return Err(RenderPassCreationError::DependencyBothSubpassesExternal { + dependency: dependency_num, + }); + } + + for (subpass, stages) in [ + (source_subpass, source_stages), + (destination_subpass, destination_stages), + ] { + if let Some(subpass) = subpass { + // VUID-VkRenderPassCreateInfo2-srcSubpass-02526 + // VUID-VkRenderPassCreateInfo2-dstSubpass-02527 + if subpass as usize >= subpasses.len() { + return Err(RenderPassCreationError::DependencySubpassOutOfRange { + dependency: dependency_num, + subpass, + }); + } + + let remaining_stages = PipelineStages { + draw_indirect: false, + //index_input: false, + //vertex_attribute_input: false, + vertex_shader: false, + tessellation_control_shader: false, + tessellation_evaluation_shader: false, + geometry_shader: false, + //transform_feedback: false, + //fragment_shading_rate_attachment: false, + early_fragment_tests: false, + fragment_shader: false, + late_fragment_tests: false, + color_attachment_output: false, + all_graphics: false, + ..*stages + }; + + // VUID-VkRenderPassCreateInfo2-pDependencies-03054 + // VUID-VkRenderPassCreateInfo2-pDependencies-03055 + if remaining_stages != PipelineStages::none() { + return Err(RenderPassCreationError::DependencyStageNotSupported { + dependency: dependency_num, + }); + } + } else { + // VUID-VkSubpassDependency2-dependencyFlags-03090 + // VUID-VkSubpassDependency2-dependencyFlags-03091 + if view_local.is_some() { + return Err( + RenderPassCreationError::DependencyViewLocalExternalDependency { + dependency: dependency_num, + }, + ); + } + } + } + + if let (Some(source_subpass), Some(destination_subpass)) = + (source_subpass, destination_subpass) + { + // VUID-VkSubpassDependency2-srcSubpass-03084 + if source_subpass > destination_subpass { + return Err( + RenderPassCreationError::DependencySourceSubpassAfterDestinationSubpass { + dependency: dependency_num, + }, + ); + } + + if source_subpass == destination_subpass { + let source_stages_non_framebuffer = PipelineStages { + early_fragment_tests: false, + fragment_shader: false, + late_fragment_tests: false, + color_attachment_output: false, + ..*source_stages + }; + let destination_stages_non_framebuffer = PipelineStages { + early_fragment_tests: false, + fragment_shader: false, + late_fragment_tests: false, + color_attachment_output: false, + ..*destination_stages + }; + + if source_stages_non_framebuffer != PipelineStages::none() + || destination_stages_non_framebuffer != PipelineStages::none() + { + let source_latest_stage = if source_stages.all_graphics { + 13 + } else { + let PipelineStages { + draw_indirect, + //index_input, + //vertex_attribute_input, + vertex_shader, + tessellation_control_shader, + tessellation_evaluation_shader, + geometry_shader, + //transform_feedback, + //fragment_shading_rate_attachment, + early_fragment_tests, + fragment_shader, + late_fragment_tests, + color_attachment_output, + .. + } = *source_stages; + + [ + draw_indirect as u8 * 1, + // index_input as u8 * 2, + // vertex_attribute_input as u8 * 3, + vertex_shader as u8 * 4, + tessellation_control_shader as u8 * 5, + tessellation_evaluation_shader as u8 * 6, + geometry_shader as u8 * 7, + // transform_feedback as u8 * 8, + // fragment_shading_rate_attachment as u8 * 9, + early_fragment_tests as u8 * 10, + fragment_shader as u8 * 11, + late_fragment_tests as u8 * 12, + color_attachment_output as u8 * 13, + ] + .into_iter() + .max() + .unwrap() + }; + + let destination_earliest_stage = if destination_stages.all_graphics { + 1 + } else { + let PipelineStages { + draw_indirect, + //index_input, + //vertex_attribute_input, + vertex_shader, + tessellation_control_shader, + tessellation_evaluation_shader, + geometry_shader, + //transform_feedback, + //fragment_shading_rate_attachment, + early_fragment_tests, + fragment_shader, + late_fragment_tests, + color_attachment_output, + .. + } = *destination_stages; + + [ + draw_indirect as u8 * 1, + // index_input as u8 * 2, + // vertex_attribute_input as u8 * 3, + vertex_shader as u8 * 4, + tessellation_control_shader as u8 * 5, + tessellation_evaluation_shader as u8 * 6, + geometry_shader as u8 * 7, + // transform_feedback as u8 * 8, + // fragment_shading_rate_attachment as u8 * 9, + early_fragment_tests as u8 * 10, + fragment_shader as u8 * 11, + late_fragment_tests as u8 * 12, + color_attachment_output as u8 * 13, + ] + .into_iter() + .min() + .unwrap() + }; + + // VUID-VkSubpassDependency2-srcSubpass-03087 + if source_latest_stage > destination_earliest_stage { + return Err( + RenderPassCreationError::DependencySelfDependencySourceStageAfterDestinationStage { + dependency: dependency_num, + }, + ); + } + } + + let source_has_framebuffer_stage = source_stages.fragment_shader + || source_stages.early_fragment_tests + || source_stages.late_fragment_tests + || source_stages.color_attachment_output; + let destination_has_framebuffer_stage = destination_stages.fragment_shader + || destination_stages.early_fragment_tests + || destination_stages.late_fragment_tests + || destination_stages.color_attachment_output; + + // VUID-VkSubpassDependency2-srcSubpass-02245 + if source_has_framebuffer_stage + && destination_has_framebuffer_stage + && !by_region + { + return Err( + RenderPassCreationError::DependencySelfDependencyFramebufferStagesWithoutByRegion { + dependency: dependency_num, + }, + ); + } + + if let Some(view_offset) = view_local { + // VUID-VkSubpassDependency2-viewOffset-02530 + if view_offset != 0 { + return Err( + RenderPassCreationError::DependencySelfDependencyViewLocalNonzeroOffset { + dependency: dependency_num, + }, + ); + } + } else { + // VUID-VkRenderPassCreateInfo2-pDependencies-03060 + if subpasses[source_subpass as usize].view_mask.count_ones() > 1 { + return Err( + RenderPassCreationError::DependencySelfDependencyViewMaskMultiple { + dependency: dependency_num, + subpass: source_subpass, + }, + ); + } + } + } + } + } + + /* + Correlated view masks + */ + + // VUID-VkRenderPassCreateInfo2-viewMask-03057 + if !correlated_view_masks.is_empty() { + if !is_multiview { + return Err(RenderPassCreationError::CorrelatedViewMasksMultiviewNotEnabled); + } + + // VUID-VkRenderPassCreateInfo2-pCorrelatedViewMasks-03056 + correlated_view_masks.iter().try_fold(0, |total, &mask| { + if total & mask != 0 { + Err(RenderPassCreationError::CorrelatedViewMasksOverlapping) + } else { + Ok(total | mask) + } + })?; + } + + Ok(views_used) + } + + pub(super) unsafe fn create_v2( + device: &Device, + create_info: &RenderPassCreateInfo, + ) -> Result { + let RenderPassCreateInfo { + attachments, + subpasses, + dependencies, + correlated_view_masks, + _ne: _, + } = create_info; + + let attachments_vk = attachments + .iter() + .map(|attachment| ash::vk::AttachmentDescription2 { + flags: ash::vk::AttachmentDescriptionFlags::empty(), + format: attachment + .format + .map_or(ash::vk::Format::UNDEFINED, |f| f.into()), + samples: attachment.samples.into(), + load_op: attachment.load_op.into(), + store_op: attachment.store_op.into(), + stencil_load_op: attachment.stencil_load_op.into(), + stencil_store_op: attachment.stencil_store_op.into(), + initial_layout: attachment.initial_layout.into(), + final_layout: attachment.final_layout.into(), + ..Default::default() + }) + .collect::>(); + + let attachment_references_vk = subpasses + .iter() + .flat_map(|subpass| { + (subpass.input_attachments.iter()) + .chain(subpass.color_attachments.iter()) + .chain(subpass.resolve_attachments.iter()) + .map(Option::as_ref) + .chain(subpass.depth_stencil_attachment.iter().map(Some)) + .map(|atch_ref| { + if let Some(atch_ref) = atch_ref { + ash::vk::AttachmentReference2 { + attachment: atch_ref.attachment, + layout: atch_ref.layout.into(), + aspect_mask: atch_ref.aspects.into(), + ..Default::default() + } + } else { + ash::vk::AttachmentReference2 { + attachment: ash::vk::ATTACHMENT_UNUSED, + ..Default::default() + } + } + }) + }) + .collect::>(); + + let subpasses_vk = { + // `ref_index` is increased during the loop and points to the next element to use + // in `attachment_references_vk`. + let mut ref_index = 0usize; + let out: SmallVec<[_; 4]> = subpasses + .iter() + .map(|subpass| { + let input_attachments = + attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += subpass.input_attachments.len(); + let color_attachments = + attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += subpass.color_attachments.len(); + let resolve_attachments = + attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += subpass.resolve_attachments.len(); + let depth_stencil = if subpass.depth_stencil_attachment.is_some() { + let a = attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += 1; + a + } else { + ptr::null() + }; + + ash::vk::SubpassDescription2 { + flags: ash::vk::SubpassDescriptionFlags::empty(), + pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS, // TODO: any need to make this user-specifiable? + view_mask: subpass.view_mask, + input_attachment_count: subpass.input_attachments.len() as u32, + p_input_attachments: if subpass.input_attachments.is_empty() { + ptr::null() + } else { + input_attachments + }, + color_attachment_count: subpass.color_attachments.len() as u32, + p_color_attachments: if subpass.color_attachments.is_empty() { + ptr::null() + } else { + color_attachments + }, + p_resolve_attachments: if subpass.resolve_attachments.is_empty() { + ptr::null() + } else { + resolve_attachments + }, + p_depth_stencil_attachment: depth_stencil, + preserve_attachment_count: subpass.preserve_attachments.len() as u32, + p_preserve_attachments: if subpass.preserve_attachments.is_empty() { + ptr::null() + } else { + subpass.preserve_attachments.as_ptr() + }, + ..Default::default() + } + }) + .collect(); + + // If this assertion fails, there's a serious bug in the code above ^. + debug_assert!(ref_index == attachment_references_vk.len()); + + out + }; + + let dependencies_vk = dependencies + .iter() + .map(|dependency| { + let mut dependency_flags = ash::vk::DependencyFlags::empty(); + + if dependency.by_region { + dependency_flags |= ash::vk::DependencyFlags::BY_REGION; + } + + if dependency.view_local.is_some() { + dependency_flags |= ash::vk::DependencyFlags::VIEW_LOCAL; + } + + ash::vk::SubpassDependency2 { + src_subpass: dependency + .source_subpass + .unwrap_or(ash::vk::SUBPASS_EXTERNAL), + dst_subpass: dependency + .destination_subpass + .unwrap_or(ash::vk::SUBPASS_EXTERNAL), + src_stage_mask: dependency.source_stages.into(), + dst_stage_mask: dependency.destination_stages.into(), + src_access_mask: dependency.source_access.into(), + dst_access_mask: dependency.destination_access.into(), + dependency_flags, + // VUID-VkSubpassDependency2-dependencyFlags-03092 + view_offset: dependency.view_local.unwrap_or(0), + ..Default::default() + } + }) + .collect::>(); + + let create_info = ash::vk::RenderPassCreateInfo2 { + flags: ash::vk::RenderPassCreateFlags::empty(), + attachment_count: attachments_vk.len() as u32, + p_attachments: if attachments_vk.is_empty() { + ptr::null() + } else { + attachments_vk.as_ptr() + }, + subpass_count: subpasses_vk.len() as u32, + p_subpasses: if subpasses_vk.is_empty() { + ptr::null() + } else { + subpasses_vk.as_ptr() + }, + dependency_count: dependencies_vk.len() as u32, + p_dependencies: if dependencies_vk.is_empty() { + ptr::null() + } else { + dependencies_vk.as_ptr() + }, + correlated_view_mask_count: correlated_view_masks.len() as u32, + p_correlated_view_masks: correlated_view_masks.as_ptr(), + ..Default::default() + }; + + Ok({ + let fns = device.fns(); + let mut output = MaybeUninit::uninit(); + + check_errors(if device.api_version() >= Version::V1_2 { + fns.v1_2.create_render_pass2( + device.internal_object(), + &create_info, + ptr::null(), + output.as_mut_ptr(), + ) + } else { + fns.khr_create_renderpass2.create_render_pass2_khr( + device.internal_object(), + &create_info, + ptr::null(), + output.as_mut_ptr(), + ) + })?; + + output.assume_init() + }) + } + + pub(super) unsafe fn create_v1( + device: &Device, + create_info: &RenderPassCreateInfo, + ) -> Result { + let RenderPassCreateInfo { + attachments, + subpasses, + dependencies, + correlated_view_masks, + _ne: _, + } = create_info; + + let attachments_vk = attachments + .iter() + .map(|attachment| ash::vk::AttachmentDescription { + flags: ash::vk::AttachmentDescriptionFlags::empty(), + format: attachment + .format + .map_or(ash::vk::Format::UNDEFINED, |f| f.into()), + samples: attachment.samples.into(), + load_op: attachment.load_op.into(), + store_op: attachment.store_op.into(), + stencil_load_op: attachment.stencil_load_op.into(), + stencil_store_op: attachment.stencil_store_op.into(), + initial_layout: attachment.initial_layout.into(), + final_layout: attachment.final_layout.into(), + }) + .collect::>(); + + let attachment_references_vk = subpasses + .iter() + .flat_map(|subpass| { + (subpass.input_attachments.iter()) + .chain(subpass.color_attachments.iter()) + .chain(subpass.resolve_attachments.iter()) + .map(Option::as_ref) + .chain(subpass.depth_stencil_attachment.iter().map(Some)) + .map(|atch_ref| { + if let Some(atch_ref) = atch_ref { + ash::vk::AttachmentReference { + attachment: atch_ref.attachment, + layout: atch_ref.layout.into(), + } + } else { + ash::vk::AttachmentReference { + attachment: ash::vk::ATTACHMENT_UNUSED, + layout: Default::default(), + } + } + }) + }) + .collect::>(); + + let subpasses_vk = { + // `ref_index` is increased during the loop and points to the next element to use + // in `attachment_references_vk`. + let mut ref_index = 0usize; + let out: SmallVec<[_; 4]> = subpasses + .iter() + .map(|subpass| { + let input_attachments = + attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += subpass.input_attachments.len(); + let color_attachments = + attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += subpass.color_attachments.len(); + let resolve_attachments = + attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += subpass.resolve_attachments.len(); + let depth_stencil = if subpass.depth_stencil_attachment.is_some() { + let a = attachment_references_vk.as_ptr().offset(ref_index as isize); + ref_index += 1; + a + } else { + ptr::null() + }; + + ash::vk::SubpassDescription { + flags: ash::vk::SubpassDescriptionFlags::empty(), + pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS, + input_attachment_count: subpass.input_attachments.len() as u32, + p_input_attachments: if subpass.input_attachments.is_empty() { + ptr::null() + } else { + input_attachments + }, + color_attachment_count: subpass.color_attachments.len() as u32, + p_color_attachments: if subpass.color_attachments.is_empty() { + ptr::null() + } else { + color_attachments + }, + p_resolve_attachments: if subpass.resolve_attachments.is_empty() { + ptr::null() + } else { + resolve_attachments + }, + p_depth_stencil_attachment: depth_stencil, + preserve_attachment_count: subpass.preserve_attachments.len() as u32, + p_preserve_attachments: if subpass.preserve_attachments.is_empty() { + ptr::null() + } else { + subpass.preserve_attachments.as_ptr() + }, + } + }) + .collect(); + + // If this assertion fails, there's a serious bug in the code above ^. + debug_assert!(ref_index == attachment_references_vk.len()); + + out + }; + + let dependencies_vk = dependencies + .iter() + .map(|dependency| ash::vk::SubpassDependency { + src_subpass: dependency + .source_subpass + .unwrap_or(ash::vk::SUBPASS_EXTERNAL), + dst_subpass: dependency + .destination_subpass + .unwrap_or(ash::vk::SUBPASS_EXTERNAL), + src_stage_mask: dependency.source_stages.into(), + dst_stage_mask: dependency.destination_stages.into(), + src_access_mask: dependency.source_access.into(), + dst_access_mask: dependency.destination_access.into(), + dependency_flags: if dependency.by_region { + ash::vk::DependencyFlags::BY_REGION + } else { + ash::vk::DependencyFlags::empty() + }, + }) + .collect::>(); + + /* Input attachment aspect */ + + let input_attachment_aspect_references: SmallVec<[_; 8]> = if device.api_version() + >= Version::V1_1 + || device.enabled_extensions().khr_maintenance2 + { + subpasses + .iter() + .enumerate() + .flat_map(|(subpass_num, subpass)| { + subpass.input_attachments.iter().enumerate().flat_map( + move |(atch_num, atch_ref)| { + atch_ref.as_ref().map(|atch_ref| { + ash::vk::InputAttachmentAspectReference { + subpass: subpass_num as u32, + input_attachment_index: atch_num as u32, + aspect_mask: atch_ref.aspects.into(), + } + }) + }, + ) + }) + .collect() + } else { + SmallVec::new() + }; + + let mut input_attachment_aspect_create_info = + if !input_attachment_aspect_references.is_empty() { + Some(ash::vk::RenderPassInputAttachmentAspectCreateInfo { + aspect_reference_count: input_attachment_aspect_references.len() as u32, + p_aspect_references: input_attachment_aspect_references.as_ptr(), + ..Default::default() + }) + } else { + None + }; + + /* Multiview */ + + let is_multiview = subpasses[0].view_mask != 0; + + let (multiview_view_masks, multiview_view_offsets): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = + if is_multiview { + ( + subpasses.iter().map(|subpass| subpass.view_mask).collect(), + dependencies + .iter() + .map(|dependency| dependency.view_local.unwrap_or(0)) + .collect(), + ) + } else { + (SmallVec::new(), SmallVec::new()) + }; + + let mut multiview_create_info = if is_multiview { + debug_assert!(multiview_view_masks.len() == subpasses.len()); + debug_assert!(multiview_view_offsets.len() == dependencies.len()); + + Some(ash::vk::RenderPassMultiviewCreateInfo { + subpass_count: multiview_view_masks.len() as u32, + p_view_masks: multiview_view_masks.as_ptr(), + dependency_count: multiview_view_offsets.len() as u32, + p_view_offsets: multiview_view_offsets.as_ptr(), + correlation_mask_count: correlated_view_masks.len() as u32, + p_correlation_masks: correlated_view_masks.as_ptr(), + ..Default::default() + }) + } else { + None + }; + + /* Create */ + + let mut create_info = ash::vk::RenderPassCreateInfo { + flags: ash::vk::RenderPassCreateFlags::empty(), + attachment_count: attachments_vk.len() as u32, + p_attachments: if attachments_vk.is_empty() { + ptr::null() + } else { + attachments_vk.as_ptr() + }, + subpass_count: subpasses_vk.len() as u32, + p_subpasses: if subpasses_vk.is_empty() { + ptr::null() + } else { + subpasses_vk.as_ptr() + }, + dependency_count: dependencies_vk.len() as u32, + p_dependencies: if dependencies_vk.is_empty() { + ptr::null() + } else { + dependencies_vk.as_ptr() + }, + ..Default::default() + }; + + if let Some(input_attachment_aspect_create_info) = + input_attachment_aspect_create_info.as_mut() + { + input_attachment_aspect_create_info.p_next = create_info.p_next; + create_info.p_next = input_attachment_aspect_create_info as *const _ as *const _; + } + + if let Some(multiview_create_info) = multiview_create_info.as_mut() { + multiview_create_info.p_next = create_info.p_next; + create_info.p_next = multiview_create_info as *const _ as *const _; + } + + Ok({ + let fns = device.fns(); + let mut output = MaybeUninit::uninit(); + check_errors(fns.v1_0.create_render_pass( + device.internal_object(), + &create_info, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }) + } +} + +/// Error that can happen when creating a `RenderPass`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RenderPassCreationError { + /// Not enough memory. + OomError(OomError), + + ExtensionNotEnabled { + extension: &'static str, + reason: &'static str, + }, + FeatureNotEnabled { + feature: &'static str, + reason: &'static str, + }, + + /// An attachment is first used in the render pass with a read-only layout or as an input + /// attachment, but its `load_op` or `stencil_load_op` is [`LoadOp::Clear`]. + AttachmentFirstUseLoadOpInvalid { + attachment: u32, + first_use_subpass: u32, + }, + + /// An attachment has an `initial_layout` or `final_layout` value that is invalid for the + /// provided `format`. + AttachmentLayoutInvalid { attachment: u32 }, + + /// Correlated view masks were included, but multiview is not enabled on the render pass. + CorrelatedViewMasksMultiviewNotEnabled, + + /// The provided correlated view masks contain a bit that is set in more than one element. + CorrelatedViewMasksOverlapping, + + /// A subpass dependency specified an access type that was not supported by the given stages. + DependencyAccessNotSupportedByStages { dependency: u32 }, + + /// A subpass dependency has both `source_subpass` and `destination_subpass` set to `None`. + DependencyBothSubpassesExternal { dependency: u32 }, + + /// A subpass dependency specifies a subpass self-dependency and includes framebuffer stages in + /// both `source_stages` and `destination_stages`, but the `by_region` dependency was not + /// enabled. + DependencySelfDependencyFramebufferStagesWithoutByRegion { dependency: u32 }, + + /// A subpass dependency specifies a subpass self-dependency and includes + /// non-framebuffer stages, but the latest stage in `source_stages` is after the earliest stage + /// in `destination_stages`. + DependencySelfDependencySourceStageAfterDestinationStage { dependency: u32 }, + + /// A subpass dependency specifies a subpass self-dependency and has the `view_local` dependency + /// enabled, but the inner offset value was not 0. + DependencySelfDependencyViewLocalNonzeroOffset { dependency: u32 }, + + /// A subpass dependency specifies a subpass self-dependency without the `view_local` + /// dependency, but the referenced subpass has more than one bit set in its `view_mask`. + DependencySelfDependencyViewMaskMultiple { dependency: u32, subpass: u32 }, + + /// A subpass dependency has a `source_subpass` that is later than the `destination_subpass`. + DependencySourceSubpassAfterDestinationSubpass { dependency: u32 }, + + /// A subpass dependency has a bit set in the `source_stages` or `destination_stages` that is + /// not supported for graphics pipelines. + DependencyStageNotSupported { dependency: u32 }, + + /// A subpass index in a subpass dependency is not less than the number of subpasses in the + /// render pass. + DependencySubpassOutOfRange { dependency: u32, subpass: u32 }, + + /// A subpass dependency has the `view_local` dependency enabled, but `source_subpass` or + /// `destination_subpass` were set to `None`. + DependencyViewLocalExternalDependency { dependency: u32 }, + + /// A subpass dependency has the `view_local` dependency enabled, but multiview is not enabled + /// on the render pass. + DependencyViewLocalMultiviewNotEnabled { dependency: u32 }, + + /// A reference to an attachment used other than as an input attachment in a subpass has + /// one or more aspects selected. + SubpassAttachmentAspectsNotEmpty { subpass: u32, attachment: u32 }, + + /// An attachment used as an attachment in a subpass has a layout that is not supported for + /// that usage. + SubpassAttachmentLayoutInvalid { + subpass: u32, + attachment: u32, + usage: &'static str, + }, + + /// The layouts of all uses of an attachment in a subpass do not match. + SubpassAttachmentLayoutMismatch { subpass: u32, attachment: u32 }, + + /// An attachment index in a subpass is not less than the number of attachments in the render + /// pass. + SubpassAttachmentOutOfRange { subpass: u32, attachment: u32 }, + + /// An attachment is used as both a color attachment and a depth/stencil attachment in a + /// subpass. + SubpassAttachmentUsageColorDepthStencil { subpass: u32, attachment: u32 }, + + /// An attachment used as an attachment in a subpass has a format that does not support that + /// usage. + SubpassAttachmentUsageNotSupported { + subpass: u32, + attachment: u32, + usage: &'static str, + }, + + /// An attachment used as a color attachment in a subpass with resolve attachments has a + /// `samples` value of [`SampleCount::Sample1`]. + SubpassColorAttachmentWithResolveNotMultisampled { subpass: u32, attachment: u32 }, + + /// An attachment used as a color or depth/stencil attachment in a subpass has a `samples` value + /// that is different from the first color attachment. + SubpassColorDepthStencilAttachmentSamplesMismatch { + subpass: u32, + attachment: u32, + samples: SampleCount, + first_samples: SampleCount, + }, + + /// A reference to an attachment used as an input attachment in a subpass selects aspects that + /// are not present in the format of the attachment. + SubpassInputAttachmentAspectsNotCompatible { subpass: u32, attachment: u32 }, + + /// The `max_color_attachments` limit has been exceeded for a subpass. + SubpassMaxColorAttachmentsExceeded { + subpass: u32, + color_attachments: u32, + max: u32, + }, + + /// The multiview state (whether `view_mask` is nonzero) of a subpass is different from the + /// first subpass. + SubpassMultiviewMismatch { + subpass: u32, + multiview: bool, + first_subpass_multiview: bool, + }, + + /// An attachment marked as a preserve attachment in a subpass is also used as an attachment + /// in that subpass. + SubpassPreserveAttachmentUsedElsewhere { subpass: u32, attachment: u32 }, + + /// The `resolve_attachments` field of a subpass was not empty, but its length did not match + /// the length of `color_attachments`. + SubpassResolveAttachmentsColorAttachmentsLenMismatch { subpass: u32 }, + + /// An attachment used as a resolve attachment in a subpass has a `format` value different from + /// the corresponding color attachment. + SubpassResolveAttachmentFormatMismatch { + subpass: u32, + resolve_attachment: u32, + color_attachment: u32, + }, + + /// An attachment used as a resolve attachment in a subpass has a `samples` value other than + /// [`SampleCount::Sample1`]. + SubpassResolveAttachmentMultisampled { subpass: u32, attachment: u32 }, + + /// A resolve attachment in a subpass is `Some`, but the corresponding color attachment is + /// `None`. + SubpassResolveAttachmentWithoutColorAttachment { subpass: u32 }, +} + +impl error::Error for RenderPassCreationError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + RenderPassCreationError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for RenderPassCreationError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Self::OomError(_) => write!(fmt, "not enough memory available",), + 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::AttachmentFirstUseLoadOpInvalid { attachment, first_use_subpass } => write!( + fmt, + "attachment {} is first used in the render pass in subpass {} with a read-only layout or as an input attachment, but its `load_op` or `stencil_load_op` is `LoadOp::Clear`", + attachment, first_use_subpass, + ), + Self::AttachmentLayoutInvalid { attachment } => write!( + fmt, + "attachment {} has an `initial_layout` or `final_layout` value that is invalid for the provided `format`", + attachment, + ), + Self::CorrelatedViewMasksMultiviewNotEnabled => write!( + fmt, + "correlated view masks were included, but multiview is not enabled on the render pass", + ), + Self::CorrelatedViewMasksOverlapping => write!( + fmt, + "the provided correlated view masks contain a bit that is set in more than one element", + ), + Self::DependencyAccessNotSupportedByStages { dependency } => write!( + fmt, + "subpass dependency {} specified an access type that was not supported by the given stages", + dependency, + ), + Self::DependencySelfDependencyFramebufferStagesWithoutByRegion { dependency } => write!( + fmt, + "subpass dependency {} specifies a subpass self-dependency and includes framebuffer stages in both `source_stages` and `destination_stages`, but the `by_region` dependency was not enabled", + dependency, + ), + Self::DependencySelfDependencySourceStageAfterDestinationStage { dependency } => write!( + fmt, + "subpass dependency {} specifies a subpass self-dependency and includes non-framebuffer stages, but the latest stage in `source_stages` is after the earliest stage in `destination_stages`", + dependency, + ), + Self::DependencySelfDependencyViewLocalNonzeroOffset { dependency } => write!( + fmt, + "subpass dependency {} specifies a subpass self-dependency and has the `view_local` dependency enabled, but the inner offset value was not 0", + dependency, + ), + Self::DependencySelfDependencyViewMaskMultiple { dependency, subpass } => write!( + fmt, + "subpass dependency {} specifies a subpass self-dependency without the `view_local` dependency, but the referenced subpass {} has more than one bit set in its `view_mask`", + dependency, subpass, + ), + Self::DependencySourceSubpassAfterDestinationSubpass { dependency } => write!( + fmt, + "subpass dependency {} has a `source_subpass` that is later than the `destination_subpass`", + dependency, + ), + Self::DependencyStageNotSupported { dependency } => write!( + fmt, + "subpass dependency {} has a bit set in the `source_stages` or `destination_stages` that is not supported for graphics pipelines", + dependency, + ), + Self::DependencyBothSubpassesExternal { dependency } => write!( + fmt, + "subpass dependency {} has both `source_subpass` and `destination_subpass` set to `None`", + dependency, + ), + Self::DependencySubpassOutOfRange { + dependency, + subpass, + } => write!( + fmt, + "the subpass index {} in subpass dependency {} is not less than the number of subpasses in the render pass", + subpass, dependency, + ), + Self::DependencyViewLocalExternalDependency { dependency } => write!( + fmt, + "subpass dependency {} has the `view_local` dependency enabled, but `source_subpass` or `destination_subpass` were set to `None`", + dependency, + ), + Self::DependencyViewLocalMultiviewNotEnabled { dependency } => write!( + fmt, + "subpass dependency {} has the `view_local` dependency enabled, but multiview is not enabled on the render pass", + dependency, + ), + Self::SubpassAttachmentAspectsNotEmpty { subpass, attachment } => write!( + fmt, + "a reference to attachment {} used other than as an input attachment in subpass {} has one or more aspects selected", + attachment, subpass, + ), + Self::SubpassAttachmentLayoutMismatch { subpass, attachment } => write!( + fmt, + "the layouts of all uses of attachment {} in subpass {} do not match.", + attachment, subpass, + ), + Self::SubpassAttachmentLayoutInvalid { + subpass, + attachment, + usage, + } => write!( + fmt, + "attachment {} used as {} attachment in subpass {} has a layout that is not supported for that usage", + attachment, usage, subpass, + ), + Self::SubpassAttachmentOutOfRange { subpass, attachment } => write!( + fmt, + "the attachment index {} in subpass {} is not less than the number of attachments in the render pass", + attachment, subpass, + ), + Self::SubpassAttachmentUsageColorDepthStencil { subpass, attachment } => write!( + fmt, + "attachment {} is used as both a color attachment and a depth/stencil attachment in subpass {}", + attachment, subpass, + ), + Self::SubpassAttachmentUsageNotSupported { subpass, attachment, usage, } => write!( + fmt, + "attachment {} used as {} attachment in subpass {} has a format that does not support that usage", + attachment, usage, subpass, + ), + Self::SubpassColorAttachmentWithResolveNotMultisampled { subpass, attachment } => write!( + fmt, + "attachment {} used as a color attachment in subpass {} with resolve attachments has a `samples` value of `SampleCount::Sample1`", + attachment, subpass, + ), + Self::SubpassColorDepthStencilAttachmentSamplesMismatch { + subpass, + attachment, + samples, + first_samples, + } => write!( + fmt, + "attachment {} used as a color or depth/stencil attachment in subpass {} has a `samples` value {:?} that is different from the first color attachment ({:?})", + attachment, subpass, samples, first_samples, + ), + Self::SubpassInputAttachmentAspectsNotCompatible { subpass, attachment } => write!( + fmt, + "a reference to attachment {} used as an input attachment in subpass {} selects aspects that are not present in the format of the attachment", + attachment, subpass, + ), + Self::SubpassMaxColorAttachmentsExceeded { .. } => { + write!(fmt, "the `max_color_attachments` limit has been exceeded",) + } + Self::SubpassMultiviewMismatch { + subpass, + multiview, + first_subpass_multiview, + } => write!( + fmt, + "the multiview state (whether `view_mask` is nonzero) of subpass {} is {}, which is different from the first subpass ({})", + subpass, multiview, first_subpass_multiview, + ), + Self::SubpassPreserveAttachmentUsedElsewhere { subpass, attachment } => write!( + fmt, + "attachment {} marked as a preserve attachment in subpass {} is also used as an attachment in that subpass", + attachment, subpass, + ), + Self::SubpassResolveAttachmentsColorAttachmentsLenMismatch { subpass } => write!( + fmt, + "the `resolve_attachments` field of subpass {} was not empty, but its length did not match the length of `color_attachments`", + subpass, + ), + Self::SubpassResolveAttachmentFormatMismatch { + subpass, + resolve_attachment, + color_attachment, + } => write!( + fmt, + "attachment {} used as a resolve attachment in subpass {} has a `format` value different from the corresponding color attachment {}", + subpass, resolve_attachment, color_attachment, + ), + Self::SubpassResolveAttachmentMultisampled { subpass, attachment } => write!( + fmt, + "attachment {} used as a resolve attachment in subpass {} has a `samples` value other than `SampleCount::Sample1`", + attachment, subpass, + ), + Self::SubpassResolveAttachmentWithoutColorAttachment { subpass } => write!( + fmt, + "a resolve attachment in subpass {} is `Some`, but the corresponding color attachment is `None`", + subpass, + ), + } + } +} + +impl From for RenderPassCreationError { + #[inline] + fn from(err: OomError) -> RenderPassCreationError { + RenderPassCreationError::OomError(err) + } +} + +impl From for RenderPassCreationError { + #[inline] + fn from(err: Error) -> RenderPassCreationError { + match err { + err @ Error::OutOfHostMemory => RenderPassCreationError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => { + RenderPassCreationError::OomError(OomError::from(err)) + } + _ => panic!("unexpected error: {:?}", err), + } + } +} diff --git a/vulkano/src/render_pass/desc.rs b/vulkano/src/render_pass/desc.rs deleted file mode 100644 index be8e9bc6..00000000 --- a/vulkano/src/render_pass/desc.rs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// 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::ClearValue; -use crate::format::Format; -use crate::image::ImageLayout; -use crate::image::SampleCount; -use crate::shader::ShaderInterface; -use crate::sync::AccessFlags; -use crate::sync::PipelineStages; - -/// The description of a render pass. -#[derive(Clone, Debug)] -pub struct RenderPassDesc { - attachments: Vec, - subpasses: Vec, - dependencies: Vec, - multiview: Option, -} - -impl RenderPassDesc { - /// Creates a description of a render pass. - pub fn new( - attachments: Vec, - subpasses: Vec, - dependencies: Vec, - ) -> RenderPassDesc { - RenderPassDesc { - attachments, - subpasses, - dependencies, - multiview: None, - } - } - - /// Creates a description of a render pass that uses the multiview feature. - /// See [`MultiviewDesc`] for an explanation of possible configuration options. - pub fn with_multiview( - attachments: Vec, - subpasses: Vec, - dependencies: Vec, - multiview: MultiviewDesc, - ) -> RenderPassDesc { - RenderPassDesc { - attachments, - subpasses, - dependencies, - multiview: Some(multiview), - } - } - - /// Creates a description of an empty render pass, with one subpass and no attachments. - pub fn empty() -> RenderPassDesc { - RenderPassDesc { - attachments: vec![], - subpasses: vec![SubpassDesc { - color_attachments: vec![], - depth_stencil: None, - input_attachments: vec![], - resolve_attachments: vec![], - preserve_attachments: vec![], - }], - dependencies: vec![], - multiview: None, - } - } - - // Returns the attachments of the description. - #[inline] - pub fn attachments(&self) -> &[AttachmentDesc] { - &self.attachments - } - - // Returns the subpasses of the description. - #[inline] - pub fn subpasses(&self) -> &[SubpassDesc] { - &self.subpasses - } - - // Returns the dependencies of the description. - #[inline] - pub fn dependencies(&self) -> &[SubpassDependencyDesc] { - &self.dependencies - } - - // Returns the multiview configuration of the description. - #[inline] - pub fn multiview(&self) -> &Option { - &self.multiview - } - - /// Decodes `I` into a list of clear values where each element corresponds - /// to an attachment. The size of the returned iterator must be the same as the number of - /// attachments. - /// - /// When the user enters a render pass, they need to pass a list of clear values to apply to - /// the attachments of the framebuffer. This method is then responsible for checking the - /// correctness of these values and turning them into a list that can be processed by vulkano. - /// - /// The format of the clear value **must** match the format of the attachment. Attachments - /// that are not loaded with `LoadOp::Clear` must have an entry equal to `ClearValue::None`. - pub fn convert_clear_values(&self, values: I) -> impl Iterator - where - I: IntoIterator, - { - // FIXME: safety checks - values.into_iter() - } - - /// Returns `true` if the subpass of this description is compatible with the shader's fragment - /// output definition. - pub fn is_compatible_with_shader( - &self, - subpass: u32, - shader_interface: &ShaderInterface, - ) -> bool { - let pass_descr = match self.subpasses.get(subpass as usize) { - Some(s) => s, - None => return false, - }; - - for element in shader_interface.elements() { - 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, - }; - - let attachment_desc = &self.attachments[attachment_id]; - - // FIXME: compare formats depending on the number of components and data type - /*if attachment_desc.format != element.format { - return false; - }*/ - } - } - - true - } - - /// Returns `true` if this description is compatible with the other description, - /// as defined in the `Render Pass Compatibility` section of the Vulkan specs. - // TODO: return proper error - pub fn is_compatible_with_desc(&self, other: &RenderPassDesc) -> bool { - if self.attachments().len() != other.attachments().len() { - return false; - } - - for (my_atch, other_atch) in self.attachments.iter().zip(other.attachments.iter()) { - if !my_atch.is_compatible_with(&other_atch) { - return false; - } - } - - return true; - - // FIXME: finish - } -} - -impl Default for RenderPassDesc { - fn default() -> Self { - Self::empty() - } -} - -/// Describes an attachment that will be used in a render pass. -#[derive(Debug, Clone, Copy)] -pub struct AttachmentDesc { - /// Format of the image that is going to be bound. - pub format: Format, - /// Number of samples of the image that is going to be bound. - pub samples: SampleCount, - - /// What the implementation should do with that attachment at the start of the render pass. - pub load: LoadOp, - /// What the implementation should do with that attachment at the end of the render pass. - pub store: StoreOp, - - /// Equivalent of `load` for the stencil component of the attachment, if any. Irrelevant if - /// there is no stencil component. - pub stencil_load: LoadOp, - /// Equivalent of `store` for the stencil component of the attachment, if any. Irrelevant if - /// there is no stencil component. - pub stencil_store: StoreOp, - - /// Layout that the image is going to be in at the start of the renderpass. - /// - /// The vulkano library will automatically switch to the correct layout if necessary, but it - /// is more efficient to set this to the correct value. - pub initial_layout: ImageLayout, - - /// Layout that the image will be transitioned to at the end of the renderpass. - pub final_layout: ImageLayout, -} - -impl AttachmentDesc { - /// Returns true if this attachment is compatible with another attachment, as defined in the - /// `Render Pass Compatibility` section of the Vulkan specs. - #[inline] - pub fn is_compatible_with(&self, other: &AttachmentDesc) -> bool { - self.format == other.format && self.samples == other.samples - } -} - -/// Describes one of the subpasses of a render pass. -/// -/// # Restrictions -/// -/// All these restrictions are checked when the `RenderPass` object is created. -/// TODO: that's not the case ^ -/// -/// - The number of color attachments must be less than the limit of the physical device. -/// - All the attachments in `color_attachments` and `depth_stencil` must have the same -/// samples count. -/// - If any attachment is used as both an input attachment and a color or -/// depth/stencil attachment, then each use must use the same layout. -/// - Elements of `preserve_attachments` must not be used in any of the other members. -/// - If `resolve_attachments` is not empty, then all the resolve attachments must be attachments -/// with 1 sample and all the color attachments must have more than 1 sample. -/// - If `resolve_attachments` is not empty, all the resolve attachments must have the same format -/// as the color attachments. -/// - If the first use of an attachment in this renderpass is as an input attachment and the -/// attachment is not also used as a color or depth/stencil attachment in the same subpass, -/// then the loading operation must not be `Clear`. -/// -// TODO: add tests for all these restrictions -// TODO: allow unused attachments (for example attachment 0 and 2 are used, 1 is unused) -#[derive(Debug, Clone)] -pub struct SubpassDesc { - /// Indices and layouts of attachments to use as color attachments. - pub color_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow - - /// Index and layout of the attachment to use as depth-stencil attachment. - pub depth_stencil: Option<(usize, ImageLayout)>, - - /// Indices and layouts of attachments to use as input attachments. - pub input_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow - - /// If not empty, each color attachment will be resolved into each corresponding entry of - /// this list. - /// - /// If this value is not empty, it **must** be the same length as `color_attachments`. - pub resolve_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow - - /// Indices of attachments that will be preserved during this pass. - pub preserve_attachments: Vec, // TODO: Vec is slow -} - -/// Describes a dependency between two subpasses of a render pass. -/// -/// The implementation is allowed to change the order of the subpasses within a render pass, unless -/// you specify that there exists a dependency between two subpasses (ie. the result of one will be -/// used as the input of another one). -#[derive(Debug, Clone, Copy)] -pub struct SubpassDependencyDesc { - /// Index of the subpass that writes the data that `destination_subpass` is going to use. - pub source_subpass: usize, - - /// Index of the subpass that reads the data that `source_subpass` wrote. - pub destination_subpass: usize, - - /// The pipeline stages that must be finished on the previous subpass before the destination - /// subpass can start. - pub source_stages: PipelineStages, - - /// The pipeline stages of the destination subpass that must wait for the source to be finished. - /// Stages that are earlier of the stages specified here can start before the source is - /// finished. - pub destination_stages: PipelineStages, - - /// The way the source subpass accesses the attachments on which we depend. - pub source_access: AccessFlags, - - /// The way the destination subpass accesses the attachments on which we depend. - pub destination_access: AccessFlags, - - /// If false, then the whole subpass must be finished for the next one to start. If true, then - /// the implementation can start the new subpass for some given pixels as long as the previous - /// subpass is finished for these given pixels. - /// - /// In other words, if the previous subpass has some side effects on other parts of an - /// attachment, then you should set it to false. - /// - /// Passing `false` is always safer than passing `true`, but in practice you rarely need to - /// pass `false`. - pub by_region: bool, -} - -/// Describes what the implementation should do with an attachment after all the subpasses have -/// completed. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[repr(i32)] -pub enum StoreOp { - /// The attachment will be stored. This is what you usually want. - /// - /// While this is the most intuitive option, it is also slower than `DontCare` because it can - /// take time to write the data back to memory. - Store = ash::vk::AttachmentStoreOp::STORE.as_raw(), - - /// What happens is implementation-specific. - /// - /// This is purely an optimization compared to `Store`. The implementation doesn't need to copy - /// from the internal cache to the memory, which saves memory bandwidth. - /// - /// This doesn't mean that the data won't be copied, as an implementation is also free to not - /// use a cache and write the output directly in memory. In other words, the content of the - /// image will be undefined. - DontCare = ash::vk::AttachmentStoreOp::DONT_CARE.as_raw(), -} - -impl From for ash::vk::AttachmentStoreOp { - #[inline] - fn from(val: StoreOp) -> Self { - Self::from_raw(val as i32) - } -} - -/// Describes what the implementation should do with an attachment at the start of the subpass. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[repr(i32)] -pub enum LoadOp { - /// The content of the attachment will be loaded from memory. This is what you want if you want - /// to draw over something existing. - /// - /// While this is the most intuitive option, it is also the slowest because it uses a lot of - /// memory bandwidth. - Load = ash::vk::AttachmentLoadOp::LOAD.as_raw(), - - /// The content of the attachment will be filled by the implementation with a uniform value - /// that you must provide when you start drawing. - /// - /// This is what you usually use at the start of a frame, in order to reset the content of - /// the color, depth and/or stencil buffers. - /// - /// See the `draw_inline` and `draw_secondary` methods of `PrimaryComputeBufferBuilder`. - Clear = ash::vk::AttachmentLoadOp::CLEAR.as_raw(), - - /// The attachment will have undefined content. - /// - /// This is what you should use for attachments that you intend to entirely cover with draw - /// commands. - /// If you are going to fill the attachment with a uniform value, it is better to use `Clear` - /// instead. - DontCare = ash::vk::AttachmentLoadOp::DONT_CARE.as_raw(), -} - -impl From for ash::vk::AttachmentLoadOp { - #[inline] - fn from(val: LoadOp) -> Self { - Self::from_raw(val as i32) - } -} - -/// Describes the `multiview` configuration for the render pass which is used to draw -/// to multiple layers of a framebuffer inside of a single render pass. -#[derive(Debug, Clone)] -pub struct MultiviewDesc { - /// The view masks indicate which layers of the framebuffer should be rendered for each subpass. - /// Values are bit masks which means that for example `0b11` will draw to the first two layers - /// and `0b101` will draw to the first and third layer. - pub view_masks: Vec, - - /// The correlation masks indicate sets of views that may be more efficient to render - /// concurrently (usually because they show the same geometry from almost the same perspective). - /// Values are bit masks which means that for example `0b11` means the first two layers are - /// highly correlated and `0b101` means the first and third layer are highly correlated. - pub correlation_masks: Vec, - - /// The view offsets contain additional information for each subpass dependency that indicate - /// which views in the source subpass the views of the destination subpass depend on. - pub view_offsets: Vec, -} - -impl MultiviewDesc { - /// Returns the index of the layer with the biggest index that is - /// referred to by a mask in the multiview description. - pub fn highest_used_layer(&self) -> u32 { - self.view_masks - .iter() - .chain(self.correlation_masks.iter()) - .map(|&mask| 32 - mask.leading_zeros()) // the highest set bit corresponds to the highest used layer - .max() - .unwrap_or(0) - } - - /// Returns the amount of layers that are used in the multiview description. - pub fn used_layer_count(&self) -> u32 { - self.view_masks - .iter() - .chain(self.correlation_masks.iter()) - .fold(0, |acc, &mask| acc | mask) - .count_ones() - } -} - -/// Possible resolve modes for depth and stencil attachments. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum ResolveMode { - None = ash::vk::ResolveModeFlags::NONE.as_raw(), - SampleZero = ash::vk::ResolveModeFlags::SAMPLE_ZERO.as_raw(), - Average = ash::vk::ResolveModeFlags::AVERAGE.as_raw(), - Min = ash::vk::ResolveModeFlags::MIN.as_raw(), - Max = ash::vk::ResolveModeFlags::MAX.as_raw(), -} - -#[derive(Clone, Copy, Debug)] -pub struct ResolveModes { - pub none: bool, - pub sample_zero: bool, - pub average: bool, - pub min: bool, - pub max: bool, -} - -impl From for ResolveModes { - #[inline] - fn from(val: ash::vk::ResolveModeFlags) -> Self { - Self { - none: val.intersects(ash::vk::ResolveModeFlags::NONE), - sample_zero: val.intersects(ash::vk::ResolveModeFlags::SAMPLE_ZERO), - average: val.intersects(ash::vk::ResolveModeFlags::AVERAGE), - min: val.intersects(ash::vk::ResolveModeFlags::MIN), - max: val.intersects(ash::vk::ResolveModeFlags::MAX), - } - } -} diff --git a/vulkano/src/render_pass/framebuffer.rs b/vulkano/src/render_pass/framebuffer.rs index 8bb8953e..b888eb5c 100644 --- a/vulkano/src/render_pass/framebuffer.rs +++ b/vulkano/src/render_pass/framebuffer.rs @@ -10,17 +10,19 @@ use crate::check_errors; use crate::device::Device; use crate::device::DeviceOwned; +use crate::format::Format; use crate::image::view::ImageViewAbstract; -use crate::render_pass::ensure_image_view_compatible; -use crate::render_pass::IncompatibleRenderPassAttachmentError; +use crate::image::view::ImageViewType; +use crate::image::ImageDimensions; +use crate::image::SampleCount; use crate::render_pass::RenderPass; use crate::Error; use crate::OomError; use crate::VulkanObject; use smallvec::SmallVec; -use std::cmp; use std::error; use std::fmt; +use std::hash::{Hash, Hasher}; use std::mem::MaybeUninit; use std::ops::Range; use std::ptr; @@ -29,118 +31,301 @@ use std::sync::Arc; /// The image views that are attached to a render pass during drawing. /// /// A framebuffer is a collection of images, and supplies the actual inputs and outputs of each -/// subpass within a render pass. It is created from a subpass and must match it: each attachment -/// point in the subpass must have a matching image in the framebuffer. -/// -/// Creating a framebuffer is done by calling `Framebuffer::start`, which returns a -/// `FramebufferBuilder` object. You can then add the framebuffer attachments one by one by -/// calling `add(image)`. When you are done, call `build()`. -/// -/// Both the `add` and the `build` functions perform various checks to make sure that the number -/// of images is correct and that each image is compatible with the attachment definition in the -/// render pass. +/// attachment within a render pass. Each attachment point in the render pass must have a matching +/// image in the framebuffer. /// /// ``` /// # use std::sync::Arc; /// # use vulkano::render_pass::RenderPass; -/// use vulkano::render_pass::Framebuffer; +/// # use vulkano::image::AttachmentImage; +/// # use vulkano::image::view::ImageView; +/// use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo}; /// /// # let render_pass: Arc = return; -/// # let view: Arc> = return; +/// # let view: Arc> = return; /// // let render_pass: Arc<_> = ...; -/// let framebuffer = Framebuffer::start(render_pass.clone()) -/// .add(view).unwrap() -/// .build().unwrap(); +/// let framebuffer = Framebuffer::new( +/// render_pass.clone(), +/// FramebufferCreateInfo { +/// attachments: vec![view], +/// ..Default::default() +/// }, +/// ).unwrap(); /// ``` -/// -/// All framebuffer objects implement the `FramebufferAbstract` trait. This means that you can cast -/// any `Arc>` into an `Arc` for easier storage. -/// -/// ## Framebuffer dimensions -/// -/// If you use `Framebuffer::start()` to create a framebuffer then vulkano will automatically -/// make sure that all the attachments have the same dimensions, as this is the most common -/// situation. -/// -/// Alternatively you can also use `with_intersecting_dimensions`, in which case the dimensions of -/// the framebuffer will be the intersection of the dimensions of all attachments, or -/// `with_dimensions` if you want to specify exact dimensions. If you use `with_dimensions`, you -/// are allowed to attach images that are larger than these dimensions. -/// -/// If the dimensions of the framebuffer don't match the dimensions of one of its attachment, then -/// only the top-left hand corner of the image will be drawn to. -/// +#[derive(Debug)] pub struct Framebuffer { handle: ash::vk::Framebuffer, - device: Arc, render_pass: Arc, - dimensions: [u32; 3], - resources: SmallVec<[Arc; 4]>, + + attachments: Vec>, + extent: [u32; 2], + layers: u32, } impl Framebuffer { - /// Starts building a framebuffer. - pub fn start(render_pass: Arc) -> FramebufferBuilder { - FramebufferBuilder { - render_pass, - raw_ids: SmallVec::new(), - dimensions: FramebufferBuilderDimensions::AutoIdentical(None), - attachments: SmallVec::new(), - } - } - - /// Starts building a framebuffer. The dimensions of the framebuffer will automatically be - /// the intersection of the dimensions of all the attachments. - pub fn with_intersecting_dimensions(render_pass: Arc) -> FramebufferBuilder { - FramebufferBuilder { - render_pass, - raw_ids: SmallVec::new(), - dimensions: FramebufferBuilderDimensions::AutoSmaller(None), - attachments: SmallVec::new(), - } - } - - /// Starts building a framebuffer. - pub fn with_dimensions( + /// Creates a new `Framebuffer`. + pub fn new( render_pass: Arc, - dimensions: [u32; 3], - ) -> FramebufferBuilder { - FramebufferBuilder { - render_pass, - raw_ids: SmallVec::new(), - dimensions: FramebufferBuilderDimensions::Specific(dimensions), - attachments: SmallVec::new(), + create_info: FramebufferCreateInfo, + ) -> Result, FramebufferCreationError> { + let FramebufferCreateInfo { + attachments, + mut extent, + mut layers, + _ne: _, + } = create_info; + + let device = render_pass.device(); + + // VUID-VkFramebufferCreateInfo-attachmentCount-00876 + if attachments.len() != render_pass.attachments().len() { + return Err(FramebufferCreationError::AttachmentCountMismatch { + provided: attachments.len() as u32, + required: render_pass.attachments().len() as u32, + }); } - } - /// Returns the width, height and layers of this framebuffer. - #[inline] - pub fn dimensions(&self) -> [u32; 3] { - self.dimensions - } + let auto_extent = extent[0] == 0 || extent[1] == 0; + let auto_layers = layers == 0; - /// Returns the width of the framebuffer in pixels. - #[inline] - pub fn width(&self) -> u32 { - self.dimensions[0] - } + // VUID-VkFramebufferCreateInfo-width-00885 + // VUID-VkFramebufferCreateInfo-height-00887 + if auto_extent { + if attachments.is_empty() { + return Err(FramebufferCreationError::AutoExtentAttachmentsEmpty); + } - /// Returns the height of the framebuffer in pixels. - #[inline] - pub fn height(&self) -> u32 { - self.dimensions[1] - } + extent = [u32::MAX, u32::MAX]; + } - /// Returns the number of layers (or depth) of the framebuffer. - #[inline] - pub fn layers(&self) -> u32 { - self.dimensions[2] - } + // VUID-VkFramebufferCreateInfo-layers-00889 + if auto_layers { + if attachments.is_empty() { + return Err(FramebufferCreationError::AutoLayersAttachmentsEmpty); + } - /// Returns the device that was used to create this framebuffer. - #[inline] - pub fn device(&self) -> &Arc { - &self.device + if render_pass.views_used() != 0 { + // VUID-VkFramebufferCreateInfo-renderPass-02531 + layers = 1; + } else { + layers = u32::MAX; + } + } else { + // VUID-VkFramebufferCreateInfo-renderPass-02531 + if render_pass.views_used() != 0 && layers != 1 { + return Err(FramebufferCreationError::MultiviewLayersInvalid); + } + } + + let attachments_vk = attachments + .iter() + .zip(render_pass.attachments()) + .enumerate() + .map(|(attachment_num, (image_view, attachment_desc))| { + let attachment_num = attachment_num as u32; + assert_eq!(device, image_view.device()); + + for subpass in render_pass.subpasses() { + // VUID-VkFramebufferCreateInfo-pAttachments-00877 + if subpass + .color_attachments + .iter() + .flatten() + .any(|atch_ref| atch_ref.attachment == attachment_num) + { + if !image_view.usage().color_attachment { + return Err(FramebufferCreationError::AttachmentMissingUsage { + attachment: attachment_num, + usage: "color_attachment", + }); + } + } + + // VUID-VkFramebufferCreateInfo-pAttachments-02633 + if let Some(atch_ref) = &subpass.depth_stencil_attachment { + if atch_ref.attachment == attachment_num { + if !image_view.usage().depth_stencil_attachment { + return Err(FramebufferCreationError::AttachmentMissingUsage { + attachment: attachment_num, + usage: "depth_stencil", + }); + } + } + } + + // VUID-VkFramebufferCreateInfo-pAttachments-00879 + if subpass + .input_attachments + .iter() + .flatten() + .any(|atch_ref| atch_ref.attachment == attachment_num) + { + if !image_view.usage().input_attachment { + return Err(FramebufferCreationError::AttachmentMissingUsage { + attachment: attachment_num, + usage: "input_attachment", + }); + } + } + } + + // VUID-VkFramebufferCreateInfo-pAttachments-00880 + if Some(image_view.format()) != attachment_desc.format { + return Err(FramebufferCreationError::AttachmentFormatMismatch { + attachment: attachment_num, + provided: Some(image_view.format()), + required: attachment_desc.format, + }); + } + + // VUID-VkFramebufferCreateInfo-pAttachments-00881 + if image_view.image().samples() != attachment_desc.samples { + return Err(FramebufferCreationError::AttachmentSamplesMismatch { + attachment: attachment_num, + provided: image_view.image().samples(), + required: attachment_desc.samples, + }); + } + + let image_view_extent = image_view.image().dimensions().width_height(); + let image_view_array_layers = + image_view.array_layers().end - attachments[0].array_layers().start; + + // VUID-VkFramebufferCreateInfo-renderPass-04536 + if image_view_array_layers < render_pass.views_used() { + return Err( + FramebufferCreationError::MultiviewAttachmentNotEnoughLayers { + attachment: attachment_num, + provided: image_view_array_layers, + min: render_pass.views_used(), + }, + ); + } + + // VUID-VkFramebufferCreateInfo-flags-04533 + // VUID-VkFramebufferCreateInfo-flags-04534 + if auto_extent { + extent[0] = extent[0].min(image_view_extent[0]); + extent[1] = extent[1].min(image_view_extent[1]); + } else if image_view_extent[0] < extent[0] || image_view_extent[1] < extent[1] { + return Err(FramebufferCreationError::AttachmentExtentTooSmall { + attachment: attachment_num, + provided: image_view_extent, + min: extent, + }); + } + + // VUID-VkFramebufferCreateInfo-flags-04535 + if auto_layers { + layers = layers.min(image_view_array_layers); + } else if image_view_array_layers < layers { + return Err(FramebufferCreationError::AttachmentNotEnoughLayers { + attachment: attachment_num, + provided: image_view_array_layers, + min: layers, + }); + } + + // VUID-VkFramebufferCreateInfo-pAttachments-00883 + if image_view.mip_levels().end - image_view.mip_levels().start != 1 { + return Err(FramebufferCreationError::AttachmentMultipleMipLevels { + attachment: attachment_num, + }); + } + + // VUID-VkFramebufferCreateInfo-pAttachments-00884 + if !image_view.component_mapping().is_identity() { + return Err( + FramebufferCreationError::AttachmentComponentMappingNotIdentity { + attachment: attachment_num, + }, + ); + } + + // VUID-VkFramebufferCreateInfo-pAttachments-00891 + if matches!( + image_view.ty(), + ImageViewType::Dim2d | ImageViewType::Dim2dArray + ) && matches!( + image_view.image().dimensions(), + ImageDimensions::Dim3d { .. } + ) && image_view.format().type_color().is_none() + { + return Err( + FramebufferCreationError::Attachment2dArrayCompatibleDepthStencil { + attachment: attachment_num, + }, + ); + } + + // VUID-VkFramebufferCreateInfo-flags-04113 + if image_view.ty() == ImageViewType::Dim3d { + return Err(FramebufferCreationError::AttachmentViewType3d { + attachment: attachment_num, + }); + } + + Ok(image_view.internal_object()) + }) + .collect::, _>>()?; + + { + let properties = device.physical_device().properties(); + + // VUID-VkFramebufferCreateInfo-width-00886 + // VUID-VkFramebufferCreateInfo-height-00888 + if extent[0] > properties.max_framebuffer_width + || extent[1] > properties.max_framebuffer_height + { + return Err(FramebufferCreationError::MaxFramebufferExtentExceeded { + provided: extent, + max: [ + properties.max_framebuffer_width, + properties.max_framebuffer_height, + ], + }); + } + + // VUID-VkFramebufferCreateInfo-layers-00890 + if layers > properties.max_framebuffer_layers { + return Err(FramebufferCreationError::MaxFramebufferLayersExceeded { + provided: layers, + max: properties.max_framebuffer_layers, + }); + } + } + + let create_info = ash::vk::FramebufferCreateInfo { + flags: ash::vk::FramebufferCreateFlags::empty(), + render_pass: render_pass.internal_object(), + attachment_count: attachments_vk.len() as u32, + p_attachments: attachments_vk.as_ptr(), + width: extent[0], + height: extent[1], + layers, + ..Default::default() + }; + + let handle = unsafe { + let fns = device.fns(); + let mut output = MaybeUninit::uninit(); + check_errors(fns.v1_0.create_framebuffer( + device.internal_object(), + &create_info, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Framebuffer { + handle, + render_pass, + + attachments, + extent, + layers, + })) } /// Returns the renderpass that was used to create this framebuffer. @@ -149,28 +334,42 @@ impl Framebuffer { &self.render_pass } - /// Returns the attachment of the framebuffer with the given index. - /// - /// If the `index` is not between `0` and `num_attachments`, then `None` should be returned. + /// Returns the attachments of the framebuffer. #[inline] - pub fn attached_image_view(&self, index: usize) -> Option<&Arc> { - self.resources.get(index) + pub fn attachments(&self) -> &[Arc] { + &self.attachments + } + + /// Returns the extent (width and height) of the framebuffer. + #[inline] + pub fn extent(&self) -> [u32; 2] { + self.extent + } + + /// Returns the number of layers of the framebuffer. + #[inline] + pub fn layers(&self) -> u32 { + self.layers } /// Returns the layer ranges for all attachments. #[inline] pub fn attached_layers_ranges(&self) -> SmallVec<[Range; 4]> { - self.resources + self.attachments .iter() .map(|img| img.array_layers()) .collect() } } -unsafe impl DeviceOwned for Framebuffer { +impl Drop for Framebuffer { #[inline] - fn device(&self) -> &Arc { - &self.device + fn drop(&mut self) { + unsafe { + let fns = self.device().fns(); + fns.v1_0 + .destroy_framebuffer(self.device().internal_object(), self.handle, ptr::null()); + } } } @@ -183,252 +382,183 @@ unsafe impl VulkanObject for Framebuffer { } } -impl Drop for Framebuffer { +unsafe impl DeviceOwned for Framebuffer { #[inline] - fn drop(&mut self) { - unsafe { - let fns = self.device.fns(); - fns.v1_0 - .destroy_framebuffer(self.device.internal_object(), self.handle, ptr::null()); - } + fn device(&self) -> &Arc { + self.render_pass.device() } } -/// Prototype of a framebuffer. -pub struct FramebufferBuilder { - render_pass: Arc, - raw_ids: SmallVec<[ash::vk::ImageView; 4]>, - dimensions: FramebufferBuilderDimensions, - attachments: SmallVec<[Arc; 4]>, +impl PartialEq for Framebuffer { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle && self.device() == other.device() + } } -#[derive(Debug)] -enum FramebufferBuilderDimensions { - AutoIdentical(Option<[u32; 3]>), - AutoSmaller(Option<[u32; 3]>), - Specific([u32; 3]), +impl Eq for Framebuffer {} + +impl Hash for Framebuffer { + #[inline] + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.device().hash(state); + } } -impl FramebufferBuilder { - /// Appends an attachment to the prototype of the framebuffer. +/// Parameters to create a new `Framebuffer`. +#[derive(Clone, Debug)] +pub struct FramebufferCreateInfo { + /// The attachment images that are to be used in the framebuffer. /// - /// Attachments must be added in the same order as the one defined in the render pass. - pub fn add( - self, - attachment: Arc, - ) -> Result { - if self.raw_ids.len() >= self.render_pass.desc().attachments().len() { - return Err(FramebufferCreationError::AttachmentsCountMismatch { - expected: self.render_pass.desc().attachments().len(), - obtained: self.raw_ids.len() + 1, - }); + /// Attachments are specified in the same order as they are defined in the render pass, and + /// there must be exactly as many. This implies that the list must be empty if the render pass + /// specifies no attachments. Each image must have the correct usages set to be used for the + /// types of attachment that the render pass will use it as. + /// + /// The attachment images must not be smaller than `extent` and `layers`, but can be larger and + /// have different sizes from each other. Any leftover parts of an image will be left untouched + /// during rendering. + /// + /// If the render pass has multiview enabled (`views_used` does not return 0), then each + /// image must have at least `views_used` array layers. + /// + /// The default value is empty. + pub attachments: Vec>, + + /// The extent (width and height) of the framebuffer. + /// + /// This must be no larger than the smallest width and height of the images in `attachments`. + /// If one of the elements is set to 0, the extent will be calculated automatically from the + /// extents of the attachment images to be the largest allowed. At least one attachment image + /// must be specified in that case. + /// + /// The extent, whether automatically calculated or specified explicitly, must not be larger + /// than the [`max_framebuffer_width`](crate::device::Properties::max_framebuffer_width) and + /// [`max_framebuffer_height`](crate::device::Properties::max_framebuffer_height) limits. + /// + /// The default value is `[0, 0]`. + pub extent: [u32; 2], + + /// The number of layers of the framebuffer. + /// + /// This must be no larger than the smallest number of array layers of the images in + /// `attachments`. If set to 0, the number of layers will be calculated automatically from the + /// layer ranges of the attachment images to be the largest allowed. At least one attachment + /// image must be specified in that case. + /// + /// The number of layers, whether automatically calculated or specified explicitly, must not be + /// larger than the + /// [`max_framebuffer_layers`](crate::device::Properties::max_framebuffer_layers) limit. + /// + /// If the render pass has multiview enabled (`views_used` does not return 0), then this value + /// must be 0 or 1. + /// + /// The default value is `0`. + pub layers: u32, + + pub _ne: crate::NonExhaustive, +} + +impl Default for FramebufferCreateInfo { + #[inline] + fn default() -> Self { + Self { + attachments: Vec::new(), + extent: [0, 0], + layers: 0, + _ne: crate::NonExhaustive(()), } - - match ensure_image_view_compatible( - self.render_pass.desc(), - self.raw_ids.len(), - attachment.as_ref(), - ) { - Ok(()) => (), - Err(err) => return Err(FramebufferCreationError::IncompatibleAttachment(err)), - }; - - let image_dimensions = attachment.image().dimensions(); - let array_layers = attachment.array_layers(); - debug_assert_eq!(image_dimensions.depth(), 1); - - let view_dimensions = [ - image_dimensions.width(), - image_dimensions.height(), - array_layers.end - array_layers.start, - ]; - - let dimensions = match self.dimensions { - FramebufferBuilderDimensions::AutoIdentical(None) => { - FramebufferBuilderDimensions::AutoIdentical(Some(view_dimensions)) - } - FramebufferBuilderDimensions::AutoIdentical(Some(current)) => { - if view_dimensions != current { - return Err(FramebufferCreationError::AttachmentDimensionsIncompatible { - expected: current, - obtained: view_dimensions, - }); - } - - FramebufferBuilderDimensions::AutoIdentical(Some(current)) - } - FramebufferBuilderDimensions::AutoSmaller(None) => { - FramebufferBuilderDimensions::AutoSmaller(Some(view_dimensions)) - } - FramebufferBuilderDimensions::AutoSmaller(Some(current)) => { - let new_dims = [ - cmp::min(current[0], view_dimensions[0]), - cmp::min(current[1], view_dimensions[1]), - cmp::min(current[2], view_dimensions[2]), - ]; - - FramebufferBuilderDimensions::AutoSmaller(Some(new_dims)) - } - FramebufferBuilderDimensions::Specific(current) => { - if view_dimensions[0] < current[0] - || view_dimensions[1] < current[1] - || view_dimensions[2] < current[2] - { - return Err(FramebufferCreationError::AttachmentDimensionsIncompatible { - expected: current, - obtained: view_dimensions, - }); - } - - FramebufferBuilderDimensions::Specific(view_dimensions) - } - }; - - let mut raw_ids = self.raw_ids; - raw_ids.push(attachment.internal_object()); - - let mut attachments = self.attachments; - attachments.push(attachment); - - Ok(FramebufferBuilder { - render_pass: self.render_pass, - raw_ids, - dimensions, - attachments, - }) - } - - /// Builds the framebuffer. - pub fn build(self) -> Result, FramebufferCreationError> { - let device = self.render_pass.device().clone(); - - // Check the number of attachments. - if self.raw_ids.len() != self.render_pass.desc().attachments().len() { - return Err(FramebufferCreationError::AttachmentsCountMismatch { - expected: self.render_pass.desc().attachments().len(), - obtained: self.raw_ids.len(), - }); - } - - // Compute the dimensions. - let dimensions = match self.dimensions { - FramebufferBuilderDimensions::Specific(dims) - | FramebufferBuilderDimensions::AutoIdentical(Some(dims)) - | FramebufferBuilderDimensions::AutoSmaller(Some(dims)) => dims, - FramebufferBuilderDimensions::AutoIdentical(None) - | FramebufferBuilderDimensions::AutoSmaller(None) => { - return Err(FramebufferCreationError::CantDetermineDimensions); - } - }; - - // Checking the dimensions against the limits. - { - let properties = device.physical_device().properties(); - let limits = [ - properties.max_framebuffer_width, - properties.max_framebuffer_height, - properties.max_framebuffer_layers, - ]; - if dimensions[0] > limits[0] || dimensions[1] > limits[1] || dimensions[2] > limits[2] { - return Err(FramebufferCreationError::DimensionsTooLarge); - } - } - - let mut layers = dimensions[2]; - - if let Some(multiview) = self.render_pass.desc().multiview() { - // There needs to be at least as many layers in the framebuffer - // as the highest layer that gets referenced by the multiview masking. - if multiview.highest_used_layer() > dimensions[2] { - return Err(FramebufferCreationError::InsufficientLayerCount { - minimum: multiview.highest_used_layer(), - current: dimensions[2], - }); - } - - // VUID-VkFramebufferCreateInfo-renderPass-02531 - // The framebuffer has to be created with one layer if multiview is enabled even though - // the underlying images generally have more layers - // but these layers get used by the multiview functionality. - if multiview.view_masks.iter().any(|&mask| mask != 0) { - layers = 1; - } - } - - let handle = unsafe { - let fns = device.fns(); - - let infos = ash::vk::FramebufferCreateInfo { - flags: ash::vk::FramebufferCreateFlags::empty(), - render_pass: self.render_pass.internal_object(), - attachment_count: self.raw_ids.len() as u32, - p_attachments: self.raw_ids.as_ptr(), - width: dimensions[0], - height: dimensions[1], - layers, - ..Default::default() - }; - - let mut output = MaybeUninit::uninit(); - check_errors(fns.v1_0.create_framebuffer( - device.internal_object(), - &infos, - ptr::null(), - output.as_mut_ptr(), - ))?; - output.assume_init() - }; - - Ok(Arc::new(Framebuffer { - handle, - device, - render_pass: self.render_pass, - dimensions, - resources: self.attachments, - })) } } -/// Error that can happen when creating a framebuffer object. +/// Error that can happen when creating a `Framebuffer`. #[derive(Copy, Clone, Debug)] pub enum FramebufferCreationError { /// Out of memory. OomError(OomError), - /// The requested dimensions exceed the device's limits. - DimensionsTooLarge, - /// The number of minimum layers expected by the render pass exceed the framebuffer layers. - /// This can happen when the multiview feature is enabled and the specified view or correlation - /// masks refer to more layers than the framebuffer has. - InsufficientLayerCount { - /// Minimum number of layers. - minimum: u32, - /// Number of framebuffer layers. - current: u32, - }, - /// The attachment has a size that isn't compatible with the requested framebuffer dimensions. - AttachmentDimensionsIncompatible { - /// Expected dimensions. - expected: [u32; 3], - /// Attachment dimensions. - obtained: [u32; 3], - }, + + /// An attachment image is a 2D image view created from a 3D image, and has a depth/stencil + /// format. + Attachment2dArrayCompatibleDepthStencil { attachment: u32 }, + + /// An attachment image has a non-identity component mapping. + AttachmentComponentMappingNotIdentity { attachment: u32 }, + /// The number of attachments doesn't match the number expected by the render pass. - AttachmentsCountMismatch { - /// Expected number of attachments. - expected: usize, - /// Number of attachments that were given. - obtained: usize, + AttachmentCountMismatch { provided: u32, required: u32 }, + + /// An attachment image has an extent smaller than the provided `extent`. + AttachmentExtentTooSmall { + attachment: u32, + provided: [u32; 2], + min: [u32; 2], }, - /// One of the images cannot be used as the requested attachment. - IncompatibleAttachment(IncompatibleRenderPassAttachmentError), - /// The framebuffer has no attachment and no dimension was specified. - CantDetermineDimensions, + + /// An attachment image has a `format` different from what the render pass requires. + AttachmentFormatMismatch { + attachment: u32, + provided: Option, + required: Option, + }, + + /// An attachment image is missing a usage that the render pass requires it to have. + AttachmentMissingUsage { + attachment: u32, + usage: &'static str, + }, + + /// An attachment image has multiple mip levels. + AttachmentMultipleMipLevels { attachment: u32 }, + + /// An attachment image has less array layers than the provided `layers`. + AttachmentNotEnoughLayers { + attachment: u32, + provided: u32, + min: u32, + }, + + /// An attachment image has a `samples` different from what the render pass requires. + AttachmentSamplesMismatch { + attachment: u32, + provided: SampleCount, + required: SampleCount, + }, + + /// An attachment image has a `ty` of [`ImageViewType::Dim3d`]. + AttachmentViewType3d { attachment: u32 }, + + /// One of the elements of `extent` is zero, but no attachment images were given to calculate + /// the extent from. + AutoExtentAttachmentsEmpty, + + /// `layers` is zero, but no attachment images were given to calculate the number of layers + /// from. + AutoLayersAttachmentsEmpty, + + /// The provided `extent` exceeds the `max_framebuffer_width` or `max_framebuffer_height` + /// limits. + MaxFramebufferExtentExceeded { provided: [u32; 2], max: [u32; 2] }, + + /// The provided `layers` exceeds the `max_framebuffer_layers` limit. + MaxFramebufferLayersExceeded { provided: u32, max: u32 }, + + /// The render pass has multiview enabled, and an attachment image has less layers than the + /// number of views in the render pass. + MultiviewAttachmentNotEnoughLayers { + attachment: u32, + provided: u32, + min: u32, + }, + + /// The render pass has multiview enabled, but `layers` was not 0 or 1. + MultiviewLayersInvalid, } impl From for FramebufferCreationError { #[inline] - fn from(err: OomError) -> FramebufferCreationError { - FramebufferCreationError::OomError(err) + fn from(err: OomError) -> Self { + Self::OomError(err) } } @@ -436,8 +566,7 @@ impl error::Error for FramebufferCreationError { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { - FramebufferCreationError::OomError(ref err) => Some(err), - FramebufferCreationError::IncompatibleAttachment(ref err) => Some(err), + Self::OomError(ref err) => Some(err), _ => None, } } @@ -446,38 +575,122 @@ impl error::Error for FramebufferCreationError { impl fmt::Display for FramebufferCreationError { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - FramebufferCreationError::OomError(_) => "no memory available", - FramebufferCreationError::DimensionsTooLarge => { - "the dimensions of the framebuffer are too large" - } - FramebufferCreationError::InsufficientLayerCount { .. } => { - "the number of minimum layers expected by the render pass exceed the framebuffer layers" - } - FramebufferCreationError::AttachmentDimensionsIncompatible { .. } => { - "the attachment has a size that isn't compatible with the framebuffer dimensions" - } - FramebufferCreationError::AttachmentsCountMismatch { .. } => { - "the number of attachments doesn't match the number expected by the render pass" - } - FramebufferCreationError::IncompatibleAttachment(_) => { - "one of the images cannot be used as the requested attachment" - } - FramebufferCreationError::CantDetermineDimensions => { - "the framebuffer has no attachment and no dimension was specified" - } - } - ) + match *self { + Self::OomError(_) => write!( + fmt, + "no memory available", + ), + Self::Attachment2dArrayCompatibleDepthStencil { attachment } => write!( + fmt, + "attachment image {} is a 2D image view created from a 3D image, and has a depth/stencil format", + attachment, + ), + Self::AttachmentComponentMappingNotIdentity { attachment } => write!( + fmt, + "attachment image {} has a non-identity component mapping", + attachment, + ), + Self::AttachmentCountMismatch { .. } => write!( + fmt, + "the number of attachments doesn't match the number expected by the render pass", + ), + Self::AttachmentExtentTooSmall { + attachment, + provided, + min, + } => write!( + fmt, + "attachment image {} has an extent ({:?}) smaller than the provided `extent` ({:?})", + attachment, provided, min, + ), + Self::AttachmentFormatMismatch { + attachment, + provided, + required, + } => write!( + fmt, + "attachment image {} has a `format` ({:?}) different from what the render pass requires ({:?})", + attachment, provided, required, + ), + Self::AttachmentMissingUsage { + attachment, + usage, + } => write!( + fmt, + "attachment image {} is missing usage `{}` that the render pass requires it to have", + attachment, usage, + ), + Self::AttachmentMultipleMipLevels { + attachment, + } => write!( + fmt, + "attachment image {} has multiple mip levels", + attachment, + ), + Self::AttachmentNotEnoughLayers { + attachment, + provided, + min, + } => write!( + fmt, + "attachment image {} has less layers ({}) than the provided `layers` ({})", + attachment, provided, min, + ), + Self::AttachmentSamplesMismatch { + attachment, + provided, + required, + } => write!( + fmt, + "attachment image {} has a `samples` ({:?}) different from what the render pass requires ({:?})", + attachment, provided, required, + ), + Self::AttachmentViewType3d { + attachment, + } => write!( + fmt, + "attachment image {} has a `ty` of `ImageViewType::Dim3d`", + attachment, + ), + Self::AutoExtentAttachmentsEmpty => write!( + fmt, + "one of the elements of `extent` is zero, but no attachment images were given to calculate the extent from", + ), + Self::AutoLayersAttachmentsEmpty => write!( + fmt, + "`layers` is zero, but no attachment images were given to calculate the number of layers from", + ), + Self::MaxFramebufferExtentExceeded { provided, max } => write!( + fmt, + "the provided `extent` ({:?}) exceeds the `max_framebuffer_width` or `max_framebuffer_height` limits ({:?})", + provided, max, + ), + Self::MaxFramebufferLayersExceeded { provided, max } => write!( + fmt, + "the provided `layers` ({}) exceeds the `max_framebuffer_layers` limit ({})", + provided, max, + ), + Self::MultiviewAttachmentNotEnoughLayers { + attachment, + provided, + min, + } => write!( + fmt, + "the render pass has multiview enabled, and attachment image {} has less layers ({}) than the number of views in the render pass ({})", + attachment, provided, min, + ), + Self::MultiviewLayersInvalid => write!( + fmt, + "the render pass has multiview enabled, but `layers` was not 0 or 1", + ), + } } } impl From for FramebufferCreationError { #[inline] - fn from(err: Error) -> FramebufferCreationError { - FramebufferCreationError::from(OomError::from(err)) + fn from(err: Error) -> Self { + Self::from(OomError::from(err)) } } @@ -487,6 +700,7 @@ mod tests { use crate::image::attachment::AttachmentImage; use crate::image::view::ImageView; use crate::render_pass::Framebuffer; + use crate::render_pass::FramebufferCreateInfo; use crate::render_pass::FramebufferCreationError; use crate::render_pass::RenderPass; @@ -514,21 +728,45 @@ mod tests { AttachmentImage::new(device.clone(), [1024, 768], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); - let _ = Framebuffer::start(render_pass) - .add(view) - .unwrap() - .build() - .unwrap(); + let _ = Framebuffer::new( + render_pass, + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) + .unwrap(); } #[test] fn check_device_limits() { let (device, _) = gfx_dev_and_queue!(); - let rp = RenderPass::empty_single_pass(device).unwrap(); - let res = Framebuffer::with_dimensions(rp, [0xffffffff, 0xffffffff, 0xffffffff]).build(); + let render_pass = RenderPass::empty_single_pass(device).unwrap(); + let res = Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + extent: [0xffffffff, 0xffffffff], + layers: 1, + ..Default::default() + }, + ); match res { - Err(FramebufferCreationError::DimensionsTooLarge) => (), + Err(FramebufferCreationError::MaxFramebufferExtentExceeded { .. }) => (), + _ => panic!(), + } + + let res = Framebuffer::new( + render_pass, + FramebufferCreateInfo { + extent: [1, 1], + layers: 0xffffffff, + ..Default::default() + }, + ); + + match res { + Err(FramebufferCreationError::MaxFramebufferLayersExceeded { .. }) => (), _ => panic!(), } } @@ -558,8 +796,14 @@ mod tests { ) .unwrap(); - match Framebuffer::start(render_pass).add(view) { - Err(FramebufferCreationError::IncompatibleAttachment(_)) => (), + match Framebuffer::new( + render_pass, + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ) { + Err(FramebufferCreationError::AttachmentFormatMismatch { .. }) => (), _ => panic!(), } } @@ -591,11 +835,16 @@ mod tests { ) .unwrap(); - let _ = Framebuffer::with_dimensions(render_pass, [512, 512, 1]) - .add(view) - .unwrap() - .build() - .unwrap(); + let _ = Framebuffer::new( + render_pass, + FramebufferCreateInfo { + attachments: vec![view], + extent: [512, 512], + layers: 1, + ..Default::default() + }, + ) + .unwrap(); } #[test] @@ -623,61 +872,16 @@ mod tests { ) .unwrap(); - match Framebuffer::with_dimensions(render_pass, [600, 600, 1]).add(view) { - Err(FramebufferCreationError::AttachmentDimensionsIncompatible { - expected, - obtained, - }) => { - assert_eq!(expected, [600, 600, 1]); - assert_eq!(obtained, [512, 700, 1]); - } - _ => panic!(), - } - } - - #[test] - fn multi_attachments_dims_not_identical() { - let (device, _) = gfx_dev_and_queue!(); - - let render_pass = single_pass_renderpass!(device.clone(), - attachments: { - a: { - load: Clear, - store: DontCare, - format: Format::R8G8B8A8_UNORM, - samples: 1, - }, - b: { - load: Clear, - store: DontCare, - format: Format::R8G8B8A8_UNORM, - samples: 1, - } + match Framebuffer::new( + render_pass, + FramebufferCreateInfo { + attachments: vec![view], + extent: [600, 600], + layers: 1, + ..Default::default() }, - pass: { - color: [a, b], - depth_stencil: {} - } - ) - .unwrap(); - - let a = ImageView::new( - AttachmentImage::new(device.clone(), [512, 512], Format::R8G8B8A8_UNORM).unwrap(), - ) - .unwrap(); - let b = ImageView::new( - AttachmentImage::new(device.clone(), [512, 513], Format::R8G8B8A8_UNORM).unwrap(), - ) - .unwrap(); - - match Framebuffer::start(render_pass).add(a).unwrap().add(b) { - Err(FramebufferCreationError::AttachmentDimensionsIncompatible { - expected, - obtained, - }) => { - assert_eq!(expected, [512, 512, 1]); - assert_eq!(obtained, [512, 513, 1]); - } + ) { + Err(FramebufferCreationError::AttachmentExtentTooSmall { .. }) => (), _ => panic!(), } } @@ -717,16 +921,17 @@ mod tests { ) .unwrap(); - let fb = Framebuffer::with_intersecting_dimensions(render_pass) - .add(a) - .unwrap() - .add(b) - .unwrap() - .build() - .unwrap(); + let framebuffer = Framebuffer::new( + render_pass, + FramebufferCreateInfo { + attachments: vec![a, b], + ..Default::default() + }, + ) + .unwrap(); - match (fb.width(), fb.height(), fb.layers()) { - (256, 128, 1) => (), + match (framebuffer.extent(), framebuffer.layers()) { + ([256, 128], 1) => (), _ => panic!(), } } @@ -762,15 +967,18 @@ mod tests { ) .unwrap(); - let res = Framebuffer::with_intersecting_dimensions(render_pass) - .add(view) - .unwrap() - .build(); + let res = Framebuffer::new( + render_pass, + FramebufferCreateInfo { + attachments: vec![view], + ..Default::default() + }, + ); match res { - Err(FramebufferCreationError::AttachmentsCountMismatch { - expected: 2, - obtained: 1, + Err(FramebufferCreationError::AttachmentCountMismatch { + required: 2, + provided: 1, }) => (), _ => panic!(), } @@ -805,15 +1013,18 @@ mod tests { ) .unwrap(); - let res = Framebuffer::with_intersecting_dimensions(render_pass) - .add(a) - .unwrap() - .add(b); + let res = Framebuffer::new( + render_pass, + FramebufferCreateInfo { + attachments: vec![a, b], + ..Default::default() + }, + ); match res { - Err(FramebufferCreationError::AttachmentsCountMismatch { - expected: 1, - obtained: 2, + Err(FramebufferCreationError::AttachmentCountMismatch { + required: 1, + provided: 2, }) => (), _ => panic!(), } @@ -823,32 +1034,26 @@ mod tests { fn empty_working() { let (device, _) = gfx_dev_and_queue!(); - let rp = RenderPass::empty_single_pass(device).unwrap(); - let _ = Framebuffer::with_dimensions(rp, [512, 512, 1]) - .build() - .unwrap(); + let render_pass = RenderPass::empty_single_pass(device).unwrap(); + let _ = Framebuffer::new( + render_pass, + FramebufferCreateInfo { + extent: [512, 512], + layers: 1, + ..Default::default() + }, + ) + .unwrap(); } #[test] fn cant_determine_dimensions_auto() { let (device, _) = gfx_dev_and_queue!(); - let rp = RenderPass::empty_single_pass(device).unwrap(); - let res = Framebuffer::start(rp).build(); + let render_pass = RenderPass::empty_single_pass(device).unwrap(); + let res = Framebuffer::new(render_pass, FramebufferCreateInfo::default()); match res { - Err(FramebufferCreationError::CantDetermineDimensions) => (), - _ => panic!(), - } - } - - #[test] - fn cant_determine_dimensions_intersect() { - let (device, _) = gfx_dev_and_queue!(); - - let rp = RenderPass::empty_single_pass(device).unwrap(); - let res = Framebuffer::with_intersecting_dimensions(rp).build(); - match res { - Err(FramebufferCreationError::CantDetermineDimensions) => (), + Err(FramebufferCreationError::AutoExtentAttachmentsEmpty) => (), _ => panic!(), } } diff --git a/vulkano/src/render_pass/macros.rs b/vulkano/src/render_pass/macros.rs index d96e1662..4bf82c62 100644 --- a/vulkano/src/render_pass/macros.rs +++ b/vulkano/src/render_pass/macros.rs @@ -64,63 +64,75 @@ macro_rules! ordered_passes_renderpass { ) => ({ use $crate::render_pass::RenderPass; - let desc = { - use $crate::render_pass::AttachmentDesc; - use $crate::render_pass::RenderPassDesc; - use $crate::render_pass::SubpassDependencyDesc; - use $crate::render_pass::SubpassDesc; - use $crate::image::ImageLayout; - use $crate::sync::AccessFlags; - use $crate::sync::PipelineStages; - use std::convert::TryFrom; - + let create_info = { + #[allow(unused)] let mut attachment_num = 0; $( let $atch_name = attachment_num; attachment_num += 1; )* - let mut layouts: Vec<(Option, Option)> = vec![(None, None); attachment_num]; + #[allow(unused)] + let mut layouts: Vec<( + Option<$crate::image::ImageLayout>, + Option<$crate::image::ImageLayout> + )> = vec![(None, None); attachment_num as usize]; let subpasses = vec![ $({ - let desc = SubpassDesc { + let desc = $crate::render_pass::SubpassDescription { color_attachments: vec![ $({ - let layout = &mut layouts[$color_atch]; - layout.0 = layout.0.or(Some(ImageLayout::ColorAttachmentOptimal)); - layout.1 = Some(ImageLayout::ColorAttachmentOptimal); + let layout = &mut layouts[$color_atch as usize]; + layout.0 = layout.0.or(Some($crate::image::ImageLayout::ColorAttachmentOptimal)); + layout.1 = Some($crate::image::ImageLayout::ColorAttachmentOptimal); - ($color_atch, ImageLayout::ColorAttachmentOptimal) + Some($crate::render_pass::AttachmentReference { + attachment: $color_atch, + layout: $crate::image::ImageLayout::ColorAttachmentOptimal, + ..Default::default() + }) }),* ], - depth_stencil: { - let depth: Option<(usize, ImageLayout)> = None; + depth_stencil_attachment: { + let depth: Option<$crate::render_pass::AttachmentReference> = None; $( - let layout = &mut layouts[$depth_atch]; - layout.1 = Some(ImageLayout::DepthStencilAttachmentOptimal); + let layout = &mut layouts[$depth_atch as usize]; + layout.1 = Some($crate::image::ImageLayout::DepthStencilAttachmentOptimal); layout.0 = layout.0.or(layout.1); - let depth = Some(($depth_atch, ImageLayout::DepthStencilAttachmentOptimal)); + let depth = Some($crate::render_pass::AttachmentReference { + attachment: $depth_atch, + layout: $crate::image::ImageLayout::DepthStencilAttachmentOptimal, + ..Default::default() + }); )* depth }, input_attachments: vec![ $({ - let layout = &mut layouts[$input_atch]; - layout.1 = Some(ImageLayout::ShaderReadOnlyOptimal); + let layout = &mut layouts[$input_atch as usize]; + layout.1 = Some($crate::image::ImageLayout::ShaderReadOnlyOptimal); layout.0 = layout.0.or(layout.1); - ($input_atch, ImageLayout::ShaderReadOnlyOptimal) + Some($crate::render_pass::AttachmentReference { + attachment: $input_atch, + layout: $crate::image::ImageLayout::ShaderReadOnlyOptimal, + ..Default::default() + }) }),* ], resolve_attachments: vec![ $($({ - let layout = &mut layouts[$resolve_atch]; - layout.1 = Some(ImageLayout::TransferDstOptimal); + let layout = &mut layouts[$resolve_atch as usize]; + layout.1 = Some($crate::image::ImageLayout::TransferDstOptimal); layout.0 = layout.0.or(layout.1); - ($resolve_atch, ImageLayout::TransferDstOptimal) + Some($crate::render_pass::AttachmentReference { + attachment: $resolve_atch, + layout: $crate::image::ImageLayout::TransferDstOptimal, + ..Default::default() + }) }),*)* ], preserve_attachments: (0 .. attachment_num).filter(|&a| { @@ -129,7 +141,8 @@ macro_rules! ordered_passes_renderpass { $(if a == $input_atch { return false; })* $($(if a == $resolve_atch { return false; })*)* true - }).collect() + }).collect(), + ..Default::default() }; assert!(desc.resolve_attachments.is_empty() || @@ -138,39 +151,46 @@ macro_rules! ordered_passes_renderpass { }),* ]; - let dependencies = (0..subpasses.len().saturating_sub(1)) + let dependencies: Vec<_> = (0..subpasses.len().saturating_sub(1) as u32) .map(|id| { - SubpassDependencyDesc { - source_subpass: id, - destination_subpass: id + 1, - source_stages: PipelineStages { - all_graphics: true, - ..PipelineStages::none() - }, // TODO: correct values - destination_stages: PipelineStages { - all_graphics: true, - ..PipelineStages::none() - }, // TODO: correct values - source_access: AccessFlags::all(), // TODO: correct values - destination_access: AccessFlags::all(), // TODO: correct values + // TODO: correct values + let source_stages = $crate::sync::PipelineStages { + all_graphics: true, + ..$crate::sync::PipelineStages::none() + }; + let destination_stages = $crate::sync::PipelineStages { + all_graphics: true, + ..$crate::sync::PipelineStages::none() + }; + let source_access = source_stages.supported_access(); + let destination_access = destination_stages.supported_access(); + + $crate::render_pass::SubpassDependency { + source_subpass: id.into(), + destination_subpass: (id + 1).into(), + source_stages, + destination_stages, + source_access, + destination_access, by_region: true, // TODO: correct values + ..Default::default() } }) .collect(); let attachments = vec![ $({ - let layout = &mut layouts[$atch_name]; + let layout = &mut layouts[$atch_name as usize]; $(layout.0 = Some($init_layout);)* $(layout.1 = Some($final_layout);)* - AttachmentDesc { - format: $format, + $crate::render_pass::AttachmentDescription { + format: Some($format), samples: $crate::image::SampleCount::try_from($samples).unwrap(), - load: $crate::render_pass::LoadOp::$load, - store: $crate::render_pass::StoreOp::$store, - stencil_load: $crate::render_pass::LoadOp::$load, - stencil_store: $crate::render_pass::StoreOp::$store, + load_op: $crate::render_pass::LoadOp::$load, + store_op: $crate::render_pass::StoreOp::$store, + stencil_load_op: $crate::render_pass::LoadOp::$load, + stencil_store_op: $crate::render_pass::StoreOp::$store, initial_layout: layout.0.expect( format!( "Attachment {} is missing initial_layout, this is normally \ @@ -189,18 +209,20 @@ macro_rules! ordered_passes_renderpass { ) .as_ref(), ), + ..Default::default() } }),* ]; - RenderPassDesc::new( + $crate::render_pass::RenderPassCreateInfo { attachments, subpasses, dependencies, - ) + ..Default::default() + } }; - RenderPass::new($device, desc) + RenderPass::new($device, create_info) }); } diff --git a/vulkano/src/render_pass/mod.rs b/vulkano/src/render_pass/mod.rs index 2b776c66..c91618e8 100644 --- a/vulkano/src/render_pass/mod.rs +++ b/vulkano/src/render_pass/mod.rs @@ -25,27 +25,1079 @@ //! Consequently you can create graphics pipelines from a render pass object alone. //! A `Framebuffer` object is only needed when you actually add draw commands to a command buffer. -pub use self::compat_atch::ensure_image_view_compatible; -pub use self::compat_atch::IncompatibleRenderPassAttachmentError; -pub use self::desc::AttachmentDesc; -pub use self::desc::LoadOp; -pub use self::desc::MultiviewDesc; -pub use self::desc::RenderPassDesc; -pub use self::desc::ResolveMode; -pub use self::desc::ResolveModes; -pub use self::desc::StoreOp; -pub use self::desc::SubpassDependencyDesc; -pub use self::desc::SubpassDesc; +pub use self::create::RenderPassCreationError; pub use self::framebuffer::Framebuffer; -pub use self::framebuffer::FramebufferBuilder; +pub use self::framebuffer::FramebufferCreateInfo; pub use self::framebuffer::FramebufferCreationError; -pub use self::render_pass::RenderPass; -pub use self::render_pass::RenderPassCreationError; -pub use self::render_pass::Subpass; +use crate::{ + device::{Device, DeviceOwned}, + format::{ClearValue, Format}, + image::{ImageAspects, ImageLayout, SampleCount}, + shader::ShaderInterface, + sync::{AccessFlags, PipelineStages}, + Version, VulkanObject, +}; +use std::{ + hash::{Hash, Hasher}, + mem::MaybeUninit, + ptr, + sync::Arc, +}; #[macro_use] mod macros; -mod compat_atch; -mod desc; +mod create; mod framebuffer; -mod render_pass; + +/// An object representing the discrete steps in which rendering is done. +/// +/// A render pass in Vulkan is made up of three parts: +/// - A list of attachments, which are image views that are inputs, outputs or intermediate stages +/// in the rendering process. +/// - One or more subpasses, which are the steps in which the rendering process, takes place, +/// and the attachments that are used for each step. +/// - Dependencies, which describe how the input and output data of each subpass is to be passed +/// from one subpass to the next. +/// +/// ``` +/// use vulkano::render_pass::{RenderPass, RenderPassCreateInfo, SubpassDescription}; +/// +/// # let device: std::sync::Arc = return; +/// let render_pass = RenderPass::new( +/// device.clone(), +/// RenderPassCreateInfo { +/// subpasses: vec![SubpassDescription::default()], +/// ..Default::default() +/// }, +/// ).unwrap(); +/// ``` +/// +/// This example creates a render pass with no attachment and one single subpass that doesn't draw +/// on anything. While it's sometimes useful, most of the time it's not what you want. +/// +/// The easiest way to create a "real" render pass is to use the `single_pass_renderpass!` macro. +/// +/// ``` +/// # #[macro_use] extern crate vulkano; +/// # fn main() { +/// # let device: std::sync::Arc = return; +/// use vulkano::format::Format; +/// +/// let render_pass = single_pass_renderpass!(device.clone(), +/// attachments: { +/// // `foo` is a custom name we give to the first and only attachment. +/// foo: { +/// load: Clear, +/// store: Store, +/// format: Format::R8G8B8A8_UNORM, +/// samples: 1, +/// } +/// }, +/// pass: { +/// color: [foo], // Repeat the attachment name here. +/// depth_stencil: {} +/// } +/// ).unwrap(); +/// # } +/// ``` +/// +/// See the documentation of the macro for more details. TODO: put link here +#[derive(Debug)] +pub struct RenderPass { + handle: ash::vk::RenderPass, + device: Arc, + + attachments: Vec, + subpasses: Vec, + dependencies: Vec, + correlated_view_masks: Vec, + + granularity: [u32; 2], + views_used: u32, +} + +impl RenderPass { + /// Creates a new `RenderPass`. + /// + /// # Panics + /// + /// - Panics if `create_info.subpasses` is empty. + /// - Panics if any element of `create_info.attachments` has a `format` of `None`. + pub fn new( + device: Arc, + mut create_info: RenderPassCreateInfo, + ) -> Result, RenderPassCreationError> { + let views_used = Self::validate(&device, &mut create_info)?; + + let handle = unsafe { + if device.api_version() >= Version::V1_2 + || device.enabled_extensions().khr_create_renderpass2 + { + Self::create_v2(&device, &create_info)? + } else { + Self::create_v1(&device, &create_info)? + } + }; + + let RenderPassCreateInfo { + attachments, + subpasses, + dependencies, + correlated_view_masks, + _ne: _, + } = create_info; + + let granularity = unsafe { + let fns = device.fns(); + let mut out = MaybeUninit::uninit(); + fns.v1_0.get_render_area_granularity( + device.internal_object(), + handle, + out.as_mut_ptr(), + ); + + let out = out.assume_init(); + debug_assert_ne!(out.width, 0); + debug_assert_ne!(out.height, 0); + [out.width, out.height] + }; + + Ok(Arc::new(RenderPass { + handle, + device, + + attachments, + subpasses, + dependencies, + correlated_view_masks, + + granularity, + views_used, + })) + } + + /// Builds a render pass with one subpass and no attachment. + /// + /// This method is useful for quick tests. + #[inline] + pub fn empty_single_pass( + device: Arc, + ) -> Result, RenderPassCreationError> { + RenderPass::new( + device, + RenderPassCreateInfo { + subpasses: vec![SubpassDescription::default()], + ..Default::default() + }, + ) + } + + /// Returns the attachments of the render pass. + #[inline] + pub fn attachments(&self) -> &[AttachmentDescription] { + &self.attachments + } + + /// Returns the subpasses of the render pass. + #[inline] + pub fn subpasses(&self) -> &[SubpassDescription] { + &self.subpasses + } + + /// Returns the dependencies of the render pass. + #[inline] + pub fn dependencies(&self) -> &[SubpassDependency] { + &self.dependencies + } + + /// Returns the correlated view masks of the render pass. + #[inline] + pub fn correlated_view_masks(&self) -> &[u32] { + &self.correlated_view_masks + } + + /// If the render pass has multiview enabled, returns the number of views used by the render + /// pass. Returns 0 if multiview is not enabled. + #[inline] + pub fn views_used(&self) -> u32 { + self.views_used + } + + /// Returns the granularity of this render pass. + /// + /// If the render area of a render pass in a command buffer is a multiple of this granularity, + /// then the performance will be optimal. Performances are always optimal for render areas + /// that cover the whole framebuffer. + #[inline] + pub fn granularity(&self) -> [u32; 2] { + self.granularity + } + + /// Returns the first subpass of the render pass. + #[inline] + pub fn first_subpass(self: Arc) -> Subpass { + Subpass { + render_pass: self, + subpass_id: 0, // Guaranteed to exist + } + } + + /// Returns `true` if this render pass is compatible with the other render pass, + /// as defined in the `Render Pass Compatibility` section of the Vulkan specs. + // TODO: return proper error + pub fn is_compatible_with(&self, other: &RenderPass) -> bool { + if self.attachments().len() != other.attachments().len() { + return false; + } + + for (my_atch, other_atch) in self.attachments.iter().zip(other.attachments.iter()) { + if !my_atch.is_compatible_with(&other_atch) { + return false; + } + } + + return true; + + // FIXME: finish + } + + /// Returns `true` if the subpass of this description is compatible with the shader's fragment + /// output definition. + pub fn is_compatible_with_shader( + &self, + subpass: u32, + shader_interface: &ShaderInterface, + ) -> bool { + let subpass_descr = match self.subpasses.get(subpass as usize) { + Some(s) => s, + None => return false, + }; + + for element in shader_interface.elements() { + 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 subpass_descr.color_attachments.get(location as usize) { + Some(Some(atch_ref)) => atch_ref.attachment, + _ => return false, + }; + + let attachment_desc = &self.attachments[attachment_id as usize]; + + // FIXME: compare formats depending on the number of components and data type + /*if attachment_desc.format != element.format { + return false; + }*/ + } + } + + true + } + + /// Decodes `I` into a list of clear values where each element corresponds + /// to an attachment. The size of the returned iterator must be the same as the number of + /// attachments. + /// + /// When the user enters a render pass, they need to pass a list of clear values to apply to + /// the attachments of the framebuffer. This method is then responsible for checking the + /// correctness of these values and turning them into a list that can be processed by vulkano. + /// + /// The format of the clear value **must** match the format of the attachment. Attachments + /// that are not loaded with `LoadOp::Clear` must have an entry equal to `ClearValue::None`. + pub fn convert_clear_values(&self, values: I) -> impl Iterator + where + I: IntoIterator, + { + // FIXME: safety checks + values.into_iter() + } +} + +impl Drop for RenderPass { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.device.fns(); + fns.v1_0 + .destroy_render_pass(self.device.internal_object(), self.handle, ptr::null()); + } + } +} + +unsafe impl VulkanObject for RenderPass { + type Object = ash::vk::RenderPass; + + #[inline] + fn internal_object(&self) -> ash::vk::RenderPass { + self.handle + } +} + +unsafe impl DeviceOwned for RenderPass { + #[inline] + fn device(&self) -> &Arc { + &self.device + } +} + +impl PartialEq for RenderPass { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle && self.device() == other.device() + } +} + +impl Eq for RenderPass {} + +impl Hash for RenderPass { + #[inline] + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.device.hash(state); + } +} + +/// Represents a subpass within a `RenderPass` object. +/// +/// This struct doesn't correspond to anything in Vulkan. It is simply an equivalent to a +/// tuple of a render pass and subpass index. Contrary to a tuple, however, the existence of the +/// subpass is checked when the object is created. When you have a `Subpass` you are guaranteed +/// that the given subpass does exist. +#[derive(Debug, Clone)] +pub struct Subpass { + render_pass: Arc, + subpass_id: u32, +} + +impl Subpass { + /// Returns a handle that represents a subpass of a render pass. + #[inline] + pub fn from(render_pass: Arc, id: u32) -> Option { + if (id as usize) < render_pass.subpasses().len() { + Some(Subpass { + render_pass, + subpass_id: id, + }) + } else { + None + } + } + + /// Returns the subpass description for this subpass. + #[inline] + pub fn subpass_desc(&self) -> &SubpassDescription { + &self.render_pass.subpasses()[self.subpass_id as usize] + } + + /// Returns whether this subpass is the last one in the render pass. If `true` is returned, + /// `next_subpass` will return `None`. + #[inline] + pub fn is_last_subpass(&self) -> bool { + self.subpass_id as usize == self.render_pass.subpasses().len() - 1 + } + + /// Tries to advance to the next subpass after this one, and returns `true` if successful. + #[inline] + pub fn try_next_subpass(&mut self) -> bool { + let next_id = self.subpass_id + 1; + + if (next_id as usize) < self.render_pass.subpasses().len() { + self.subpass_id = next_id; + true + } else { + false + } + } + + #[inline] + fn attachment_desc(&self, atch_num: u32) -> &AttachmentDescription { + &self.render_pass.attachments()[atch_num as usize] + } + + /// Returns the number of color attachments in this subpass. + #[inline] + pub fn num_color_attachments(&self) -> u32 { + self.subpass_desc().color_attachments.len() as u32 + } + + /// Returns true if the subpass has a depth attachment or a depth-stencil attachment. + #[inline] + pub fn has_depth(&self) -> bool { + let subpass_desc = self.subpass_desc(); + let atch_num = match &subpass_desc.depth_stencil_attachment { + Some(atch_ref) => atch_ref.attachment, + None => return false, + }; + + self.attachment_desc(atch_num) + .format + .map_or(false, |f| f.aspects().depth) + } + + /// Returns true if the subpass has a depth attachment or a depth-stencil attachment whose + /// layout is not `DepthStencilReadOnlyOptimal`. + #[inline] + pub fn has_writable_depth(&self) -> bool { + let subpass_desc = self.subpass_desc(); + let atch_num = match &subpass_desc.depth_stencil_attachment { + Some(atch_ref) => { + if atch_ref.layout == ImageLayout::DepthStencilReadOnlyOptimal { + return false; + } + atch_ref.attachment + } + None => return false, + }; + + self.attachment_desc(atch_num) + .format + .map_or(false, |f| f.aspects().depth) + } + + /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment. + #[inline] + pub fn has_stencil(&self) -> bool { + let subpass_desc = self.subpass_desc(); + let atch_num = match &subpass_desc.depth_stencil_attachment { + Some(atch_ref) => atch_ref.attachment, + None => return false, + }; + + self.attachment_desc(atch_num) + .format + .map_or(false, |f| f.aspects().stencil) + } + + /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment whose + /// layout is not `DepthStencilReadOnlyOptimal`. + #[inline] + pub fn has_writable_stencil(&self) -> bool { + let subpass_desc = self.subpass_desc(); + + let atch_num = match &subpass_desc.depth_stencil_attachment { + Some(atch_ref) => { + if atch_ref.layout == ImageLayout::DepthStencilReadOnlyOptimal { + return false; + } + atch_ref.attachment + } + None => return false, + }; + + self.attachment_desc(atch_num) + .format + .map_or(false, |f| f.aspects().stencil) + } + + /// Returns true if the subpass has any depth/stencil attachment. + #[inline] + pub fn has_depth_stencil_attachment(&self) -> bool { + let subpass_desc = self.subpass_desc(); + match &subpass_desc.depth_stencil_attachment { + Some(atch_ref) => true, + None => false, + } + } + + /// Returns true if the subpass has any color or depth/stencil attachment. + #[inline] + pub fn has_color_or_depth_stencil_attachment(&self) -> bool { + if self.num_color_attachments() >= 1 { + return true; + } + + let subpass_desc = self.subpass_desc(); + match &subpass_desc.depth_stencil_attachment { + Some(atch_ref) => true, + None => false, + } + } + + /// Returns the number of samples in the color and/or depth/stencil attachments. Returns `None` + /// if there is no such attachment in this subpass. + #[inline] + pub fn num_samples(&self) -> Option { + let subpass_desc = self.subpass_desc(); + + // TODO: chain input attachments as well? + subpass_desc + .color_attachments + .iter() + .flatten() + .chain(subpass_desc.depth_stencil_attachment.iter()) + .filter_map(|atch_ref| { + self.render_pass + .attachments() + .get(atch_ref.attachment as usize) + }) + .next() + .map(|atch_desc| atch_desc.samples) + } + + /// Returns the render pass of this subpass. + #[inline] + pub fn render_pass(&self) -> &Arc { + &self.render_pass + } + + /// Returns the index of this subpass within the renderpass. + #[inline] + pub fn index(&self) -> u32 { + self.subpass_id + } + + /// Returns `true` if this subpass is compatible with the fragment output definition. + // TODO: return proper error + pub fn is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool { + self.render_pass + .is_compatible_with_shader(self.subpass_id, shader_interface) + } +} + +impl From for (Arc, u32) { + #[inline] + fn from(value: Subpass) -> (Arc, u32) { + (value.render_pass, value.subpass_id) + } +} + +/// Parameters to create a new `RenderPass`. +#[derive(Clone, Debug)] +pub struct RenderPassCreateInfo { + /// The attachments available for the render pass. + /// + /// The default value is empty. + pub attachments: Vec, + + /// The subpasses that make up this render pass. + /// + /// A render pass must contain at least one subpass. + /// + /// The default value is empty, which must be overridden. + pub subpasses: Vec, + + /// The dependencies between subpasses. + /// + /// The default value is empty. + pub dependencies: Vec, + + /// If multiview rendering is being used (the subpasses have a nonzero `view_mask`), + /// this specifies sets of views that may be more efficient to render concurrently, for example + /// because they show the same geometry from almost the same perspective. This is an + /// optimization hint to the implementation, and does not affect the final result. + /// + /// The value is a bitmask, so that that for example `0b11` means that the first two views are + /// highly correlated, and `0b101` means the first and third view are highly correlated. Each + /// view bit must appear in at most one element of the list. + /// + /// If multiview rendering is not being used, the value must be empty. + /// + /// The default value is empty. + pub correlated_view_masks: Vec, + + pub _ne: crate::NonExhaustive, +} + +impl Default for RenderPassCreateInfo { + #[inline] + fn default() -> Self { + Self { + attachments: Vec::new(), + subpasses: Vec::new(), + dependencies: Vec::new(), + correlated_view_masks: Vec::new(), + _ne: crate::NonExhaustive(()), + } + } +} + +/// Describes an attachment that will be used in a render pass. +#[derive(Clone, Copy, Debug)] +pub struct AttachmentDescription { + /// The format of the image that is going to be bound. + /// + /// The default value is `None`, which must be overridden. + pub format: Option, + + /// The number of samples of the image that is going to be bound. + /// + /// The default value is [`SampleCount::Sample1`]. + pub samples: SampleCount, + + /// What the implementation should do with the attachment at the start of the subpass that first + /// uses it. + /// + /// The default value is [`LoadOp::DontCare`]. + pub load_op: LoadOp, + + /// What the implementation should do with the attachment at the end of the subpass that last + /// uses it. + /// + /// The default value is [`StoreOp::DontCare`]. + pub store_op: StoreOp, + + /// The equivalent of `load_op` for the stencil component of the attachment, if any. Irrelevant + /// if there is no stencil component. + /// + /// The default value is [`LoadOp::DontCare`]. + pub stencil_load_op: LoadOp, + + /// The equivalent of `store_op` for the stencil component of the attachment, if any. Irrelevant + /// if there is no stencil component. + /// + /// The default value is [`StoreOp::DontCare`]. + pub stencil_store_op: StoreOp, + + /// The layout that the image must in at the start of the render pass. + /// + /// The vulkano library will automatically switch to the correct layout if necessary, but it + /// is more efficient to set this to the correct value. + /// + /// The default value is [`ImageLayout::Undefined`], which must be overridden. + pub initial_layout: ImageLayout, + + /// The layout that the image will be transitioned to at the end of the render pass. + /// + /// The default value is [`ImageLayout::Undefined`], which must be overridden. + pub final_layout: ImageLayout, + + pub _ne: crate::NonExhaustive, +} + +impl Default for AttachmentDescription { + #[inline] + fn default() -> Self { + Self { + format: None, + samples: SampleCount::Sample1, + load_op: LoadOp::DontCare, + store_op: StoreOp::DontCare, + stencil_load_op: LoadOp::DontCare, + stencil_store_op: StoreOp::DontCare, + initial_layout: ImageLayout::Undefined, + final_layout: ImageLayout::Undefined, + _ne: crate::NonExhaustive(()), + } + } +} + +impl AttachmentDescription { + /// Returns true if this attachment is compatible with another attachment, as defined in the + /// `Render Pass Compatibility` section of the Vulkan specs. + #[inline] + pub fn is_compatible_with(&self, other: &AttachmentDescription) -> bool { + self.format == other.format && self.samples == other.samples + } +} + +/// Describes one of the subpasses of a render pass. +/// +/// A subpass can use zero or more attachments of various types. Attachment types of which there can +/// be multiple are listed in a `Vec` in this structure. The index in these `Vec`s corresponds to +/// the index used for that attachment type in the shader. +/// +/// If a particular index is not used in the shader, it can be set to `None` in this structure. +/// This is useful if an unused index needs to be skipped but a higher index needs to be specified. +/// +/// If an attachment is used more than once, i.e. a given `AttachmentReference::attachment` occurs +/// more than once in the `SubpassDescription`, then their `AttachmentReference::layout` must be +/// the same as well. +#[derive(Debug, Clone)] +pub struct SubpassDescription { + /// If not `0`, enables multiview rendering, and specifies the view indices that are rendered to + /// in this subpass. The value is a bitmask, so that that for example `0b11` will draw to the + /// first two views and `0b101` will draw to the first and third view. + /// + /// If set to a nonzero value, it must be nonzero for all subpasses in the render pass, and the + /// [`multiview`](crate::device::Features::multiview) feature must be enabled on the device. + /// + /// The default value is `0`. + pub view_mask: u32, + + /// The attachments of the render pass that are to be used as input attachments in this subpass. + /// + /// If an attachment is used here for the first time in this render pass, and it's is not also + /// used as a color or depth/stencil attachment in this subpass, then the attachment's `load_op` + /// must not be [`LoadOp::Clear`]. + /// + /// The default value is empty. + pub input_attachments: Vec>, + + /// The attachments of the render pass that are to be used as color attachments in this subpass. + /// + /// The number of color attachments must be less than the + /// [`max_color_attachments`](crate::device::Properties::max_color_attachments) limit of the + /// physical device. All color attachments must have the same `samples` value. + /// + /// The default value is empty. + pub color_attachments: Vec>, + + /// The attachments of the render pass that are to be used as resolve attachments in this + /// subpass. + /// + /// This list must either be empty or have the same length as `color_attachments`. If it's not + /// empty, then each resolve attachment is paired with the color attachment of the same index. + /// The resolve attachments must all have a `samples` value of [`SampleCount::Sample1`], while + /// the color attachments must have a `samples` value other than [`SampleCount::Sample1`]. + /// Each resolve attachment must have the same `format` as the corresponding color attachment. + /// + /// The default value is empty. + pub resolve_attachments: Vec>, + + /// The single attachment of the render pass that is to be used as depth-stencil attachment in + /// this subpass. + /// + /// If set to `Some`, the referenced attachment must have the same `samples` value as those in + /// `color_attachments`. + /// + /// The default value is `None`. + pub depth_stencil_attachment: Option, + + /// The indices of attachments of the render pass that will be preserved during this subpass. + /// + /// The referenced attachments must not be used as any other attachment type in the subpass. + /// + /// The default value is empty. + pub preserve_attachments: Vec, + + pub _ne: crate::NonExhaustive, +} + +impl Default for SubpassDescription { + #[inline] + fn default() -> Self { + Self { + view_mask: 0, + color_attachments: Vec::new(), + depth_stencil_attachment: None, + input_attachments: Vec::new(), + resolve_attachments: Vec::new(), + preserve_attachments: Vec::new(), + _ne: crate::NonExhaustive(()), + } + } +} + +/// A reference in a subpass description to a particular attachment of the render pass. +#[derive(Clone, Debug)] +pub struct AttachmentReference { + /// The number of the attachment being referred to. + /// + /// The default value is `0`. + pub attachment: u32, + + /// The image layout that the attachment should be transitioned to at the start of the subpass. + /// + /// The layout is restricted by the type of attachment that an attachment is being used as. A + /// full listing of allowed layouts per type can be found in + /// [the Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap8.html#attachment-type-imagelayout). + /// + /// The default value is [`ImageLayout::Undefined`], which must be overridden. + pub layout: ImageLayout, + + /// For references to input attachments, the aspects of the image that should be selected. + /// For attachment types other than input attachments, the value must be empty. + /// + /// If empty, all aspects available in the input attachment's `format` will be selected. + /// If any fields are set, they must be aspects that are available in the `format` of the + /// attachment. + /// + /// If the value is neither empty nor identical to the aspects of the `format`, the device API + /// version must be at least 1.1, or either the + /// [`khr_create_renderpass2`](crate::device::DeviceExtensions::khr_create_renderpass2) or the + /// [`khr_maintenance2`](crate::device::DeviceExtensions::khr_maintenance2) extensions must be + /// enabled on the device. + /// + /// The default value is [`ImageAspects::none()`]. + pub aspects: ImageAspects, + + pub _ne: crate::NonExhaustive, +} + +impl Default for AttachmentReference { + #[inline] + fn default() -> Self { + Self { + attachment: 0, + layout: ImageLayout::Undefined, + aspects: ImageAspects::none(), + _ne: crate::NonExhaustive(()), + } + } +} + +/// A dependency between two subpasses of a render pass. +/// +/// The implementation is allowed to change the order of the subpasses within a render pass, unless +/// you specify that there exists a dependency between two subpasses (ie. the result of one will be +/// used as the input of another one). Subpass dependencies work similar to pipeline barriers, +/// except that they operate on whole subpasses instead of individual images. +/// +/// If `source_subpass` and `destination_subpass` are equal, then this specifies a +/// [subpass self-dependency](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-barriers-subpass-self-dependencies). +/// The `source_stages` must all be +/// [logically earlier in the pipeline](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-stages-order) +/// than the `destination_stages`, and if they both contain a +/// [framebuffer-space stage](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-framebuffer-regions), +/// then `by_region` must be activated. +/// +/// If `source_subpass` or `destination_subpass` are set to `None`, this specifies an external +/// dependency. An external dependency specifies a dependency on commands that were submitted before +/// the render pass instance began (for `source_subpass`), or on commands that will be submitted +/// after the render pass instance ends (for `destination_subpass`). The values must not both be +/// `None`. +#[derive(Clone, Debug)] +pub struct SubpassDependency { + /// The index of the subpass that writes the data that `destination_subpass` is going to use. + /// + /// `None` specifies an external dependency. + /// + /// The default value is `None`. + pub source_subpass: Option, + + /// The index of the subpass that reads the data that `source_subpass` wrote. + /// + /// `None` specifies an external dependency. + /// + /// The default value is `None`. + pub destination_subpass: Option, + + /// The pipeline stages that must be finished on `source_subpass` before the + /// `destination_stages` of `destination_subpass` can start. + /// + /// The default value is [`PipelineStages::none()`]. + pub source_stages: PipelineStages, + + /// The pipeline stages of `destination_subpass` that must wait for the `source_stages` of + /// `source_subpass` to be finished. Stages that are earlier than the stages specified here can + /// start before the `source_stages` are finished. + /// + /// The default value is [`PipelineStages::none()`]. + pub destination_stages: PipelineStages, + + /// The way `source_subpass` accesses the attachments on which we depend. + /// + /// The default value is [`AccessFlags::none()`]. + pub source_access: AccessFlags, + + /// The way `destination_subpass` accesses the attachments on which we depend. + /// + /// The default value is [`AccessFlags::none()`]. + pub destination_access: AccessFlags, + + /// If false, then the source operations must be fully finished for the destination operations + /// to start. If true, then the implementation can start the destination operation for some + /// given pixels as long as the source operation is finished for these given pixels. + /// + /// In other words, if the previous subpass has some side effects on other parts of an + /// attachment, then you should set it to false. + /// + /// Passing `false` is always safer than passing `true`, but in practice you rarely need to + /// pass `false`. + /// + /// The default value is `false`. + pub by_region: bool, + + /// If multiview rendering is being used (the subpasses have a nonzero `view_mask`), then + /// setting this to `Some` creates a view-local dependency, between views in `source_subpass` + /// and views in `destination_subpass`. + /// + /// The inner value specifies an offset relative to the view index of `destination_subpass`: + /// each view `d` in `destination_subpass` depends on view `d + view_offset` in + /// `source_subpass`. If the source view index does not exist, the dependency is ignored for + /// that view. + /// + /// If multiview rendering is not being used, the value must be `None`. If `source_subpass` + /// and `destination_subpass` are the same, only `Some(0)` and `None` are allowed as values, and + /// if that subpass also has multiple bits set in its `view_mask`, the value must be `Some(0)`. + /// + /// The default value is `None`. + pub view_local: Option, + + pub _ne: crate::NonExhaustive, +} + +impl Default for SubpassDependency { + #[inline] + fn default() -> Self { + Self { + source_subpass: None, + destination_subpass: None, + source_stages: PipelineStages::none(), + destination_stages: PipelineStages::none(), + source_access: AccessFlags::none(), + destination_access: AccessFlags::none(), + by_region: false, + view_local: None, + _ne: crate::NonExhaustive(()), + } + } +} + +/// Describes what the implementation should do with an attachment at the start of the subpass. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[repr(i32)] +#[non_exhaustive] +pub enum LoadOp { + /// The content of the attachment will be loaded from memory. This is what you want if you want + /// to draw over something existing. + /// + /// While this is the most intuitive option, it is also the slowest because it uses a lot of + /// memory bandwidth. + Load = ash::vk::AttachmentLoadOp::LOAD.as_raw(), + + /// The content of the attachment will be filled by the implementation with a uniform value + /// that you must provide when you start drawing. + /// + /// This is what you usually use at the start of a frame, in order to reset the content of + /// the color, depth and/or stencil buffers. + Clear = ash::vk::AttachmentLoadOp::CLEAR.as_raw(), + + /// The attachment will have undefined content. + /// + /// This is what you should use for attachments that you intend to entirely cover with draw + /// commands. + /// If you are going to fill the attachment with a uniform value, it is better to use `Clear` + /// instead. + DontCare = ash::vk::AttachmentLoadOp::DONT_CARE.as_raw(), +} + +impl From for ash::vk::AttachmentLoadOp { + #[inline] + fn from(val: LoadOp) -> Self { + Self::from_raw(val as i32) + } +} + +/// Describes what the implementation should do with an attachment after all the subpasses have +/// completed. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[repr(i32)] +#[non_exhaustive] +pub enum StoreOp { + /// The attachment will be stored. This is what you usually want. + /// + /// While this is the most intuitive option, it is also slower than `DontCare` because it can + /// take time to write the data back to memory. + Store = ash::vk::AttachmentStoreOp::STORE.as_raw(), + + /// What happens is implementation-specific. + /// + /// This is purely an optimization compared to `Store`. The implementation doesn't need to copy + /// from the internal cache to the memory, which saves memory bandwidth. + /// + /// This doesn't mean that the data won't be copied, as an implementation is also free to not + /// use a cache and write the output directly in memory. In other words, the content of the + /// image will be undefined. + DontCare = ash::vk::AttachmentStoreOp::DONT_CARE.as_raw(), +} + +impl From for ash::vk::AttachmentStoreOp { + #[inline] + fn from(val: StoreOp) -> Self { + Self::from_raw(val as i32) + } +} + +/// Possible resolve modes for depth and stencil attachments. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +#[non_exhaustive] +pub enum ResolveMode { + None = ash::vk::ResolveModeFlags::NONE.as_raw(), + SampleZero = ash::vk::ResolveModeFlags::SAMPLE_ZERO.as_raw(), + Average = ash::vk::ResolveModeFlags::AVERAGE.as_raw(), + Min = ash::vk::ResolveModeFlags::MIN.as_raw(), + Max = ash::vk::ResolveModeFlags::MAX.as_raw(), +} + +#[derive(Clone, Copy, Debug)] +pub struct ResolveModes { + pub none: bool, + pub sample_zero: bool, + pub average: bool, + pub min: bool, + pub max: bool, +} + +impl From for ResolveModes { + #[inline] + fn from(val: ash::vk::ResolveModeFlags) -> Self { + Self { + none: val.intersects(ash::vk::ResolveModeFlags::NONE), + sample_zero: val.intersects(ash::vk::ResolveModeFlags::SAMPLE_ZERO), + average: val.intersects(ash::vk::ResolveModeFlags::AVERAGE), + min: val.intersects(ash::vk::ResolveModeFlags::MIN), + max: val.intersects(ash::vk::ResolveModeFlags::MAX), + } + } +} + +#[cfg(test)] +mod tests { + use crate::format::Format; + use crate::render_pass::RenderPass; + use crate::render_pass::RenderPassCreationError; + + #[test] + fn empty() { + let (device, _) = gfx_dev_and_queue!(); + let _ = RenderPass::empty_single_pass(device).unwrap(); + } + + #[test] + fn too_many_color_atch() { + let (device, _) = gfx_dev_and_queue!(); + + if device.physical_device().properties().max_color_attachments >= 10 { + return; // test ignored + } + + let rp = single_pass_renderpass! { + device.clone(), + attachments: { + a1: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a2: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a3: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a4: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a5: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a6: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a7: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a8: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a9: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, + a10: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, } + }, + pass: { + color: [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10], + depth_stencil: {} + } + }; + + match rp { + Err(RenderPassCreationError::SubpassMaxColorAttachmentsExceeded { .. }) => (), + _ => panic!(), + } + } + + #[test] + fn non_zero_granularity() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = single_pass_renderpass! { + device.clone(), + attachments: { + a: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, } + }, + pass: { + color: [a], + depth_stencil: {} + } + } + .unwrap(); + + let granularity = rp.granularity(); + assert_ne!(granularity[0], 0); + assert_ne!(granularity[1], 0); + } +} diff --git a/vulkano/src/render_pass/render_pass.rs b/vulkano/src/render_pass/render_pass.rs deleted file mode 100644 index 994c71a9..00000000 --- a/vulkano/src/render_pass/render_pass.rs +++ /dev/null @@ -1,911 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// 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::check_errors; -use crate::device::Device; -use crate::device::DeviceOwned; -use crate::image::ImageLayout; -use crate::image::SampleCount; -use crate::render_pass::AttachmentDesc; -use crate::render_pass::LoadOp; -use crate::render_pass::RenderPassDesc; -use crate::render_pass::SubpassDesc; -use crate::shader::ShaderInterface; -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; -use std::sync::Mutex; - -/// An object representing the discrete steps in which rendering is done. -/// -/// A render pass in Vulkan is made up of three parts: -/// - A list of attachments, which are image views that are inputs, outputs or intermediate stages -/// in the rendering process. -/// - One or more subpasses, which are the steps in which the rendering process, takes place, -/// and the attachments that are used for each step. -/// - Dependencies, which describe how the input and output data of each subpass is to be passed -/// from one subpass to the next. -/// -/// In order to create a render pass, you must create a `RenderPassDesc` object that describes the -/// render pass, then pass it to `RenderPass::new`. -/// -/// ``` -/// use vulkano::render_pass::RenderPass; -/// use vulkano::render_pass::RenderPassDesc; -/// -/// # let device: std::sync::Arc = return; -/// let desc = RenderPassDesc::empty(); -/// let render_pass = RenderPass::new(device.clone(), desc).unwrap(); -/// ``` -/// -/// This example creates a render pass with no attachment and one single subpass that doesn't draw -/// on anything. While it's sometimes useful, most of the time it's not what you want. -/// -/// The easiest way to create a "real" render pass is to use the `single_pass_renderpass!` macro. -/// -/// ``` -/// # #[macro_use] extern crate vulkano; -/// # fn main() { -/// # let device: std::sync::Arc = return; -/// use vulkano::format::Format; -/// -/// let render_pass = single_pass_renderpass!(device.clone(), -/// attachments: { -/// // `foo` is a custom name we give to the first and only attachment. -/// foo: { -/// load: Clear, -/// store: Store, -/// format: Format::R8G8B8A8_UNORM, -/// samples: 1, -/// } -/// }, -/// pass: { -/// color: [foo], // Repeat the attachment name here. -/// depth_stencil: {} -/// } -/// ).unwrap(); -/// # } -/// ``` -/// -/// See the documentation of the macro for more details. TODO: put link here -pub struct RenderPass { - // The internal Vulkan object. - handle: ash::vk::RenderPass, - - // Device this render pass was created from. - device: Arc, - - // Description of the render pass. - desc: RenderPassDesc, - - // Cache of the granularity of the render pass. - granularity: Mutex>, -} - -impl RenderPass { - /// Builds a new render pass. - /// - /// # Panic - /// - /// - Can panic if it detects some violations in the restrictions. Only inexpensive checks are - /// performed. `debug_assert!` is used, so some restrictions are only checked in debug - /// mode. - /// - pub fn new( - device: Arc, - description: RenderPassDesc, - ) -> Result, RenderPassCreationError> { - let fns = device.fns(); - - // If the first use of an attachment in this render pass is as an input attachment, and - // the attachment is not also used as a color or depth/stencil attachment in the same - // subpass, then loadOp must not be VK_ATTACHMENT_LOAD_OP_CLEAR - debug_assert!(description.attachments().into_iter().enumerate().all( - |(atch_num, attachment)| { - if attachment.load != LoadOp::Clear { - return true; - } - - for p in description.subpasses() { - if p.color_attachments - .iter() - .find(|&&(a, _)| a == atch_num) - .is_some() - { - return true; - } - if let Some((a, _)) = p.depth_stencil { - if a == atch_num { - return true; - } - } - if p.input_attachments - .iter() - .find(|&&(a, _)| a == atch_num) - .is_some() - { - return false; - } - } - - true - } - )); - - let attachments = description - .attachments() - .iter() - .map(|attachment| { - ash::vk::AttachmentDescription { - flags: ash::vk::AttachmentDescriptionFlags::empty(), // FIXME: may alias flag - format: attachment.format.into(), - samples: attachment.samples.into(), - load_op: attachment.load.into(), - store_op: attachment.store.into(), - stencil_load_op: attachment.stencil_load.into(), - stencil_store_op: attachment.stencil_store.into(), - initial_layout: attachment.initial_layout.into(), - final_layout: attachment.final_layout.into(), - } - }) - .collect::>(); - - // We need to pass pointers to vkAttachmentReference structs when creating the render pass. - // Therefore we need to allocate them in advance. - // - // This block allocates, for each pass, in order, all color attachment references, then all - // input attachment references, then all resolve attachment references, then the depth - // stencil attachment reference. - let attachment_references = description - .subpasses() - .iter() - .flat_map(|pass| { - // Performing some validation with debug asserts. - debug_assert!( - pass.resolve_attachments.is_empty() - || pass.resolve_attachments.len() == pass.color_attachments.len() - ); - debug_assert!(pass - .resolve_attachments - .iter() - .all(|a| attachments[a.0].samples == ash::vk::SampleCountFlags::TYPE_1)); - debug_assert!( - pass.resolve_attachments.is_empty() - || pass - .color_attachments - .iter() - .all(|a| attachments[a.0].samples.as_raw() > 1) - ); - debug_assert!( - pass.resolve_attachments.is_empty() - || pass - .resolve_attachments - .iter() - .zip(pass.color_attachments.iter()) - .all(|(r, c)| { attachments[r.0].format == attachments[c.0].format }) - ); - debug_assert!(pass - .color_attachments - .iter() - .cloned() - .chain(pass.depth_stencil.clone().into_iter()) - .chain(pass.input_attachments.iter().cloned()) - .chain(pass.resolve_attachments.iter().cloned()) - .all(|(a, _)| { - pass.preserve_attachments - .iter() - .find(|&&b| a == b) - .is_none() - })); - debug_assert!(pass - .color_attachments - .iter() - .cloned() - .chain(pass.depth_stencil.clone().into_iter()) - .all(|(atch, layout)| { - if let Some(r) = pass.input_attachments.iter().find(|r| r.0 == atch) { - r.1 == layout - } else { - true - } - })); - - let resolve = pass.resolve_attachments.iter().map(|&(offset, img_la)| { - debug_assert!(offset < attachments.len()); - ash::vk::AttachmentReference { - attachment: offset as u32, - layout: img_la.into(), - } - }); - - let color = pass.color_attachments.iter().map(|&(offset, img_la)| { - debug_assert!(offset < attachments.len()); - ash::vk::AttachmentReference { - attachment: offset as u32, - layout: img_la.into(), - } - }); - - let input = pass.input_attachments.iter().map(|&(offset, img_la)| { - debug_assert!(offset < attachments.len()); - ash::vk::AttachmentReference { - attachment: offset as u32, - layout: img_la.into(), - } - }); - - let depthstencil = if let Some((offset, img_la)) = pass.depth_stencil { - Some(ash::vk::AttachmentReference { - attachment: offset as u32, - layout: img_la.into(), - }) - } else { - None - } - .into_iter(); - - color.chain(input).chain(resolve).chain(depthstencil) - }) - .collect::>(); - - // Same as `attachment_references` but only for the preserve attachments. - // This is separate because attachment references are u32s and not `vkAttachmentReference` - // structs. - let preserve_attachments_references = description - .subpasses() - .iter() - .flat_map(|pass| { - pass.preserve_attachments - .iter() - .map(|&offset| offset as u32) - }) - .collect::>(); - - // Now iterating over passes. - let passes = unsafe { - // `ref_index` and `preserve_ref_index` are increased during the loop and point to the - // next element to use in respectively `attachment_references` and - // `preserve_attachments_references`. - let mut ref_index = 0usize; - let mut preserve_ref_index = 0usize; - let mut out: SmallVec<[_; 16]> = SmallVec::new(); - - for pass in description.subpasses() { - if pass.color_attachments.len() as u32 - > device.physical_device().properties().max_color_attachments - { - return Err(RenderPassCreationError::ColorAttachmentsLimitExceeded); - } - - let color_attachments = attachment_references.as_ptr().offset(ref_index as isize); - ref_index += pass.color_attachments.len(); - let input_attachments = attachment_references.as_ptr().offset(ref_index as isize); - ref_index += pass.input_attachments.len(); - let resolve_attachments = attachment_references.as_ptr().offset(ref_index as isize); - ref_index += pass.resolve_attachments.len(); - let depth_stencil = if pass.depth_stencil.is_some() { - let a = attachment_references.as_ptr().offset(ref_index as isize); - ref_index += 1; - a - } else { - ptr::null() - }; - - let preserve_attachments = preserve_attachments_references - .as_ptr() - .offset(preserve_ref_index as isize); - preserve_ref_index += pass.preserve_attachments.len(); - - out.push(ash::vk::SubpassDescription { - flags: ash::vk::SubpassDescriptionFlags::empty(), - pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS, - input_attachment_count: pass.input_attachments.len() as u32, - p_input_attachments: if pass.input_attachments.is_empty() { - ptr::null() - } else { - input_attachments - }, - color_attachment_count: pass.color_attachments.len() as u32, - p_color_attachments: if pass.color_attachments.is_empty() { - ptr::null() - } else { - color_attachments - }, - p_resolve_attachments: if pass.resolve_attachments.is_empty() { - ptr::null() - } else { - resolve_attachments - }, - p_depth_stencil_attachment: depth_stencil, - preserve_attachment_count: pass.preserve_attachments.len() as u32, - p_preserve_attachments: if pass.preserve_attachments.is_empty() { - ptr::null() - } else { - preserve_attachments - }, - }); - } - - assert!(!out.is_empty()); - // If these assertions fails, there's a serious bug in the code above ^. - debug_assert!(ref_index == attachment_references.len()); - debug_assert!(preserve_ref_index == preserve_attachments_references.len()); - - out - }; - - let dependencies = description - .dependencies() - .iter() - .map(|dependency| { - debug_assert!( - dependency.source_subpass as u32 == ash::vk::SUBPASS_EXTERNAL - || dependency.source_subpass < passes.len() - ); - debug_assert!( - dependency.destination_subpass as u32 == ash::vk::SUBPASS_EXTERNAL - || dependency.destination_subpass < passes.len() - ); - - ash::vk::SubpassDependency { - src_subpass: dependency.source_subpass as u32, - dst_subpass: dependency.destination_subpass as u32, - src_stage_mask: dependency.source_stages.into(), - dst_stage_mask: dependency.destination_stages.into(), - src_access_mask: dependency.source_access.into(), - dst_access_mask: dependency.destination_access.into(), - dependency_flags: if dependency.by_region { - ash::vk::DependencyFlags::BY_REGION - } else { - ash::vk::DependencyFlags::empty() - }, - } - }) - .collect::>(); - - let multiview_create_info = match description.multiview() { - Some(multiview) => { - debug_assert!(device.enabled_features().multiview); - debug_assert!( - device - .physical_device() - .properties() - .max_multiview_view_count - .unwrap_or(0) - >= multiview.used_layer_count() - ); - - // each subpass must have a corresponding view mask - // or there are no view masks at all (which is probably a bug because - // nothing will get drawn) - debug_assert!( - multiview.view_masks.len() == passes.len() || multiview.view_masks.is_empty() - ); - - // either all subpasses must have a non-zero view mask or all must be zero - // (multiview is considered to be disabled when all view masks are zero) - debug_assert!( - multiview.view_masks.iter().all(|&mask| mask != 0) - || multiview.view_masks.iter().all(|&mask| mask == 0) - ); - - // one view offset for each dependency - // or no view offsets at all - debug_assert!( - dependencies.len() == multiview.view_offsets.len() - || multiview.view_offsets.is_empty() - ); - - // VUID-VkRenderPassCreateInfo-pNext-02512 - debug_assert!(dependencies.iter().zip(&multiview.view_offsets).all( - |(dependency, &view_offset)| dependency - .dependency_flags - .contains(ash::vk::DependencyFlags::VIEW_LOCAL) - || view_offset == 0 - )); - - // VUID-VkRenderPassCreateInfo-pNext-02514 - debug_assert!( - multiview.view_masks.iter().any(|&view_mask| view_mask != 0) - || dependencies.iter().all(|dependency| !dependency - .dependency_flags - .contains(ash::vk::DependencyFlags::VIEW_LOCAL)) - ); - - // VUID-VkRenderPassCreateInfo-pNext-02515 - debug_assert!( - multiview.view_masks.iter().any(|&view_mask| view_mask != 0) - || multiview.correlation_masks.is_empty() - ); - - // VUID-VkRenderPassMultiviewCreateInfo-pCorrelationMasks-00841 - // ensure that each view index is contained in at most one correlation mask - // by checking for any overlap in all pairs of correlation masks - debug_assert!(multiview - .correlation_masks - .iter() - .enumerate() - .all(|(i, &mask)| multiview.correlation_masks[i + 1..] - .iter() - .all(|&other_mask| other_mask & mask == 0))); - - ash::vk::RenderPassMultiviewCreateInfo { - subpass_count: passes.len() as u32, - p_view_masks: multiview.view_masks.as_ptr(), - dependency_count: dependencies.len() as u32, - p_view_offsets: multiview.view_offsets.as_ptr(), - correlation_mask_count: multiview.correlation_masks.len() as u32, - p_correlation_masks: multiview.correlation_masks.as_ptr(), - ..Default::default() - } - } - None => ash::vk::RenderPassMultiviewCreateInfo::default(), - }; - - let handle = unsafe { - let infos = ash::vk::RenderPassCreateInfo { - p_next: if description.multiview().is_none() { - ptr::null() - } else { - &multiview_create_info as *const _ as _ - }, - flags: ash::vk::RenderPassCreateFlags::empty(), - attachment_count: attachments.len() as u32, - p_attachments: if attachments.is_empty() { - ptr::null() - } else { - attachments.as_ptr() - }, - subpass_count: passes.len() as u32, - p_subpasses: if passes.is_empty() { - ptr::null() - } else { - passes.as_ptr() - }, - dependency_count: dependencies.len() as u32, - p_dependencies: if dependencies.is_empty() { - ptr::null() - } else { - dependencies.as_ptr() - }, - ..Default::default() - }; - - let mut output = MaybeUninit::uninit(); - check_errors(fns.v1_0.create_render_pass( - device.internal_object(), - &infos, - ptr::null(), - output.as_mut_ptr(), - ))?; - output.assume_init() - }; - - Ok(Arc::new(RenderPass { - handle, - device: device.clone(), - desc: description, - granularity: Mutex::new(None), - })) - } - - /// Builds a render pass with one subpass and no attachment. - /// - /// This method is useful for quick tests. - #[inline] - pub fn empty_single_pass( - device: Arc, - ) -> Result, RenderPassCreationError> { - RenderPass::new(device, RenderPassDesc::empty()) - } - - /// Returns the granularity of this render pass. - /// - /// If the render area of a render pass in a command buffer is a multiple of this granularity, - /// then the performance will be optimal. Performances are always optimal for render areas - /// that cover the whole framebuffer. - pub fn granularity(&self) -> [u32; 2] { - let mut granularity = self.granularity.lock().unwrap(); - - if let Some(&granularity) = granularity.as_ref() { - return granularity; - } - - unsafe { - let fns = self.device.fns(); - let mut out = MaybeUninit::uninit(); - fns.v1_0.get_render_area_granularity( - self.device.internal_object(), - self.handle, - out.as_mut_ptr(), - ); - - let out = out.assume_init(); - debug_assert_ne!(out.width, 0); - debug_assert_ne!(out.height, 0); - let gran = [out.width, out.height]; - *granularity = Some(gran); - gran - } - } - - /// Returns the description of the render pass. - #[inline] - pub fn desc(&self) -> &RenderPassDesc { - &self.desc - } - - /// Returns the first subpass of the render pass. - #[inline] - pub fn first_subpass(self: Arc) -> Subpass { - Subpass { - render_pass: self, - subpass_id: 0, // Guaranteed to exist - } - } -} - -unsafe impl DeviceOwned for RenderPass { - #[inline] - fn device(&self) -> &Arc { - &self.device - } -} - -impl fmt::Debug for RenderPass { - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - fmt.debug_struct("RenderPass") - .field("handle", &self.handle) - .field("device", &self.device) - .field("desc", &self.desc) - .finish() - } -} - -impl Drop for RenderPass { - #[inline] - fn drop(&mut self) { - unsafe { - let fns = self.device.fns(); - fns.v1_0 - .destroy_render_pass(self.device.internal_object(), self.handle, ptr::null()); - } - } -} - -unsafe impl VulkanObject for RenderPass { - type Object = ash::vk::RenderPass; - - #[inline] - fn internal_object(&self) -> ash::vk::RenderPass { - self.handle - } -} - -/// Error that can happen when creating a compute pipeline. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RenderPassCreationError { - /// Not enough memory. - OomError(OomError), - /// The maximum number of color attachments has been exceeded. - ColorAttachmentsLimitExceeded, -} - -impl error::Error for RenderPassCreationError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - RenderPassCreationError::OomError(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for RenderPassCreationError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - RenderPassCreationError::OomError(_) => "not enough memory available", - RenderPassCreationError::ColorAttachmentsLimitExceeded => { - "the maximum number of color attachments has been exceeded" - } - } - ) - } -} - -impl From for RenderPassCreationError { - #[inline] - fn from(err: OomError) -> RenderPassCreationError { - RenderPassCreationError::OomError(err) - } -} - -impl From for RenderPassCreationError { - #[inline] - fn from(err: Error) -> RenderPassCreationError { - match err { - err @ Error::OutOfHostMemory => RenderPassCreationError::OomError(OomError::from(err)), - err @ Error::OutOfDeviceMemory => { - RenderPassCreationError::OomError(OomError::from(err)) - } - _ => panic!("unexpected error: {:?}", err), - } - } -} - -/// Represents a subpass within a `RenderPass` object. -/// -/// This struct doesn't correspond to anything in Vulkan. It is simply an equivalent to a -/// tuple of a render pass and subpass index. Contrary to a tuple, however, the existence of the -/// subpass is checked when the object is created. When you have a `Subpass` you are guaranteed -/// that the given subpass does exist. -#[derive(Debug, Clone)] -pub struct Subpass { - render_pass: Arc, - subpass_id: u32, -} - -impl Subpass { - /// Returns a handle that represents a subpass of a render pass. - #[inline] - pub fn from(render_pass: Arc, id: u32) -> Option { - if (id as usize) < render_pass.desc().subpasses().len() { - Some(Subpass { - render_pass, - subpass_id: id, - }) - } else { - None - } - } - - /// Returns the subpass description for this subpass. - #[inline] - pub fn subpass_desc(&self) -> &SubpassDesc { - &self.render_pass.desc().subpasses()[self.subpass_id as usize] - } - - /// Returns whether this subpass is the last one in the render pass. If `true` is returned, - /// `next_subpass` will return `None`. - #[inline] - pub fn is_last_subpass(&self) -> bool { - self.subpass_id as usize == self.render_pass.desc().subpasses().len() - 1 - } - - /// Tries to advance to the next subpass after this one, and returns `true` if successful. - #[inline] - pub fn try_next_subpass(&mut self) -> bool { - let next_id = self.subpass_id + 1; - - if (next_id as usize) < self.render_pass.desc().subpasses().len() { - self.subpass_id = next_id; - true - } else { - false - } - } - - #[inline] - fn attachment_desc(&self, atch_num: usize) -> &AttachmentDesc { - &self.render_pass.desc().attachments()[atch_num] - } - - /// Returns the number of color attachments in this subpass. - #[inline] - pub fn num_color_attachments(&self) -> u32 { - self.subpass_desc().color_attachments.len() as u32 - } - - /// Returns true if the subpass has a depth attachment or a depth-stencil attachment. - #[inline] - pub fn has_depth(&self) -> bool { - let subpass_desc = self.subpass_desc(); - let atch_num = match subpass_desc.depth_stencil { - Some((d, _)) => d, - None => return false, - }; - - self.attachment_desc(atch_num).format.aspects().depth - } - - /// Returns true if the subpass has a depth attachment or a depth-stencil attachment whose - /// layout is not `DepthStencilReadOnlyOptimal`. - #[inline] - pub fn has_writable_depth(&self) -> bool { - let subpass_desc = self.subpass_desc(); - let atch_num = match subpass_desc.depth_stencil { - Some((d, l)) => { - if l == ImageLayout::DepthStencilReadOnlyOptimal { - return false; - } - d - } - None => return false, - }; - - self.attachment_desc(atch_num).format.aspects().depth - } - - /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment. - #[inline] - pub fn has_stencil(&self) -> bool { - let subpass_desc = self.subpass_desc(); - let atch_num = match subpass_desc.depth_stencil { - Some((d, _)) => d, - None => return false, - }; - - self.attachment_desc(atch_num).format.aspects().stencil - } - - /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment whose - /// layout is not `DepthStencilReadOnlyOptimal`. - #[inline] - pub fn has_writable_stencil(&self) -> bool { - let subpass_desc = self.subpass_desc(); - - let atch_num = match subpass_desc.depth_stencil { - Some((d, l)) => { - if l == ImageLayout::DepthStencilReadOnlyOptimal { - return false; - } - d - } - None => return false, - }; - - self.attachment_desc(atch_num).format.aspects().stencil - } - - /// Returns true if the subpass has any depth/stencil attachment. - #[inline] - pub fn has_depth_stencil_attachment(&self) -> bool { - let subpass_desc = self.subpass_desc(); - match subpass_desc.depth_stencil { - Some((d, _)) => true, - None => false, - } - } - - /// Returns true if the subpass has any color or depth/stencil attachment. - #[inline] - pub fn has_color_or_depth_stencil_attachment(&self) -> bool { - if self.num_color_attachments() >= 1 { - return true; - } - - let subpass_desc = self.subpass_desc(); - match subpass_desc.depth_stencil { - Some((d, _)) => true, - None => false, - } - } - - /// Returns the number of samples in the color and/or depth/stencil attachments. Returns `None` - /// if there is no such attachment in this subpass. - #[inline] - pub fn num_samples(&self) -> Option { - let subpass_desc = self.subpass_desc(); - - // TODO: chain input attachments as well? - subpass_desc - .color_attachments - .iter() - .cloned() - .chain(subpass_desc.depth_stencil.clone().into_iter()) - .filter_map(|a| self.render_pass.desc().attachments().get(a.0)) - .next() - .map(|a| a.samples) - } - - /// Returns the render pass of this subpass. - #[inline] - pub fn render_pass(&self) -> &Arc { - &self.render_pass - } - - /// Returns the index of this subpass within the renderpass. - #[inline] - pub fn index(&self) -> u32 { - self.subpass_id - } - - /// Returns `true` if this subpass is compatible with the fragment output definition. - // TODO: return proper error - pub fn is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool { - self.render_pass - .desc() - .is_compatible_with_shader(self.subpass_id, shader_interface) - } -} - -impl From for (Arc, u32) { - #[inline] - fn from(value: Subpass) -> (Arc, u32) { - (value.render_pass, value.subpass_id) - } -} - -#[cfg(test)] -mod tests { - use crate::format::Format; - use crate::render_pass::RenderPass; - use crate::render_pass::RenderPassCreationError; - - #[test] - fn empty() { - let (device, _) = gfx_dev_and_queue!(); - let _ = RenderPass::empty_single_pass(device).unwrap(); - } - - #[test] - fn too_many_color_atch() { - let (device, _) = gfx_dev_and_queue!(); - - if device.physical_device().properties().max_color_attachments >= 10 { - return; // test ignored - } - - let rp = single_pass_renderpass! { - device.clone(), - attachments: { - a1: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a2: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a3: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a4: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a5: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a6: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a7: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a8: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a9: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, - a10: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, } - }, - pass: { - color: [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10], - depth_stencil: {} - } - }; - - match rp { - Err(RenderPassCreationError::ColorAttachmentsLimitExceeded) => (), - _ => panic!(), - } - } - - #[test] - fn non_zero_granularity() { - let (device, _) = gfx_dev_and_queue!(); - - let rp = single_pass_renderpass! { - device.clone(), - attachments: { - a: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, } - }, - pass: { - color: [a], - depth_stencil: {} - } - } - .unwrap(); - - let granularity = rp.granularity(); - assert_ne!(granularity[0], 0); - assert_ne!(granularity[1], 0); - } -} diff --git a/vulkano/src/sampler/mod.rs b/vulkano/src/sampler/mod.rs index f7487067..0295a3ce 100644 --- a/vulkano/src/sampler/mod.rs +++ b/vulkano/src/sampler/mod.rs @@ -596,11 +596,10 @@ impl SamplerBuilder { if let Some(sampler_ycbcr_conversion) = &sampler_ycbcr_conversion { assert_eq!(&device, sampler_ycbcr_conversion.device()); - let format_properties = device + let potential_format_features = device .physical_device() - .format_properties(sampler_ycbcr_conversion.format().unwrap()); - let potential_format_features = &format_properties.linear_tiling_features - | &format_properties.optimal_tiling_features; + .format_properties(sampler_ycbcr_conversion.format().unwrap()) + .potential_format_features(); // VUID-VkSamplerCreateInfo-minFilter-01645 if !potential_format_features diff --git a/vulkano/src/sampler/ycbcr.rs b/vulkano/src/sampler/ycbcr.rs index 68467baf..eba18312 100644 --- a/vulkano/src/sampler/ycbcr.rs +++ b/vulkano/src/sampler/ycbcr.rs @@ -223,9 +223,10 @@ impl SamplerYcbcrConversionBuilder { return Err(SamplerYcbcrConversionCreationError::FormatNotUnorm); } - let format_properties = device.physical_device().format_properties(format); - let potential_format_features = - &format_properties.linear_tiling_features | &format_properties.optimal_tiling_features; + let potential_format_features = device + .physical_device() + .format_properties(format) + .potential_format_features(); // VUID-VkSamplerYcbcrConversionCreateInfo-format-01650 if !(potential_format_features.midpoint_chroma_samples diff --git a/vulkano/src/sync/pipeline.rs b/vulkano/src/sync/pipeline.rs index 93ad3149..fa3f83c4 100644 --- a/vulkano/src/sync/pipeline.rs +++ b/vulkano/src/sync/pipeline.rs @@ -83,6 +83,127 @@ macro_rules! pipeline_stages { ); } +impl PipelineStages { + /// Returns the access types that are supported with the given pipeline stages. + /// + /// Corresponds to the table + /// "[Supported access types](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-access-types-supported)" + /// in the Vulkan specification. + #[inline] + pub fn supported_access(&self) -> AccessFlags { + if self.all_commands { + return AccessFlags::all(); + } + + let PipelineStages { + top_of_pipe, + mut draw_indirect, + mut vertex_input, + mut vertex_shader, + mut tessellation_control_shader, + mut tessellation_evaluation_shader, + mut geometry_shader, + mut fragment_shader, + mut early_fragment_tests, + mut late_fragment_tests, + mut color_attachment_output, + compute_shader, + transfer, + bottom_of_pipe, + host, + all_graphics, + all_commands, + ray_tracing_shader, + } = *self; + + if all_graphics { + draw_indirect = true; + //task_shader = true; + //mesh_shader = true; + vertex_input = true; + vertex_shader = true; + tessellation_control_shader = true; + tessellation_evaluation_shader = true; + geometry_shader = true; + fragment_shader = true; + early_fragment_tests = true; + late_fragment_tests = true; + color_attachment_output = true; + //conditional_rendering = true; + //transform_feedback = true; + //fragment_shading_rate_attachment = true; + //fragment_density_process = true; + } + + AccessFlags { + indirect_command_read: draw_indirect, /*|| acceleration_structure_build*/ + index_read: vertex_input, + vertex_attribute_read: vertex_input, + uniform_read: + // task_shader + // mesh_shader + ray_tracing_shader + || vertex_shader + || tessellation_control_shader + || tessellation_evaluation_shader + || geometry_shader + || fragment_shader + || compute_shader, + shader_read: + // acceleration_structure_build + // task_shader + // mesh_shader + ray_tracing_shader + || vertex_shader + || tessellation_control_shader + || tessellation_evaluation_shader + || geometry_shader + || fragment_shader + || compute_shader, + shader_write: + // task_shader + // mesh_shader + ray_tracing_shader + || vertex_shader + || tessellation_control_shader + || tessellation_evaluation_shader + || geometry_shader + || fragment_shader + || compute_shader, + input_attachment_read: + // subpass_shading + fragment_shader, + color_attachment_read: color_attachment_output, + color_attachment_write: color_attachment_output, + depth_stencil_attachment_read: early_fragment_tests || late_fragment_tests, + depth_stencil_attachment_write: early_fragment_tests || late_fragment_tests, + transfer_read: transfer, + // acceleration_structure_build + transfer_write: transfer, + // acceleration_structure_build + host_read: host, + host_write: host, + memory_read: true, + memory_write: true, + + /* + color_attachment_read_noncoherent: color_attachment_output, + preprocess_read: command_preprocess, + preprocess_write: command_preprocess, + conditional_rendering_read: conditional_rendering, + fragment_shading_rate_attachment_read: fragment_shading_rate_attachment, + invocation_mask_read: invocation_mask, + transform_feedback_write: transform_feedback, + transform_feedback_counter_write: transform_feedback, + transform_feedback_counter_read: transform_feedback || draw_indirect, + acceleration_structure_read: task_shader || mesh_shader || vertex_shader || tessellation_control_shader || tessellation_evaluation_shader || geometry_shader || fragment_shader || compute_shader || ray_tracing_shader || acceleration_structure_build, + acceleration_structure_write: acceleration_structure_build, + fragment_density_map_read: fragment_density_process, + */ + } + } +} + impl From for ash::vk::PipelineStageFlags { #[inline] fn from(val: PipelineStage) -> Self { @@ -139,6 +260,13 @@ macro_rules! access_flags { )+ } } + + /// Returns whether all flags in `other` are also set in `self`. + pub const fn contains(&self, other: &Self) -> bool { + $( + (self.$elem || !other.$elem) + )&&+ + } } impl From for ash::vk::AccessFlags { @@ -196,70 +324,6 @@ access_flags! { memory_write => ash::vk::AccessFlags::MEMORY_WRITE, } -impl AccessFlags { - /// Returns true if the access flags can be used with the given pipeline stages. - /// - /// Corresponds to `Table 4. Supported access types` in section `6.1.3. Access Types` of the - /// Vulkan specs. - pub fn is_compatible_with(&self, stages: &PipelineStages) -> bool { - if stages.all_commands { - return true; - } - - if self.indirect_command_read && !stages.draw_indirect && !stages.all_graphics { - return false; - } - - if (self.index_read || self.vertex_attribute_read) - && !stages.vertex_input - && !stages.all_graphics - { - return false; - } - - if (self.uniform_read || self.shader_read || self.shader_write) - && !stages.vertex_shader - && !stages.tessellation_control_shader - && !stages.tessellation_evaluation_shader - && !stages.geometry_shader - && !stages.fragment_shader - && !stages.compute_shader - && !stages.all_graphics - { - return false; - } - - if self.input_attachment_read && !stages.fragment_shader && !stages.all_graphics { - return false; - } - - if (self.color_attachment_read || self.color_attachment_write) - && !stages.color_attachment_output - && !stages.all_graphics - { - return false; - } - - if (self.depth_stencil_attachment_read || self.depth_stencil_attachment_write) - && !stages.early_fragment_tests - && !stages.late_fragment_tests - && !stages.all_graphics - { - return false; - } - - if (self.transfer_read || self.transfer_write) && !stages.transfer { - return false; - } - - if (self.host_read || self.host_write) && !stages.host { - return false; - } - - true - } -} - /// The full specification of memory access by the pipeline for a particular resource. #[derive(Clone, Copy, Debug)] pub struct PipelineMemoryAccess {