mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2025-02-16 09:02:25 +00:00
[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:
parent
34d54547f2
commit
32095992f9
@ -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)
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -156,6 +156,7 @@ impl AmbientLightingSystem {
|
||||
vec![self.vertex_buffer.clone()],
|
||||
descriptor_set,
|
||||
push_constants,
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
builder.build().unwrap()
|
||||
|
@ -170,6 +170,7 @@ impl DirectionalLightingSystem {
|
||||
vec![self.vertex_buffer.clone()],
|
||||
descriptor_set,
|
||||
push_constants,
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
builder.build().unwrap()
|
||||
|
@ -185,6 +185,7 @@ impl PointLightingSystem {
|
||||
vec![self.vertex_buffer.clone()],
|
||||
descriptor_set,
|
||||
push_constants,
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
builder.build().unwrap()
|
||||
|
@ -104,6 +104,7 @@ impl TriangleDrawSystem {
|
||||
vec![self.vertex_buffer.clone()],
|
||||
(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
builder.build().unwrap()
|
||||
|
210
examples/src/bin/dynamic-buffers.rs
Normal file
210
examples/src/bin/dynamic-buffers.rs
Normal 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>>()
|
||||
);
|
||||
}
|
@ -204,6 +204,7 @@ fn main() {
|
||||
pipeline.clone(),
|
||||
set.clone(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.copy_image_to_buffer(image.clone(), buf.clone())
|
||||
|
@ -286,6 +286,7 @@ fn main() {
|
||||
vertex_buffer.clone(),
|
||||
set.clone(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
|
@ -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()
|
||||
|
@ -331,6 +331,7 @@ fn main() {
|
||||
(triangle_vertex_buffer.clone(), instance_data_buffer.clone()),
|
||||
(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
|
@ -279,6 +279,7 @@ fn main() {
|
||||
vertex_buffer.clone(),
|
||||
(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
|
@ -383,6 +383,7 @@ fn main() {
|
||||
vertex_buffer.clone(),
|
||||
(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
|
@ -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();
|
||||
|
||||
|
@ -531,6 +531,7 @@ fn main() {
|
||||
vertex_buffer.clone(),
|
||||
(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
|
@ -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())
|
||||
|
@ -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();
|
||||
|
||||
|
@ -266,6 +266,7 @@ fn main() {
|
||||
index_buffer.clone(),
|
||||
set.clone(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
|
@ -361,6 +361,7 @@ fn main() {
|
||||
vertex_buffer.clone(),
|
||||
(),
|
||||
(),
|
||||
vec![],
|
||||
)
|
||||
.unwrap()
|
||||
.end_render_pass()
|
||||
|
@ -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
|
||||
|
@ -221,7 +221,7 @@ fn descriptor_infos(
|
||||
|
||||
let desc = quote! {
|
||||
DescriptorDescTy::Buffer(DescriptorBufferDesc {
|
||||
dynamic: Some(false),
|
||||
dynamic: None,
|
||||
storage: #is_ssbo,
|
||||
})
|
||||
};
|
||||
|
@ -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())
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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> {
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
Loading…
Reference in New Issue
Block a user