mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-25 16:25:31 +00:00
Add RenderPassCreateInfo
and FramebufferCreateInfo
(#1828)
This commit is contained in:
parent
0eeaaf4b95
commit
21330c747a
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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.
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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>())
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>();
|
||||
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)]
|
||||
|
@ -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>;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
1710
vulkano/src/render_pass/create.rs
Normal file
1710
vulkano/src/render_pass/create.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
@ -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
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user