[Breaking] Add support for dynamic buffers (#1480)

* Add glue to allow using dynamic uniform buffers

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* **EXTREMELY BREAKING** Add `dynamic_offsets` argument to all draw-related calls in AutoCommandBufferBuilder

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* **BREAKING** Pass data to update_buffer by reference (this allows using unsized data)

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* run cargo fmt

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Dynamic offset safety checks™

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Fix tests

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Make `PipelineLayoutDescTweaks` public

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Add a dynamic (uniform) buffer example

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* all the breaking changes

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>
This commit is contained in:
Arc'blroth 2021-02-05 07:38:36 -08:00 committed by GitHub
parent 34d54547f2
commit 32095992f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 384 additions and 64 deletions

View File

@ -1,10 +1,14 @@
# Unreleased
- **Breaking** On `AutoCommandBufferBuilder`, methods that bind a descriptor set now take a `dynamic_offsets` parameter
- **Breaking** On `AutoCommandBufferBuilder` and `SyncCommandBufferBuilder`, the `update_buffer` method now takes `data` by reference
- **Breaking** Made `PipelineLayoutDescTweaks` public, for use with compute pipelines
- Added support for `ImageAspect` and YV12/NV12 formats, for use with the UnsafeImage API.
- Added basic VK_KHR_external_memory, VK_KHR_external_memory_fd, and VK_EXT_external_memory_dma_buf support.
- Fixed potential segmentation fault in `ComputePipeline` when referencing `PipelineCache` objects.
- Fixed race condition in `StandardCommandPool` when allocating buffers.
- Fixed potential stack overflow error in loading large shaders by storing the bytecode as static.
- Added basic support and safety checks for dynamic uniform/storage buffers
# Version 0.20.0 (2020-12-26)

View File

@ -139,7 +139,7 @@ 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(), ())
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), (), vec![])
.unwrap();
// Finish building the command buffer by calling `build`.
let command_buffer = builder.build().unwrap();

View File

@ -286,7 +286,7 @@ fn main() {
)
.unwrap()
// Draw our buffer
.draw(pipeline.clone(), &dynamic_state, buffer, (), ())
.draw(pipeline.clone(), &dynamic_state, buffer, (), (), vec![])
.unwrap()
.end_render_pass()
.unwrap();

View File

@ -156,6 +156,7 @@ impl AmbientLightingSystem {
vec![self.vertex_buffer.clone()],
descriptor_set,
push_constants,
vec![],
)
.unwrap();
builder.build().unwrap()

View File

@ -170,6 +170,7 @@ impl DirectionalLightingSystem {
vec![self.vertex_buffer.clone()],
descriptor_set,
push_constants,
vec![],
)
.unwrap();
builder.build().unwrap()

View File

@ -185,6 +185,7 @@ impl PointLightingSystem {
vec![self.vertex_buffer.clone()],
descriptor_set,
push_constants,
vec![],
)
.unwrap();
builder.build().unwrap()

View File

@ -104,6 +104,7 @@ impl TriangleDrawSystem {
vec![self.vertex_buffer.clone()],
(),
(),
vec![],
)
.unwrap();
builder.build().unwrap()

View File

@ -0,0 +1,210 @@
// 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.
// This example demonstrates how to use dynamic uniform buffers.
//
// Dynamic uniform and storage buffers store buffer data for different
// calls in one large buffer. Each draw or dispatch call can specify an
// offset into the buffer to read object data from, without having to
// rebind descriptor sets.
use std::mem;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::AutoCommandBufferBuilder;
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
use vulkano::descriptor::pipeline_layout::{PipelineLayoutDesc, PipelineLayoutDescTweaks};
use vulkano::descriptor::PipelineLayoutAbstract;
use vulkano::device::{Device, DeviceExtensions};
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice};
use vulkano::pipeline::shader::EntryPointAbstract;
use vulkano::pipeline::ComputePipeline;
use vulkano::sync;
use vulkano::sync::GpuFuture;
fn main() {
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
let queue_family = physical
.queue_families()
.find(|&q| q.supports_compute())
.unwrap();
let (device, mut queues) = Device::new(
physical,
physical.supported_features(),
&DeviceExtensions {
khr_storage_buffer_storage_class: true,
..DeviceExtensions::none()
},
[(queue_family, 0.5)].iter().cloned(),
)
.unwrap();
let queue = queues.next().unwrap();
mod shader {
vulkano_shaders::shader! {
ty: "compute",
src: "
#version 450
layout(local_size_x = 12) in;
// Uniform Buffer Object
layout(set = 0, binding = 0) uniform readonly InData {
uint data;
} ubo;
// Output Buffer
layout(set = 0, binding = 1) buffer OutData {
uint data[];
} data;
// Toy shader that only runs for the index specified in `ubo`.
void main() {
uint index = gl_GlobalInvocationID.x;
if(index == ubo.data) {
data.data[index] = index;
}
}
"
}
}
let shader = shader::Shader::load(device.clone()).unwrap();
// For Graphics pipelines, use the `with_auto_layout` method
// instead of the `build` method to specify dynamic buffers.
// `with_auto_layout` will automatically handle tweaking the
// pipeline.
let pipeline = Arc::new(
ComputePipeline::with_pipeline_layout(
device.clone(),
&shader.main_entry_point(),
&(),
Box::new(
PipelineLayoutDescTweaks::new(
shader
.main_entry_point()
.layout()
.clone()
.build(device.clone())
.unwrap(),
vec![(0, 0)], // The dynamic uniform buffer is at set 0, descriptor 0
)
.build(device.clone())
.unwrap(),
),
None,
)
.unwrap(),
);
// Declare input buffer.
// Data in a dynamic buffer **MUST** be aligned to min_uniform_buffer_offset_align
// or min_storage_buffer_offset_align, depending on the type of buffer.
let data: Vec<u8> = vec![3, 11, 7];
let min_dynamic_align = device
.physical_device()
.limits()
.min_uniform_buffer_offset_alignment() as usize;
println!(
"Minimum uniform buffer offset alignment: {}",
min_dynamic_align
);
println!("Input: {:?}", data);
// Round size up to the next multiple of align.
let align = (mem::size_of::<u32>() + min_dynamic_align - 1) & !(min_dynamic_align - 1);
let aligned_data = {
let mut aligned_data = Vec::with_capacity(align * data.len());
for i in 0..data.len() {
let bytes = data[i].to_ne_bytes();
// Fill up the buffer with data
for bi in 0..bytes.len() {
aligned_data.push(bytes[bi]);
}
// Zero out any padding needed for alignment
for _ in 0..align - bytes.len() {
aligned_data.push(0);
}
}
aligned_data
};
let input_buffer = CpuAccessibleBuffer::from_iter(
device.clone(),
BufferUsage::all(),
false,
aligned_data.into_iter(),
)
.unwrap();
let output_buffer = CpuAccessibleBuffer::from_iter(
device.clone(),
BufferUsage::all(),
false,
(0..12).map(|_| 0u32),
)
.unwrap();
let layout = pipeline.layout().descriptor_set_layout(0).unwrap();
let set = Arc::new(
PersistentDescriptorSet::start(layout.clone())
.add_buffer(input_buffer.clone())
.unwrap()
.add_buffer(output_buffer.clone())
.unwrap()
.build()
.unwrap(),
);
// Build the command buffer, using different offsets for each call.
let mut builder =
AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap();
builder
.dispatch(
[12, 1, 1],
pipeline.clone(),
set.clone(),
(),
vec![0 * align as u32],
)
.unwrap()
.dispatch(
[12, 1, 1],
pipeline.clone(),
set.clone(),
(),
vec![1 * align as u32],
)
.unwrap()
.dispatch(
[12, 1, 1],
pipeline.clone(),
set.clone(),
(),
vec![2 * align as u32],
)
.unwrap();
let command_buffer = builder.build().unwrap();
let future = sync::now(device.clone())
.then_execute(queue.clone(), command_buffer)
.unwrap()
.then_signal_fence_and_flush()
.unwrap();
future.wait(None).unwrap();
let output_content = output_buffer.read().unwrap();
println!(
"Output: {:?}",
output_content.iter().cloned().collect::<Vec<u32>>()
);
}

View File

@ -204,6 +204,7 @@ fn main() {
pipeline.clone(),
set.clone(),
(),
vec![],
)
.unwrap()
.copy_image_to_buffer(image.clone(), buf.clone())

View File

@ -286,6 +286,7 @@ fn main() {
vertex_buffer.clone(),
set.clone(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -349,6 +349,7 @@ fn main() {
compute_pipeline.clone(),
cs_desciptor_set.clone(),
(),
vec![],
)
.unwrap()
.begin_render_pass(
@ -366,6 +367,7 @@ fn main() {
indirect_args.clone(),
(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -331,6 +331,7 @@ fn main() {
(triangle_vertex_buffer.clone(), instance_data_buffer.clone()),
(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -279,6 +279,7 @@ fn main() {
vertex_buffer.clone(),
(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -383,6 +383,7 @@ fn main() {
vertex_buffer.clone(),
(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -104,7 +104,7 @@ fn main() {
let mut builder =
AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap();
builder
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), push_constants)
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), push_constants, vec![])
.unwrap();
let command_buffer = builder.build().unwrap();

View File

@ -531,6 +531,7 @@ fn main() {
vertex_buffer.clone(),
(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -94,7 +94,7 @@ fn main() {
let mut builder =
AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap();
builder
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), ())
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), (), vec![])
.unwrap();
let command_buffer = builder.build().unwrap();
let future = sync::now(device.clone())

View File

@ -102,7 +102,7 @@ fn main() {
let mut builder =
AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()).unwrap();
builder
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), ())
.dispatch([1024, 1, 1], pipeline.clone(), set.clone(), (), vec![])
.unwrap();
let command_buffer = builder.build().unwrap();

View File

@ -266,6 +266,7 @@ fn main() {
index_buffer.clone(),
set.clone(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -361,6 +361,7 @@ fn main() {
vertex_buffer.clone(),
(),
(),
vec![],
)
.unwrap()
.end_render_pass()

View File

@ -477,6 +477,7 @@ fn main() {
vertex_buffer.clone(),
(),
(),
vec![],
)
.unwrap()
// We leave the render pass by calling `draw_end`. Note that if we had multiple

View File

@ -221,7 +221,7 @@ fn descriptor_infos(
let desc = quote! {
DescriptorDescTy::Buffer(DescriptorBufferDesc {
dynamic: Some(false),
dynamic: None,
storage: #is_ssbo,
})
};

View File

@ -91,7 +91,7 @@ use OomError;
/// .unwrap()
/// // For the sake of the example we just call `update_buffer` on the buffer, even though
/// // it is pointless to do that.
/// .update_buffer(sub_buffer.clone(), [0.2, 0.3, 0.4, 0.5])
/// .update_buffer(sub_buffer.clone(), &[0.2, 0.3, 0.4, 0.5])
/// .unwrap()
/// .build().unwrap()
/// .execute(queue.clone())

View File

@ -43,7 +43,8 @@ use command_buffer::KindSecondaryRenderPass;
use command_buffer::StateCacher;
use command_buffer::StateCacherOutcome;
use command_buffer::SubpassContents;
use descriptor::descriptor_set::DescriptorSetsCollection;
use descriptor::descriptor::{DescriptorBufferDesc, DescriptorDescTy};
use descriptor::descriptor_set::{DescriptorSetDesc, DescriptorSetsCollection};
use descriptor::pipeline_layout::PipelineLayoutAbstract;
use device::Device;
use device::DeviceOwned;
@ -70,13 +71,14 @@ use pipeline::ComputePipelineAbstract;
use pipeline::GraphicsPipelineAbstract;
use query::QueryPipelineStatisticFlags;
use sampler::Filter;
use smallvec::SmallVec;
use std::ffi::CStr;
use sync::AccessCheckError;
use sync::AccessFlagBits;
use sync::GpuFuture;
use sync::PipelineStages;
use OomError;
use VulkanObject;
use {OomError, SafeDeref};
/// Note that command buffers allocated from the default command pool (`Arc<StandardCommandPool>`)
/// don't implement the `Send` and `Sync` traits. If you use this pool, then the
@ -1223,16 +1225,19 @@ impl<P> AutoCommandBufferBuilder<P> {
}
#[inline]
pub fn dispatch<Cp, S, Pc>(
pub fn dispatch<Cp, S, Pc, Do, Doi>(
&mut self,
dimensions: [u32; 3],
pipeline: Cp,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DispatchError>
where
Cp: ComputePipelineAbstract + Send + Sync + 'static + Clone, // TODO: meh for Clone
S: DescriptorSetsCollection,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
if !self.compute_allowed {
@ -1257,6 +1262,7 @@ impl<P> AutoCommandBufferBuilder<P> {
false,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
self.inner.dispatch(dimensions);
@ -1268,17 +1274,20 @@ impl<P> AutoCommandBufferBuilder<P> {
///
/// To use only some data in the buffer, wrap it in a `vulkano::buffer::BufferSlice`.
#[inline]
pub fn draw<V, Gp, S, Pc>(
pub fn draw<V, Gp, S, Pc, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
vertex_buffer: V,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
S: DescriptorSetsCollection,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
// TODO: must check that pipeline is compatible with render pass
@ -1305,6 +1314,7 @@ impl<P> AutoCommandBufferBuilder<P> {
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
@ -1328,7 +1338,7 @@ impl<P> AutoCommandBufferBuilder<P> {
///
/// To use only some data in a buffer, wrap it in a `vulkano::buffer::BufferSlice`.
#[inline]
pub fn draw_indexed<V, Gp, S, Pc, Ib, I>(
pub fn draw_indexed<V, Gp, S, Pc, Ib, I, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
@ -1336,12 +1346,15 @@ impl<P> AutoCommandBufferBuilder<P> {
index_buffer: Ib,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawIndexedError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
S: DescriptorSetsCollection,
Ib: BufferAccess + TypedBufferAccess<Content = [I]> + Send + Sync + 'static,
I: Index + 'static,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
// TODO: must check that pipeline is compatible with render pass
@ -1375,6 +1388,7 @@ impl<P> AutoCommandBufferBuilder<P> {
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
@ -1401,7 +1415,7 @@ impl<P> AutoCommandBufferBuilder<P> {
///
/// To use only some data in a buffer, wrap it in a `vulkano::buffer::BufferSlice`.
#[inline]
pub fn draw_indirect<V, Gp, S, Pc, Ib>(
pub fn draw_indirect<V, Gp, S, Pc, Ib, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
@ -1409,6 +1423,7 @@ impl<P> AutoCommandBufferBuilder<P> {
indirect_buffer: Ib,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawIndirectError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
@ -1418,6 +1433,8 @@ impl<P> AutoCommandBufferBuilder<P> {
+ Send
+ Sync
+ 'static,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
// TODO: must check that pipeline is compatible with render pass
@ -1446,6 +1463,7 @@ impl<P> AutoCommandBufferBuilder<P> {
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
@ -1469,7 +1487,7 @@ impl<P> AutoCommandBufferBuilder<P> {
///
/// To use only some data in a buffer, wrap it in a `vulkano::buffer::BufferSlice`.
#[inline]
pub fn draw_indexed_indirect<V, Gp, S, Pc, Ib, Inb, I>(
pub fn draw_indexed_indirect<V, Gp, S, Pc, Ib, Inb, I, Do, Doi>(
&mut self,
pipeline: Gp,
dynamic: &DynamicState,
@ -1478,6 +1496,7 @@ impl<P> AutoCommandBufferBuilder<P> {
indirect_buffer: Inb,
sets: S,
constants: Pc,
dynamic_offsets: Do,
) -> Result<&mut Self, DrawIndexedIndirectError>
where
Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
@ -1489,6 +1508,8 @@ impl<P> AutoCommandBufferBuilder<P> {
+ Sync
+ 'static,
I: Index + 'static,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
unsafe {
// TODO: must check that pipeline is compatible with render pass
@ -1524,6 +1545,7 @@ impl<P> AutoCommandBufferBuilder<P> {
true,
pipeline.clone(),
sets,
dynamic_offsets,
)?;
vertex_buffers(
&mut self.inner,
@ -1718,22 +1740,22 @@ impl<P> AutoCommandBufferBuilder<P> {
///
/// If `data` is larger than the buffer, only the part of `data` that fits is written. If the
/// buffer is larger than `data`, only the start of the buffer is written.
// TODO: allow unsized values
#[inline]
pub fn update_buffer<B, D>(
pub fn update_buffer<B, D, Dd>(
&mut self,
buffer: B,
data: D,
data: Dd,
) -> Result<&mut Self, UpdateBufferError>
where
B: TypedBufferAccess<Content = D> + Send + Sync + 'static,
D: Send + Sync + 'static,
D: ?Sized,
Dd: SafeDeref<Target = D> + Send + Sync + 'static,
{
unsafe {
self.ensure_outside_render_pass()?;
check_update_buffer(self.device(), &buffer, &data)?;
check_update_buffer(self.device(), &buffer, data.deref())?;
let size_of_data = mem::size_of_val(&data);
let size_of_data = mem::size_of_val(data.deref());
if buffer.size() >= size_of_data {
self.inner.update_buffer(buffer, data);
} else {
@ -1846,23 +1868,77 @@ unsafe fn vertex_buffers<P>(
Ok(())
}
unsafe fn descriptor_sets<P, Pl, S>(
unsafe fn descriptor_sets<P, Pl, S, Do, Doi>(
destination: &mut SyncCommandBufferBuilder<P>,
state_cacher: &mut StateCacher,
gfx: bool,
pipeline: Pl,
sets: S,
dynamic_offsets: Do,
) -> Result<(), SyncCommandBufferBuilderError>
where
Pl: PipelineLayoutAbstract + Send + Sync + Clone + 'static,
S: DescriptorSetsCollection,
Do: IntoIterator<Item = u32, IntoIter = Doi>,
Doi: Iterator<Item = u32> + Send + Sync + 'static,
{
let sets = sets.into_vec();
let dynamic_offsets: SmallVec<[u32; 32]> = dynamic_offsets.into_iter().collect();
// Ensure that the number of dynamic_offsets is correct and that each
// dynamic offset is a multiple of the minimum offset alignment specified
// by the physical device.
let limits = pipeline.device().physical_device().limits();
let min_uniform_off_align = limits.min_uniform_buffer_offset_alignment() as u32;
let min_storage_off_align = limits.min_storage_buffer_offset_alignment() as u32;
let mut dynamic_offset_index = 0;
for set in &sets {
for desc_index in 0..set.num_bindings() {
let desc = DescriptorSetDesc::descriptor(&set, desc_index).unwrap();
if let DescriptorDescTy::Buffer(DescriptorBufferDesc {
dynamic: Some(true),
storage,
}) = desc.ty
{
// Don't check alignment if there are not enough offsets anyway
if dynamic_offsets.len() > dynamic_offset_index {
if storage {
assert!(
dynamic_offsets[dynamic_offset_index] % min_storage_off_align == 0,
"Dynamic storage buffer offset must be a multiple of min_storage_buffer_offset_alignment: got {}, expected a multiple of {}",
dynamic_offsets[dynamic_offset_index],
min_storage_off_align
);
} else {
assert!(
dynamic_offsets[dynamic_offset_index] % min_uniform_off_align == 0,
"Dynamic uniform buffer offset must be a multiple of min_uniform_buffer_offset_alignment: got {}, expected a multiple of {}",
dynamic_offsets[dynamic_offset_index],
min_uniform_off_align
);
}
}
dynamic_offset_index += 1;
}
}
}
assert!(
!(dynamic_offsets.len() < dynamic_offset_index),
"Too few dynamic offsets: got {}, expected {}",
dynamic_offsets.len(),
dynamic_offset_index
);
assert!(
!(dynamic_offsets.len() > dynamic_offset_index),
"Too many dynamic offsets: got {}, expected {}",
dynamic_offsets.len(),
dynamic_offset_index
);
let first_binding = {
let mut compare = state_cacher.bind_descriptor_sets(gfx);
for set in sets.iter() {
compare.add(set);
compare.add(set, &dynamic_offsets);
}
compare.compare()
};
@ -1876,7 +1952,12 @@ where
for set in sets.into_iter().skip(first_binding as usize) {
sets_binder.add(set);
}
sets_binder.submit(gfx, pipeline.clone(), first_binding, iter::empty())?;
sets_binder.submit(
gfx,
pipeline.clone(),
first_binding,
dynamic_offsets.into_iter(),
)?;
Ok(())
}

View File

@ -31,9 +31,9 @@ pub struct StateCacher {
// The graphics pipeline currently bound. 0 if nothing bound.
graphics_pipeline: vk::Pipeline,
// The descriptor sets for the compute pipeline.
compute_descriptor_sets: SmallVec<[vk::DescriptorSet; 12]>,
compute_descriptor_sets: SmallVec<[(vk::DescriptorSet, SmallVec<[u32; 32]>); 12]>,
// The descriptor sets for the graphics pipeline.
graphics_descriptor_sets: SmallVec<[vk::DescriptorSet; 12]>,
graphics_descriptor_sets: SmallVec<[(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
@ -241,7 +241,7 @@ 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<[vk::DescriptorSet; 12]>,
state: &'s mut SmallVec<[(vk::DescriptorSet, SmallVec<[u32; 32]>); 12]>,
// Next offset within the list to compare to.
offset: usize,
// Contains the return value of `compare`.
@ -251,21 +251,21 @@ pub struct StateCacherDescriptorSets<'s> {
impl<'s> StateCacherDescriptorSets<'s> {
/// Adds a descriptor set to the list to compare.
#[inline]
pub fn add<S>(&mut self, set: &S)
pub fn add<S>(&mut self, set: &S, dynamic_offsets: &SmallVec<[u32; 32]>)
where
S: ?Sized + DescriptorSet,
{
let raw = set.inner().internal_object();
if self.offset < self.state.len() {
if self.state[self.offset] == raw {
if (&self.state[self.offset].0, &self.state[self.offset].1) == (&raw, dynamic_offsets) {
self.offset += 1;
return;
}
self.state[self.offset] = raw;
self.state[self.offset] = (raw, dynamic_offsets.clone());
} else {
self.state.push(raw);
self.state.push((raw, dynamic_offsets.clone()));
}
if self.found_diff.is_none() {

View File

@ -49,6 +49,7 @@ use std::ffi::CStr;
use sync::AccessFlagBits;
use sync::Event;
use sync::PipelineStages;
use SafeDeref;
impl<P> SyncCommandBufferBuilder<P> {
/// Calls `vkBeginRenderPass` on the builder.
@ -2097,27 +2098,29 @@ impl<P> SyncCommandBufferBuilder<P> {
/// Calls `vkCmdUpdateBuffer` on the builder.
#[inline]
pub unsafe fn update_buffer<B, D>(&mut self, buffer: B, data: D)
pub unsafe fn update_buffer<B, D, Dd>(&mut self, buffer: B, data: Dd)
where
B: BufferAccess + Send + Sync + 'static,
D: Send + Sync + 'static,
D: ?Sized,
Dd: SafeDeref<Target = D> + Send + Sync + 'static,
{
struct Cmd<B, D> {
struct Cmd<B, Dd> {
buffer: B,
data: D,
data: Dd,
}
impl<P, B, D> Command<P> for Cmd<B, D>
impl<P, B, D, Dd> Command<P> for Cmd<B, Dd>
where
B: BufferAccess + Send + Sync + 'static,
D: Send + Sync + 'static,
D: ?Sized,
Dd: SafeDeref<Target = D> + Send + Sync + 'static,
{
fn name(&self) -> &'static str {
"vkCmdUpdateBuffer"
}
unsafe fn send(&mut self, out: &mut UnsafeCommandBufferBuilder<P>) {
out.update_buffer(&self.buffer, &self.data);
out.update_buffer(&self.buffer, self.data.deref());
}
fn into_final_command(self: Box<Self>) -> Box<dyn FinalCommand + Send + Sync> {

View File

@ -254,11 +254,9 @@ pub enum DescriptorDescTy {
impl DescriptorDescTy {
/// Returns the type of descriptor.
///
/// Returns `None` if there's not enough info to determine the type.
// TODO: add example
pub fn ty(&self) -> Option<DescriptorType> {
Some(match *self {
pub fn ty(&self) -> DescriptorType {
match *self {
DescriptorDescTy::Sampler => DescriptorType::Sampler,
DescriptorDescTy::CombinedImageSampler(_) => DescriptorType::CombinedImageSampler,
DescriptorDescTy::Image(ref desc) => {
@ -270,10 +268,7 @@ impl DescriptorDescTy {
}
DescriptorDescTy::InputAttachment { .. } => DescriptorType::InputAttachment,
DescriptorDescTy::Buffer(ref desc) => {
let dynamic = match desc.dynamic {
Some(d) => d,
None => return None,
};
let dynamic = desc.dynamic.unwrap_or(false);
match (desc.storage, dynamic) {
(false, false) => DescriptorType::UniformBuffer,
(true, false) => DescriptorType::StorageBuffer,
@ -288,7 +283,7 @@ impl DescriptorDescTy {
DescriptorType::UniformTexelBuffer
}
}
})
}
}
/// Checks whether we are a superset of another descriptor type.
@ -529,10 +524,12 @@ impl DescriptorImageDescDimensions {
}
}
// TODO: documentation
/// Additional description for descriptors that contain buffers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DescriptorBufferDesc {
/// If `true`, this buffer is a dynamic buffer. Assumes false if `None`.
pub dynamic: Option<bool>,
/// If `true`, this buffer is a storage buffer.
pub storage: bool,
}

View File

@ -266,7 +266,7 @@ impl<R> PersistentDescriptorSetBuilder<R> {
None => (),
Some(desc) => {
return Err(PersistentDescriptorSetError::WrongDescriptorTy {
expected: desc.ty.ty().unwrap(),
expected: desc.ty.ty(),
})
}
}
@ -484,18 +484,28 @@ impl<R> PersistentDescriptorSetBuilderArray<R> {
));
}
unsafe {
DescriptorWrite::uniform_buffer(
self.builder.binding_id as u32,
self.array_element as u32,
&buffer,
)
if buffer_desc.dynamic.unwrap_or(false) {
unsafe {
DescriptorWrite::dynamic_uniform_buffer(
self.builder.binding_id as u32,
self.array_element as u32,
&buffer,
)
}
} else {
unsafe {
DescriptorWrite::uniform_buffer(
self.builder.binding_id as u32,
self.array_element as u32,
&buffer,
)
}
}
}
}
ref d => {
return Err(PersistentDescriptorSetError::WrongDescriptorTy {
expected: d.ty().unwrap(),
expected: d.ty(),
});
}
});
@ -577,7 +587,7 @@ impl<R> PersistentDescriptorSetBuilderArray<R> {
}
ref d => {
return Err(PersistentDescriptorSetError::WrongDescriptorTy {
expected: d.ty().unwrap(),
expected: d.ty(),
});
}
});
@ -699,7 +709,7 @@ impl<R> PersistentDescriptorSetBuilderArray<R> {
}
ty => {
return Err(PersistentDescriptorSetError::WrongDescriptorTy {
expected: ty.ty().unwrap(),
expected: ty.ty(),
});
}
});
@ -778,7 +788,7 @@ impl<R> PersistentDescriptorSetBuilderArray<R> {
}
ty => {
return Err(PersistentDescriptorSetError::WrongDescriptorTy {
expected: ty.ty().unwrap(),
expected: ty.ty(),
});
}
});
@ -841,7 +851,7 @@ impl<R> PersistentDescriptorSetBuilderArray<R> {
),
ty => {
return Err(PersistentDescriptorSetError::WrongDescriptorTy {
expected: ty.ty().unwrap(),
expected: ty.ty(),
});
}
});

View File

@ -68,7 +68,7 @@ impl UnsafeDescriptorSetLayout {
// FIXME: it is not legal to pass eg. the TESSELLATION_SHADER bit when the device
// doesn't have tess shaders enabled
let ty = desc.ty.ty().unwrap(); // TODO: shouldn't panic
let ty = desc.ty.ty();
descriptors_count.add_one(ty);
Some(vk::DescriptorSetLayoutBinding {

View File

@ -50,7 +50,7 @@ where
num_resources.increment(descriptor.array_count, &descriptor.stages);
match descriptor.ty.ty().expect("Not implemented yet") {
match descriptor.ty.ty() {
// TODO:
DescriptorType::Sampler => {
num_samplers.increment(descriptor.array_count, &descriptor.stages);

View File

@ -63,10 +63,9 @@ pub use self::traits::PipelineLayoutNotSupersetError;
pub use self::traits::PipelineLayoutPushConstantsCompatible;
pub use self::traits::PipelineLayoutSetsCompatible;
pub use self::traits::PipelineLayoutSuperset;
pub use self::tweaks::PipelineLayoutDescTweaks;
pub use self::union::PipelineLayoutDescUnion;
pub(crate) use self::tweaks::PipelineLayoutDescTweaks;
mod empty;
mod limits_check;
mod runtime_desc;

View File

@ -558,7 +558,8 @@ mod tests {
let mut cbb =
AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family())
.unwrap();
cbb.dispatch([1, 1, 1], pipeline.clone(), set, ()).unwrap();
cbb.dispatch([1, 1, 1], pipeline.clone(), set, (), vec![])
.unwrap();
let cb = cbb.build().unwrap();
let future = now(device.clone())