Move command buffer binding and state-setting into their own commands (#1684)

* Move binding and state-setting into their own commands

* Fix test failing on documentation
This commit is contained in:
Rua 2021-08-27 08:24:16 +02:00 committed by GitHub
parent 7948b71a12
commit aa1c6eb607
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 2093 additions and 2187 deletions

View File

@ -20,7 +20,7 @@ use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::ComputePipeline;
use vulkano::pipeline::{ComputePipeline, PipelineBindPoint};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::Version;
@ -165,7 +165,14 @@ fn main() {
// `Arc`, this only clones the `Arc` and not the whole pipeline or set (which aren't
// cloneable anyway). In this example we would avoid cloning them since this is the last
// time we use them, but in a real code you would probably need to clone them.
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), ())
.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone(),
)
.dispatch([1024, 1, 1])
.unwrap();
// Finish building the command buffer by calling `build`.
let command_buffer = builder.build().unwrap();

View File

@ -21,10 +21,8 @@
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use vulkano::buffer::{CpuBufferPool, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::buffer::CpuBufferPool;
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
@ -180,16 +178,12 @@ fn main() {
.unwrap(),
);
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
@ -223,7 +217,7 @@ fn main() {
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
&mut viewport,
);
recreate_swapchain = false;
}
@ -274,6 +268,7 @@ fn main() {
],
},
];
let num_vertices = data.len() as u32;
// Allocate a new chunk from buffer_pool
let buffer = buffer_pool.chunk(data.to_vec()).unwrap();
@ -290,18 +285,11 @@ fn main() {
clear_values,
)
.unwrap()
.set_viewport(0, [viewport.clone()])
// Draw our buffer
.draw(
buffer.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
buffer,
(),
(),
)
.bind_pipeline_graphics(pipeline.clone())
.bind_vertex_buffers(0, buffer)
.draw(num_vertices, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
@ -339,16 +327,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -12,7 +12,7 @@ use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SecondaryAutoCommandBuffer,
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Queue;
@ -22,6 +22,7 @@ use vulkano::pipeline::blend::BlendFactor;
use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::PipelineBindPoint;
use vulkano::render_pass::Subpass;
/// Allows applying an ambient lighting to a scene.
@ -134,13 +135,10 @@ impl AmbientLightingSystem {
.build()
.unwrap();
let dynamic_state = DynamicState {
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
}]),
..DynamicState::none()
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
};
let mut builder = AutoCommandBufferBuilder::secondary_graphics(
@ -151,17 +149,17 @@ impl AmbientLightingSystem {
)
.unwrap();
builder
.draw(
self.vertex_buffer.len() as u32,
1,
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(self.pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
self.pipeline.layout().clone(),
0,
0,
self.pipeline.clone(),
&dynamic_state,
self.vertex_buffer.clone(),
descriptor_set,
push_constants,
)
.push_constants(self.pipeline.layout().clone(), 0, push_constants)
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
builder.build().unwrap()
}

View File

@ -13,7 +13,7 @@ use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SecondaryAutoCommandBuffer,
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Queue;
@ -23,6 +23,7 @@ use vulkano::pipeline::blend::BlendFactor;
use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::PipelineBindPoint;
use vulkano::render_pass::Subpass;
/// Allows applying a directional light source to a scene.
@ -148,13 +149,10 @@ impl DirectionalLightingSystem {
.build()
.unwrap();
let dynamic_state = DynamicState {
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
}]),
..DynamicState::none()
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
};
let mut builder = AutoCommandBufferBuilder::secondary_graphics(
@ -165,17 +163,17 @@ impl DirectionalLightingSystem {
)
.unwrap();
builder
.draw(
self.vertex_buffer.len() as u32,
1,
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(self.pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
self.pipeline.layout().clone(),
0,
0,
self.pipeline.clone(),
&dynamic_state,
self.vertex_buffer.clone(),
descriptor_set,
push_constants,
)
.push_constants(self.pipeline.layout().clone(), 0, push_constants)
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
builder.build().unwrap()
}

View File

@ -9,10 +9,12 @@
use cgmath::Matrix4;
use cgmath::Vector3;
use std::sync::Arc;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SecondaryAutoCommandBuffer,
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::Queue;
@ -22,11 +24,9 @@ use vulkano::pipeline::blend::BlendFactor;
use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::PipelineBindPoint;
use vulkano::render_pass::Subpass;
use std::sync::Arc;
use vulkano::buffer::TypedBufferAccess;
pub struct PointLightingSystem {
gfx_queue: Arc<Queue>,
vertex_buffer: Arc<CpuAccessibleBuffer<[Vertex]>>,
@ -164,13 +164,10 @@ impl PointLightingSystem {
.build()
.unwrap();
let dynamic_state = DynamicState {
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
}]),
..DynamicState::none()
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
};
let mut builder = AutoCommandBufferBuilder::secondary_graphics(
@ -181,17 +178,17 @@ impl PointLightingSystem {
)
.unwrap();
builder
.draw(
self.vertex_buffer.len() as u32,
1,
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(self.pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
self.pipeline.layout().clone(),
0,
0,
self.pipeline.clone(),
&dynamic_state,
self.vertex_buffer.clone(),
descriptor_set,
push_constants,
)
.push_constants(self.pipeline.layout().clone(), 0, push_constants)
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
builder.build().unwrap()
}

View File

@ -12,7 +12,7 @@ use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
use vulkano::buffer::TypedBufferAccess;
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SecondaryAutoCommandBuffer,
AutoCommandBufferBuilder, CommandBufferUsage, SecondaryAutoCommandBuffer,
};
use vulkano::device::Queue;
use vulkano::pipeline::viewport::Viewport;
@ -87,24 +87,17 @@ impl TriangleDrawSystem {
)
.unwrap();
builder
.draw(
self.vertex_buffer.len() as u32,
1,
.set_viewport(
0,
0,
self.pipeline.clone(),
&DynamicState {
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
}]),
..DynamicState::none()
},
self.vertex_buffer.clone(),
(),
(),
[Viewport {
origin: [0.0, 0.0],
dimensions: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32],
depth_range: 0.0..1.0,
}],
)
.bind_pipeline_graphics(self.pipeline.clone())
.bind_vertex_buffers(0, self.vertex_buffer.clone())
.draw(self.vertex_buffer.len() as u32, 1, 0, 0)
.unwrap();
builder.build().unwrap()
}

View File

@ -25,7 +25,7 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::layout::PipelineLayout;
use vulkano::pipeline::shader::EntryPointAbstract;
use vulkano::pipeline::ComputePipeline;
use vulkano::pipeline::{ComputePipeline, PipelineBindPoint};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::OomError;
@ -217,26 +217,30 @@ fn main() {
)
.unwrap();
builder
.dispatch(
[12, 1, 1],
pipeline.clone(),
.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone().offsets([0 * align as u32]),
(),
)
.dispatch([12, 1, 1])
.unwrap()
.dispatch(
[12, 1, 1],
pipeline.clone(),
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone().offsets([1 * align as u32]),
(),
)
.dispatch([12, 1, 1])
.unwrap()
.dispatch(
[12, 1, 1],
pipeline.clone(),
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone().offsets([2 * align as u32]),
(),
)
.dispatch([12, 1, 1])
.unwrap();
let command_buffer = builder.build().unwrap();

View File

@ -26,7 +26,7 @@ use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::format::Format;
use vulkano::image::{view::ImageView, ImageDimensions, StorageImage};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::ComputePipeline;
use vulkano::pipeline::{ComputePipeline, PipelineBindPoint};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::Version;
@ -221,16 +221,18 @@ fn main() {
)
.unwrap();
builder
.dispatch(
[
1024 / local_size_x, // Note that dispatch dimensions must be
1024 / local_size_y, // proportional to local size
1,
],
pipeline.clone(),
.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone(),
(),
)
.dispatch([
1024 / local_size_x, // Note that dispatch dimensions must be
1024 / local_size_y, // proportional to local size
1,
])
.unwrap()
.copy_image_to_buffer(image.clone(), buf.clone())
.unwrap();

View File

@ -11,9 +11,7 @@ use png;
use std::io::Cursor;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
@ -23,7 +21,7 @@ use vulkano::image::{
};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
use vulkano::swapchain;
@ -214,16 +212,12 @@ fn main() {
.unwrap(),
);
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(tex_future.boxed());
@ -254,11 +248,8 @@ fn main() {
};
swapchain = new_swapchain;
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
);
framebuffers =
window_size_dependent_setup(&new_images, render_pass.clone(), &mut viewport);
recreate_swapchain = false;
}
@ -290,17 +281,16 @@ fn main() {
clear_values,
)
.unwrap()
.draw(
vertex_buffer.len() as u32,
1,
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
pipeline.layout().clone(),
0,
0,
pipeline.clone(),
&dynamic_state,
vertex_buffer.clone(),
set.clone(),
(),
)
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
@ -337,16 +327,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -16,7 +16,7 @@ use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::ComputePipeline;
use vulkano::pipeline::{ComputePipeline, PipelineBindPoint};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::Version;
@ -158,7 +158,14 @@ void main() {
.unwrap();
builder
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), ())
.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone(),
)
.dispatch([1024, 1, 1])
.unwrap();
let command_buffer = builder.build().unwrap();

View File

@ -30,12 +30,10 @@ extern crate vulkano_shaders;
extern crate vulkano_win;
extern crate winit;
use std::iter;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuBufferPool};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DrawIndirectCommand, DynamicState,
SubpassContents,
AutoCommandBufferBuilder, CommandBufferUsage, DrawIndirectCommand, SubpassContents,
};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
@ -44,7 +42,7 @@ use vulkano::image::view::ImageView;
use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::{ComputePipeline, GraphicsPipeline};
use vulkano::pipeline::{ComputePipeline, GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
@ -245,16 +243,12 @@ fn main() {
.unwrap(),
);
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
@ -288,7 +282,7 @@ fn main() {
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
&mut viewport,
);
recreate_swapchain = false;
}
@ -312,12 +306,12 @@ fn main() {
// Allocate a GPU buffer to hold the arguments for this frames draw call. The compute
// shader will only update vertex_count, so set the other parameters correctly here.
let indirect_args = indirect_args_pool
.chunk(iter::once(DrawIndirectCommand {
.chunk([DrawIndirectCommand {
vertex_count: 0,
instance_count: 1,
first_vertex: 0,
first_instance: 0,
}))
}])
.unwrap();
// Allocate a GPU buffer to hold this frames vertices. This needs to be large enough to hold
@ -352,12 +346,14 @@ fn main() {
// First in the command buffer we dispatch the compute shader to generate the vertices and fill out the draw
// call arguments
builder
.dispatch(
[1, 1, 1],
compute_pipeline.clone(),
.bind_pipeline_compute(compute_pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
compute_pipeline.layout().clone(),
0,
cs_desciptor_set.clone(),
(),
)
.dispatch([1, 1, 1])
.unwrap()
.begin_render_pass(
framebuffers[image_num].clone(),
@ -367,14 +363,10 @@ fn main() {
.unwrap()
// The indirect draw call is placed in the command buffer with a reference to the GPU buffer that will
// contain the arguments when the draw is executed on the GPU
.draw_indirect(
render_pipeline.clone(),
&dynamic_state,
vertices.clone(),
indirect_args.clone(),
(),
(),
)
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(render_pipeline.clone())
.bind_vertex_buffers(0, vertices.clone())
.draw_indirect(indirect_args.clone())
.unwrap()
.end_render_pass()
.unwrap();
@ -412,16 +404,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -20,9 +20,7 @@ extern crate winit;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
@ -256,16 +254,12 @@ fn main() {
.unwrap(),
);
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
@ -299,7 +293,7 @@ fn main() {
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
&mut viewport,
);
recreate_swapchain = false;
}
@ -333,17 +327,18 @@ fn main() {
clear_values,
)
.unwrap()
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
// We pass both our lists of vertices here.
.bind_vertex_buffers(
0,
(triangle_vertex_buffer.clone(), instance_data_buffer.clone()),
)
.draw(
triangle_vertex_buffer.len() as u32,
instance_data_buffer.len() as u32,
0,
0,
pipeline.clone(),
&dynamic_state,
// We pass both our lists of vertices here.
(triangle_vertex_buffer.clone(), instance_data_buffer.clone()),
(),
(),
)
.unwrap()
.end_render_pass()
@ -382,16 +377,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -71,8 +71,7 @@ use std::path::Path;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, PrimaryCommandBuffer,
SubpassContents,
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBuffer, SubpassContents,
};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
@ -281,13 +280,10 @@ fn main() {
.unwrap(),
);
let dynamic_state = DynamicState {
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [1024.0, 1024.0],
depth_range: 0.0..1.0,
}]),
..DynamicState::none()
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [1024.0, 1024.0],
depth_range: 0.0..1.0,
};
let buf = CpuAccessibleBuffer::from_iter(
@ -311,17 +307,10 @@ fn main() {
vec![[0.0, 0.0, 1.0, 1.0].into(), ClearValue::None],
)
.unwrap()
.draw(
vertex_buffer.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
vertex_buffer.clone(),
(),
(),
)
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap()

View File

@ -19,9 +19,7 @@
use std::collections::HashMap;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
@ -224,13 +222,10 @@ fn main() {
.unwrap(),
);
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
window_surfaces.insert(
@ -239,11 +234,7 @@ fn main() {
surface,
swapchain,
recreate_swapchain: false,
framebuffers: window_size_dependent_setup(
&images,
render_pass.clone(),
&mut dynamic_state,
),
framebuffers: window_size_dependent_setup(&images, render_pass.clone(), &mut viewport),
previous_frame_end: Some(sync::now(device.clone()).boxed()),
},
);
@ -310,7 +301,7 @@ fn main() {
framebuffers: window_size_dependent_setup(
&images,
render_pass.clone(),
&mut dynamic_state,
&mut viewport,
),
previous_frame_end: Some(sync::now(device.clone()).boxed()),
},
@ -342,11 +333,8 @@ fn main() {
};
*swapchain = new_swapchain;
*framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
);
*framebuffers =
window_size_dependent_setup(&new_images, render_pass.clone(), &mut viewport);
*recreate_swapchain = false;
}
@ -380,17 +368,10 @@ fn main() {
clear_values,
)
.unwrap()
.draw(
vertex_buffer.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
vertex_buffer.clone(),
(),
(),
)
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
@ -426,16 +407,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -15,14 +15,10 @@
use std::fs::File;
use std::io::BufWriter;
use std::iter;
use std::path::Path;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::format::Format;
@ -245,22 +241,20 @@ fn main() {
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports(iter::once(Viewport {
.viewports([Viewport {
origin: [0.0, 0.0],
dimensions: [
image.dimensions().width() as f32,
image.dimensions().height() as f32,
],
depth_range: 0.0..1.0,
}))
}])
.fragment_shader(fs.main_entry_point(), ())
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())
.unwrap(),
);
let dynamic_state = DynamicState::none();
let clear_values = vec![[0.0, 0.0, 1.0, 1.0].into()];
let create_buffer = || {
@ -288,17 +282,9 @@ fn main() {
builder
.begin_render_pass(framebuffer.clone(), SubpassContents::Inline, clear_values)
.unwrap()
.draw(
vertex_buffer.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
vertex_buffer.clone(),
(),
(),
)
.bind_pipeline_graphics(pipeline.clone())
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();

View File

@ -13,9 +13,7 @@
use std::sync::Arc;
use vulkano::buffer::{BufferAccess, BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, DeviceOwned, Features};
use vulkano::format::Format;
@ -255,17 +253,13 @@ fn main() {
.unwrap(),
);
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
@ -296,11 +290,8 @@ fn main() {
};
swapchain = new_swapchain;
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
);
framebuffers =
window_size_dependent_setup(&new_images, render_pass.clone(), &mut viewport);
recreate_swapchain = false;
}
@ -334,6 +325,8 @@ fn main() {
// This must be done outside a render pass.
.reset_query_pool(query_pool.clone(), 0..3)
.unwrap()
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
.begin_render_pass(
framebuffers[image_num].clone(),
SubpassContents::Inline,
@ -345,17 +338,8 @@ fn main() {
// the `occlusion_query_precise` feature to be enabled on the device.
.begin_query(query_pool.clone(), 0, QueryControlFlags { precise: false })
.unwrap()
.draw(
triangle1.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
triangle1.clone(),
(),
(),
)
.bind_vertex_buffers(0, triangle1.clone())
.draw(triangle1.len() as u32, 1, 0, 0)
.unwrap()
// End query 0.
.end_query(query_pool.clone(), 0)
@ -363,34 +347,16 @@ fn main() {
// Begin query 1 for the cyan triangle.
.begin_query(query_pool.clone(), 1, QueryControlFlags { precise: false })
.unwrap()
.draw(
triangle2.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
triangle2.clone(),
(),
(),
)
.bind_vertex_buffers(0, triangle2.clone())
.draw(triangle2.len() as u32, 1, 0, 0)
.unwrap()
.end_query(query_pool.clone(), 1)
.unwrap()
// Finally, query 2 for the green triangle.
.begin_query(query_pool.clone(), 2, QueryControlFlags { precise: false })
.unwrap()
.draw(
triangle3.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
triangle3.clone(),
(),
(),
)
.bind_vertex_buffers(0, triangle3.clone())
.draw(triangle3.len() as u32, 1, 0, 0)
.unwrap()
.end_query(query_pool.clone(), 2)
.unwrap()
@ -473,16 +439,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
let depth_attachment = ImageView::new(
AttachmentImage::with_usage(

View File

@ -16,7 +16,7 @@ use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::ComputePipeline;
use vulkano::pipeline::{ComputePipeline, PipelineBindPoint};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::Version;
@ -129,7 +129,15 @@ fn main() {
)
.unwrap();
builder
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), push_constants)
.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone(),
)
.push_constants(pipeline.layout().clone(), 0, push_constants)
.dispatch([1024, 1, 1])
.unwrap();
let command_buffer = builder.build().unwrap();

View File

@ -26,9 +26,7 @@ use std::io::Read;
use std::sync::Arc;
use vulkano::buffer::cpu_access::CpuAccessibleBuffer;
use vulkano::buffer::{BufferUsage, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::format::Format;
@ -220,7 +218,7 @@ fn main() {
let vert_main = unsafe {
vs.graphics_entry_point(
CStr::from_bytes_with_nul_unchecked(b"main\0"),
[], // No descriptor sets.
[], // No descriptor sets.
None, // No push constants.
<()>::descriptors(),
vertex_input,
@ -232,7 +230,7 @@ fn main() {
let frag_main = unsafe {
fs.graphics_entry_point(
CStr::from_bytes_with_nul_unchecked(b"main\0"),
[], // No descriptor sets.
[], // No descriptor sets.
None, // No push constants.
<()>::descriptors(),
fragment_input,
@ -285,16 +283,12 @@ fn main() {
// note that passing wrong types, providing sets at wrong indexes will cause
// descriptor set builder to return Err!
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
event_loop.run(move |event, _, control_flow| match event {
@ -323,11 +317,8 @@ fn main() {
};
swapchain = new_swapchain;
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
);
framebuffers =
window_size_dependent_setup(&new_images, render_pass.clone(), &mut viewport);
recreate_swapchain = false;
}
@ -359,17 +350,10 @@ fn main() {
clear_values,
)
.unwrap()
.draw(
vertex_buffer.len() as u32,
1,
0,
0,
graphics_pipeline.clone(),
&dynamic_state,
vertex_buffer.clone(),
(),
(),
)
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(graphics_pipeline.clone())
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
@ -406,16 +390,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -18,7 +18,7 @@ use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::ComputePipeline;
use vulkano::pipeline::{ComputePipeline, PipelineBindPoint};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::Version;
@ -115,7 +115,14 @@ fn main() {
)
.unwrap();
builder
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), ())
.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone(),
)
.dispatch([1024, 1, 1])
.unwrap();
let command_buffer = builder.build().unwrap();
let future = sync::now(device.clone())

View File

@ -16,7 +16,7 @@ use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::instance::{Instance, InstanceExtensions};
use vulkano::pipeline::ComputePipeline;
use vulkano::pipeline::{ComputePipeline, PipelineBindPoint};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::Version;
@ -127,7 +127,14 @@ fn main() {
)
.unwrap();
builder
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), ())
.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set.clone(),
)
.dispatch([1024, 1, 1])
.unwrap();
let command_buffer = builder.build().unwrap();

View File

@ -9,15 +9,12 @@
use cgmath::{Matrix3, Matrix4, Point3, Rad, Vector3};
use examples::{Normal, Vertex, INDICES, NORMALS, VERTICES};
use std::iter;
use std::sync::Arc;
use std::time::Instant;
use vulkano::buffer::cpu_pool::CpuBufferPool;
use vulkano::buffer::TypedBufferAccess;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::descriptor_set::PersistentDescriptorSet;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
@ -28,7 +25,7 @@ use vulkano::image::{ImageUsage, SwapchainImage};
use vulkano::instance::Instance;
use vulkano::pipeline::vertex::BuffersDefinition;
use vulkano::pipeline::viewport::Viewport;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::swapchain;
use vulkano::swapchain::{AcquireError, Swapchain, SwapchainCreationError};
@ -263,19 +260,16 @@ fn main() {
vec![[0.0, 0.0, 1.0, 1.0].into(), 1f32.into()],
)
.unwrap()
.draw_indexed(
index_buffer.len() as u32,
1,
.bind_pipeline_graphics(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
pipeline.layout().clone(),
0,
0,
0,
pipeline.clone(),
&DynamicState::none(),
(vertex_buffer.clone(), normals_buffer.clone()),
index_buffer.clone(),
set.clone(),
(),
)
.bind_vertex_buffers(0, (vertex_buffer.clone(), normals_buffer.clone()))
.bind_index_buffer(index_buffer.clone())
.draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
@ -357,11 +351,11 @@ fn window_size_dependent_setup(
.vertex_shader(vs.main_entry_point(), ())
.triangle_list()
.viewports_dynamic_scissors_irrelevant(1)
.viewports(iter::once(Viewport {
.viewports([Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
}))
}])
.fragment_shader(fs.main_entry_point(), ())
.depth_stencil_simple_depth()
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())

View File

@ -20,9 +20,7 @@
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
@ -294,16 +292,12 @@ fn main() {
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(sync::now(device.clone()).boxed());
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
@ -331,11 +325,8 @@ fn main() {
};
swapchain = new_swapchain;
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
);
framebuffers =
window_size_dependent_setup(&new_images, render_pass.clone(), &mut viewport);
recreate_swapchain = false;
}
@ -366,17 +357,10 @@ fn main() {
vec![[0.0, 0.0, 0.0, 1.0].into()],
)
.unwrap()
.draw(
vertex_buffer.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
vertex_buffer.clone(),
(),
(),
)
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
@ -413,16 +397,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -18,9 +18,7 @@
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, DynamicState, SubpassContents,
};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::image::view::ImageView;
@ -344,13 +342,10 @@ fn main() {
// Dynamic viewports allow us to recreate just the viewport when the window is resized
// Otherwise we would have to recreate the whole pipeline.
let mut dynamic_state = DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
let mut viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [0.0, 0.0],
depth_range: 0.0..1.0,
};
// The render pass we created above only describes the layout of our framebuffers. Before we
@ -358,8 +353,7 @@ fn main() {
//
// Since we need to draw to multiple images, we are going to create a different framebuffer for
// each image.
let mut framebuffers =
window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state);
let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport);
// Initialization is finally finished!
@ -423,7 +417,7 @@ fn main() {
framebuffers = window_size_dependent_setup(
&new_images,
render_pass.clone(),
&mut dynamic_state,
&mut viewport,
);
recreate_swapchain = false;
}
@ -489,17 +483,10 @@ fn main() {
//
// The last two parameters contain the list of resources to pass to the shaders.
// Since we used an `EmptyPipeline` object, the objects have to be `()`.
.draw(
vertex_buffer.len() as u32,
1,
0,
0,
pipeline.clone(),
&dynamic_state,
vertex_buffer.clone(),
(),
(),
)
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
// We leave the render pass by calling `draw_end`. Note that if we had multiple
// subpasses we could have called `next_inline` (or `next_secondary`) to jump to the
@ -548,16 +535,10 @@ fn main() {
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
dynamic_state: &mut DynamicState,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0..1.0,
};
dynamic_state.viewports = Some(vec![viewport]);
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()

View File

@ -104,7 +104,7 @@ pub(super) fn write_push_constant_ranges(
};
let (_, size, _) = crate::structs::type_from_id(doc, type_id, types_meta);
let size = size.expect("Found runtime-sized push constants");
let size = size.expect("Found runtime-sized push constants") as u32;
push_constants_size = cmp::max(push_constants_size, size);
}

View File

@ -225,6 +225,15 @@ where
type Content = <T::Target as TypedBufferAccess>::Content;
}
impl PartialEq for &dyn BufferAccess {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner() && self.size() == other.size()
}
}
impl Eq for &dyn BufferAccess {}
impl PartialEq for dyn BufferAccess + Send + Sync {
#[inline]
fn eq(&self, other: &Self) -> bool {

File diff suppressed because it is too large Load Diff

View File

@ -103,14 +103,10 @@ pub use self::auto::ResetQueryPoolError;
pub use self::auto::SecondaryAutoCommandBuffer;
pub use self::auto::UpdateBufferError;
pub use self::auto::WriteTimestampError;
pub use self::state_cacher::StateCacher;
pub use self::state_cacher::StateCacherOutcome;
pub use self::traits::CommandBufferExecError;
pub use self::traits::CommandBufferExecFuture;
pub use self::traits::PrimaryCommandBuffer;
pub use self::traits::SecondaryCommandBuffer;
use crate::pipeline::depth_stencil::DynamicStencilValue;
use crate::pipeline::viewport::{Scissor, Viewport};
use crate::query::QueryControlFlags;
use crate::query::QueryPipelineStatisticFlags;
use crate::render_pass::{Framebuffer, Subpass};
@ -118,7 +114,6 @@ use std::sync::Arc;
mod auto;
pub mod pool;
mod state_cacher;
pub mod submit;
pub mod synced;
pub mod sys;
@ -167,39 +162,6 @@ pub struct DispatchIndirectCommand {
pub z: u32,
}
/// The dynamic state to use for a draw command.
// TODO: probably not the right location
#[derive(Debug, Clone)]
pub struct DynamicState {
pub line_width: Option<f32>,
pub viewports: Option<Vec<Viewport>>,
pub scissors: Option<Vec<Scissor>>,
pub compare_mask: Option<DynamicStencilValue>,
pub write_mask: Option<DynamicStencilValue>,
pub reference: Option<DynamicStencilValue>,
}
impl DynamicState {
#[inline]
pub fn none() -> DynamicState {
DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: None,
write_mask: None,
reference: None,
}
}
}
impl Default for DynamicState {
#[inline]
fn default() -> DynamicState {
DynamicState::none()
}
}
/// Describes what a subpass in a command buffer will contain.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]

View File

@ -1,478 +0,0 @@
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::buffer::BufferAccess;
use crate::command_buffer::DynamicState;
use crate::descriptor_set::DescriptorSetWithOffsets;
use crate::pipeline::input_assembly::IndexType;
use crate::pipeline::ComputePipeline;
use crate::pipeline::GraphicsPipeline;
use crate::pipeline::PipelineBindPoint;
use crate::DeviceSize;
use crate::VulkanObject;
use smallvec::SmallVec;
use std::ops::Range;
/// Keep track of the state of a command buffer builder, so that you don't need to bind objects
/// that were already bound.
///
/// > **Important**: Executing a secondary command buffer invalidates the state of a command buffer
/// > builder. When you do so, you need to call `invalidate()`.
pub struct StateCacher {
// The dynamic state to synchronize with `CmdSetState`.
dynamic_state: DynamicState,
// The compute pipeline currently bound. 0 if nothing bound.
compute_pipeline: ash::vk::Pipeline,
// The graphics pipeline currently bound. 0 if nothing bound.
graphics_pipeline: ash::vk::Pipeline,
// The descriptor sets for the compute pipeline.
compute_descriptor_sets: SmallVec<[(ash::vk::DescriptorSet, SmallVec<[u32; 32]>); 12]>,
// The descriptor sets for the graphics pipeline.
graphics_descriptor_sets: SmallVec<[(ash::vk::DescriptorSet, SmallVec<[u32; 32]>); 12]>,
// If the user starts comparing descriptor sets, but drops the helper struct in the middle of
// the processing then we will end up in a weird state. This bool is true when we start
// comparing sets, and is set to false when we end up comparing. If it was true when we start
// comparing, we know that something bad happened and we flush the cache.
poisoned_descriptor_sets: bool,
// The vertex buffers currently bound.
vertex_buffers: SmallVec<[(ash::vk::Buffer, DeviceSize); 12]>,
// Same as `poisoned_descriptor_sets` but for vertex buffers.
poisoned_vertex_buffers: bool,
// The index buffer, offset, and index type currently bound. `None` if nothing bound.
index_buffer: Option<(ash::vk::Buffer, DeviceSize, IndexType)>,
}
/// Outcome of an operation.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum StateCacherOutcome {
/// The caller needs to perform the state change in the actual command buffer builder.
NeedChange,
/// The state change is not necessary.
AlreadyOk,
}
impl StateCacher {
/// Builds a new `StateCacher`.
#[inline]
pub fn new() -> StateCacher {
StateCacher {
dynamic_state: DynamicState::none(),
compute_pipeline: ash::vk::Pipeline::null(),
graphics_pipeline: ash::vk::Pipeline::null(),
compute_descriptor_sets: SmallVec::new(),
graphics_descriptor_sets: SmallVec::new(),
poisoned_descriptor_sets: false,
vertex_buffers: SmallVec::new(),
poisoned_vertex_buffers: false,
index_buffer: None,
}
}
/// Resets the cache to its default state. You **must** call this after executing a secondary
/// command buffer.
#[inline]
pub fn invalidate(&mut self) {
self.dynamic_state = DynamicState::none();
self.compute_pipeline = ash::vk::Pipeline::null();
self.graphics_pipeline = ash::vk::Pipeline::null();
self.compute_descriptor_sets = SmallVec::new();
self.graphics_descriptor_sets = SmallVec::new();
self.vertex_buffers = SmallVec::new();
self.index_buffer = None;
}
/// Compares the current state with `incoming`, and returns a new state that contains the
/// states that differ and that need to be actually set in the command buffer builder.
///
/// This function also updates the state cacher. The state cacher assumes that the state
/// changes are going to be performed after this function returns.
pub fn dynamic_state(&mut self, incoming: &DynamicState) -> DynamicState {
let mut changed = DynamicState::none();
macro_rules! cmp {
($field:ident) => {
if self.dynamic_state.$field != incoming.$field {
changed.$field = incoming.$field.clone();
if incoming.$field.is_some() {
self.dynamic_state.$field = incoming.$field.clone();
}
}
};
}
cmp!(line_width);
cmp!(viewports);
cmp!(scissors);
cmp!(compare_mask);
cmp!(reference);
cmp!(write_mask);
changed
}
/// Starts the process of comparing a list of descriptor sets to the descriptor sets currently
/// in cache.
///
/// After calling this function, call `add` for each set one by one. Then call `compare` in
/// order to get the index of the first set to bind, or `None` if the sets were identical to
/// what is in cache.
///
/// This process also updates the state cacher. The state cacher assumes that the state
/// changes are going to be performed after the `compare` function returns.
#[inline]
pub fn bind_descriptor_sets(
&mut self,
pipeline_bind_point: PipelineBindPoint,
) -> StateCacherDescriptorSets {
if self.poisoned_descriptor_sets {
self.compute_descriptor_sets = SmallVec::new();
self.graphics_descriptor_sets = SmallVec::new();
}
self.poisoned_descriptor_sets = true;
StateCacherDescriptorSets {
poisoned: &mut self.poisoned_descriptor_sets,
state: match pipeline_bind_point {
PipelineBindPoint::Compute => &mut self.compute_descriptor_sets,
PipelineBindPoint::Graphics => &mut self.graphics_descriptor_sets,
},
offset: 0,
found_diff: None,
}
}
/// Checks whether we need to bind a graphics pipeline. Returns `StateCacherOutcome::AlreadyOk`
/// if the pipeline was already bound earlier, and `StateCacherOutcome::NeedChange` if you need
/// to actually bind the pipeline.
///
/// This function also updates the state cacher. The state cacher assumes that the state
/// changes are going to be performed after this function returns.
pub fn bind_graphics_pipeline(&mut self, pipeline: &GraphicsPipeline) -> StateCacherOutcome {
let inner = pipeline.internal_object();
if inner == self.graphics_pipeline {
StateCacherOutcome::AlreadyOk
} else {
self.graphics_pipeline = inner;
StateCacherOutcome::NeedChange
}
}
/// Checks whether we need to bind a compute pipeline. Returns `StateCacherOutcome::AlreadyOk`
/// if the pipeline was already bound earlier, and `StateCacherOutcome::NeedChange` if you need
/// to actually bind the pipeline.
///
/// This function also updates the state cacher. The state cacher assumes that the state
/// changes are going to be performed after this function returns.
pub fn bind_compute_pipeline(&mut self, pipeline: &ComputePipeline) -> StateCacherOutcome {
let inner = pipeline.internal_object();
if inner == self.compute_pipeline {
StateCacherOutcome::AlreadyOk
} else {
self.compute_pipeline = inner;
StateCacherOutcome::NeedChange
}
}
/// Starts the process of comparing a list of vertex buffers to the vertex buffers currently
/// in cache.
///
/// After calling this function, call `add` for each set one by one. Then call `compare` in
/// order to get the range of the vertex buffers to bind, or `None` if the sets were identical
/// to what is in cache.
///
/// This process also updates the state cacher. The state cacher assumes that the state
/// changes are going to be performed after the `compare` function returns.
#[inline]
pub fn bind_vertex_buffers(&mut self) -> StateCacherVertexBuffers {
if self.poisoned_vertex_buffers {
self.vertex_buffers = SmallVec::new();
}
self.poisoned_vertex_buffers = true;
StateCacherVertexBuffers {
poisoned: &mut self.poisoned_vertex_buffers,
state: &mut self.vertex_buffers,
offset: 0,
first_diff: None,
last_diff: 0,
}
}
/// Checks whether we need to bind an index buffer. Returns `StateCacherOutcome::AlreadyOk`
/// if the index buffer was already bound earlier, and `StateCacherOutcome::NeedChange` if you
/// need to actually bind the buffer.
///
/// This function also updates the state cacher. The state cacher assumes that the state
/// changes are going to be performed after this function returns.
pub fn bind_index_buffer<B>(&mut self, index_buffer: &B, ty: IndexType) -> StateCacherOutcome
where
B: ?Sized + BufferAccess,
{
let value = {
let inner = index_buffer.inner();
(inner.buffer.internal_object(), inner.offset, ty)
};
if self.index_buffer == Some(value) {
StateCacherOutcome::AlreadyOk
} else {
self.index_buffer = Some(value);
StateCacherOutcome::NeedChange
}
}
}
/// Helper struct for comparing descriptor sets.
///
/// > **Note**: For reliability reasons, if you drop/leak this struct before calling `compare` then
/// > the cache of the currently bound descriptor sets will be reset.
pub struct StateCacherDescriptorSets<'s> {
// Reference to the parent's `poisoned_descriptor_sets`.
poisoned: &'s mut bool,
// Reference to the descriptor sets list to compare to.
state: &'s mut SmallVec<[(ash::vk::DescriptorSet, SmallVec<[u32; 32]>); 12]>,
// Next offset within the list to compare to.
offset: usize,
// Contains the return value of `compare`.
found_diff: Option<u32>,
}
impl<'s> StateCacherDescriptorSets<'s> {
/// Adds a descriptor set to the list to compare.
#[inline]
pub fn add(&mut self, descriptor_set: &DescriptorSetWithOffsets) {
let (descriptor_set, dynamic_offsets) = descriptor_set.as_ref();
let raw = descriptor_set.inner().internal_object();
let dynamic_offsets = dynamic_offsets.iter().copied().collect();
if let Some(state) = self.state.get_mut(self.offset) {
if (&state.0, &state.1) == (&raw, &dynamic_offsets) {
self.offset += 1;
return;
}
*state = (raw, dynamic_offsets);
} else {
self.state.push((raw, dynamic_offsets));
}
if self.found_diff.is_none() {
self.found_diff = Some(self.offset as u32);
}
self.offset += 1;
}
/// Compares your list to the list in cache, and returns the offset of the first set to bind.
/// Returns `None` if the two lists were identical.
///
/// After this function returns, the cache will be updated to match your list.
#[inline]
pub fn compare(self) -> Option<u32> {
*self.poisoned = false;
// Removing from the cache any set that wasn't added with `add`.
self.state.truncate(self.offset);
self.found_diff
}
}
/// Helper struct for comparing vertex buffers.
///
/// > **Note**: For reliability reasons, if you drop/leak this struct before calling `compare` then
/// > the cache of the currently bound vertex buffers will be reset.
pub struct StateCacherVertexBuffers<'s> {
// Reference to the parent's `poisoned_vertex_buffers`.
poisoned: &'s mut bool,
// Reference to the vertex buffers list to compare to.
state: &'s mut SmallVec<[(ash::vk::Buffer, DeviceSize); 12]>,
// Next offset within the list to compare to.
offset: usize,
// Contains the offset of the first vertex buffer that differs.
first_diff: Option<u32>,
// Offset of the last vertex buffer that differs.
last_diff: u32,
}
impl<'s> StateCacherVertexBuffers<'s> {
/// Adds a vertex buffer to the list to compare.
#[inline]
pub fn add<B>(&mut self, buffer: &B)
where
B: ?Sized + BufferAccess,
{
let raw = {
let inner = buffer.inner();
let raw = inner.buffer.internal_object();
let offset = inner.offset;
(raw, offset)
};
if self.offset < self.state.len() {
if self.state[self.offset] == raw {
self.offset += 1;
return;
}
self.state[self.offset] = raw;
} else {
self.state.push(raw);
}
self.last_diff = self.offset as u32;
if self.first_diff.is_none() {
self.first_diff = Some(self.offset as u32);
}
self.offset += 1;
}
/// Compares your list to the list in cache, and returns the range of the vertex buffers to
/// bind. Returns `None` if the two lists were identical.
///
/// After this function returns, the cache will be updated to match your list.
///
/// > **Note**: Keep in mind that `range.end` is *after* the last element. For example the
/// > range `1 .. 2` only contains one element.
#[inline]
pub fn compare(self) -> Option<Range<u32>> {
*self.poisoned = false;
// Removing from the cache any set that wasn't added with `add`.
self.state.truncate(self.offset);
self.first_diff.map(|first| {
debug_assert!(first <= self.last_diff);
first..(self.last_diff + 1)
})
}
}
#[cfg(test)]
mod tests {
use crate::buffer::BufferUsage;
use crate::buffer::CpuAccessibleBuffer;
use crate::command_buffer::state_cacher::StateCacher;
#[test]
fn vb_caching_single() {
let (device, queue) = gfx_dev_and_queue!();
const EMPTY: [i32; 0] = [];
let buf =
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), false, EMPTY)
.unwrap();
let mut cacher = StateCacher::new();
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf);
assert_eq!(bind_vb.compare(), Some(0..1));
}
for _ in 0..3 {
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf);
assert_eq!(bind_vb.compare(), None);
}
}
#[test]
fn vb_caching_invalidated() {
let (device, queue) = gfx_dev_and_queue!();
const EMPTY: [i32; 0] = [];
let buf =
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), false, EMPTY)
.unwrap();
let mut cacher = StateCacher::new();
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf);
assert_eq!(bind_vb.compare(), Some(0..1));
}
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf);
assert_eq!(bind_vb.compare(), None);
}
cacher.invalidate();
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf);
assert_eq!(bind_vb.compare(), Some(0..1));
}
}
#[test]
fn vb_caching_multi() {
let (device, queue) = gfx_dev_and_queue!();
const EMPTY: [i32; 0] = [];
let buf1 = CpuAccessibleBuffer::from_data(
device.clone(),
BufferUsage::vertex_buffer(),
false,
EMPTY,
)
.unwrap();
let buf2 = CpuAccessibleBuffer::from_data(
device.clone(),
BufferUsage::vertex_buffer(),
false,
EMPTY,
)
.unwrap();
let buf3 =
CpuAccessibleBuffer::from_data(device, BufferUsage::vertex_buffer(), false, EMPTY)
.unwrap();
let mut cacher = StateCacher::new();
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf1);
bind_vb.add(&buf2);
assert_eq!(bind_vb.compare(), Some(0..2));
}
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf1);
bind_vb.add(&buf2);
bind_vb.add(&buf3);
assert_eq!(bind_vb.compare(), Some(2..3));
}
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf1);
assert_eq!(bind_vb.compare(), None);
}
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf1);
bind_vb.add(&buf3);
assert_eq!(bind_vb.compare(), Some(1..2));
}
{
let mut bind_vb = cacher.bind_vertex_buffers();
bind_vb.add(&buf2);
bind_vb.add(&buf3);
assert_eq!(bind_vb.compare(), Some(0..1));
}
}
}

View File

@ -15,7 +15,6 @@ use super::ResourceFinalState;
use super::ResourceKey;
use super::ResourceLocation;
use super::SyncCommandBuffer;
use crate::buffer::BufferAccess;
use crate::command_buffer::pool::UnsafeCommandPoolAlloc;
use crate::command_buffer::sys::UnsafeCommandBufferBuilder;
use crate::command_buffer::sys::UnsafeCommandBufferBuilderPipelineBarrier;
@ -23,16 +22,16 @@ use crate::command_buffer::CommandBufferExecError;
use crate::command_buffer::CommandBufferLevel;
use crate::command_buffer::CommandBufferUsage;
use crate::command_buffer::ImageUninitializedSafe;
use crate::descriptor_set::DescriptorSet;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::image::ImageLayout;
use crate::pipeline::{ComputePipeline, GraphicsPipeline, PipelineBindPoint};
use crate::render_pass::FramebufferAbstract;
use crate::sync::AccessFlags;
use crate::sync::PipelineMemoryAccess;
use crate::sync::PipelineStages;
use crate::OomError;
use commands::CurrentState;
pub use commands::StencilState;
use fnv::FnvHashMap;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
@ -97,8 +96,8 @@ pub struct SyncCommandBufferBuilder {
ImageUninitializedSafe,
)>,
// State of bindings.
bindings: BindingState,
// Current binding/setting state.
current_state: CurrentState,
// `true` if the builder has been put in an inconsistent state. This happens when
// `append_command` throws an error, because some changes to the internal state have already
@ -168,12 +167,21 @@ impl SyncCommandBufferBuilder {
resources: FnvHashMap::default(),
buffers: Vec::new(),
images: Vec::new(),
bindings: Default::default(),
current_state: Default::default(),
is_poisoned: false,
is_secondary,
}
}
/// Resets the binding/setting state.
///
/// This must be called after any command that changes the state in an undefined way, e.g.
/// executing a secondary command buffer.
#[inline]
pub fn reset_state(&mut self) {
self.current_state = Default::default();
}
// Adds a command to be processed by the builder.
//
// The `resources` argument should contain each buffer or image used by the command.
@ -601,55 +609,6 @@ impl SyncCommandBufferBuilder {
barriers: self.barriers,
})
}
/// Returns the descriptor set currently bound to a given set number, or `None` if nothing has
/// been bound yet.
pub(crate) fn bound_descriptor_set(
&self,
pipeline_bind_point: PipelineBindPoint,
set_num: u32,
) -> Option<(&dyn DescriptorSet, &[u32])> {
self.bindings
.descriptor_sets
.get(&pipeline_bind_point)
.and_then(|sets| {
sets.get(&set_num)
.map(|cmd| cmd.bound_descriptor_set(set_num))
})
}
/// Returns the index buffer currently bound, or `None` if nothing has been bound yet.
pub(crate) fn bound_index_buffer(&self) -> Option<&dyn BufferAccess> {
self.bindings
.index_buffer
.as_ref()
.map(|cmd| cmd.bound_index_buffer())
}
/// Returns the compute pipeline currently bound, or `None` if nothing has been bound yet.
pub(crate) fn bound_pipeline_compute(&self) -> Option<&Arc<ComputePipeline>> {
self.bindings
.pipeline_compute
.as_ref()
.map(|cmd| cmd.bound_pipeline_compute())
}
/// Returns the graphics pipeline currently bound, or `None` if nothing has been bound yet.
pub(crate) fn bound_pipeline_graphics(&self) -> Option<&Arc<GraphicsPipeline>> {
self.bindings
.pipeline_graphics
.as_ref()
.map(|cmd| cmd.bound_pipeline_graphics())
}
/// Returns the vertex buffer currently bound to a given binding slot number, or `None` if
/// nothing has been bound yet.
pub(crate) fn bound_vertex_buffer(&self, binding_num: u32) -> Option<&dyn BufferAccess> {
self.bindings
.vertex_buffers
.get(&binding_num)
.map(|cmd| cmd.bound_vertex_buffer(binding_num))
}
}
unsafe impl DeviceOwned for SyncCommandBufferBuilder {
@ -735,14 +694,3 @@ struct ResourceState {
// Extra context of how the image will be used
image_uninitialized_safe: ImageUninitializedSafe,
}
/// Holds the index of the most recent command that binds a particular resource, or `None` if
/// nothing has been bound yet.
#[derive(Debug, Default)]
struct BindingState {
descriptor_sets: FnvHashMap<PipelineBindPoint, FnvHashMap<u32, Arc<dyn Command + Send + Sync>>>,
index_buffer: Option<Arc<dyn Command + Send + Sync>>,
pipeline_compute: Option<Arc<dyn Command + Send + Sync>>,
pipeline_graphics: Option<Arc<dyn Command + Send + Sync>>,
vertex_buffers: FnvHashMap<u32, Arc<dyn Command + Send + Sync>>,
}

View File

@ -55,14 +55,56 @@ use crate::sync::PipelineStages;
use crate::DeviceSize;
use crate::SafeDeref;
use crate::VulkanObject;
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::ffi::CStr;
use std::mem;
use std::ops::Range;
use std::ptr;
use std::sync::{Arc, Mutex};
/// Holds the current binding and setting state.
#[derive(Debug, Default)]
pub(super) struct CurrentState {
descriptor_sets: FnvHashMap<PipelineBindPoint, DescriptorSetState>,
index_buffer: Option<Arc<dyn Command + Send + Sync>>,
pipeline_compute: Option<Arc<dyn Command + Send + Sync>>,
pipeline_graphics: Option<Arc<dyn Command + Send + Sync>>,
vertex_buffers: FnvHashMap<u32, Arc<dyn Command + Send + Sync>>,
push_constants: Option<PushConstantState>,
blend_constants: Option<[f32; 4]>,
depth_bias: Option<(f32, f32, f32)>,
depth_bounds: Option<(f32, f32)>,
line_width: Option<f32>,
stencil_compare_mask: StencilState,
stencil_reference: StencilState,
stencil_write_mask: StencilState,
scissor: FnvHashMap<u32, Scissor>,
viewport: FnvHashMap<u32, Viewport>,
}
#[derive(Debug)]
struct DescriptorSetState {
descriptor_sets: FnvHashMap<u32, Arc<dyn Command + Send + Sync>>,
pipeline_layout: Arc<PipelineLayout>,
}
#[derive(Debug)]
struct PushConstantState {
pipeline_layout: Arc<PipelineLayout>,
}
/// Holds the current stencil state of a `SyncCommandBufferBuilder`.
#[derive(Clone, Copy, Debug, Default)]
pub struct StencilState {
pub front: Option<u32>,
pub back: Option<u32>,
}
impl SyncCommandBufferBuilder {
/// Calls `vkCmdBeginQuery` on the builder.
#[inline]
@ -206,13 +248,42 @@ impl SyncCommandBufferBuilder {
}
}
/// Returns the descriptor set currently bound to a given set number, or `None` if nothing has
/// been bound yet.
pub fn bound_descriptor_set(
&self,
pipeline_bind_point: PipelineBindPoint,
set_num: u32,
) -> Option<(&dyn DescriptorSet, &[u32])> {
self.current_state
.descriptor_sets
.get(&pipeline_bind_point)
.and_then(|state| {
state
.descriptor_sets
.get(&set_num)
.map(|cmd| cmd.bound_descriptor_set(set_num))
})
}
/// Returns the pipeline layout that describes all currently bound descriptor sets.
///
/// This can be the layout used to perform the last bind operation, but it can also be the
/// layout of an earlier bind if it was compatible with more recent binds.
#[inline]
pub fn bound_descriptor_sets_pipeline_layout(
&self,
pipeline_bind_point: PipelineBindPoint,
) -> Option<&Arc<PipelineLayout>> {
self.current_state
.descriptor_sets
.get(&pipeline_bind_point)
.map(|state| &state.pipeline_layout)
}
/// Calls `vkCmdBindIndexBuffer` on the builder.
#[inline]
pub unsafe fn bind_index_buffer<B>(
&mut self,
buffer: B,
index_ty: IndexType,
) -> Result<(), SyncCommandBufferBuilderError>
pub unsafe fn bind_index_buffer<B>(&mut self, buffer: B, index_ty: IndexType)
where
B: BufferAccess + Send + Sync + 'static,
{
@ -233,15 +304,21 @@ impl SyncCommandBufferBuilder {
out.bind_index_buffer(&self.buffer, self.index_ty);
}
fn bound_index_buffer(&self) -> &dyn BufferAccess {
&self.buffer
fn bound_index_buffer(&self) -> (&dyn BufferAccess, IndexType) {
(&self.buffer, self.index_ty)
}
}
self.append_command(Cmd { buffer, index_ty }, &[])?;
self.bindings.index_buffer = self.commands.last().cloned();
self.append_command(Cmd { buffer, index_ty }, &[]).unwrap();
self.current_state.index_buffer = self.commands.last().cloned();
}
Ok(())
/// Returns the index buffer currently bound, or `None` if nothing has been bound yet.
pub fn bound_index_buffer(&self) -> Option<(&dyn BufferAccess, IndexType)> {
self.current_state
.index_buffer
.as_ref()
.map(|cmd| cmd.bound_index_buffer())
}
/// Calls `vkCmdBindPipeline` on the builder with a compute pipeline.
@ -266,7 +343,15 @@ impl SyncCommandBufferBuilder {
}
self.append_command(Cmd { pipeline }, &[]).unwrap();
self.bindings.pipeline_compute = self.commands.last().cloned();
self.current_state.pipeline_compute = self.commands.last().cloned();
}
/// Returns the compute pipeline currently bound, or `None` if nothing has been bound yet.
pub fn bound_pipeline_compute(&self) -> Option<&Arc<ComputePipeline>> {
self.current_state
.pipeline_compute
.as_ref()
.map(|cmd| cmd.bound_pipeline_compute())
}
/// Calls `vkCmdBindPipeline` on the builder with a graphics pipeline.
@ -291,7 +376,15 @@ impl SyncCommandBufferBuilder {
}
self.append_command(Cmd { pipeline }, &[]).unwrap();
self.bindings.pipeline_graphics = self.commands.last().cloned();
self.current_state.pipeline_graphics = self.commands.last().cloned();
}
/// Returns the graphics pipeline currently bound, or `None` if nothing has been bound yet.
pub fn bound_pipeline_graphics(&self) -> Option<&Arc<GraphicsPipeline>> {
self.current_state
.pipeline_graphics
.as_ref()
.map(|cmd| cmd.bound_pipeline_graphics())
}
/// Starts the process of binding vertex buffers. Returns an intermediate struct which can be
@ -305,6 +398,15 @@ impl SyncCommandBufferBuilder {
}
}
/// Returns the vertex buffer currently bound to a given binding slot number, or `None` if
/// nothing has been bound yet.
pub fn bound_vertex_buffer(&self, binding_num: u32) -> Option<&dyn BufferAccess> {
self.current_state
.vertex_buffers
.get(&binding_num)
.map(|cmd| cmd.bound_vertex_buffer(binding_num))
}
/// Calls `vkCmdCopyImage` on the builder.
///
/// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid
@ -1214,7 +1316,7 @@ impl SyncCommandBufferBuilder {
}
let pipeline = self
.bindings
.current_state
.pipeline_compute
.as_ref()
.unwrap()
@ -1333,7 +1435,7 @@ impl SyncCommandBufferBuilder {
}
let pipeline = self
.bindings
.current_state
.pipeline_compute
.as_ref()
.unwrap()
@ -1478,7 +1580,7 @@ impl SyncCommandBufferBuilder {
}
let pipeline = self
.bindings
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
@ -1568,7 +1670,7 @@ impl SyncCommandBufferBuilder {
}
if num == 0 {
return self.index_buffer.bound_index_buffer();
return self.index_buffer.bound_index_buffer().0;
}
panic!()
@ -1639,7 +1741,7 @@ impl SyncCommandBufferBuilder {
}
let pipeline = self
.bindings
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
@ -1798,7 +1900,7 @@ impl SyncCommandBufferBuilder {
}
let pipeline = self
.bindings
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
@ -1885,7 +1987,7 @@ impl SyncCommandBufferBuilder {
}
if num == 0 {
return self.index_buffer.bound_index_buffer();
return self.index_buffer.bound_index_buffer().0;
} else if num == 1 {
return &self.indirect_buffer;
}
@ -1960,7 +2062,7 @@ impl SyncCommandBufferBuilder {
}
let pipeline = self
.bindings
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
@ -2169,7 +2271,7 @@ impl SyncCommandBufferBuilder {
self.append_command(
Cmd {
pipeline_layout,
pipeline_layout: pipeline_layout.clone(),
stages,
offset,
size,
@ -2178,6 +2280,24 @@ impl SyncCommandBufferBuilder {
&[],
)
.unwrap();
// TODO: Track the state of which push constant bytes are set, and potential invalidations.
// The Vulkan spec currently is unclear about this, so Vulkano can't do much more for the
// moment. See:
// https://github.com/KhronosGroup/Vulkan-Docs/issues/1485
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2711
self.current_state.push_constants = Some(PushConstantState { pipeline_layout });
}
/// Returns the pipeline layout that describes the current push constants.
///
/// This is the layout used to perform the last push constant write operation.
#[inline]
pub fn current_push_constants_pipeline_layout(&self) -> Option<&Arc<PipelineLayout>> {
self.current_state
.push_constants
.as_ref()
.map(|state| &state.pipeline_layout)
}
/// Calls `vkCmdResetEvent` on the builder.
@ -2247,6 +2367,13 @@ impl SyncCommandBufferBuilder {
}
self.append_command(Cmd { constants }, &[]).unwrap();
self.current_state.blend_constants = Some(constants);
}
/// Returns the current blend constants, or `None` if nothing has been set yet.
#[inline]
pub fn current_blend_constants(&self) -> Option<[f32; 4]> {
self.current_state.blend_constants
}
/// Calls `vkCmdSetDepthBias` on the builder.
@ -2277,6 +2404,13 @@ impl SyncCommandBufferBuilder {
&[],
)
.unwrap();
self.current_state.depth_bias = Some((constant_factor, clamp, slope_factor));
}
/// Returns the current depth bias settings, or `None` if nothing has been set yet.
#[inline]
pub fn current_depth_bias(&self) -> Option<(f32, f32, f32)> {
self.current_state.depth_bias
}
/// Calls `vkCmdSetDepthBounds` on the builder.
@ -2298,6 +2432,13 @@ impl SyncCommandBufferBuilder {
}
self.append_command(Cmd { min, max }, &[]).unwrap();
self.current_state.depth_bounds = Some((min, max));
}
/// Returns the current depth bounds settings, or `None` if nothing has been set yet.
#[inline]
pub fn current_depth_bounds(&self) -> Option<(f32, f32)> {
self.current_state.depth_bounds
}
/// Calls `vkCmdSetEvent` on the builder.
@ -2339,13 +2480,20 @@ impl SyncCommandBufferBuilder {
}
self.append_command(Cmd { line_width }, &[]).unwrap();
self.current_state.line_width = Some(line_width);
}
/// Returns the current line width, or `None` if nothing has been set yet.
#[inline]
pub fn current_line_width(&self) -> Option<f32> {
self.current_state.line_width
}
/// Calls `vkCmdSetStencilCompareMask` on the builder.
#[inline]
pub unsafe fn set_stencil_compare_mask(&mut self, face_mask: StencilFaces, compare_mask: u32) {
pub unsafe fn set_stencil_compare_mask(&mut self, faces: StencilFaces, compare_mask: u32) {
struct Cmd {
face_mask: StencilFaces,
faces: StencilFaces,
compare_mask: u32,
}
@ -2355,25 +2503,41 @@ impl SyncCommandBufferBuilder {
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_stencil_compare_mask(self.face_mask, self.compare_mask);
out.set_stencil_compare_mask(self.faces, self.compare_mask);
}
}
self.append_command(
Cmd {
face_mask,
faces,
compare_mask,
},
&[],
)
.unwrap();
let faces = ash::vk::StencilFaceFlags::from(faces);
if faces.intersects(ash::vk::StencilFaceFlags::FRONT) {
self.current_state.stencil_compare_mask.front = Some(compare_mask);
}
if faces.intersects(ash::vk::StencilFaceFlags::BACK) {
self.current_state.stencil_compare_mask.back = Some(compare_mask);
}
}
/// Returns the current stencil compare masks.
#[inline]
pub fn current_stencil_compare_mask(&self) -> StencilState {
self.current_state.stencil_compare_mask
}
/// Calls `vkCmdSetStencilReference` on the builder.
#[inline]
pub unsafe fn set_stencil_reference(&mut self, face_mask: StencilFaces, reference: u32) {
pub unsafe fn set_stencil_reference(&mut self, faces: StencilFaces, reference: u32) {
struct Cmd {
face_mask: StencilFaces,
faces: StencilFaces,
reference: u32,
}
@ -2383,25 +2547,34 @@ impl SyncCommandBufferBuilder {
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_stencil_reference(self.face_mask, self.reference);
out.set_stencil_reference(self.faces, self.reference);
}
}
self.append_command(
Cmd {
face_mask,
reference,
},
&[],
)
.unwrap();
self.append_command(Cmd { faces, reference }, &[]).unwrap();
let faces = ash::vk::StencilFaceFlags::from(faces);
if faces.intersects(ash::vk::StencilFaceFlags::FRONT) {
self.current_state.stencil_reference.front = Some(reference);
}
if faces.intersects(ash::vk::StencilFaceFlags::BACK) {
self.current_state.stencil_reference.back = Some(reference);
}
}
/// Returns the current stencil references.
#[inline]
pub fn current_stencil_reference(&self) -> StencilState {
self.current_state.stencil_reference
}
/// Calls `vkCmdSetStencilWriteMask` on the builder.
#[inline]
pub unsafe fn set_stencil_write_mask(&mut self, face_mask: StencilFaces, write_mask: u32) {
pub unsafe fn set_stencil_write_mask(&mut self, faces: StencilFaces, write_mask: u32) {
struct Cmd {
face_mask: StencilFaces,
faces: StencilFaces,
write_mask: u32,
}
@ -2411,18 +2584,27 @@ impl SyncCommandBufferBuilder {
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_stencil_write_mask(self.face_mask, self.write_mask);
out.set_stencil_write_mask(self.faces, self.write_mask);
}
}
self.append_command(
Cmd {
face_mask,
write_mask,
},
&[],
)
.unwrap();
self.append_command(Cmd { faces, write_mask }, &[]).unwrap();
let faces = ash::vk::StencilFaceFlags::from(faces);
if faces.intersects(ash::vk::StencilFaceFlags::FRONT) {
self.current_state.stencil_write_mask.front = Some(write_mask);
}
if faces.intersects(ash::vk::StencilFaceFlags::BACK) {
self.current_state.stencil_write_mask.back = Some(write_mask);
}
}
/// Returns the current stencil write masks.
#[inline]
pub fn current_stencil_write_mask(&self) -> StencilState {
self.current_state.stencil_write_mask
}
/// Calls `vkCmdSetScissor` on the builder.
@ -2431,56 +2613,60 @@ impl SyncCommandBufferBuilder {
#[inline]
pub unsafe fn set_scissor<I>(&mut self, first_scissor: u32, scissors: I)
where
I: IntoIterator<Item = Scissor> + Send + Sync + 'static,
I: IntoIterator<Item = Scissor>,
{
struct Cmd<I> {
struct Cmd {
first_scissor: u32,
scissors: Mutex<Option<I>>,
scissors: Mutex<SmallVec<[Scissor; 2]>>,
}
impl<I> Command for Cmd<I>
where
I: IntoIterator<Item = Scissor>,
{
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetScissor"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_scissor(
self.first_scissor,
self.scissors.lock().unwrap().take().unwrap(),
);
out.set_scissor(self.first_scissor, self.scissors.lock().unwrap().drain(..));
}
}
let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect();
for (num, scissor) in scissors.iter().enumerate() {
let num = num as u32 + first_scissor;
self.current_state.scissor.insert(num, scissor.clone());
}
self.append_command(
Cmd {
first_scissor,
scissors: Mutex::new(Some(scissors)),
scissors: Mutex::new(scissors),
},
&[],
)
.unwrap();
}
/// Returns the current scissor for a given viewport slot, or `None` if nothing has been set yet.
#[inline]
pub fn current_scissor(&self, num: u32) -> Option<&Scissor> {
self.current_state.scissor.get(&num)
}
/// Calls `vkCmdSetViewport` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn set_viewport<I>(&mut self, first_viewport: u32, viewports: I)
where
I: IntoIterator<Item = Viewport> + Send + Sync + 'static,
I: IntoIterator<Item = Viewport>,
{
struct Cmd<I> {
struct Cmd {
first_viewport: u32,
viewports: Mutex<Option<I>>,
viewports: Mutex<SmallVec<[Viewport; 2]>>,
}
impl<I> Command for Cmd<I>
where
I: IntoIterator<Item = Viewport>,
{
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdSetViewport"
}
@ -2488,21 +2674,34 @@ impl SyncCommandBufferBuilder {
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.set_viewport(
self.first_viewport,
self.viewports.lock().unwrap().take().unwrap(),
self.viewports.lock().unwrap().drain(..),
);
}
}
let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect();
for (num, viewport) in viewports.iter().enumerate() {
let num = num as u32 + first_viewport;
self.current_state.viewport.insert(num, viewport.clone());
}
self.append_command(
Cmd {
first_viewport,
viewports: Mutex::new(Some(viewports)),
viewports: Mutex::new(viewports),
},
&[],
)
.unwrap();
}
/// Returns the current viewport for a given viewport slot, or `None` if nothing has been set yet.
#[inline]
pub fn current_viewport(&self, num: u32) -> Option<&Viewport> {
self.current_state.viewport.get(&num)
}
/// Calls `vkCmdUpdateBuffer` on the builder.
#[inline]
pub unsafe fn update_buffer<B, D, Dd>(&mut self, buffer: B, data: Dd)
@ -2614,12 +2813,14 @@ impl SyncCommandBufferBuilder {
pipeline_layout: &PipelineLayout,
pipeline_bind_point: PipelineBindPoint,
) -> SmallVec<[Arc<dyn Command + Send + Sync>; 12]> {
let descriptor_sets: SmallVec<[Arc<dyn Command + Send + Sync>; 12]> = (0..pipeline_layout
.descriptor_set_layouts()
.len()
as u32)
.map(|set_num| self.bindings.descriptor_sets[&pipeline_bind_point][&set_num].clone())
.collect();
let descriptor_sets: SmallVec<[Arc<dyn Command + Send + Sync>; 12]> =
(0..pipeline_layout.descriptor_set_layouts().len() as u32)
.map(|set_num| {
self.current_state.descriptor_sets[&pipeline_bind_point].descriptor_sets
[&set_num]
.clone()
})
.collect();
for ds in descriptor_sets
.iter()
@ -2629,7 +2830,7 @@ impl SyncCommandBufferBuilder {
for buf_num in 0..ds.num_buffers() {
let desc = ds
.layout()
.descriptor(ds.buffer(buf_num).unwrap().1 as usize)
.descriptor(ds.buffer(buf_num).unwrap().1)
.unwrap();
let exclusive = desc.mutable;
let (stages, access) = desc.pipeline_stages_and_access();
@ -2649,7 +2850,7 @@ impl SyncCommandBufferBuilder {
}
for img_num in 0..ds.num_images() {
let (image_view, desc_num) = ds.image(img_num).unwrap();
let desc = ds.layout().descriptor(desc_num as usize).unwrap();
let desc = ds.layout().descriptor(desc_num).unwrap();
let exclusive = desc.mutable;
let (stages, access) = desc.pipeline_stages_and_access();
let mut ignore_me_hack = false;
@ -2713,7 +2914,7 @@ impl SyncCommandBufferBuilder {
.map(|(binding_num, _)| {
(
binding_num,
self.bindings.vertex_buffers[&binding_num].clone(),
self.current_state.vertex_buffers[&binding_num].clone(),
)
})
.collect();
@ -2755,7 +2956,7 @@ impl SyncCommandBufferBuilder {
)>,
)>,
) -> Arc<dyn Command + Send + Sync> {
let index_buffer = self.bindings.index_buffer.as_ref().unwrap().clone();
let index_buffer = self.current_state.index_buffer.as_ref().unwrap().clone();
resources.push((
KeyTy::Buffer,
@ -2834,17 +3035,17 @@ impl<'b> SyncCommandBufferBuilderBindDescriptorSets<'b> {
self,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: Arc<PipelineLayout>,
first_binding: u32,
) -> Result<(), SyncCommandBufferBuilderError> {
first_set: u32,
) {
if self.descriptor_sets.is_empty() {
return Ok(());
return;
}
struct Cmd {
descriptor_sets: SmallVec<[DescriptorSetWithOffsets; 12]>,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: Arc<PipelineLayout>,
first_binding: u32,
first_set: u32,
}
impl Command for Cmd {
@ -2863,43 +3064,84 @@ impl<'b> SyncCommandBufferBuilderBindDescriptorSets<'b> {
out.bind_descriptor_sets(
self.pipeline_bind_point,
&self.pipeline_layout,
self.first_binding,
self.first_set,
descriptor_sets,
dynamic_offsets,
);
}
fn bound_descriptor_set(&self, set_num: u32) -> (&dyn DescriptorSet, &[u32]) {
let index = set_num.checked_sub(self.first_binding).unwrap() as usize;
let index = set_num.checked_sub(self.first_set).unwrap() as usize;
self.descriptor_sets[index].as_ref()
}
}
let num_descriptor_sets = self.descriptor_sets.len() as u32;
self.builder.append_command(
Cmd {
descriptor_sets: self.descriptor_sets,
pipeline_bind_point,
pipeline_layout,
first_binding,
},
&[],
)?;
self.builder
.append_command(
Cmd {
descriptor_sets: self.descriptor_sets,
pipeline_bind_point,
pipeline_layout: pipeline_layout.clone(),
first_set,
},
&[],
)
.unwrap();
let cmd = self.builder.commands.last().unwrap();
let sets = self
let state = match self
.builder
.bindings
.current_state
.descriptor_sets
.entry(pipeline_bind_point)
.or_default();
sets.retain(|&set_num, _| set_num < first_binding); // Remove all descriptor sets with a higher number
{
Entry::Vacant(entry) => entry.insert(DescriptorSetState {
descriptor_sets: Default::default(),
pipeline_layout,
}),
Entry::Occupied(entry) => {
let state = entry.into_mut();
let invalidate_from = if state.pipeline_layout.internal_object()
== pipeline_layout.internal_object()
{
// If we're still using the exact same layout, then of course it's compatible.
None
} else if state.pipeline_layout.push_constant_ranges()
!= pipeline_layout.push_constant_ranges()
{
// If the push constant ranges don't match,
// all bound descriptor sets are disturbed.
Some(0)
} else {
// Find the first descriptor set layout in the current pipeline layout that
// isn't compatible with the corresponding set in the new pipeline layout.
// If an incompatible set was found, all bound sets from that slot onwards will
// be disturbed.
let current_layouts = state.pipeline_layout.descriptor_set_layouts();
let new_layouts = pipeline_layout.descriptor_set_layouts();
(0..first_set + num_descriptor_sets).find(|&num| {
let num = num as usize;
!current_layouts[num].is_compatible_with(&new_layouts[num])
})
};
// Remove disturbed sets and set new pipeline layout.
if let Some(invalidate_from) = invalidate_from {
state
.descriptor_sets
.retain(|&num, _| num < invalidate_from);
state.pipeline_layout = pipeline_layout;
}
state
}
};
for i in 0..num_descriptor_sets {
sets.insert(first_binding + i, cmd.clone());
state.descriptor_sets.insert(first_set + i, cmd.clone());
}
Ok(())
}
}
@ -2922,9 +3164,9 @@ impl<'a> SyncCommandBufferBuilderBindVertexBuffer<'a> {
}
#[inline]
pub unsafe fn submit(self, first_binding: u32) -> Result<(), SyncCommandBufferBuilderError> {
pub unsafe fn submit(self, first_set: u32) {
struct Cmd {
first_binding: u32,
first_set: u32,
inner: Mutex<Option<UnsafeCommandBufferBuilderBindVertexBuffer>>,
buffers: SmallVec<[Box<dyn BufferAccess + Send + Sync>; 4]>,
}
@ -2935,37 +3177,34 @@ impl<'a> SyncCommandBufferBuilderBindVertexBuffer<'a> {
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.bind_vertex_buffers(
self.first_binding,
self.inner.lock().unwrap().take().unwrap(),
);
out.bind_vertex_buffers(self.first_set, self.inner.lock().unwrap().take().unwrap());
}
fn bound_vertex_buffer(&self, binding_num: u32) -> &dyn BufferAccess {
let index = binding_num.checked_sub(self.first_binding).unwrap() as usize;
let index = binding_num.checked_sub(self.first_set).unwrap() as usize;
&self.buffers[index]
}
}
let num_buffers = self.buffers.len() as u32;
self.builder.append_command(
Cmd {
first_binding,
inner: Mutex::new(Some(self.inner)),
buffers: self.buffers,
},
&[],
)?;
self.builder
.append_command(
Cmd {
first_set,
inner: Mutex::new(Some(self.inner)),
buffers: self.buffers,
},
&[],
)
.unwrap();
let cmd = self.builder.commands.last().unwrap();
for i in 0..num_buffers {
self.builder
.bindings
.current_state
.vertex_buffers
.insert(first_binding + i, cmd.clone());
.insert(first_set + i, cmd.clone());
}
Ok(())
}
}

View File

@ -64,6 +64,7 @@
//! queue. If not possible, the queue will be entirely flushed and the command added to a fresh new
//! queue with a fresh new barrier prototype.
pub use self::builder::StencilState;
pub use self::builder::SyncCommandBufferBuilder;
pub use self::builder::SyncCommandBufferBuilderBindDescriptorSets;
pub use self::builder::SyncCommandBufferBuilderBindVertexBuffer;
@ -80,6 +81,7 @@ use crate::device::DeviceOwned;
use crate::device::Queue;
use crate::image::ImageAccess;
use crate::image::ImageLayout;
use crate::pipeline::input_assembly::IndexType;
use crate::pipeline::{ComputePipeline, GraphicsPipeline};
use crate::sync::AccessCheckError;
use crate::sync::AccessError;
@ -492,7 +494,7 @@ trait Command {
panic!()
}
fn bound_index_buffer(&self) -> &dyn BufferAccess {
fn bound_index_buffer(&self) -> (&dyn BufferAccess, IndexType) {
panic!()
}
@ -685,7 +687,7 @@ mod tests {
CpuAccessibleBuffer::from_data(device, BufferUsage::all(), false, 0u32).unwrap();
let mut buf_builder = sync.bind_vertex_buffers();
buf_builder.add(buf);
buf_builder.submit(1).unwrap();
buf_builder.submit(1);
assert!(sync.bound_vertex_buffer(0).is_none());
assert!(sync.bound_vertex_buffer(1).is_some());
@ -723,8 +725,8 @@ mod tests {
.unwrap(),
);
let set = Arc::new(
PersistentDescriptorSet::start(set_layout)
.add_sampler(Sampler::simple_repeat_linear(device))
PersistentDescriptorSet::start(set_layout.clone())
.add_sampler(Sampler::simple_repeat_linear(device.clone()))
.unwrap()
.build()
.unwrap(),
@ -732,9 +734,7 @@ mod tests {
let mut set_builder = sync.bind_descriptor_sets();
set_builder.add(set.clone());
set_builder
.submit(PipelineBindPoint::Graphics, pipeline_layout.clone(), 1)
.unwrap();
set_builder.submit(PipelineBindPoint::Graphics, pipeline_layout.clone(), 1);
assert!(sync
.bound_descriptor_set(PipelineBindPoint::Compute, 0)
@ -751,16 +751,44 @@ mod tests {
let mut set_builder = sync.bind_descriptor_sets();
set_builder.add(set);
set_builder
.submit(PipelineBindPoint::Graphics, pipeline_layout, 0)
.unwrap();
set_builder.submit(PipelineBindPoint::Graphics, pipeline_layout, 0);
assert!(sync
.bound_descriptor_set(PipelineBindPoint::Graphics, 0)
.is_some());
assert!(sync
.bound_descriptor_set(PipelineBindPoint::Graphics, 1)
.is_some());
let pipeline_layout = Arc::new(
PipelineLayout::new(
device.clone(),
[
Arc::new(DescriptorSetLayout::new(device.clone(), []).unwrap()),
set_layout.clone(),
],
[],
)
.unwrap(),
);
let set = Arc::new(
PersistentDescriptorSet::start(set_layout.clone())
.add_sampler(Sampler::simple_repeat_linear(device.clone()))
.unwrap()
.build()
.unwrap(),
);
let mut set_builder = sync.bind_descriptor_sets();
set_builder.add(set);
set_builder.submit(PipelineBindPoint::Graphics, pipeline_layout, 1);
assert!(sync
.bound_descriptor_set(PipelineBindPoint::Graphics, 0)
.is_none());
assert!(sync
.bound_descriptor_set(PipelineBindPoint::Graphics, 1)
.is_some());
}
}
}

View File

@ -318,7 +318,7 @@ impl UnsafeCommandBufferBuilder {
&mut self,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: &PipelineLayout,
first_binding: u32,
first_set: u32,
sets: S,
dynamic_offsets: I,
) where
@ -336,14 +336,14 @@ impl UnsafeCommandBufferBuilder {
let num_bindings = sets.len() as u32;
debug_assert!(
first_binding + num_bindings <= pipeline_layout.descriptor_set_layouts().len() as u32
first_set + num_bindings <= pipeline_layout.descriptor_set_layouts().len() as u32
);
fns.v1_0.cmd_bind_descriptor_sets(
cmd,
pipeline_bind_point.into(),
pipeline_layout.internal_object(),
first_binding,
first_set,
num_bindings,
sets.as_ptr(),
dynamic_offsets.len() as u32,
@ -1400,11 +1400,7 @@ impl UnsafeCommandBufferBuilder {
|| self.device().enabled_features().multi_viewport
);
debug_assert!({
let max = self
.device()
.physical_device()
.properties()
.max_viewports;
let max = self.device().physical_device().properties().max_viewports;
first_scissor + scissors.len() as u32 <= max
});
@ -1435,11 +1431,7 @@ impl UnsafeCommandBufferBuilder {
|| self.device().enabled_features().multi_viewport
);
debug_assert!({
let max = self
.device()
.physical_device()
.properties()
.max_viewports;
let max = self.device().physical_device().properties().max_viewports;
first_viewport + viewports.len() as u32 <= max
});

View File

@ -7,26 +7,46 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::command_buffer::synced::SyncCommandBufferBuilder;
use crate::descriptor_set::layout::DescriptorSetCompatibilityError;
use crate::descriptor_set::DescriptorSetWithOffsets;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::PipelineBindPoint;
use crate::VulkanObject;
use std::error;
use std::fmt;
/// Checks whether descriptor sets are compatible with the pipeline.
pub fn check_descriptor_sets_validity(
pub(in super::super) fn check_descriptor_sets_validity(
builder: &SyncCommandBufferBuilder,
pipeline_layout: &PipelineLayout,
descriptor_sets: &[DescriptorSetWithOffsets],
pipeline_bind_point: PipelineBindPoint,
) -> Result<(), CheckDescriptorSetsValidityError> {
for (set_index, pipeline_set) in pipeline_layout.descriptor_set_layouts().iter().enumerate() {
let set_num = set_index as u32;
if pipeline_layout.descriptor_set_layouts().is_empty() {
return Ok(());
}
let descriptor_set = match descriptor_sets.get(set_index) {
let bindings_pipeline_layout = match builder
.bound_descriptor_sets_pipeline_layout(pipeline_bind_point)
{
Some(x) => x,
None => return Err(CheckDescriptorSetsValidityError::MissingDescriptorSet { set_num: 0 }),
};
if bindings_pipeline_layout.internal_object() != pipeline_layout.internal_object()
&& bindings_pipeline_layout.push_constant_ranges() != pipeline_layout.push_constant_ranges()
{
return Err(CheckDescriptorSetsValidityError::IncompatiblePushConstants);
}
for (set_num, pipeline_set) in pipeline_layout.descriptor_set_layouts().iter().enumerate() {
let set_num = set_num as u32;
let descriptor_set = match builder.bound_descriptor_set(pipeline_bind_point, set_num) {
Some(s) => s,
None => return Err(CheckDescriptorSetsValidityError::MissingDescriptorSet { set_num }),
};
match pipeline_set.ensure_compatible_with_bind(descriptor_set.as_ref().0.layout()) {
match pipeline_set.ensure_compatible_with_bind(descriptor_set.0.layout()) {
Ok(_) => (),
Err(error) => {
return Err(
@ -51,6 +71,7 @@ pub enum CheckDescriptorSetsValidityError {
/// The index of the set of the descriptor.
set_num: u32,
},
IncompatiblePushConstants,
}
impl error::Error for CheckDescriptorSetsValidityError {
@ -73,6 +94,9 @@ impl fmt::Display for CheckDescriptorSetsValidityError {
Self::IncompatibleDescriptorSet { set_num, .. } => {
write!(fmt, "compatibility error in descriptor set {}", set_num)
}
Self::IncompatiblePushConstants => {
write!(fmt, "the push constant ranges in the bound pipeline do not match the ranges of layout used to bind the descriptor sets")
}
}
}
}

View File

@ -7,93 +7,73 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::command_buffer::DynamicState;
use crate::command_buffer::synced::SyncCommandBufferBuilder;
use crate::pipeline::GraphicsPipeline;
use std::error;
use std::fmt;
/// Checks whether states that are about to be set are correct.
pub fn check_dynamic_state_validity(
pub(in super::super) fn check_dynamic_state_validity(
builder: &SyncCommandBufferBuilder,
pipeline: &GraphicsPipeline,
state: &DynamicState,
) -> Result<(), CheckDynamicStateValidityError> {
let device = pipeline.device();
if pipeline.has_dynamic_blend_constants() {
if builder.current_blend_constants().is_none() {
return Err(CheckDynamicStateValidityError::BlendConstantsNotSet);
}
}
if pipeline.has_dynamic_depth_bounds() {
if builder.current_blend_constants().is_none() {
return Err(CheckDynamicStateValidityError::BlendConstantsNotSet);
}
}
if pipeline.has_dynamic_line_width() {
if let Some(value) = state.line_width {
if value != 1.0 && !pipeline.device().enabled_features().wide_lines {
return Err(CheckDynamicStateValidityError::LineWidthMissingExtension);
}
} else {
return Err(CheckDynamicStateValidityError::LineWidthMissing);
}
} else {
if state.line_width.is_some() {
return Err(CheckDynamicStateValidityError::LineWidthNotDynamic);
if builder.current_line_width().is_none() {
return Err(CheckDynamicStateValidityError::LineWidthNotSet);
}
}
if pipeline.has_dynamic_viewports() {
if let Some(ref viewports) = state.viewports {
if viewports.len() != pipeline.num_viewports() as usize {
return Err(CheckDynamicStateValidityError::ViewportsCountMismatch {
expected: pipeline.num_viewports() as usize,
obtained: viewports.len(),
});
if pipeline.has_dynamic_scissor() {
for num in 0..pipeline.num_viewports() {
if builder.current_scissor(num).is_none() {
return Err(CheckDynamicStateValidityError::ScissorNotSet { num });
}
} else {
return Err(CheckDynamicStateValidityError::ViewportsMissing);
}
} else {
if state.viewports.is_some() {
return Err(CheckDynamicStateValidityError::ViewportsNotDynamic);
}
}
if pipeline.has_dynamic_scissors() {
if let Some(ref scissors) = state.scissors {
if scissors.len() != pipeline.num_viewports() as usize {
return Err(CheckDynamicStateValidityError::ScissorsCountMismatch {
expected: pipeline.num_viewports() as usize,
obtained: scissors.len(),
});
}
} else {
return Err(CheckDynamicStateValidityError::ScissorsMissing);
}
} else {
if state.scissors.is_some() {
return Err(CheckDynamicStateValidityError::ScissorsNotDynamic);
}
}
if pipeline.has_dynamic_stencil_compare_mask() {
if let None = state.compare_mask {
return Err(CheckDynamicStateValidityError::CompareMaskMissing);
}
} else {
if state.compare_mask.is_some() {
return Err(CheckDynamicStateValidityError::CompareMaskNotDynamic);
}
}
let state = builder.current_stencil_compare_mask();
if pipeline.has_dynamic_stencil_write_mask() {
if let None = state.write_mask {
return Err(CheckDynamicStateValidityError::WriteMaskMissing);
}
} else {
if state.write_mask.is_some() {
return Err(CheckDynamicStateValidityError::WriteMaskNotDynamic);
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilCompareMaskNotSet);
}
}
if pipeline.has_dynamic_stencil_reference() {
if let None = state.reference {
return Err(CheckDynamicStateValidityError::ReferenceMissing);
let state = builder.current_stencil_reference();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilReferenceNotSet);
}
} else {
if state.reference.is_some() {
return Err(CheckDynamicStateValidityError::ReferenceNotDynamic);
}
if pipeline.has_dynamic_stencil_write_mask() {
let state = builder.current_stencil_write_mask();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilWriteMaskNotSet);
}
}
if pipeline.has_dynamic_viewport() {
for num in 0..pipeline.num_viewports() {
if builder.current_viewport(num).is_none() {
return Err(CheckDynamicStateValidityError::ViewportNotSet { num });
}
}
}
@ -103,47 +83,22 @@ pub fn check_dynamic_state_validity(
/// Error that can happen when validating dynamic states.
#[derive(Debug, Copy, Clone)]
pub enum CheckDynamicStateValidityError {
/// Passed a dynamic line width, while the pipeline doesn't have line width set as dynamic.
LineWidthNotDynamic,
/// The pipeline has a dynamic line width, but no line width value was passed.
LineWidthMissing,
/// The `wide_lines` extension must be enabled in order to use line width values different
/// from 1.0.
LineWidthMissingExtension,
/// Passed dynamic viewports, while the pipeline doesn't have viewports set as dynamic.
ViewportsNotDynamic,
/// The pipeline has dynamic viewports, but no viewports were passed.
ViewportsMissing,
/// The number of dynamic viewports doesn't match the expected number of viewports.
ViewportsCountMismatch {
/// Expected number of viewports.
expected: usize,
/// Number of viewports that were passed.
obtained: usize,
},
/// Passed dynamic scissors, while the pipeline doesn't have scissors set as dynamic.
ScissorsNotDynamic,
/// The pipeline has dynamic scissors, but no scissors were passed.
ScissorsMissing,
/// The number of dynamic scissors doesn't match the expected number of scissors.
ScissorsCountMismatch {
/// Expected number of scissors.
expected: usize,
/// Number of scissors that were passed.
obtained: usize,
},
/// Passed dynamic compare mask, while the pipeline doesn't have the compare mask set as dynamic.
CompareMaskNotDynamic,
/// The pipeline has dynamic compare mask, but no compare mask was passed.
CompareMaskMissing,
/// Passed dynamic write mask, while the pipeline doesn't have the write mask set as dynamic.
WriteMaskNotDynamic,
/// The pipeline has dynamic write mask, but no write mask was passed.
WriteMaskMissing,
/// Passed dynamic reference, while the pipeline doesn't have the reference set as dynamic.
ReferenceNotDynamic,
/// The pipeline has dynamic reference, but no reference was passed.
ReferenceMissing,
/// The pipeline has dynamic blend constants, but no blend constants value was set.
BlendConstantsNotSet,
/// The pipeline has dynamic depth bounds, but no depth bounds value was set.
DepthBoundsNotSet,
/// The pipeline has a dynamic line width, but no line width value was set.
LineWidthNotSet,
/// The pipeline has a dynamic scissor, but the scissor for a slot used by the pipeline was not set.
ScissorNotSet { num: u32 },
/// The pipeline has dynamic stencil compare mask, but no compare mask was set for the front or back face.
StencilCompareMaskNotSet,
/// The pipeline has dynamic stencil reference, but no reference was set for the front or back face.
StencilReferenceNotSet,
/// The pipeline has dynamic stencil write mask, but no write mask was set for the front or back face.
StencilWriteMaskNotSet,
/// The pipeline has a dynamic viewport, but the viewport for a slot used by the pipeline was not set.
ViewportNotSet { num: u32 },
}
impl error::Error for CheckDynamicStateValidityError {}
@ -155,53 +110,29 @@ impl fmt::Display for CheckDynamicStateValidityError {
fmt,
"{}",
match *self {
CheckDynamicStateValidityError::LineWidthNotDynamic => {
"passed a dynamic line width, while the pipeline doesn't have line width set as \
dynamic"
CheckDynamicStateValidityError::BlendConstantsNotSet => {
"the pipeline has dynamic blend constants, but no blend constants value was set"
}
CheckDynamicStateValidityError::LineWidthMissing => {
"the pipeline has a dynamic line width, but no line width value was passed"
CheckDynamicStateValidityError::DepthBoundsNotSet => {
"the pipeline has dynamic depth bounds, but no depth bounds value was set"
}
CheckDynamicStateValidityError::LineWidthMissingExtension => {
"the `wide_lines` extension must be enabled in order to use line width values \
different from 1.0"
CheckDynamicStateValidityError::LineWidthNotSet => {
"the pipeline has a dynamic line width, but no line width value was set"
}
CheckDynamicStateValidityError::ViewportsNotDynamic => {
"passed dynamic viewports, while the pipeline doesn't have viewports set as \
dynamic"
CheckDynamicStateValidityError::ScissorNotSet { .. } => {
"The pipeline has a dynamic scissor, but the scissor for a slot used by the pipeline was not set"
}
CheckDynamicStateValidityError::ViewportsMissing => {
"the pipeline has dynamic viewports, but no viewports were passed"
CheckDynamicStateValidityError::StencilCompareMaskNotSet => {
"the pipeline has dynamic stencil compare mask, but no compare mask was set for the front or back face"
}
CheckDynamicStateValidityError::ViewportsCountMismatch { .. } => {
"the number of dynamic viewports doesn't match the expected number of viewports"
CheckDynamicStateValidityError::StencilReferenceNotSet => {
"the pipeline has dynamic stencil reference, but no reference was set for the front or back face"
}
CheckDynamicStateValidityError::ScissorsNotDynamic => {
"passed dynamic scissors, while the pipeline doesn't have scissors set as dynamic"
CheckDynamicStateValidityError::StencilWriteMaskNotSet => {
"the pipeline has dynamic stencil write mask, but no write mask was set for the front or back face"
}
CheckDynamicStateValidityError::ScissorsMissing => {
"the pipeline has dynamic scissors, but no scissors were passed"
}
CheckDynamicStateValidityError::ScissorsCountMismatch { .. } => {
"the number of dynamic scissors doesn't match the expected number of scissors"
}
CheckDynamicStateValidityError::CompareMaskNotDynamic => {
"passed dynamic compare mask, while the pipeline doesn't have compare mask set as dynamic"
}
CheckDynamicStateValidityError::CompareMaskMissing => {
"the pipeline has dynamic compare mask, but no compare mask was passed"
}
CheckDynamicStateValidityError::WriteMaskNotDynamic => {
"passed dynamic write mask, while the pipeline doesn't have write mask set as dynamic"
}
CheckDynamicStateValidityError::WriteMaskMissing => {
"the pipeline has dynamic write mask, but no write mask was passed"
}
CheckDynamicStateValidityError::ReferenceNotDynamic => {
"passed dynamic Reference, while the pipeline doesn't have reference set as dynamic"
}
CheckDynamicStateValidityError::ReferenceMissing => {
"the pipeline has dynamic reference, but no reference was passed"
CheckDynamicStateValidityError::ViewportNotSet { .. } => {
"the pipeline has a dynamic viewport, but the viewport for a slot used by the pipeline was not set"
}
}
)

View File

@ -7,47 +7,52 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::command_buffer::synced::SyncCommandBufferBuilder;
use std::error;
use std::fmt;
use crate::buffer::BufferAccess;
use crate::buffer::TypedBufferAccess;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::pipeline::input_assembly::Index;
use crate::VulkanObject;
/// Checks whether an index buffer can be bound.
///
/// # Panic
///
/// - Panics if the buffer was not created with `device`.
///
pub fn check_index_buffer<B, I>(device: &Device, buffer: &B) -> Result<(), CheckIndexBufferError>
where
B: ?Sized + BufferAccess + TypedBufferAccess<Content = [I]>,
I: Index,
{
assert_eq!(
buffer.inner().buffer.device().internal_object(),
device.internal_object()
);
pub(in super::super) fn check_index_buffer(
builder: &SyncCommandBufferBuilder,
indices: Option<(u32, u32)>,
) -> Result<(), CheckIndexBufferError> {
let (index_buffer, index_type) = match builder.bound_index_buffer() {
Some(x) => x,
None => return Err(CheckIndexBufferError::BufferNotBound),
};
if !buffer.inner().buffer.usage().index_buffer {
return Err(CheckIndexBufferError::BufferMissingUsage);
if let Some((first_index, index_count)) = indices {
let max_index_count = (index_buffer.size() / index_type.size()) as u32;
if first_index + index_count > max_index_count {
return Err(CheckIndexBufferError::TooManyIndices {
index_count,
max_index_count,
}
.into());
}
}
// TODO: The sum of offset and the address of the range of VkDeviceMemory object that is
// backing buffer, must be a multiple of the type indicated by indexType
// TODO: full_draw_index_uint32 feature
Ok(())
}
/// Error that can happen when checking whether binding an index buffer is valid.
#[derive(Debug, Copy, Clone)]
pub enum CheckIndexBufferError {
/// No index buffer was bound.
BufferNotBound,
/// A draw command requested too many indices.
TooManyIndices {
/// The used amount of indices.
index_count: u32,
/// The allowed amount of indices.
max_index_count: u32,
},
/// The "index buffer" usage must be enabled on the index buffer.
BufferMissingUsage,
/// The data or size must be 4-bytes aligned.
@ -65,6 +70,12 @@ impl fmt::Display for CheckIndexBufferError {
fmt,
"{}",
match *self {
CheckIndexBufferError::BufferNotBound => {
"no index buffer was bound"
}
CheckIndexBufferError::TooManyIndices { .. } => {
"the draw command requested too many indices"
}
CheckIndexBufferError::BufferMissingUsage => {
"the index buffer usage must be enabled on the index buffer"
}
@ -79,40 +90,3 @@ impl fmt::Display for CheckIndexBufferError {
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::buffer::BufferUsage;
use crate::buffer::CpuAccessibleBuffer;
#[test]
fn missing_usage() {
let (device, queue) = gfx_dev_and_queue!();
let buffer = CpuAccessibleBuffer::from_iter(
device.clone(),
BufferUsage::vertex_buffer(),
false,
0..500u32,
)
.unwrap();
match check_index_buffer(&device, &buffer) {
Err(CheckIndexBufferError::BufferMissingUsage) => (),
_ => panic!(),
}
}
#[test]
fn wrong_device() {
let (dev1, queue) = gfx_dev_and_queue!();
let (dev2, _) = gfx_dev_and_queue!();
let buffer =
CpuAccessibleBuffer::from_iter(dev1, BufferUsage::all(), false, 0..500u32).unwrap();
assert_should_panic!({
let _ = check_index_buffer(&dev2, &buffer);
});
}
}

View File

@ -17,20 +17,25 @@ pub use self::copy_image_buffer::{
check_copy_buffer_image, CheckCopyBufferImageError, CheckCopyBufferImageTy,
};
pub use self::debug_marker::{check_debug_marker_color, CheckColorError};
pub use self::descriptor_sets::{check_descriptor_sets_validity, CheckDescriptorSetsValidityError};
pub use self::descriptor_sets::CheckDescriptorSetsValidityError;
pub use self::dispatch::{check_dispatch, CheckDispatchError};
pub use self::dynamic_state::{check_dynamic_state_validity, CheckDynamicStateValidityError};
pub use self::dynamic_state::CheckDynamicStateValidityError;
pub use self::fill_buffer::{check_fill_buffer, CheckFillBufferError};
pub use self::index_buffer::{check_index_buffer, CheckIndexBufferError};
pub use self::index_buffer::CheckIndexBufferError;
pub use self::indirect_buffer::{check_indirect_buffer, CheckIndirectBufferError};
pub use self::push_constants::{check_push_constants_validity, CheckPushConstantsValidityError};
pub use self::pipeline::CheckPipelineError;
pub use self::push_constants::CheckPushConstantsValidityError;
pub use self::query::{
check_begin_query, check_copy_query_pool_results, check_end_query, check_reset_query_pool,
check_write_timestamp, CheckBeginQueryError, CheckCopyQueryPoolResultsError,
CheckEndQueryError, CheckResetQueryPoolError, CheckWriteTimestampError,
};
pub use self::update_buffer::{check_update_buffer, CheckUpdateBufferError};
pub use self::vertex_buffers::{check_vertex_buffers, CheckVertexBufferError};
pub use self::vertex_buffers::CheckVertexBufferError;
pub(super) use {
descriptor_sets::*, dynamic_state::*, index_buffer::*, pipeline::*, push_constants::*,
vertex_buffers::*,
};
mod blit_image;
mod clear_color_image;
@ -44,6 +49,7 @@ mod dynamic_state;
mod fill_buffer;
mod index_buffer;
mod indirect_buffer;
mod pipeline;
mod push_constants;
mod query;
mod update_buffer;

View File

@ -0,0 +1,57 @@
// Copyright (c) 2021 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::{
command_buffer::synced::SyncCommandBufferBuilder,
pipeline::{ComputePipeline, GraphicsPipeline},
};
use std::{error, fmt};
pub(in super::super) fn check_pipeline_compute(
builder: &SyncCommandBufferBuilder,
) -> Result<&ComputePipeline, CheckPipelineError> {
let pipeline = match builder.bound_pipeline_compute() {
Some(x) => x,
None => return Err(CheckPipelineError::PipelineNotBound),
};
Ok(pipeline)
}
pub(in super::super) fn check_pipeline_graphics(
builder: &SyncCommandBufferBuilder,
) -> Result<&GraphicsPipeline, CheckPipelineError> {
let pipeline = match builder.bound_pipeline_graphics() {
Some(x) => x,
None => return Err(CheckPipelineError::PipelineNotBound),
};
Ok(pipeline)
}
/// Error that can happen when checking whether the pipeline is valid.
#[derive(Debug, Copy, Clone)]
pub enum CheckPipelineError {
/// No pipeline was bound to the bind point used by the operation.
PipelineNotBound,
}
impl error::Error for CheckPipelineError {}
impl fmt::Display for CheckPipelineError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
CheckPipelineError::PipelineNotBound => write!(
fmt,
"no pipeline was bound to the bind point used by the operation",
),
}
}
}

View File

@ -7,23 +7,39 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::command_buffer::synced::SyncCommandBufferBuilder;
use crate::pipeline::layout::PipelineLayout;
use crate::VulkanObject;
use std::error;
use std::fmt;
/// Checks whether push constants are compatible with the pipeline.
pub fn check_push_constants_validity<Pc>(
pub(in super::super) fn check_push_constants_validity(
builder: &SyncCommandBufferBuilder,
pipeline_layout: &PipelineLayout,
push_constants: &Pc,
) -> Result<(), CheckPushConstantsValidityError>
where
Pc: ?Sized,
{
// TODO
if !true {
) -> Result<(), CheckPushConstantsValidityError> {
if pipeline_layout.push_constant_ranges().is_empty() {
return Ok(());
}
let constants_pipeline_layout = match builder.current_push_constants_pipeline_layout() {
Some(x) => x,
None => return Err(CheckPushConstantsValidityError::MissingPushConstants),
};
if pipeline_layout.internal_object() != constants_pipeline_layout.internal_object()
&& pipeline_layout.push_constant_ranges()
!= constants_pipeline_layout.push_constant_ranges()
{
return Err(CheckPushConstantsValidityError::IncompatiblePushConstants);
}
// TODO: Check that the push constants that the pipeline needs have all been set correctly.
// The Vulkan spec currently is unclear about push constant invalidation, so Vulkano can't do
// much more for the moment. See:
// https://github.com/KhronosGroup/Vulkan-Docs/issues/1485
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2711
Ok(())
}
@ -32,6 +48,8 @@ where
pub enum CheckPushConstantsValidityError {
/// The push constants are incompatible with the pipeline layout.
IncompatiblePushConstants,
/// Not all push constants used by the pipeline have been set.
MissingPushConstants,
}
impl error::Error for CheckPushConstantsValidityError {}
@ -39,14 +57,19 @@ impl error::Error for CheckPushConstantsValidityError {}
impl fmt::Display for CheckPushConstantsValidityError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
CheckPushConstantsValidityError::IncompatiblePushConstants => {
match *self {
CheckPushConstantsValidityError::IncompatiblePushConstants => {
write!(
fmt,
"the push constants are incompatible with the pipeline layout"
}
)
}
)
CheckPushConstantsValidityError::MissingPushConstants => {
write!(
fmt,
"not all push constants used by the pipeline have been set"
)
}
}
}
}

View File

@ -7,31 +7,108 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use crate::buffer::BufferAccess;
use crate::device::DeviceOwned;
use crate::command_buffer::synced::SyncCommandBufferBuilder;
use crate::pipeline::vertex::VertexInputRate;
use crate::pipeline::GraphicsPipeline;
use crate::VulkanObject;
use crate::DeviceSize;
use std::convert::TryInto;
use std::error;
use std::fmt;
/// Checks whether vertex buffers can be bound.
///
/// # Panic
///
/// - Panics if one of the vertex buffers was not created with the same device as `pipeline`.
///
pub fn check_vertex_buffers(
pub(in super::super) fn check_vertex_buffers(
builder: &SyncCommandBufferBuilder,
pipeline: &GraphicsPipeline,
vertex_buffers: &[Box<dyn BufferAccess + Send + Sync>],
vertices: Option<(u32, u32)>,
instances: Option<(u32, u32)>,
) -> Result<(), CheckVertexBufferError> {
for (num, buf) in vertex_buffers.iter().enumerate() {
assert_eq!(
buf.inner().buffer.device().internal_object(),
pipeline.device().internal_object()
);
let vertex_input = pipeline.vertex_input();
let mut max_vertex_count: Option<u32> = None;
let mut max_instance_count: Option<u32> = None;
if !buf.inner().buffer.usage().vertex_buffer {
return Err(CheckVertexBufferError::BufferMissingUsage { num_buffer: num });
for (binding_num, binding_desc) in vertex_input.bindings() {
let vertex_buffer = match builder.bound_vertex_buffer(binding_num) {
Some(x) => x,
None => return Err(CheckVertexBufferError::BufferNotBound { binding_num }),
};
let mut num_elements = (vertex_buffer.size() / binding_desc.stride as DeviceSize)
.try_into()
.unwrap_or(u32::MAX);
match binding_desc.input_rate {
VertexInputRate::Vertex => {
max_vertex_count = Some(if let Some(x) = max_vertex_count {
x.min(num_elements)
} else {
num_elements
});
}
VertexInputRate::Instance { divisor } => {
if divisor == 0 {
// A divisor of 0 means the same instance data is used for all instances,
// so we can draw any number of instances from a single element.
// The buffer must contain at least one element though.
if num_elements != 0 {
num_elements = u32::MAX;
}
} else {
// If divisor is 2, we use only half the amount of data from the source buffer,
// so the number of instances that can be drawn is twice as large.
num_elements = num_elements.saturating_mul(divisor);
}
max_instance_count = Some(if let Some(x) = max_instance_count {
x.min(num_elements)
} else {
num_elements
});
}
};
}
if let Some((first_vertex, vertex_count)) = vertices {
if let Some(max_vertex_count) = max_vertex_count {
if first_vertex + vertex_count > max_vertex_count {
return Err(CheckVertexBufferError::TooManyVertices {
vertex_count,
max_vertex_count,
});
}
}
}
if let Some((first_instance, instance_count)) = instances {
if let Some(max_instance_count) = max_instance_count {
if first_instance + instance_count > max_instance_count {
return Err(CheckVertexBufferError::TooManyInstances {
instance_count,
max_instance_count,
}
.into());
}
}
if pipeline
.subpass()
.render_pass()
.desc()
.multiview()
.is_some()
{
let max_instance_index = pipeline
.device()
.physical_device()
.properties()
.max_multiview_instance_index
.unwrap_or(0);
if first_instance + instance_count > max_instance_index + 1 {
return Err(CheckVertexBufferError::TooManyInstances {
instance_count,
max_instance_count: max_instance_index + 1, // TODO: this can overflow
}
.into());
}
}
}
@ -41,10 +118,13 @@ pub fn check_vertex_buffers(
/// Error that can happen when checking whether the vertex buffers are valid.
#[derive(Debug, Copy, Clone)]
pub enum CheckVertexBufferError {
/// No buffer was bound to a binding slot needed by the pipeline.
BufferNotBound { binding_num: u32 },
/// The "vertex buffer" usage must be enabled on the buffer.
BufferMissingUsage {
/// Index of the buffer that is missing usage.
num_buffer: usize,
binding_num: u32,
},
/// A draw command requested too many vertices.
@ -65,14 +145,6 @@ pub enum CheckVertexBufferError {
/// The allowed amount of instances.
max_instance_count: u32,
},
/// A draw command requested too many indices.
TooManyIndices {
/// The used amount of indices.
index_count: u32,
/// The allowed amount of indices.
max_index_count: u32,
},
}
impl error::Error for CheckVertexBufferError {}
@ -84,6 +156,9 @@ impl fmt::Display for CheckVertexBufferError {
fmt,
"{}",
match *self {
CheckVertexBufferError::BufferNotBound { .. } => {
"no buffer was bound to a binding slot needed by the pipeline"
}
CheckVertexBufferError::BufferMissingUsage { .. } => {
"the vertex buffer usage is missing on a vertex buffer"
}
@ -93,9 +168,6 @@ impl fmt::Display for CheckVertexBufferError {
CheckVertexBufferError::TooManyInstances { .. } => {
"the draw command requested too many instances"
}
CheckVertexBufferError::TooManyIndices { .. } => {
"the draw command requested too many indices"
}
}
)
}

View File

@ -187,6 +187,23 @@ impl DescriptorSetDesc {
}
}
/// Returns whether `self` is compatible with `other`.
///
/// "Compatible" in this sense is defined by the Vulkan specification under the section
/// "Pipeline layout compatibility": the two must be identically defined to the Vulkan API,
/// meaning that all descriptors are compatible.
#[inline]
pub fn is_compatible_with(&self, other: &DescriptorSetDesc) -> bool {
let num_bindings = cmp::max(self.descriptors.len(), other.descriptors.len());
(0..num_bindings).all(|binding_num| {
match (self.descriptor(binding_num), other.descriptor(binding_num)) {
(None, None) => true,
(Some(first), Some(second)) => first.is_compatible_with(second),
_ => false,
}
})
}
/// Checks whether the descriptor of a pipeline layout `self` is compatible with the descriptor
/// of a shader `other`.
pub fn ensure_compatible_with_shader(
@ -307,6 +324,18 @@ pub struct DescriptorDesc {
}
impl DescriptorDesc {
/// Returns whether `self` is compatible with `other`.
///
/// "Compatible" in this sense is defined by the Vulkan specification under the section
/// "Pipeline layout compatibility": the two must be identically defined to the Vulkan API,
/// meaning they have identical `VkDescriptorSetLayoutBinding` values.
#[inline]
pub fn is_compatible_with(&self, other: &DescriptorDesc) -> bool {
self.ty.ty() == other.ty.ty()
&& self.descriptor_count == other.descriptor_count
&& self.stages == other.stages
}
/// Checks whether the descriptor of a pipeline layout `self` is compatible with the descriptor
/// of a shader `other`.
#[inline]
@ -543,6 +572,7 @@ pub enum DescriptorDescTy {
impl DescriptorDescTy {
/// Returns the type of descriptor.
// TODO: add example
#[inline]
pub fn ty(&self) -> DescriptorType {
match *self {
DescriptorDescTy::Sampler => DescriptorType::Sampler,

View File

@ -114,14 +114,28 @@ impl DescriptorSetLayout {
/// Returns the number of binding slots in the set.
#[inline]
pub fn num_bindings(&self) -> usize {
self.desc.bindings().len()
pub fn num_bindings(&self) -> u32 {
self.desc.bindings().len() as u32
}
/// Returns a description of a descriptor, or `None` if out of range.
#[inline]
pub fn descriptor(&self, binding: usize) -> Option<DescriptorDesc> {
self.desc.bindings().get(binding).cloned().unwrap_or(None)
pub fn descriptor(&self, binding: u32) -> Option<DescriptorDesc> {
self.desc
.bindings()
.get(binding as usize)
.cloned()
.unwrap_or(None)
}
/// Returns whether `self` is compatible with `other`.
///
/// "Compatible" in this sense is defined by the Vulkan specification under the section
/// "Pipeline layout compatibility": either the two are the same descriptor set layout, or they
/// must be identically defined to the Vulkan API.
#[inline]
pub fn is_compatible_with(&self, other: &DescriptorSetLayout) -> bool {
self.handle == other.handle || self.desc.is_compatible_with(&other.desc)
}
/// Checks whether the descriptor of a pipeline layout `self` is compatible with the descriptor
@ -130,7 +144,7 @@ impl DescriptorSetLayout {
&self,
other: &DescriptorSetLayout,
) -> Result<(), DescriptorSetCompatibilityError> {
if self.internal_object() == other.internal_object() {
if self.handle == other.handle {
return Ok(());
}

View File

@ -69,7 +69,7 @@ impl PersistentDescriptorSet<()> {
/// - Panics if the set id is out of range.
///
pub fn start(layout: Arc<DescriptorSetLayout>) -> PersistentDescriptorSetBuilder<()> {
let cap = layout.num_bindings();
let cap = layout.num_bindings() as usize;
PersistentDescriptorSetBuilder {
layout,
@ -163,7 +163,7 @@ pub struct PersistentDescriptorSetBuilder<R> {
// The descriptor set layout.
layout: Arc<DescriptorSetLayout>,
// Binding currently being filled.
binding_id: usize,
binding_id: u32,
// The writes to perform on a descriptor set in order to put the resources in it.
writes: Vec<DescriptorWrite>,
// Holds the resources alive.

View File

@ -401,8 +401,8 @@ impl Device {
/// Returns the Vulkan version supported by the device.
///
/// This is the lower of the
/// [physical device's supported version](crate::instance::PhysicalDevice::api_version) and
/// the instance's [`max_api_version`](crate::instance::Instance::max_api_version).
/// [physical device's supported version](crate::device::physical::PhysicalDevice::api_version)
/// and the instance's [`max_api_version`](crate::instance::Instance::max_api_version).
#[inline]
pub fn api_version(&self) -> Version {
self.api_version

View File

@ -69,7 +69,7 @@ use std::sync::Arc;
/// before creation with
/// [`FunctionPointers::api_version`](crate::instance::loader::FunctionPointers::api_version),
/// while for a device it can be retrieved with
/// [`PhysicalDevice::api_version`](crate::instance::PhysicalDevice::api_version).
/// [`PhysicalDevice::api_version`](crate::device::physical::PhysicalDevice::api_version).
///
/// When creating an `Instance`, you have to specify a maximum API version that you will use.
/// This restricts the API version that is available for the instance and any devices created from

View File

@ -213,6 +213,15 @@ impl fmt::Debug for ComputePipeline {
}
}
impl PartialEq for ComputePipeline {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.internal_object() == other.internal_object()
}
}
impl Eq for ComputePipeline {}
/// Opaque object that represents the inside of the compute pipeline. Can be made into a trait
/// object.
#[derive(Debug, Copy, Clone)]
@ -352,6 +361,7 @@ mod tests {
use crate::pipeline::shader::SpecializationConstants;
use crate::pipeline::shader::SpecializationMapEntry;
use crate::pipeline::ComputePipeline;
use crate::pipeline::PipelineBindPoint;
use crate::sync::now;
use crate::sync::GpuFuture;
use std::ffi::CStr;
@ -468,7 +478,15 @@ mod tests {
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
cbb.dispatch([1, 1, 1], pipeline.clone(), set, ()).unwrap();
cbb.bind_pipeline_compute(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Compute,
pipeline.layout().clone(),
0,
set,
)
.dispatch([1, 1, 1])
.unwrap();
let cb = cbb.build().unwrap();
let future = now(device.clone())

View File

@ -101,28 +101,16 @@ impl GraphicsPipeline {
&self.vertex_input
}
/// Returns true if the line width used by this pipeline is dynamic.
#[inline]
pub fn has_dynamic_line_width(&self) -> bool {
self.dynamic_line_width
}
/// Returns the number of viewports and scissors of this pipeline.
#[inline]
pub fn num_viewports(&self) -> u32 {
self.num_viewports
}
/// Returns true if the viewports used by this pipeline are dynamic.
/// Returns true if the blend constants used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_viewports(&self) -> bool {
self.dynamic_viewport
}
/// Returns true if the scissors used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_scissors(&self) -> bool {
self.dynamic_scissor
pub fn has_dynamic_blend_constants(&self) -> bool {
self.dynamic_blend_constants
}
/// Returns true if the depth bounds used by this pipeline are dynamic.
@ -131,22 +119,40 @@ impl GraphicsPipeline {
self.dynamic_depth_bounds
}
/// Returns true if the line width used by this pipeline is dynamic.
#[inline]
pub fn has_dynamic_line_width(&self) -> bool {
self.dynamic_line_width
}
/// Returns true if the scissors used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_scissor(&self) -> bool {
self.dynamic_scissor
}
/// Returns true if the stencil compare masks used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_stencil_compare_mask(&self) -> bool {
self.dynamic_stencil_compare_mask
}
/// Returns true if the stencil references used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_stencil_reference(&self) -> bool {
self.dynamic_stencil_reference
}
/// Returns true if the stencil write masks used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_stencil_write_mask(&self) -> bool {
self.dynamic_stencil_write_mask
}
/// Returns true if the stencil references used by this pipeline are dynamic.
/// Returns true if the viewports used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_stencil_reference(&self) -> bool {
self.dynamic_stencil_reference
pub fn has_dynamic_viewport(&self) -> bool {
self.dynamic_viewport
}
}

View File

@ -12,6 +12,8 @@
//! The input assembly is the stage where lists of vertices are turned into primitives.
//!
use crate::DeviceSize;
/// How the input assembly stage should behave.
#[derive(Copy, Clone, Debug)]
#[deprecated]
@ -105,6 +107,13 @@ pub unsafe trait Index {
fn ty() -> IndexType;
}
unsafe impl Index for u8 {
#[inline(always)]
fn ty() -> IndexType {
IndexType::U8
}
}
unsafe impl Index for u16 {
#[inline(always)]
fn ty() -> IndexType {
@ -124,10 +133,23 @@ unsafe impl Index for u32 {
#[allow(missing_docs)]
#[repr(i32)]
pub enum IndexType {
U8 = ash::vk::IndexType::UINT8_EXT.as_raw(),
U16 = ash::vk::IndexType::UINT16.as_raw(),
U32 = ash::vk::IndexType::UINT32.as_raw(),
}
impl IndexType {
/// Returns the size in bytes of indices of this type.
#[inline]
pub fn size(&self) -> DeviceSize {
match self {
IndexType::U8 => 1,
IndexType::U16 => 2,
IndexType::U32 => 4,
}
}
}
impl From<IndexType> for ash::vk::IndexType {
#[inline]
fn from(val: IndexType) -> Self {

View File

@ -207,9 +207,9 @@ pub fn check_desc_against_limits(
}
for &PipelineLayoutPcRange { offset, size, .. } in push_constants_ranges {
if offset + size > properties.max_push_constants_size as usize {
if offset + size > properties.max_push_constants_size {
return Err(PipelineLayoutLimitsError::MaxPushConstantsSizeExceeded {
limit: properties.max_push_constants_size as usize,
limit: properties.max_push_constants_size,
requested: offset + size,
});
}
@ -232,9 +232,9 @@ pub enum PipelineLayoutLimitsError {
/// The maximum size of push constants has been exceeded.
MaxPushConstantsSizeExceeded {
/// The limit that must be fulfilled.
limit: usize,
limit: u32,
/// What was requested.
requested: usize,
requested: u32,
},
/// The `max_per_stage_resources()` limit has been exceeded.

View File

@ -7,35 +7,67 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
//! A pipeline layout describes the layout of descriptors and push constants used by a graphics
//! pipeline or a compute pipeline.
//! A pipeline layout describes the layout of descriptors and push constants used by a pipeline.
//!
//! # Overview
//!
//! The layout itself only *describes* the descriptors and push constants, and does not contain
//! the content of the push constants or the actual list of resources that are going to be
//! available through the descriptors. Push constants are set when you submit a draw command, and
//! the list of resources is set by creating *descriptor set* objects and passing these sets when
//! you submit a draw command.
//! their content itself. Instead, you can think of it as a `struct` definition that states which
//! members there are, what types they have, and in what order.
//! One could imagine a Rust definition somewhat like this:
//!
//! # Pipeline layout objects
//! ```text
//! #[repr(C)]
//! struct MyPipelineLayout {
//! push_constants: Pc,
//! descriptor_set0: Ds0,
//! descriptor_set1: Ds1,
//! descriptor_set2: Ds2,
//! descriptor_set3: Ds3,
//! }
//! ```
//!
//! A pipeline layout is something that you must describe to the Vulkan implementation by creating
//! a **pipeline layout object**, represented by the `PipelineLayout` struct in vulkano.
//! Of course, a pipeline layout is created at runtime, unlike a Rust type.
//!
//! Each graphics pipeline or compute pipeline that you create therefore holds a
//! **pipeline layout object** By default, creating a pipeline automatically builds a new pipeline
//! layout object describing the union of all the descriptors and push constants of all the shaders
//! used by the pipeline.
//! # Layout compatibility
//!
//! The `PipelineLayout` struct describes the pipeline layout to both the Vulkan implementation and
//! to vulkano. It holds a `PipelineLayoutDesc` value.
//! When binding descriptor sets or setting push constants, you must provide a pipeline layout.
//! This pipeline is used to decide where in memory Vulkan should write the new data. The
//! descriptor sets and push constants can later be read by dispatch or draw calls, but only if
//! the bound pipeline being used for the command has a layout that is *compatible* with the layout
//! that was used to bind the resources.
//!
//! # Custom pipeline layouts
//! *Compatible* means that the pipeline layout must be the same object, or a different layout in
//! which the push constant ranges and descriptor set layouts were be identically defined.
//! However, Vulkan allows for partial compatibility as well. In the `struct` analogy used above,
//! one could imagine that using a different definition would leave some members with the same
//! offset and size within the struct as in the old definition, while others are no longer
//! positioned correctly. For example, if a new, incompatible type were used for `Ds1`, then the
//! `descriptor_set1`, `descriptor_set2` and `descriptor_set3` members would no longer be correct,
//! but `descriptor_set0` and `push_constants` would remain accessible in the new layout.
//! Because of this behaviour, the following rules apply to compatibility between the layouts used
//! in subsequent descriptor set binding calls:
//!
//! In some situations, it is better (as in, faster) to share the same descriptor set or sets
//! between multiple pipelines that each use different descriptors. To do so, you have to create a
//! pipeline layout object in advance and pass it when you create the pipelines.
//! - An incompatible definition of `Pc` invalidates all bound descriptor sets.
//! - An incompatible definition of `DsN` invalidates all bound descriptor sets *N* and higher.
//! - If *N* is the highest set being assigned in a bind command, and it and all lower sets
//! have compatible definitions, including the push constants, then descriptor sets above *N*
//! remain valid.
//!
//! TODO: write this section
//! [`SyncCommandBufferBuilder`](crate::command_buffer::synced::SyncCommandBufferBuilder) keeps
//! track of this state and will automatically remove descriptor sets that have been invalidated
//! by incompatible layouts in subsequent binding commands.
//!
//! # Creating pipeline layouts
//!
//! A pipeline layout is a Vulkan object type, represented in Vulkano with the `PipelineLayout`
//! type. Each pipeline that you create holds a pipeline layout object.
//!
//! By default, creating a pipeline automatically builds a new pipeline layout object describing the
//! union of all the descriptors and push constants of all the shaders used by the pipeline.
//! However, it is also possible to create a pipeline layout separately, and provide that to the
//! pipeline constructor. This can in some cases be more efficient than using the auto-generated
//! pipeline layouts.
pub use self::limits_check::PipelineLayoutLimitsError;
pub use self::sys::PipelineLayout;

View File

@ -51,7 +51,7 @@ impl PipelineLayout {
let fns = device.fns();
let descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 16]> =
descriptor_set_layouts.into_iter().collect();
let push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 8]> =
let mut push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 8]> =
push_constant_ranges.into_iter().collect();
// Check for overlapping stages
@ -66,6 +66,17 @@ impl PipelineLayout {
}
}
// Sort the ranges for the purpose of comparing for equality.
// The stage mask is guaranteed to be unique by the above check, so it's a suitable
// sorting key.
push_constant_ranges.sort_unstable_by_key(|range| {
(
range.offset,
range.size,
ash::vk::ShaderStageFlags::from(range.stages),
)
});
// Check against device limits
limits_check::check_desc_against_limits(
device.physical_device().properties(),
@ -95,8 +106,8 @@ impl PipelineLayout {
out.push(ash::vk::PushConstantRange {
stage_flags: stages.into(),
offset: offset as u32,
size: size as u32,
offset,
size,
});
}
@ -150,9 +161,7 @@ impl PipelineLayout {
push_constant_ranges,
})
}
}
impl PipelineLayout {
/// Returns the descriptor set layouts this pipeline layout was created from.
#[inline]
pub fn descriptor_set_layouts(&self) -> &[Arc<DescriptorSetLayout>] {
@ -160,6 +169,9 @@ impl PipelineLayout {
}
/// Returns a slice containing the push constant ranges this pipeline layout was created from.
///
/// The ranges are guaranteed to be sorted deterministically by offset, size, then stages.
/// This means that two slices containing the same elements will always have the same order.
#[inline]
pub fn push_constant_ranges(&self) -> &[PipelineLayoutPcRange] {
&self.push_constant_ranges
@ -393,15 +405,14 @@ impl fmt::Display for PipelineLayoutSupersetError {
}
/// Description of a range of the push constants of a pipeline layout.
// TODO: should contain the layout as well
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct PipelineLayoutPcRange {
/// Offset in bytes from the start of the push constants to this range.
pub offset: usize,
pub offset: u32,
/// Size in bytes of the range.
pub size: usize,
/// The stages which can access this range. Note that the same shader stage can't access two
/// different ranges.
pub size: u32,
/// The stages which can access this range.
/// A stage can access at most one push constant range.
pub stages: ShaderStages,
}

View File

@ -704,6 +704,19 @@ impl ShaderStages {
|| (self.fragment && other.fragment)
|| (self.compute && other.compute)
}
/// Returns the union of the stages in `self` and `other`.
#[inline]
pub const fn union(&self, other: &Self) -> Self {
Self {
vertex: self.vertex || other.vertex,
tessellation_control: self.tessellation_control || other.tessellation_control,
tessellation_evaluation: self.tessellation_evaluation || other.tessellation_evaluation,
geometry: self.geometry || other.geometry,
fragment: self.fragment || other.fragment,
compute: self.compute || other.compute,
}
}
}
impl From<ShaderStages> for ash::vk::ShaderStageFlags {

View File

@ -71,11 +71,8 @@ pub use self::impl_vertex::VertexMember;
pub use self::vertex::Vertex;
pub use self::vertex::VertexMemberInfo;
pub use self::vertex::VertexMemberTy;
use crate::buffer::BufferAccess;
use crate::format::Format;
use crate::DeviceSize;
use fnv::FnvHashMap;
use std::convert::TryInto;
mod buffers;
mod collection;
@ -135,52 +132,6 @@ impl VertexInput {
pub fn attributes(&self) -> impl ExactSizeIterator<Item = (u32, &VertexInputAttribute)> {
self.attributes.iter().map(|(&key, val)| (key, val))
}
/// Given an iterator of vertex buffers and their binding numbers, returns the maximum number
/// of vertices and instances that can be drawn with them.
///
/// # Panics
///
/// Panics if the binding number of a provided vertex buffer does not exist in `self`.
pub fn max_vertices_instances<'a>(
&self,
buffers: impl IntoIterator<Item = (u32, &'a dyn BufferAccess)>,
) -> (u32, u32) {
let buffers = buffers.into_iter();
let mut max_vertices = u32::MAX;
let mut max_instances = u32::MAX;
for (binding, buffer) in buffers {
let binding_desc = &self.bindings[&binding];
let mut num_elements = (buffer.size() / binding_desc.stride as DeviceSize)
.try_into()
.unwrap_or(u32::MAX);
match binding_desc.input_rate {
VertexInputRate::Vertex => {
max_vertices = max_vertices.min(num_elements);
}
VertexInputRate::Instance { divisor } => {
if divisor == 0 {
// A divisor of 0 means the same instance data is used for all instances,
// so we can draw any number of instances from a single element.
// The buffer must contain at least one element though.
if num_elements != 0 {
num_elements = u32::MAX;
}
} else {
// If divisor is 2, we use only half the amount of data from the source buffer,
// so the number of instances that can be drawn is twice as large.
num_elements = num_elements.saturating_mul(divisor);
}
max_instances = max_instances.min(num_elements);
}
};
}
(max_vertices, max_instances)
}
}
/// Describes a single vertex buffer binding in a graphics pipeline.

View File

@ -161,7 +161,7 @@ impl From<Viewport> for ash::vk::Viewport {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Scissor {
/// Coordinates in pixels of the top-left hand corner of the box.
pub origin: [i32; 2],
pub origin: [u32; 2],
/// Dimensions in pixels of the box.
pub dimensions: [u32; 2],
@ -190,8 +190,8 @@ impl From<Scissor> for ash::vk::Rect2D {
fn from(val: Scissor) -> Self {
ash::vk::Rect2D {
offset: ash::vk::Offset2D {
x: val.origin[0],
y: val.origin[1],
x: val.origin[0] as i32,
y: val.origin[1] as i32,
},
extent: ash::vk::Extent2D {
width: val.dimensions[0],