Add RenderPassCreateInfo and FramebufferCreateInfo (#1828)

This commit is contained in:
Rua 2022-02-20 01:14:16 +01:00 committed by GitHub
parent 0eeaaf4b95
commit 21330c747a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 3954 additions and 2484 deletions

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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<f32>, 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<f32>, 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(),

View File

@ -557,7 +557,7 @@ fn window_size_dependent_setup(
render_pass: Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<Framebuffer>> {
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<Framebuffer> {
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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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(),

View File

@ -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.

View File

@ -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::<Vec<_>>()
}

View File

@ -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(),

View File

@ -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::<Vertex>())

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>();

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -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::<Vec<_>>()
}

View File

@ -135,7 +135,7 @@ struct RenderPassState {
subpass: Subpass,
contents: SubpassContents,
attached_layers_ranges: SmallVec<[Range<u32>; 4]>,
dimensions: [u32; 3],
extent: [u32; 2],
framebuffer: ash::vk::Framebuffer, // Always null for secondary command buffers
}
@ -296,10 +296,7 @@ impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
}| 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<L, P> AutoCommandBufferBuilder<L, P> {
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<L, P> AutoCommandBufferBuilder<L, P> {
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<L, P> AutoCommandBufferBuilder<L, P> {
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<L, P> AutoCommandBufferBuilder<L, P> {
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::<Vec<_>>().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();
}

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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()

View File

@ -1162,6 +1162,15 @@ pub struct FormatProperties {
pub _ne: crate::NonExhaustive,
}
impl FormatProperties {
/// Returns the potential format features, following the definition of
/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap43.html#potential-format-features>.
#[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)]

View File

@ -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<ImageViewType> for ash::vk::ImageViewType {
/// Trait for types that represent the GPU can access an image view.
pub unsafe trait ImageViewAbstract:
VulkanObject<Object = ash::vk::ImageView> + DeviceOwned + Send + Sync
VulkanObject<Object = ash::vk::ImageView> + DeviceOwned + Debug + Send + Sync
{
/// Returns the wrapped image that this image view was created from.
fn image(&self) -> Arc<dyn ImageAccess>;

View File

@ -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

View File

@ -1,259 +0,0 @@
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! 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<I>(
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
}

File diff suppressed because it is too large Load Diff

View File

@ -1,437 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::format::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<AttachmentDesc>,
subpasses: Vec<SubpassDesc>,
dependencies: Vec<SubpassDependencyDesc>,
multiview: Option<MultiviewDesc>,
}
impl RenderPassDesc {
/// Creates a description of a render pass.
pub fn new(
attachments: Vec<AttachmentDesc>,
subpasses: Vec<SubpassDesc>,
dependencies: Vec<SubpassDependencyDesc>,
) -> 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<AttachmentDesc>,
subpasses: Vec<SubpassDesc>,
dependencies: Vec<SubpassDependencyDesc>,
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<MultiviewDesc> {
&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<I>(&self, values: I) -> impl Iterator<Item = ClearValue>
where
I: IntoIterator<Item = ClearValue>,
{
// 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<usize>, // 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<StoreOp> 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<LoadOp> 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<u32>,
/// 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<u32>,
/// 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<i32>,
}
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<ash::vk::ResolveModeFlags> 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),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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<ImageLayout>, Option<ImageLayout>)> = 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)
});
}

File diff suppressed because it is too large Load Diff

View File

@ -1,911 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::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<vulkano::device::Device> = 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<vulkano::device::Device> = 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<Device>,
// Description of the render pass.
desc: RenderPassDesc,
// Cache of the granularity of the render pass.
granularity: Mutex<Option<[u32; 2]>>,
}
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<Device>,
description: RenderPassDesc,
) -> Result<Arc<RenderPass>, 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::<SmallVec<[_; 16]>>();
// 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::<SmallVec<[_; 16]>>();
// 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::<SmallVec<[_; 16]>>();
// 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::<SmallVec<[_; 16]>>();
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<Device>,
) -> Result<Arc<RenderPass>, 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<Self>) -> Subpass {
Subpass {
render_pass: self,
subpass_id: 0, // Guaranteed to exist
}
}
}
unsafe impl DeviceOwned for RenderPass {
#[inline]
fn device(&self) -> &Arc<Device> {
&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<OomError> for RenderPassCreationError {
#[inline]
fn from(err: OomError) -> RenderPassCreationError {
RenderPassCreationError::OomError(err)
}
}
impl From<Error> 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<RenderPass>,
subpass_id: u32,
}
impl Subpass {
/// Returns a handle that represents a subpass of a render pass.
#[inline]
pub fn from(render_pass: Arc<RenderPass>, id: u32) -> Option<Subpass> {
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<SampleCount> {
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<RenderPass> {
&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<Subpass> for (Arc<RenderPass>, u32) {
#[inline]
fn from(value: Subpass) -> (Arc<RenderPass>, 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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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<PipelineStage> 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<AccessFlags> 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 {