Add basic support for khr_push_descriptor (#1727)

* Add basic support for khr_push_descriptor

* Add basic support for khr_push_descriptor

* Support arrays in `DescriptorWrite`

* Use assert when creating the builder
This commit is contained in:
Rua 2021-10-20 16:56:56 +02:00 committed by GitHub
parent 9aba801319
commit 35fed6b577
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1929 additions and 1287 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,377 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use png;
use std::io::Cursor;
use std::sync::Arc;
use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, TypedBufferAccess};
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents};
use vulkano::descriptor_set::DescriptorSetBuilder;
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceExtensions, Features};
use vulkano::format::Format;
use vulkano::image::{
view::ImageView, ImageDimensions, ImageUsage, ImmutableImage, MipmapsCount, SwapchainImage,
};
use vulkano::instance::Instance;
use vulkano::pipeline::color_blend::ColorBlendState;
use vulkano::pipeline::input_assembly::{InputAssemblyState, PrimitiveTopology};
use vulkano::pipeline::viewport::{Viewport, ViewportState};
use vulkano::pipeline::{GraphicsPipeline, PipelineBindPoint};
use vulkano::render_pass::{Framebuffer, FramebufferAbstract, RenderPass, Subpass};
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
use vulkano::swapchain::{self, AcquireError, Swapchain, SwapchainCreationError};
use vulkano::sync::{self, FlushError, GpuFuture};
use vulkano::Version;
use vulkano_win::VkSurfaceBuild;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{Window, WindowBuilder};
fn main() {
let required_extensions = vulkano_win::required_extensions();
let instance = Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
let event_loop = EventLoop::new();
let surface = WindowBuilder::new()
.build_vk_surface(&event_loop, instance.clone())
.unwrap();
let device_extensions = DeviceExtensions {
khr_swapchain: true,
khr_push_descriptor: true,
..DeviceExtensions::none()
};
let (physical_device, queue_family) = PhysicalDevice::enumerate(&instance)
.filter(|&p| p.supported_extensions().is_superset_of(&device_extensions))
.filter_map(|p| {
p.queue_families()
.find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false))
.map(|q| (p, q))
})
.min_by_key(|(p, _)| match p.properties().device_type {
PhysicalDeviceType::DiscreteGpu => 0,
PhysicalDeviceType::IntegratedGpu => 1,
PhysicalDeviceType::VirtualGpu => 2,
PhysicalDeviceType::Cpu => 3,
PhysicalDeviceType::Other => 4,
})
.expect("No suitable physical device found");
println!(
"Using device: {} (type: {:?})",
physical_device.properties().device_name,
physical_device.properties().device_type,
);
let (device, mut queues) = Device::new(
physical_device,
&Features::none(),
&physical_device
.required_extensions()
.union(&device_extensions),
[(queue_family, 0.5)].iter().cloned(),
)
.unwrap();
let queue = queues.next().unwrap();
let (mut swapchain, images) = {
let caps = surface.capabilities(physical_device).unwrap();
let composite_alpha = caps.supported_composite_alpha.iter().next().unwrap();
let format = caps.supported_formats[0].0;
let dimensions: [u32; 2] = surface.window().inner_size().into();
Swapchain::start(device.clone(), surface.clone())
.num_images(caps.min_image_count)
.format(format)
.dimensions(dimensions)
.usage(ImageUsage::color_attachment())
.sharing_mode(&queue)
.composite_alpha(composite_alpha)
.build()
.unwrap()
};
#[derive(Default, Debug, Clone)]
struct Vertex {
position: [f32; 2],
}
vulkano::impl_vertex!(Vertex, position);
let vertex_buffer = CpuAccessibleBuffer::<[Vertex]>::from_iter(
device.clone(),
BufferUsage::all(),
false,
[
Vertex {
position: [-0.5, -0.5],
},
Vertex {
position: [-0.5, 0.5],
},
Vertex {
position: [0.5, -0.5],
},
Vertex {
position: [0.5, 0.5],
},
]
.iter()
.cloned(),
)
.unwrap();
let vs = vs::Shader::load(device.clone()).unwrap();
let fs = fs::Shader::load(device.clone()).unwrap();
let render_pass = Arc::new(
vulkano::single_pass_renderpass!(device.clone(),
attachments: {
color: {
load: Clear,
store: Store,
format: swapchain.format(),
samples: 1,
}
},
pass: {
color: [color],
depth_stencil: {}
}
)
.unwrap(),
);
let (texture, tex_future) = {
let png_bytes = include_bytes!("image_img.png").to_vec();
let cursor = Cursor::new(png_bytes);
let decoder = png::Decoder::new(cursor);
let mut reader = decoder.read_info().unwrap();
let info = reader.info();
let dimensions = ImageDimensions::Dim2d {
width: info.width,
height: info.height,
array_layers: 1,
};
let mut image_data = Vec::new();
image_data.resize((info.width * info.height * 4) as usize, 0);
reader.next_frame(&mut image_data).unwrap();
let (image, future) = ImmutableImage::from_iter(
image_data.iter().cloned(),
dimensions,
MipmapsCount::One,
Format::R8G8B8A8_SRGB,
queue.clone(),
)
.unwrap();
(ImageView::new(image).unwrap(), future)
};
let sampler = Sampler::new(
device.clone(),
Filter::Linear,
Filter::Linear,
MipmapMode::Nearest,
SamplerAddressMode::Repeat,
SamplerAddressMode::Repeat,
SamplerAddressMode::Repeat,
0.0,
1.0,
0.0,
0.0,
)
.unwrap();
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = Arc::new(
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs.main_entry_point(), ())
.input_assembly_state(
InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip),
)
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.main_entry_point(), ())
.color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha())
.render_pass(subpass)
.with_auto_layout(device.clone(), |set_descs| {
set_descs[0].set_push_descriptor(true);
set_descs[0].set_immutable_samplers(0, [sampler]);
})
.unwrap(),
);
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 viewport);
let mut recreate_swapchain = false;
let mut previous_frame_end = Some(tex_future.boxed());
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
*control_flow = ControlFlow::Exit;
}
Event::WindowEvent {
event: WindowEvent::Resized(_),
..
} => {
recreate_swapchain = true;
}
Event::RedrawEventsCleared => {
previous_frame_end.as_mut().unwrap().cleanup_finished();
if recreate_swapchain {
let dimensions: [u32; 2] = surface.window().inner_size().into();
let (new_swapchain, new_images) =
match swapchain.recreate().dimensions(dimensions).build() {
Ok(r) => r,
Err(SwapchainCreationError::UnsupportedDimensions) => return,
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
};
swapchain = new_swapchain;
framebuffers =
window_size_dependent_setup(&new_images, render_pass.clone(), &mut viewport);
recreate_swapchain = false;
}
let (image_num, suboptimal, acquire_future) =
match swapchain::acquire_next_image(swapchain.clone(), None) {
Ok(r) => r,
Err(AcquireError::OutOfDate) => {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
};
if suboptimal {
recreate_swapchain = true;
}
let clear_values = vec![[0.0, 0.0, 1.0, 1.0].into()];
let mut builder = AutoCommandBufferBuilder::primary(
device.clone(),
queue.family(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
builder
.begin_render_pass(
framebuffers[image_num].clone(),
SubpassContents::Inline,
clear_values,
)
.unwrap()
.set_viewport(0, [viewport.clone()])
.bind_pipeline_graphics(pipeline.clone())
.push_descriptor_set(PipelineBindPoint::Graphics, pipeline.layout().clone(), 0, {
let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap();
let mut set_builder = DescriptorSetBuilder::start(layout.clone());
set_builder.add_image(texture.clone()).unwrap();
set_builder.build().unwrap()
})
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(vertex_buffer.len() as u32, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
let command_buffer = builder.build().unwrap();
let future = previous_frame_end
.take()
.unwrap()
.join(acquire_future)
.then_execute(queue.clone(), command_buffer)
.unwrap()
.then_swapchain_present(queue.clone(), swapchain.clone(), image_num)
.then_signal_fence_and_flush();
match future {
Ok(future) => {
previous_frame_end = Some(future.boxed());
}
Err(FlushError::OutOfDate) => {
recreate_swapchain = true;
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
previous_frame_end = Some(sync::now(device.clone()).boxed());
}
}
}
_ => (),
});
}
/// This method is called once during initialization, then again whenever the window is resized
fn window_size_dependent_setup(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
viewport: &mut Viewport,
) -> Vec<Arc<dyn FramebufferAbstract>> {
let dimensions = images[0].dimensions();
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
images
.iter()
.map(|image| {
let view = ImageView::new(image.clone()).unwrap();
Arc::new(
Framebuffer::start(render_pass.clone())
.add(view)
.unwrap()
.build()
.unwrap(),
) as Arc<dyn FramebufferAbstract>
})
.collect::<Vec<_>>()
}
mod vs {
vulkano_shaders::shader! {
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
layout(location = 0) out vec2 tex_coords;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
tex_coords = position + vec2(0.5);
}"
}
}
mod fs {
vulkano_shaders::shader! {
ty: "fragment",
src: "
#version 450
layout(location = 0) in vec2 tex_coords;
layout(location = 0) out vec4 f_color;
layout(set = 0, binding = 0) uniform sampler2D tex;
void main() {
f_color = texture(tex, tex_coords);
}"
}
}

View File

@ -37,6 +37,7 @@ use crate::command_buffer::ImageUninitializedSafe;
use crate::command_buffer::PrimaryCommandBuffer;
use crate::command_buffer::SecondaryCommandBuffer;
use crate::command_buffer::SubpassContents;
use crate::descriptor_set::builder::DescriptorSetBuilderOutput;
use crate::descriptor_set::DescriptorSetsCollection;
use crate::device::physical::QueueFamily;
use crate::device::Device;
@ -1778,6 +1779,61 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
/// Pushes descriptor data directly into the command buffer for future dispatch or draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`.
/// - Panics if the
/// [`khr_push_descriptor`](crate::device::DeviceExtensions::khr_push_descriptor)
/// extension is not enabled on the device.
/// - Panics if `set_num` is not less than the number of sets in `pipeline_layout`.
pub fn push_descriptor_set(
&mut self,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: Arc<PipelineLayout>,
set_num: u32,
descriptor_writes: DescriptorSetBuilderOutput, // TODO: make partial writes possible
) -> &mut Self {
match pipeline_bind_point {
PipelineBindPoint::Compute => assert!(
self.queue_family().supports_compute(),
"the queue family of the command buffer must support compute operations"
),
PipelineBindPoint::Graphics => assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
),
}
assert!(
self.device().enabled_extensions().khr_push_descriptor,
"the khr_push_descriptor extension must be enabled on the device"
);
assert!(
set_num as usize <= pipeline_layout.descriptor_set_layouts().len(),
"the descriptor set slot being bound must be less than the number of sets in pipeline_layout"
);
let pipeline_set = &pipeline_layout.descriptor_set_layouts()[set_num as usize];
assert!(
pipeline_set.is_compatible_with(descriptor_writes.layout()),
"descriptor_writes is not compatible with slot {} in pipeline_layout",
set_num as usize,
);
unsafe {
self.inner.push_descriptor_set(
pipeline_bind_point,
pipeline_layout,
set_num,
descriptor_writes,
);
}
self
}
// Helper function for dynamic state setting.
fn has_fixed_state(&self, state: DynamicState) -> bool {
self.state()

View File

@ -23,10 +23,13 @@ use crate::command_buffer::CommandBufferExecError;
use crate::command_buffer::CommandBufferLevel;
use crate::command_buffer::CommandBufferUsage;
use crate::command_buffer::ImageUninitializedSafe;
use crate::descriptor_set::builder::DescriptorSetBuilderOutput;
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::DescriptorSet;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::image::ImageLayout;
use crate::image::ImageViewAbstract;
use crate::pipeline::color_blend::LogicOp;
use crate::pipeline::depth_stencil::CompareOp;
use crate::pipeline::depth_stencil::StencilOp;
@ -50,6 +53,7 @@ use crate::sync::AccessFlags;
use crate::sync::PipelineMemoryAccess;
use crate::sync::PipelineStages;
use crate::OomError;
use crate::VulkanObject;
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::borrow::Cow;
@ -808,6 +812,69 @@ impl CurrentState {
}
}
}
fn invalidate_descriptor_sets(
&mut self,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: Arc<PipelineLayout>,
first_set: u32,
num_descriptor_sets: u32,
cmd: &Arc<dyn Command>,
) {
let state = match self.descriptor_sets.entry(pipeline_bind_point) {
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();
let max = (current_layouts.len() as u32).min(first_set + num_descriptor_sets);
(0..max).find(|&num| {
let num = num as usize;
!current_layouts[num].is_compatible_with(&new_layouts[num])
})
};
if let Some(invalidate_from) = invalidate_from {
// Remove disturbed sets and set new pipeline layout.
state
.descriptor_sets
.retain(|&num, _| num < invalidate_from);
state.pipeline_layout = pipeline_layout;
} else if (first_set + num_descriptor_sets) as usize
>= state.pipeline_layout.descriptor_set_layouts().len()
{
// New layout is a superset of the old one.
state.pipeline_layout = pipeline_layout;
}
state
}
};
for i in 0..num_descriptor_sets {
state.descriptor_sets.insert(first_set + i, cmd.clone());
}
}
}
#[derive(Debug)]
@ -830,16 +897,18 @@ impl<'a> CommandBufferState<'a> {
&self,
pipeline_bind_point: PipelineBindPoint,
set_num: u32,
) -> Option<(&'a dyn DescriptorSet, &'a [u32])> {
self.current_state
.descriptor_sets
.get(&pipeline_bind_point)
.and_then(|state| {
) -> Option<SetOrPush<'a>> {
let state =
if let Some(state) = self.current_state.descriptor_sets.get(&pipeline_bind_point) {
state
.descriptor_sets
.get(&set_num)
.map(|cmd| cmd.bound_descriptor_set(set_num))
})
} else {
return None;
};
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.
@ -1086,6 +1155,53 @@ impl<'a> CommandBufferState<'a> {
}
}
#[derive(Clone)]
pub enum SetOrPush<'a> {
Set(&'a dyn DescriptorSet, &'a [u32]),
Push(&'a DescriptorSetBuilderOutput),
}
impl<'a> SetOrPush<'a> {
pub fn layout(&self) -> &'a Arc<DescriptorSetLayout> {
match *self {
Self::Set(set, offsets) => set.layout(),
Self::Push(writes) => writes.layout(),
}
}
#[inline]
pub fn num_buffers(&self) -> usize {
match self {
Self::Set(set, offsets) => set.num_buffers(),
Self::Push(writes) => writes.resources().num_buffers(),
}
}
#[inline]
pub fn buffer(&self, num: usize) -> Option<(&'a dyn BufferAccess, u32)> {
match *self {
Self::Set(set, offsets) => set.buffer(num),
Self::Push(writes) => writes.resources().buffer(num),
}
}
#[inline]
pub fn num_images(&self) -> usize {
match self {
Self::Set(set, offsets) => set.num_images(),
Self::Push(writes) => writes.resources().num_images(),
}
}
#[inline]
pub fn image(&self, num: usize) -> Option<(&'a dyn ImageViewAbstract, u32)> {
match *self {
Self::Set(set, offsets) => set.image(num),
Self::Push(writes) => writes.resources().image(num),
}
}
}
/// Holds the current stencil state of a command buffer builder.
#[derive(Clone, Copy, Debug, Default)]
pub struct StencilStateDynamic {

View File

@ -25,8 +25,8 @@ use crate::command_buffer::CommandBufferExecError;
use crate::command_buffer::ImageUninitializedSafe;
use crate::command_buffer::SecondaryCommandBuffer;
use crate::command_buffer::SubpassContents;
use crate::descriptor_set::builder::DescriptorSetBuilderOutput;
use crate::descriptor_set::layout::DescriptorDescTy;
use crate::descriptor_set::DescriptorSet;
use crate::descriptor_set::DescriptorSetWithOffsets;
use crate::format::ClearValue;
use crate::image::ImageAccess;
@ -58,7 +58,6 @@ use crate::SafeDeref;
use crate::VulkanObject;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::ffi::CStr;
use std::mem;
use std::ops::Range;
@ -1247,7 +1246,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(buf) = set.buffer(num) {
return buf.0;
@ -1262,7 +1261,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(buf) = set.buffer(num) {
return format!("Buffer bound to set {} descriptor {}", set_num, buf.1)
@ -1278,7 +1277,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(img) = set.image(num) {
return img.0.image();
@ -1293,7 +1292,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(img) = set.image(num) {
return format!("Image bound to set {} descriptor {}", set_num, img.1)
@ -1360,7 +1359,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(buf) = set.buffer(num) {
return buf.0;
@ -1378,7 +1377,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(buf) = set.buffer(num) {
return format!("Buffer bound to set {} descriptor {}", set_num, buf.1)
@ -1397,7 +1396,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(img) = set.image(num) {
return img.0.image();
@ -1412,7 +1411,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(img) = set.image(num) {
return format!("Image bound to set {} descriptor {}", set_num, img.1)
@ -1487,7 +1486,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(buf) = set.buffer(num) {
return buf.0;
@ -1514,7 +1513,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(buf) = set.buffer(num) {
return format!("Buffer bound to set {} descriptor {}", set_num, buf.1)
@ -1542,7 +1541,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(img) = set.image(num) {
return img.0.image();
@ -1557,7 +1556,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(img) = set.image(num) {
return format!("Image bound to set {} descriptor {}", set_num, img.1)
@ -1640,7 +1639,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(buf) = set.buffer(num) {
return buf.0;
@ -1671,7 +1670,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(buf) = set.buffer(num) {
return format!("Buffer bound to set {} descriptor {}", set_num, buf.1)
@ -1703,7 +1702,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(img) = set.image(num) {
return img.0.image();
@ -1718,7 +1717,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(img) = set.image(num) {
return format!("Image bound to set {} descriptor {}", set_num, img.1)
@ -1799,7 +1798,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(buf) = set.buffer(num) {
return buf.0;
@ -1830,7 +1829,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(buf) = set.buffer(num) {
return format!("Buffer bound to set {} descriptor {}", set_num, buf.1)
@ -1862,7 +1861,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(img) = set.image(num) {
return img.0.image();
@ -1877,7 +1876,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(img) = set.image(num) {
return format!("Image bound to set {} descriptor {}", set_num, img.1)
@ -1957,7 +1956,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(buf) = set.buffer(num) {
return buf.0;
@ -1990,7 +1989,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(buf) = set.buffer(num) {
return format!("Buffer bound to set {} descriptor {}", set_num, buf.1)
@ -2024,7 +2023,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
if let Some(img) = set.image(num) {
return img.0.image();
@ -2039,7 +2038,7 @@ impl SyncCommandBufferBuilder {
.descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32).0))
.map(|(set_num, cmd)| (set_num, cmd.bound_descriptor_set(set_num as u32)))
{
if let Some(img) = set.image(num) {
return format!("Image bound to set {} descriptor {}", set_num, img.1)
@ -2282,6 +2281,61 @@ impl SyncCommandBufferBuilder {
self.current_state.push_constants_pipeline_layout = Some(pipeline_layout);
}
/// Calls `vkCmdPushDescriptorSetKHR` on the builder.
pub unsafe fn push_descriptor_set(
&mut self,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: Arc<PipelineLayout>,
set_num: u32,
descriptor_writes: DescriptorSetBuilderOutput,
) {
struct Cmd {
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: Arc<PipelineLayout>,
set_num: u32,
descriptor_writes: DescriptorSetBuilderOutput,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdPushDescriptorSetKHR"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.push_descriptor_set(
self.pipeline_bind_point,
&self.pipeline_layout,
self.set_num,
self.descriptor_writes.writes(),
);
}
fn bound_descriptor_set(&self, num: u32) -> SetOrPush {
debug_assert!(num == self.set_num);
SetOrPush::Push(&self.descriptor_writes)
}
}
self.append_command(
Cmd {
pipeline_bind_point,
pipeline_layout: pipeline_layout.clone(),
set_num,
descriptor_writes,
},
&[],
)
.unwrap();
self.current_state.invalidate_descriptor_sets(
pipeline_bind_point,
pipeline_layout,
set_num,
1,
self.commands.last().unwrap(),
);
}
/// Calls `vkCmdResetEvent` on the builder.
#[inline]
pub unsafe fn reset_event(&mut self, event: Arc<Event>, stages: PipelineStages) {
@ -3266,7 +3320,7 @@ impl SyncCommandBufferBuilder {
for ds in descriptor_sets
.iter()
.enumerate()
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32).0)
.map(|(set_num, cmd)| cmd.bound_descriptor_set(set_num as u32))
{
for buf_num in 0..ds.num_buffers() {
let desc = ds
@ -3511,9 +3565,10 @@ impl<'b> SyncCommandBufferBuilderBindDescriptorSets<'b> {
);
}
fn bound_descriptor_set(&self, set_num: u32) -> (&dyn DescriptorSet, &[u32]) {
fn bound_descriptor_set(&self, set_num: u32) -> SetOrPush {
let index = set_num.checked_sub(self.first_set).unwrap() as usize;
self.descriptor_sets[index].as_ref()
let (set, offset) = self.descriptor_sets[index].as_ref();
SetOrPush::Set(set, offset)
}
}
@ -3530,65 +3585,13 @@ impl<'b> SyncCommandBufferBuilderBindDescriptorSets<'b> {
)
.unwrap();
let cmd = self.builder.commands.last().unwrap();
let state = match self
.builder
.current_state
.descriptor_sets
.entry(pipeline_bind_point)
{
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();
let max = (current_layouts.len() as u32).min(first_set + num_descriptor_sets);
(0..max).find(|&num| {
let num = num as usize;
!current_layouts[num].is_compatible_with(&new_layouts[num])
})
};
if let Some(invalidate_from) = invalidate_from {
// Remove disturbed sets and set new pipeline layout.
state
.descriptor_sets
.retain(|&num, _| num < invalidate_from);
state.pipeline_layout = pipeline_layout;
} else if (first_set + num_descriptor_sets) as usize
>= state.pipeline_layout.descriptor_set_layouts().len()
{
// New layout is a superset of the old one.
state.pipeline_layout = pipeline_layout;
}
state
}
};
for i in 0..num_descriptor_sets {
state.descriptor_sets.insert(first_set + i, cmd.clone());
}
self.builder.current_state.invalidate_descriptor_sets(
pipeline_bind_point,
pipeline_layout,
first_set,
num_descriptor_sets,
self.builder.commands.last().unwrap(),
);
}
}

View File

@ -65,6 +65,7 @@
//! queue with a fresh new barrier prototype.
pub use self::builder::CommandBufferState;
pub use self::builder::SetOrPush;
pub use self::builder::StencilOpStateDynamic;
pub use self::builder::StencilStateDynamic;
pub use self::builder::SyncCommandBufferBuilder;
@ -77,7 +78,6 @@ use crate::command_buffer::sys::UnsafeCommandBuffer;
use crate::command_buffer::sys::UnsafeCommandBufferBuilder;
use crate::command_buffer::CommandBufferExecError;
use crate::command_buffer::ImageUninitializedSafe;
use crate::descriptor_set::DescriptorSet;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::device::Queue;
@ -492,7 +492,7 @@ trait Command: Send + Sync {
panic!()
}
fn bound_descriptor_set(&self, set_num: u32) -> (&dyn DescriptorSet, &[u32]) {
fn bound_descriptor_set(&self, set_num: u32) -> SetOrPush {
panic!()
}

View File

@ -17,6 +17,7 @@ use crate::command_buffer::CommandBufferLevel;
use crate::command_buffer::CommandBufferUsage;
use crate::command_buffer::SecondaryCommandBuffer;
use crate::command_buffer::SubpassContents;
use crate::descriptor_set::sys::DescriptorWrite;
use crate::descriptor_set::sys::UnsafeDescriptorSet;
use crate::device::Device;
use crate::device::DeviceOwned;
@ -1354,6 +1355,41 @@ impl UnsafeCommandBufferBuilder {
);
}
/// Calls `vkCmdPushDescriptorSetKHR` on the builder.
///
/// If the list is empty then the command is automatically ignored.
#[inline]
pub unsafe fn push_descriptor_set(
&mut self,
pipeline_bind_point: PipelineBindPoint,
pipeline_layout: &PipelineLayout,
set_num: u32,
descriptor_writes: &[DescriptorWrite],
) {
debug_assert!(self.device().enabled_extensions().khr_push_descriptor);
if descriptor_writes.is_empty() {
return;
}
let fns = self.device().fns();
let cmd = self.internal_object();
let raw_writes: SmallVec<[_; 8]> = descriptor_writes
.iter()
.map(|write| write.to_vulkan(ash::vk::DescriptorSet::null()))
.collect();
fns.khr_push_descriptor.cmd_push_descriptor_set_khr(
cmd,
pipeline_bind_point.into(),
pipeline_layout.internal_object(),
set_num,
raw_writes.len() as u32,
raw_writes.as_ptr(),
);
}
/// Calls `vkCmdResetEvent` on the builder.
#[inline]
pub unsafe fn reset_event(&mut self, event: &Event, stages: PipelineStages) {

View File

@ -8,6 +8,7 @@
// according to those terms.
use crate::command_buffer::synced::CommandBufferState;
use crate::command_buffer::synced::SetOrPush;
use crate::descriptor_set::layout::DescriptorSetCompatibilityError;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::PipelineBindPoint;
@ -46,7 +47,12 @@ pub(in super::super) fn check_descriptor_sets_validity(
None => return Err(CheckDescriptorSetsValidityError::MissingDescriptorSet { set_num }),
};
match pipeline_set.ensure_compatible_with_bind(descriptor_set.0.layout()) {
let descriptor_set_layout = match descriptor_set {
SetOrPush::Set(descriptor_set, _dynamic_offsets) => descriptor_set.layout(),
SetOrPush::Push(descriptor_writes) => descriptor_writes.layout(),
};
match pipeline_set.ensure_compatible_with_bind(descriptor_set_layout) {
Ok(_) => (),
Err(error) => {
return Err(

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,8 @@ use std::sync::Arc;
#[derive(Clone, Debug, Default)]
pub struct DescriptorSetDesc {
descriptors: SmallVec<[Option<DescriptorDesc>; 32]>,
descriptors: SmallVec<[Option<DescriptorDesc>; 4]>,
push_descriptor: bool,
}
impl DescriptorSetDesc {
@ -71,6 +72,7 @@ impl DescriptorSetDesc {
{
DescriptorSetDesc {
descriptors: descriptors.into_iter().collect(),
push_descriptor: false,
}
}
@ -79,10 +81,12 @@ impl DescriptorSetDesc {
pub fn empty() -> DescriptorSetDesc {
DescriptorSetDesc {
descriptors: SmallVec::new(),
push_descriptor: false,
}
}
/// Returns the descriptors in the set.
#[inline]
pub fn bindings(&self) -> &[Option<DescriptorDesc>] {
&self.descriptors
}
@ -93,6 +97,12 @@ impl DescriptorSetDesc {
self.descriptors.get(num as usize).and_then(|b| b.as_ref())
}
/// Returns whether the description is set to be a push descriptor.
#[inline]
pub fn is_push_descriptor(&self) -> bool {
self.push_descriptor
}
/// Builds the union of this layout description and another.
#[inline]
pub fn union(
@ -116,7 +126,10 @@ impl DescriptorSetDesc {
)
})
.collect::<Result<_, ()>>()?;
Ok(DescriptorSetDesc { descriptors })
Ok(DescriptorSetDesc {
descriptors,
push_descriptor: false,
})
}
/// Builds the union of multiple descriptor sets.
@ -139,7 +152,16 @@ impl DescriptorSetDesc {
}
/// Changes a buffer descriptor's type to dynamic.
///
/// # Panics
///
/// - Panics if the description is set to be a push descriptor.
/// - Panics if `binding_num` does not refer to a `StorageBuffer` or `UniformBuffer` descriptor.
pub fn set_buffer_dynamic(&mut self, binding_num: u32) {
assert!(
!self.push_descriptor,
"push descriptor is enabled, which does not allow dynamic buffer descriptors"
);
assert!(
self.descriptor(binding_num).map_or(false, |desc| matches!(
desc.ty,
@ -197,6 +219,30 @@ impl DescriptorSetDesc {
immutable_samplers.extend(samplers.into_iter());
}
/// Sets the descriptor set layout to use push descriptors instead of descriptor sets.
///
/// If set to enabled, the
/// [`khr_push_descriptor`](crate::device::DeviceExtensions::khr_push_descriptor) extension must
/// be enabled on the device.
///
/// # Panics
///
/// - If enabled, panics if the description contains a dynamic buffer descriptor.
pub fn set_push_descriptor(&mut self, enabled: bool) {
if enabled {
assert!(
!self.descriptors.iter().flatten().any(|desc| {
matches!(
desc.ty.ty(),
DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic
)
}),
"descriptor set contains a dynamic buffer descriptor"
);
}
self.push_descriptor = enabled;
}
/// Sets the descriptor count for a descriptor that has a variable count.
pub fn set_variable_descriptor_count(&mut self, binding_num: u32, descriptor_count: u32) {
// TODO: Errors instead of panic
@ -221,9 +267,13 @@ impl DescriptorSetDesc {
///
/// "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.
/// meaning that all descriptors are compatible and flags are identical.
#[inline]
pub fn is_compatible_with(&self, other: &DescriptorSetDesc) -> bool {
if self.push_descriptor != other.push_descriptor {
return false;
}
let num_bindings = cmp::max(self.descriptors.len(), other.descriptors.len()) as u32;
(0..num_bindings).all(|binding_num| {
match (self.descriptor(binding_num), other.descriptor(binding_num)) {
@ -240,6 +290,8 @@ impl DescriptorSetDesc {
&self,
other: &DescriptorSetDesc,
) -> Result<(), DescriptorSetCompatibilityError> {
// Don't care about push descriptors.
if self.descriptors.len() < other.descriptors.len() {
return Err(DescriptorSetCompatibilityError::DescriptorsCountMismatch {
self_num: self.descriptors.len() as u32,
@ -276,12 +328,22 @@ impl DescriptorSetDesc {
Ok(())
}
/// Checks whether the descriptor of a pipeline layout `self` is compatible with the descriptor
/// of a descriptor set being bound `other`.
/// Checks whether the descriptor set of a pipeline layout `self` is compatible with the
/// descriptor set being bound `other`.
///
/// This performs the same check as `is_compatible_with`, but additionally ensures that the
/// shader can accept the binding.
pub fn ensure_compatible_with_bind(
&self,
other: &DescriptorSetDesc,
) -> Result<(), DescriptorSetCompatibilityError> {
if self.push_descriptor != other.push_descriptor {
return Err(DescriptorSetCompatibilityError::PushDescriptorMismatch {
self_enabled: self.push_descriptor,
other_enabled: other.push_descriptor,
});
}
if self.descriptors.len() != other.descriptors.len() {
return Err(DescriptorSetCompatibilityError::DescriptorsCountMismatch {
self_num: self.descriptors.len() as u32,
@ -327,6 +389,7 @@ where
fn from(val: I) -> Self {
DescriptorSetDesc {
descriptors: val.into_iter().collect(),
push_descriptor: false,
}
}
}
@ -790,6 +853,12 @@ pub enum DescriptorSetCompatibilityError {
error: DescriptorCompatibilityError,
binding_num: u32,
},
/// The push descriptor settings of the two sets are not compatible.
PushDescriptorMismatch {
self_enabled: bool,
other_enabled: bool,
},
}
impl error::Error for DescriptorSetCompatibilityError {
@ -807,18 +876,23 @@ impl error::Error for DescriptorSetCompatibilityError {
impl fmt::Display for DescriptorSetCompatibilityError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
DescriptorSetCompatibilityError::DescriptorsCountMismatch { .. } => {
"the number of descriptors in the two sets is not compatible."
}
DescriptorSetCompatibilityError::IncompatibleDescriptors { .. } => {
"two descriptors are incompatible"
}
match *self {
DescriptorSetCompatibilityError::DescriptorsCountMismatch { .. } => {
write!(
fmt,
"the number of descriptors in the two sets is not compatible"
)
}
)
DescriptorSetCompatibilityError::IncompatibleDescriptors { .. } => {
write!(fmt, "two descriptors are incompatible")
}
DescriptorSetCompatibilityError::PushDescriptorMismatch { .. } => {
write!(
fmt,
"the push descriptor settings of the two sets are not compatible"
)
}
}
}
}
@ -828,9 +902,6 @@ pub enum DescriptorCompatibilityError {
/// The number of descriptors is not compatible.
DescriptorCount { first: u32, second: u32 },
/// The variable counts of the descriptors is not compatible.
VariableCount { first: bool, second: bool },
/// The presence or absence of a descriptor in a binding is not compatible.
Empty { first: bool, second: bool },
@ -866,6 +937,9 @@ pub enum DescriptorCompatibilityError {
first: DescriptorType,
second: DescriptorType,
},
/// The variable counts of the descriptors is not compatible.
VariableCount { first: bool, second: bool },
}
impl error::Error for DescriptorCompatibilityError {}

View File

@ -12,6 +12,7 @@ use crate::descriptor_set::layout::DescriptorDesc;
use crate::descriptor_set::layout::DescriptorDescTy;
use crate::descriptor_set::layout::DescriptorSetCompatibilityError;
use crate::descriptor_set::layout::DescriptorSetDesc;
use crate::descriptor_set::layout::DescriptorType;
use crate::descriptor_set::pool::DescriptorsCount;
use crate::device::Device;
use crate::device::DeviceOwned;
@ -31,7 +32,7 @@ pub struct DescriptorSetLayout {
device: Arc<Device>,
// Descriptors.
desc: DescriptorSetDesc,
// Number of descriptors.
// Number of descriptors of each type.
descriptors_count: DescriptorsCount,
// Number of descriptors in a variable count descriptor. Will be zero if no variable count descriptors are present.
variable_descriptor_count: u32,
@ -45,21 +46,38 @@ impl DescriptorSetLayout {
/// empty, you can make the iterator yield `None` for an element.
pub fn new<D>(
device: Arc<Device>,
desc: D,
set_desc: D,
) -> Result<DescriptorSetLayout, DescriptorSetLayoutError>
where
D: Into<DescriptorSetDesc>,
{
let desc = desc.into();
let set_desc = set_desc.into();
let mut descriptors_count = DescriptorsCount::zero();
let mut variable_descriptor_count = 0;
let bindings = desc.bindings();
let bindings = set_desc.bindings();
let mut bindings_vk = Vec::with_capacity(bindings.len());
let mut binding_flags_vk = Vec::with_capacity(bindings.len());
let mut immutable_samplers_vk: Vec<Box<[ash::vk::Sampler]>> = Vec::new(); // only to keep the arrays of handles alive
for (binding, desc) in bindings.iter().enumerate() {
let desc = match desc {
let mut flags = ash::vk::DescriptorSetLayoutCreateFlags::empty();
if set_desc.is_push_descriptor() {
if !device.enabled_extensions().khr_push_descriptor {
return Err(DescriptorSetLayoutError::ExtensionNotEnabled {
extension: "khr_push_descriptor",
reason: "description was set to be a push descriptor",
});
}
// TODO: VUID-VkDescriptorSetLayoutCreateInfo-flags-04590
// If flags contains VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR, flags must
// not contain VK_DESCRIPTOR_SET_LAYOUT_CREATE_HOST_ONLY_POOL_BIT_VALVE
flags |= ash::vk::DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR;
}
for (binding, binding_desc) in bindings.iter().enumerate() {
let binding_desc = match binding_desc {
Some(d) => d,
None => continue,
};
@ -67,15 +85,34 @@ impl DescriptorSetLayout {
// 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();
descriptors_count.add_num(ty, desc.descriptor_count);
let ty = binding_desc.ty.ty();
if set_desc.is_push_descriptor() {
if matches!(
ty,
DescriptorType::StorageBufferDynamic | DescriptorType::UniformBufferDynamic
) {
return Err(DescriptorSetLayoutError::PushDescriptorDynamicBuffer);
}
// TODO: VUID-VkDescriptorSetLayoutCreateInfo-flags-02208
// If flags contains VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR, then all
// elements of pBindings must not have a descriptorType of
// VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT
// TODO: VUID-VkDescriptorSetLayoutCreateInfo-flags-04591
// If flags contains VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR,
// pBindings must not have a descriptorType of VK_DESCRIPTOR_TYPE_MUTABLE_VALVE
}
descriptors_count.add_num(ty, binding_desc.descriptor_count);
let mut binding_flags = ash::vk::DescriptorBindingFlags::empty();
let immutable_samplers = desc.ty.immutable_samplers();
let immutable_samplers = binding_desc.ty.immutable_samplers();
let p_immutable_samplers = if !immutable_samplers.is_empty() {
if desc.descriptor_count != immutable_samplers.len() as u32 {
if binding_desc.descriptor_count != immutable_samplers.len() as u32 {
return Err(DescriptorSetLayoutError::ImmutableSamplersCountMismatch {
descriptor_count: desc.descriptor_count,
descriptor_count: binding_desc.descriptor_count,
sampler_count: immutable_samplers.len() as u32,
});
}
@ -97,43 +134,42 @@ impl DescriptorSetLayout {
ptr::null()
};
if desc.variable_count {
if binding_desc.variable_count {
if binding != bindings.len() - 1 {
return Err(DescriptorSetLayoutError::VariableCountDescMustBeLast);
}
if desc.ty == DescriptorDescTy::UniformBufferDynamic
|| desc.ty == DescriptorDescTy::StorageBufferDynamic
if binding_desc.ty == DescriptorDescTy::UniformBufferDynamic
|| binding_desc.ty == DescriptorDescTy::StorageBufferDynamic
{
return Err(DescriptorSetLayoutError::VariableCountDescMustNotBeDynamic);
}
if !device.enabled_features().runtime_descriptor_array {
return Err(DescriptorSetLayoutError::VariableCountIncompatibleDevice(
IncompatibleDevice::MissingFeature(MissingFeature::RuntimeDescriptorArray),
));
return Err(DescriptorSetLayoutError::FeatureNotEnabled {
feature: "runtime_descriptor_array",
reason: "binding has a variable count",
});
}
if !device
.enabled_features()
.descriptor_binding_variable_descriptor_count
{
return Err(DescriptorSetLayoutError::VariableCountIncompatibleDevice(
IncompatibleDevice::MissingFeature(
MissingFeature::DescriptorBindingVariableDescriptorCount,
),
));
return Err(DescriptorSetLayoutError::FeatureNotEnabled {
feature: "descriptor_binding_variable_descriptor_count",
reason: "binding has a variable count",
});
}
if !device.enabled_features().descriptor_binding_partially_bound {
return Err(DescriptorSetLayoutError::VariableCountIncompatibleDevice(
IncompatibleDevice::MissingFeature(
MissingFeature::DescriptorBindingPartiallyBound,
),
));
return Err(DescriptorSetLayoutError::FeatureNotEnabled {
feature: "descriptor_binding_partially_bound",
reason: "binding has a variable count",
});
}
variable_descriptor_count = desc.descriptor_count;
variable_descriptor_count = binding_desc.descriptor_count;
// TODO: should these be settable separately by the user?
binding_flags |= ash::vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT;
binding_flags |= ash::vk::DescriptorBindingFlags::PARTIALLY_BOUND;
@ -142,20 +178,37 @@ impl DescriptorSetLayout {
bindings_vk.push(ash::vk::DescriptorSetLayoutBinding {
binding: binding as u32,
descriptor_type: ty.into(),
descriptor_count: desc.descriptor_count,
stage_flags: desc.stages.into(),
descriptor_count: binding_desc.descriptor_count,
stage_flags: binding_desc.stages.into(),
p_immutable_samplers,
});
binding_flags_vk.push(binding_flags);
}
// Note that it seems legal to have no descriptor at all in the set.
if set_desc.is_push_descriptor()
&& descriptors_count.total()
> device
.physical_device()
.properties()
.max_push_descriptors
.unwrap_or(0)
{
return Err(DescriptorSetLayoutError::MaxPushDescriptorsExceeded {
max: device
.physical_device()
.properties()
.max_push_descriptors
.unwrap_or(0),
obtained: descriptors_count.total(),
});
}
let handle = unsafe {
let binding_flags_infos = if device.api_version() >= Version::V1_2
|| device.enabled_extensions().ext_descriptor_indexing
{
Some(ash::vk::DescriptorSetLayoutBindingFlagsCreateInfo {
// Note that it seems legal to have no descriptor at all in the set.
binding_count: binding_flags_vk.len() as u32,
p_binding_flags: binding_flags_vk.as_ptr(),
..Default::default()
@ -165,7 +218,7 @@ impl DescriptorSetLayout {
};
let infos = ash::vk::DescriptorSetLayoutCreateInfo {
flags: ash::vk::DescriptorSetLayoutCreateFlags::empty(),
flags,
binding_count: bindings_vk.len() as u32,
p_bindings: bindings_vk.as_ptr(),
p_next: if let Some(next) = binding_flags_infos.as_ref() {
@ -193,7 +246,7 @@ impl DescriptorSetLayout {
Ok(DescriptorSetLayout {
handle,
device,
desc,
desc: set_desc,
descriptors_count,
variable_descriptor_count,
})
@ -285,11 +338,17 @@ impl Drop for DescriptorSetLayout {
}
}
/// Error related to descriptor set layout
/// Error related to descriptor set layout.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DescriptorSetLayoutError {
/// Out of Memory
OomError(OomError),
ExtensionNotEnabled {
extension: &'static str,
reason: &'static str,
},
FeatureNotEnabled {
feature: &'static str,
reason: &'static str,
},
/// The number of immutable samplers does not match the descriptor count.
ImmutableSamplersCountMismatch {
@ -297,42 +356,25 @@ pub enum DescriptorSetLayoutError {
sampler_count: u32,
},
/// Variable count descriptor must be last binding
/// The maximum number of push descriptors has been exceeded.
MaxPushDescriptorsExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// Out of Memory.
OomError(OomError),
/// The layout was being created for push descriptors, but included a dynamic buffer descriptor.
PushDescriptorDynamicBuffer,
/// Variable count descriptor must be last binding.
VariableCountDescMustBeLast,
/// Variable count descriptor must not be a dynamic buffer
/// Variable count descriptor must not be a dynamic buffer.
VariableCountDescMustNotBeDynamic,
/// Device is not compatible with variable count descriptors
VariableCountIncompatibleDevice(IncompatibleDevice),
}
// Part of the DescriptorSetLayoutError for the case
// of missing features on a device.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IncompatibleDevice {
MissingExtension(MissingExtension),
MissingFeature(MissingFeature),
InsufficientApiVersion {
required: Version,
obtained: Version,
},
}
// Part of the IncompatibleDevice for the case
// of missing features on a device.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MissingFeature {
RuntimeDescriptorArray,
DescriptorBindingVariableDescriptorCount,
DescriptorBindingPartiallyBound,
}
// Part of the IncompatibleDevice for the case
// of missing extensions on a device.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MissingExtension {
DescriptorIndexing,
}
impl From<OomError> for DescriptorSetLayoutError {
@ -346,21 +388,45 @@ impl std::error::Error for DescriptorSetLayoutError {}
impl std::fmt::Display for DescriptorSetLayoutError {
#[inline]
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::OomError(_) => "out of memory",
Self::ImmutableSamplersCountMismatch { .. } =>
"the number of immutable samplers does not match the descriptor count",
Self::VariableCountDescMustBeLast =>
"variable count descriptor must be last binding",
Self::VariableCountDescMustNotBeDynamic =>
"variable count descriptor must not be a dynamic buffer",
Self::VariableCountIncompatibleDevice(_) =>
"device is not compatible with variable count descriptors",
match *self {
Self::ExtensionNotEnabled { extension, reason } => {
write!(
fmt,
"the extension {} must be enabled: {}",
extension, reason
)
}
)
Self::FeatureNotEnabled { feature, reason } => {
write!(fmt, "the feature {} must be enabled: {}", feature, reason)
}
Self::ImmutableSamplersCountMismatch { .. } => {
write!(
fmt,
"the number of immutable samplers does not match the descriptor count"
)
}
Self::MaxPushDescriptorsExceeded { .. } => {
write!(
fmt,
"the maximum number of push descriptors has been exceeded"
)
}
Self::PushDescriptorDynamicBuffer => {
write!(fmt, "the layout was being created for push descriptors, but included a dynamic buffer descriptor")
}
Self::OomError(_) => {
write!(fmt, "out of memory")
}
Self::VariableCountDescMustBeLast => {
write!(fmt, "variable count descriptor must be last binding")
}
Self::VariableCountDescMustNotBeDynamic => {
write!(
fmt,
"variable count descriptor must not be a dynamic buffer"
)
}
}
}
}

View File

@ -73,6 +73,7 @@
//! - The `DescriptorSetsCollection` trait is implemented on collections of types that implement
//! `DescriptorSet`. It is what you pass to the draw functions.
pub use self::builder::DescriptorSetBuilder;
pub use self::collection::DescriptorSetsCollection;
use self::layout::DescriptorSetLayout;
pub use self::persistent::PersistentDescriptorSet;
@ -94,7 +95,7 @@ use std::hash::Hash;
use std::hash::Hasher;
use std::sync::Arc;
mod builder;
pub mod builder;
mod collection;
pub mod layout;
pub mod persistent;
@ -293,9 +294,6 @@ pub enum DescriptorSetError {
/// Builder is already within an array.
AlreadyInArray,
/// The builder has previously return an error and is an unknown state.
BuilderPoisoned,
/// The number of array layers of an image doesn't match what was expected.
ArrayLayersMismatch {
/// Number of expected array layers for the image.
@ -320,8 +318,8 @@ pub enum DescriptorSetError {
obtained: u32,
},
/// Expected a multisampled image, but got a single-sampled image.
ExpectedMultisampled,
/// The builder has previously return an error and is an unknown state.
BuilderPoisoned,
/// Operation can not be performed on an empty descriptor.
DescriptorIsEmpty,
@ -334,6 +332,9 @@ pub enum DescriptorSetError {
obtained: u32,
},
/// Expected a multisampled image, but got a single-sampled image.
ExpectedMultisampled,
/// The format of an image view doesn't match what was expected.
ImageViewFormatMismatch {
/// Expected format.

View File

@ -22,7 +22,6 @@
//! TODO:
use super::builder::DescriptorSetBuilder;
use super::builder::DescriptorSetBuilderOutput;
use super::resources::DescriptorSetResources;
use super::DescriptorSetError;
use crate::buffer::BufferView;
@ -52,9 +51,13 @@ pub struct PersistentDescriptorSet<P = StdDescriptorPoolAlloc> {
impl PersistentDescriptorSet {
/// Starts the process of building a `PersistentDescriptorSet`. Returns a builder.
pub fn start(layout: Arc<DescriptorSetLayout>) -> PersistentDescriptorSetBuilder {
assert!(
!layout.desc().is_push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to build a descriptor set object"
);
PersistentDescriptorSetBuilder {
inner: DescriptorSetBuilder::start(layout),
poisoned: false,
}
}
}
@ -128,7 +131,6 @@ where
/// Prototype of a `PersistentDescriptorSet`.
pub struct PersistentDescriptorSetBuilder {
inner: DescriptorSetBuilder,
poisoned: bool,
}
impl PersistentDescriptorSetBuilder {
@ -142,17 +144,8 @@ impl PersistentDescriptorSetBuilder {
/// the "array", add one element, then leave.
#[inline]
pub fn enter_array(&mut self) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.enter_array() {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.enter_array()?;
Ok(self)
}
/// Leaves the array. Call this once you added all the elements of the array.
@ -160,33 +153,15 @@ impl PersistentDescriptorSetBuilder {
/// Returns an error if the array is missing elements, or if the builder is not in an array.
#[inline]
pub fn leave_array(&mut self) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.leave_array() {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.leave_array()?;
Ok(self)
}
/// Skips the current descriptor if it is empty.
#[inline]
pub fn add_empty(&mut self) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_empty() {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_empty()?;
Ok(self)
}
/// Binds a buffer as the next descriptor.
@ -197,17 +172,8 @@ impl PersistentDescriptorSetBuilder {
&mut self,
buffer: Arc<dyn BufferAccess + 'static>,
) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_buffer(buffer) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_buffer(buffer)?;
Ok(self)
}
/// Binds a buffer view as the next descriptor.
@ -221,17 +187,8 @@ impl PersistentDescriptorSetBuilder {
where
B: BufferAccess + 'static,
{
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_buffer_view(view) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_buffer_view(view)?;
Ok(self)
}
/// Binds an image view as the next descriptor.
@ -242,17 +199,8 @@ impl PersistentDescriptorSetBuilder {
&mut self,
image_view: Arc<dyn ImageViewAbstract + 'static>,
) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_image(image_view) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_image(image_view)?;
Ok(self)
}
/// Binds an image view with a sampler as the next descriptor.
@ -267,17 +215,8 @@ impl PersistentDescriptorSetBuilder {
image_view: Arc<dyn ImageViewAbstract + 'static>,
sampler: Arc<Sampler>,
) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_sampled_image(image_view, sampler) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_sampled_image(image_view, sampler)?;
Ok(self)
}
/// Binds a sampler as the next descriptor.
@ -285,17 +224,8 @@ impl PersistentDescriptorSetBuilder {
/// An error is returned if the sampler isn't compatible with the descriptor.
#[inline]
pub fn add_sampler(&mut self, sampler: Arc<Sampler>) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_sampler(sampler) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_sampler(sampler)?;
Ok(self)
}
/// Builds a `PersistentDescriptorSet` from the builder.
@ -315,19 +245,10 @@ impl PersistentDescriptorSetBuilder {
where
P: ?Sized + DescriptorPool,
{
if self.poisoned {
return Err(DescriptorSetError::BuilderPoisoned);
}
let DescriptorSetBuilderOutput {
layout,
writes,
resources,
} = self.inner.output()?;
let (layout, writes, resources) = self.inner.build()?.into();
let set = unsafe {
let mut set = pool.alloc(&layout)?;
set.inner_mut().write(pool.device(), writes.into_iter());
set.inner_mut().write(pool.device(), &writes);
set
};

View File

@ -79,6 +79,13 @@ macro_rules! descriptors_count {
)+
}
}
/// Returns the total number of descriptors.
#[inline]
pub fn total(&self) -> u32 {
std::array::IntoIter::new([$(self.$name,)+]).sum()
}
/// Adds one descriptor of the given type to the count.
#[inline]
pub fn add_one(&mut self, ty: DescriptorType) {

View File

@ -160,16 +160,16 @@ impl UnsafeDescriptorPool {
let layouts: SmallVec<[_; 8]> = layouts
.into_iter()
.map(|l| {
.map(|layout| {
assert_eq!(
self.device.internal_object(),
l.device().internal_object(),
"Tried to allocate from a pool with a set layout of a different \
device"
layout.device().internal_object(),
"Tried to allocate from a pool with a set layout of a different device"
);
debug_assert!(!layout.desc().is_push_descriptor());
variable_descriptor_counts.push(l.variable_descriptor_count());
l.internal_object()
variable_descriptor_counts.push(layout.variable_descriptor_count());
layout.internal_object()
})
.collect();

View File

@ -8,7 +8,6 @@
// according to those terms.
use super::builder::DescriptorSetBuilder;
use super::builder::DescriptorSetBuilderOutput;
use super::resources::DescriptorSetResources;
use super::DescriptorSetError;
use crate::buffer::BufferView;
@ -49,11 +48,14 @@ impl SingleLayoutDescSetPool {
/// Initializes a new pool. The pool is configured to allocate sets that corresponds to the
/// parameters passed to this function.
pub fn new(layout: Arc<DescriptorSetLayout>) -> Self {
let device = layout.device().clone();
assert!(
!layout.desc().is_push_descriptor(),
"the provided descriptor set layout is for push descriptors, and cannot be used to build a descriptor set object"
);
Self {
inner: None,
device,
device: layout.device().clone(),
set_count: 4,
layout,
}
@ -68,7 +70,6 @@ impl SingleLayoutDescSetPool {
SingleLayoutDescSetBuilder {
pool: self,
inner: DescriptorSetBuilder::start(layout),
poisoned: false,
}
}
@ -239,7 +240,6 @@ impl Hash for SingleLayoutDescSet {
pub struct SingleLayoutDescSetBuilder<'a> {
pool: &'a mut SingleLayoutDescSetPool,
inner: DescriptorSetBuilder,
poisoned: bool,
}
impl<'a> SingleLayoutDescSetBuilder<'a> {
@ -253,17 +253,8 @@ impl<'a> SingleLayoutDescSetBuilder<'a> {
/// the "array", add one element, then leave.
#[inline]
pub fn enter_array(&mut self) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.enter_array() {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.enter_array()?;
Ok(self)
}
/// Leaves the array. Call this once you added all the elements of the array.
@ -271,33 +262,15 @@ impl<'a> SingleLayoutDescSetBuilder<'a> {
/// Returns an error if the array is missing elements, or if the builder is not in an array.
#[inline]
pub fn leave_array(&mut self) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.leave_array() {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.leave_array()?;
Ok(self)
}
/// Skips the current descriptor if it is empty.
#[inline]
pub fn add_empty(&mut self) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_empty() {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_empty()?;
Ok(self)
}
/// Binds a buffer as the next descriptor.
@ -308,17 +281,8 @@ impl<'a> SingleLayoutDescSetBuilder<'a> {
&mut self,
buffer: Arc<dyn BufferAccess + 'static>,
) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_buffer(buffer) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_buffer(buffer)?;
Ok(self)
}
/// Binds a buffer view as the next descriptor.
@ -332,17 +296,8 @@ impl<'a> SingleLayoutDescSetBuilder<'a> {
where
B: BufferAccess + 'static,
{
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_buffer_view(view) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_buffer_view(view)?;
Ok(self)
}
/// Binds an image view as the next descriptor.
@ -353,17 +308,8 @@ impl<'a> SingleLayoutDescSetBuilder<'a> {
&mut self,
image_view: Arc<dyn ImageViewAbstract + 'static>,
) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_image(image_view) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_image(image_view)?;
Ok(self)
}
/// Binds an image view with a sampler as the next descriptor.
@ -378,17 +324,8 @@ impl<'a> SingleLayoutDescSetBuilder<'a> {
image_view: Arc<dyn ImageViewAbstract + 'static>,
sampler: Arc<Sampler>,
) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_sampled_image(image_view, sampler) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_sampled_image(image_view, sampler)?;
Ok(self)
}
/// Binds a sampler as the next descriptor.
@ -396,36 +333,16 @@ impl<'a> SingleLayoutDescSetBuilder<'a> {
/// An error is returned if the sampler isn't compatible with the descriptor.
#[inline]
pub fn add_sampler(&mut self, sampler: Arc<Sampler>) -> Result<&mut Self, DescriptorSetError> {
if self.poisoned {
Err(DescriptorSetError::BuilderPoisoned)
} else {
match self.inner.add_sampler(sampler) {
Ok(_) => Ok(self),
Err(e) => {
self.poisoned = true;
Err(e)
}
}
}
self.inner.add_sampler(sampler)?;
Ok(self)
}
/// Builds a `SingleLayoutDescSet` from the builder.
pub fn build(self) -> Result<SingleLayoutDescSet, DescriptorSetError> {
if self.poisoned {
return Err(DescriptorSetError::BuilderPoisoned);
}
let DescriptorSetBuilderOutput {
layout,
writes,
resources,
} = self.inner.output()?;
let (layout, writes, resources) = self.inner.build()?.into();
let mut alloc = self.pool.next_alloc()?;
unsafe {
alloc
.inner_mut()
.write(&self.pool.device, writes.into_iter());
alloc.inner_mut().write(&self.pool.device, &writes);
}
Ok(SingleLayoutDescSet {

View File

@ -54,170 +54,27 @@ impl UnsafeDescriptorSet {
/// command buffer contains a pointer/reference to a descriptor set, it is illegal to write
/// to it.
///
pub unsafe fn write<I>(&mut self, device: &Device, writes: I)
where
I: IntoIterator<Item = DescriptorWrite>,
{
pub unsafe fn write(&mut self, device: &Device, writes: &[DescriptorWrite]) {
let fns = device.fns();
// In this function, we build 4 arrays: one array of image descriptors (image_descriptors),
// one for buffer descriptors (buffer_descriptors), one for buffer view descriptors
// (buffer_views_descriptors), and one for the final list of writes (raw_writes).
// Only the final list is passed to Vulkan, but it will contain pointers to the first three
// lists in `pImageInfo`, `pBufferInfo` and `pTexelBufferView`.
//
// In order to handle that, we start by writing null pointers as placeholders in the final
// writes, and we store in `raw_writes_img_infos`, `raw_writes_buf_infos` and
// `raw_writes_buf_view_infos` the offsets of the pointers compared to the start of the
// list.
// Once we have finished iterating all the writes requested by the user, we modify
// `raw_writes` to point to the correct locations.
let mut buffer_descriptors: SmallVec<[_; 64]> = SmallVec::new();
let mut image_descriptors: SmallVec<[_; 64]> = SmallVec::new();
let mut buffer_views_descriptors: SmallVec<[_; 64]> = SmallVec::new();
let mut raw_writes: SmallVec<[_; 64]> = SmallVec::new();
let mut raw_writes_img_infos: SmallVec<[_; 64]> = SmallVec::new();
let mut raw_writes_buf_infos: SmallVec<[_; 64]> = SmallVec::new();
let mut raw_writes_buf_view_infos: SmallVec<[_; 64]> = SmallVec::new();
for indiv_write in writes {
// Since the `DescriptorWrite` objects are built only through functions, we know for
// sure that it's impossible to have an empty descriptor write.
debug_assert!(!indiv_write.inner.is_empty());
// The whole struct thats written here is valid, except for pImageInfo, pBufferInfo
// and pTexelBufferView which are placeholder values.
raw_writes.push(ash::vk::WriteDescriptorSet {
dst_set: self.set,
dst_binding: indiv_write.binding,
dst_array_element: indiv_write.first_array_element,
descriptor_count: indiv_write.inner.len() as u32,
descriptor_type: indiv_write.ty().into(),
p_image_info: ptr::null(),
p_buffer_info: ptr::null(),
p_texel_buffer_view: ptr::null(),
..Default::default()
});
match indiv_write.inner[0] {
DescriptorWriteInner::Sampler(_)
| DescriptorWriteInner::CombinedImageSampler(_, _, _)
| DescriptorWriteInner::SampledImage(_, _)
| DescriptorWriteInner::StorageImage(_, _)
| DescriptorWriteInner::InputAttachment(_, _) => {
raw_writes_img_infos.push(Some(image_descriptors.len()));
raw_writes_buf_infos.push(None);
raw_writes_buf_view_infos.push(None);
}
DescriptorWriteInner::UniformBuffer(_, _, _)
| DescriptorWriteInner::StorageBuffer(_, _, _)
| DescriptorWriteInner::DynamicUniformBuffer(_, _, _)
| DescriptorWriteInner::DynamicStorageBuffer(_, _, _) => {
raw_writes_img_infos.push(None);
raw_writes_buf_infos.push(Some(buffer_descriptors.len()));
raw_writes_buf_view_infos.push(None);
}
DescriptorWriteInner::UniformTexelBuffer(_)
| DescriptorWriteInner::StorageTexelBuffer(_) => {
raw_writes_img_infos.push(None);
raw_writes_buf_infos.push(None);
raw_writes_buf_view_infos.push(Some(buffer_views_descriptors.len()));
}
}
for elem in indiv_write.inner.iter() {
match *elem {
DescriptorWriteInner::UniformBuffer(buffer, offset, size)
| DescriptorWriteInner::DynamicUniformBuffer(buffer, offset, size) => {
buffer_descriptors.push(ash::vk::DescriptorBufferInfo {
buffer,
offset,
range: size,
});
}
DescriptorWriteInner::StorageBuffer(buffer, offset, size)
| DescriptorWriteInner::DynamicStorageBuffer(buffer, offset, size) => {
buffer_descriptors.push(ash::vk::DescriptorBufferInfo {
buffer,
offset,
range: size,
});
}
DescriptorWriteInner::Sampler(sampler) => {
image_descriptors.push(ash::vk::DescriptorImageInfo {
sampler,
image_view: ash::vk::ImageView::null(),
image_layout: ash::vk::ImageLayout::UNDEFINED,
});
}
DescriptorWriteInner::CombinedImageSampler(sampler, view, layout) => {
image_descriptors.push(ash::vk::DescriptorImageInfo {
sampler,
image_view: view,
image_layout: layout,
});
}
DescriptorWriteInner::StorageImage(view, layout) => {
image_descriptors.push(ash::vk::DescriptorImageInfo {
sampler: ash::vk::Sampler::null(),
image_view: view,
image_layout: layout,
});
}
DescriptorWriteInner::SampledImage(view, layout) => {
image_descriptors.push(ash::vk::DescriptorImageInfo {
sampler: ash::vk::Sampler::null(),
image_view: view,
image_layout: layout,
});
}
DescriptorWriteInner::InputAttachment(view, layout) => {
image_descriptors.push(ash::vk::DescriptorImageInfo {
sampler: ash::vk::Sampler::null(),
image_view: view,
image_layout: layout,
});
}
DescriptorWriteInner::UniformTexelBuffer(view)
| DescriptorWriteInner::StorageTexelBuffer(view) => {
buffer_views_descriptors.push(view);
}
}
}
}
// Now that `image_descriptors`, `buffer_descriptors` and `buffer_views_descriptors` are
// entirely filled and will never move again, we can fill the pointers in `raw_writes`.
for (i, write) in raw_writes.iter_mut().enumerate() {
write.p_image_info = match raw_writes_img_infos[i] {
Some(off) => image_descriptors.as_ptr().offset(off as isize),
None => ptr::null(),
};
write.p_buffer_info = match raw_writes_buf_infos[i] {
Some(off) => buffer_descriptors.as_ptr().offset(off as isize),
None => ptr::null(),
};
write.p_texel_buffer_view = match raw_writes_buf_view_infos[i] {
Some(off) => buffer_views_descriptors.as_ptr().offset(off as isize),
None => ptr::null(),
};
}
let raw_writes: SmallVec<[_; 8]> = writes
.iter()
.map(|write| write.to_vulkan(self.set))
.collect();
// It is forbidden to call `vkUpdateDescriptorSets` with 0 writes, so we need to perform
// this emptiness check.
if !raw_writes.is_empty() {
fns.v1_0.update_descriptor_sets(
device.internal_object(),
raw_writes.len() as u32,
raw_writes.as_ptr(),
0,
ptr::null(),
);
if raw_writes.is_empty() {
return;
}
fns.v1_0.update_descriptor_sets(
device.internal_object(),
raw_writes.len() as u32,
raw_writes.as_ptr(),
0,
ptr::null(),
);
}
}
@ -240,349 +97,443 @@ impl fmt::Debug for UnsafeDescriptorSet {
///
/// Use the various constructors to build a `DescriptorWrite`. While it is safe to build a
/// `DescriptorWrite`, it is unsafe to actually use it to write to a descriptor set.
// TODO: allow binding whole arrays at once
pub struct DescriptorWrite {
binding: u32,
first_array_element: u32,
inner: SmallVec<[DescriptorWriteInner; 1]>,
descriptor_type: DescriptorType,
info: DescriptorWriteInfo,
}
#[derive(Debug, Clone)]
enum DescriptorWriteInner {
Sampler(ash::vk::Sampler),
StorageImage(ash::vk::ImageView, ash::vk::ImageLayout),
SampledImage(ash::vk::ImageView, ash::vk::ImageLayout),
CombinedImageSampler(ash::vk::Sampler, ash::vk::ImageView, ash::vk::ImageLayout),
UniformTexelBuffer(ash::vk::BufferView),
StorageTexelBuffer(ash::vk::BufferView),
UniformBuffer(ash::vk::Buffer, DeviceSize, DeviceSize),
StorageBuffer(ash::vk::Buffer, DeviceSize, DeviceSize),
DynamicUniformBuffer(ash::vk::Buffer, DeviceSize, DeviceSize),
DynamicStorageBuffer(ash::vk::Buffer, DeviceSize, DeviceSize),
InputAttachment(ash::vk::ImageView, ash::vk::ImageLayout),
}
macro_rules! smallvec {
($elem:expr) => {{
let mut s = SmallVec::new();
s.push($elem);
s
}};
#[derive(Clone, Debug)]
enum DescriptorWriteInfo {
Image(SmallVec<[ash::vk::DescriptorImageInfo; 1]>),
Buffer(SmallVec<[ash::vk::DescriptorBufferInfo; 1]>),
BufferView(SmallVec<[ash::vk::BufferView; 1]>),
}
impl DescriptorWrite {
#[inline]
pub fn storage_image<I>(binding: u32, array_element: u32, image_view: &I) -> DescriptorWrite
where
I: ImageViewAbstract,
{
let layouts = image_view
.image()
.descriptor_layouts()
.expect("descriptor_layouts must return Some when used in an image view");
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!({
DescriptorWriteInner::StorageImage(
image_view.inner().internal_object(),
layouts.storage_image.into(),
)
}),
}
}
#[inline]
pub fn sampler(binding: u32, array_element: u32, sampler: &Arc<Sampler>) -> DescriptorWrite {
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!(DescriptorWriteInner::Sampler(sampler.internal_object())),
}
}
#[inline]
pub fn sampled_image<I>(binding: u32, array_element: u32, image_view: &I) -> DescriptorWrite
where
I: ImageViewAbstract,
{
let layouts = image_view
.image()
.descriptor_layouts()
.expect("descriptor_layouts must return Some when used in an image view");
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!({
DescriptorWriteInner::SampledImage(
image_view.inner().internal_object(),
layouts.sampled_image.into(),
)
}),
}
}
#[inline]
pub fn combined_image_sampler<I>(
pub fn storage_image<'a, I>(
binding: u32,
array_element: u32,
sampler: Option<&Arc<Sampler>>, // Some for dynamic sampler, None for immutable
image_view: &I,
first_array_element: u32,
image_views: impl IntoIterator<Item = &'a I>,
) -> DescriptorWrite
where
I: ImageViewAbstract,
I: ImageViewAbstract + 'a,
{
let layouts = image_view
.image()
.descriptor_layouts()
.expect("descriptor_layouts must return Some when used in an image view");
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!({
DescriptorWriteInner::CombinedImageSampler(
sampler.map(|s| s.internal_object()).unwrap_or_default(),
image_view.inner().internal_object(),
layouts.combined_image_sampler.into(),
)
}),
first_array_element,
descriptor_type: DescriptorType::StorageImage,
info: DescriptorWriteInfo::Image(
image_views
.into_iter()
.map(|image_view| {
let layouts = image_view.image().descriptor_layouts().expect(
"descriptor_layouts must return Some when used in an image view",
);
ash::vk::DescriptorImageInfo {
sampler: ash::vk::Sampler::null(),
image_view: image_view.inner().internal_object(),
image_layout: layouts.storage_image.into(),
}
})
.collect(),
),
}
}
#[inline]
pub fn sampler<'a>(
binding: u32,
first_array_element: u32,
samplers: impl IntoIterator<Item = &'a Arc<Sampler>>,
) -> DescriptorWrite {
DescriptorWrite {
binding,
first_array_element,
descriptor_type: DescriptorType::Sampler,
info: DescriptorWriteInfo::Image(
samplers
.into_iter()
.map(|sampler| ash::vk::DescriptorImageInfo {
sampler: sampler.internal_object(),
image_view: ash::vk::ImageView::null(),
image_layout: ash::vk::ImageLayout::UNDEFINED,
})
.collect(),
),
}
}
#[inline]
pub fn sampled_image<'a, I>(
binding: u32,
first_array_element: u32,
image_views: impl IntoIterator<Item = &'a I>,
) -> DescriptorWrite
where
I: ImageViewAbstract + 'a,
{
DescriptorWrite {
binding,
first_array_element,
descriptor_type: DescriptorType::SampledImage,
info: DescriptorWriteInfo::Image(
image_views
.into_iter()
.map(|image_view| {
let layouts = image_view.image().descriptor_layouts().expect(
"descriptor_layouts must return Some when used in an image view",
);
ash::vk::DescriptorImageInfo {
sampler: ash::vk::Sampler::null(),
image_view: image_view.inner().internal_object(),
image_layout: layouts.sampled_image.into(),
}
})
.collect(),
),
}
}
#[inline]
pub fn combined_image_sampler<'a, I>(
binding: u32,
first_array_element: u32,
image_views_samplers: impl IntoIterator<Item = (Option<&'a Arc<Sampler>>, &'a I)>, // Some for dynamic sampler, None for immutable
) -> DescriptorWrite
where
I: ImageViewAbstract + 'a,
{
DescriptorWrite {
binding,
first_array_element,
descriptor_type: DescriptorType::CombinedImageSampler,
info: DescriptorWriteInfo::Image(
image_views_samplers
.into_iter()
.map(|(sampler, image_view)| {
let layouts = image_view.image().descriptor_layouts().expect(
"descriptor_layouts must return Some when used in an image view",
);
ash::vk::DescriptorImageInfo {
sampler: sampler.map(|s| s.internal_object()).unwrap_or_default(),
image_view: image_view.inner().internal_object(),
image_layout: layouts.combined_image_sampler.into(),
}
})
.collect(),
),
}
}
#[inline]
pub fn uniform_texel_buffer<'a, B>(
binding: u32,
array_element: u32,
view: &BufferView<B>,
first_array_element: u32,
buffer_views: impl IntoIterator<Item = &'a BufferView<B>>,
) -> DescriptorWrite
where
B: BufferAccess,
B: BufferAccess + 'a,
{
assert!(view.uniform_texel_buffer());
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!(DescriptorWriteInner::UniformTexelBuffer(
view.internal_object()
)),
first_array_element,
descriptor_type: DescriptorType::UniformTexelBuffer,
info: DescriptorWriteInfo::BufferView(
buffer_views
.into_iter()
.map(|buffer_view| {
assert!(buffer_view.uniform_texel_buffer());
buffer_view.internal_object()
})
.collect(),
),
}
}
#[inline]
pub fn storage_texel_buffer<'a, B>(
binding: u32,
array_element: u32,
view: &BufferView<B>,
first_array_element: u32,
buffer_view: impl IntoIterator<Item = &'a BufferView<B>>,
) -> DescriptorWrite
where
B: BufferAccess,
B: BufferAccess + 'a,
{
assert!(view.storage_texel_buffer());
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!(DescriptorWriteInner::StorageTexelBuffer(
view.internal_object()
)),
first_array_element,
descriptor_type: DescriptorType::StorageTexelBuffer,
info: DescriptorWriteInfo::BufferView(
buffer_view
.into_iter()
.map(|buffer_view| {
assert!(buffer_view.storage_texel_buffer());
buffer_view.internal_object()
})
.collect(),
),
}
}
#[inline]
pub unsafe fn uniform_buffer<B>(binding: u32, array_element: u32, buffer: &B) -> DescriptorWrite
where
B: BufferAccess,
{
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_uniform_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_uniform_buffer_range as DeviceSize
);
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!({
DescriptorWriteInner::UniformBuffer(buffer.internal_object(), offset, size)
}),
}
}
#[inline]
pub unsafe fn storage_buffer<B>(binding: u32, array_element: u32, buffer: &B) -> DescriptorWrite
where
B: BufferAccess,
{
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_storage_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_storage_buffer_range as DeviceSize
);
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!({
DescriptorWriteInner::StorageBuffer(buffer.internal_object(), offset, size)
}),
}
}
#[inline]
pub unsafe fn dynamic_uniform_buffer<B>(
pub unsafe fn uniform_buffer<'a, B>(
binding: u32,
array_element: u32,
buffer: &B,
first_array_element: u32,
buffers: impl IntoIterator<Item = &'a B>,
) -> DescriptorWrite
where
B: BufferAccess,
B: BufferAccess + 'a,
{
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_uniform_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_uniform_buffer_range as DeviceSize
);
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!(DescriptorWriteInner::DynamicUniformBuffer(
buffer.internal_object(),
offset,
size
)),
first_array_element,
descriptor_type: DescriptorType::UniformBuffer,
info: DescriptorWriteInfo::Buffer(
buffers
.into_iter()
.map(|buffer| {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_uniform_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_uniform_buffer_range
as DeviceSize
);
ash::vk::DescriptorBufferInfo {
buffer: buffer.internal_object(),
offset,
range: size,
}
})
.collect(),
),
}
}
#[inline]
pub unsafe fn dynamic_storage_buffer<B>(
pub unsafe fn storage_buffer<'a, B>(
binding: u32,
array_element: u32,
buffer: &B,
first_array_element: u32,
buffers: impl IntoIterator<Item = &'a B>,
) -> DescriptorWrite
where
B: BufferAccess,
B: BufferAccess + 'a,
{
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_storage_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_storage_buffer_range as DeviceSize
);
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!(DescriptorWriteInner::DynamicStorageBuffer(
buffer.internal_object(),
offset,
size
)),
first_array_element,
descriptor_type: DescriptorType::StorageBuffer,
info: DescriptorWriteInfo::Buffer(
buffers
.into_iter()
.map(|buffer| {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_storage_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_storage_buffer_range
as DeviceSize
);
ash::vk::DescriptorBufferInfo {
buffer: buffer.internal_object(),
offset,
range: size,
}
})
.collect(),
),
}
}
#[inline]
pub fn input_attachment<I>(binding: u32, array_element: u32, image_view: &I) -> DescriptorWrite
pub unsafe fn dynamic_uniform_buffer<'a, B>(
binding: u32,
first_array_element: u32,
buffers: impl IntoIterator<Item = &'a B>,
) -> DescriptorWrite
where
I: ImageViewAbstract,
B: BufferAccess + 'a,
{
let layouts = image_view
.image()
.descriptor_layouts()
.expect("descriptor_layouts must return Some when used in an image view");
DescriptorWrite {
binding,
first_array_element: array_element,
inner: smallvec!({
DescriptorWriteInner::InputAttachment(
image_view.inner().internal_object(),
layouts.input_attachment.into(),
)
}),
first_array_element,
descriptor_type: DescriptorType::UniformBufferDynamic,
info: DescriptorWriteInfo::Buffer(
buffers
.into_iter()
.map(|buffer| {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_uniform_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_uniform_buffer_range
as DeviceSize
);
ash::vk::DescriptorBufferInfo {
buffer: buffer.internal_object(),
offset,
range: size,
}
})
.collect(),
),
}
}
/// Returns the type corresponding to this write.
#[inline]
pub fn ty(&self) -> DescriptorType {
match self.inner[0] {
DescriptorWriteInner::Sampler(_) => DescriptorType::Sampler,
DescriptorWriteInner::CombinedImageSampler(_, _, _) => {
DescriptorType::CombinedImageSampler
}
DescriptorWriteInner::SampledImage(_, _) => DescriptorType::SampledImage,
DescriptorWriteInner::StorageImage(_, _) => DescriptorType::StorageImage,
DescriptorWriteInner::UniformTexelBuffer(_) => DescriptorType::UniformTexelBuffer,
DescriptorWriteInner::StorageTexelBuffer(_) => DescriptorType::StorageTexelBuffer,
DescriptorWriteInner::UniformBuffer(_, _, _) => DescriptorType::UniformBuffer,
DescriptorWriteInner::StorageBuffer(_, _, _) => DescriptorType::StorageBuffer,
DescriptorWriteInner::DynamicUniformBuffer(_, _, _) => {
DescriptorType::UniformBufferDynamic
}
DescriptorWriteInner::DynamicStorageBuffer(_, _, _) => {
DescriptorType::StorageBufferDynamic
}
DescriptorWriteInner::InputAttachment(_, _) => DescriptorType::InputAttachment,
pub unsafe fn dynamic_storage_buffer<'a, B>(
binding: u32,
first_array_element: u32,
buffers: impl IntoIterator<Item = &'a B>,
) -> DescriptorWrite
where
B: BufferAccess + 'a,
{
DescriptorWrite {
binding,
first_array_element,
descriptor_type: DescriptorType::StorageBufferDynamic,
info: DescriptorWriteInfo::Buffer(
buffers
.into_iter()
.map(|buffer| {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(
offset
% buffer
.device()
.physical_device()
.properties()
.min_storage_buffer_offset_alignment,
0
);
debug_assert!(
size <= buffer
.device()
.physical_device()
.properties()
.max_storage_buffer_range
as DeviceSize
);
ash::vk::DescriptorBufferInfo {
buffer: buffer.internal_object(),
offset,
range: size,
}
})
.collect(),
),
}
}
#[inline]
pub fn input_attachment<'a, I>(
binding: u32,
first_array_element: u32,
image_views: impl IntoIterator<Item = &'a I>,
) -> DescriptorWrite
where
I: ImageViewAbstract + 'a,
{
DescriptorWrite {
binding,
first_array_element,
descriptor_type: DescriptorType::InputAttachment,
info: DescriptorWriteInfo::Image(
image_views
.into_iter()
.map(|image_view| {
let layouts = image_view.image().descriptor_layouts().expect(
"descriptor_layouts must return Some when used in an image view",
);
ash::vk::DescriptorImageInfo {
sampler: ash::vk::Sampler::null(),
image_view: image_view.inner().internal_object(),
image_layout: layouts.input_attachment.into(),
}
})
.collect(),
),
}
}
pub(crate) fn to_vulkan(&self, dst_set: ash::vk::DescriptorSet) -> ash::vk::WriteDescriptorSet {
let mut result = ash::vk::WriteDescriptorSet {
dst_set,
dst_binding: self.binding,
dst_array_element: self.first_array_element,
descriptor_count: 0,
descriptor_type: self.descriptor_type.into(),
p_image_info: ptr::null(),
p_buffer_info: ptr::null(),
p_texel_buffer_view: ptr::null(),
..Default::default()
};
// Set the pointers separately.
// You must keep `*self` alive and unmoved until the function call is done.
match &self.info {
DescriptorWriteInfo::Image(info) => {
result.descriptor_count = info.len() as u32;
result.p_image_info = info.as_ptr();
}
DescriptorWriteInfo::Buffer(info) => {
result.descriptor_count = info.len() as u32;
result.p_buffer_info = info.as_ptr();
}
DescriptorWriteInfo::BufferView(info) => {
result.descriptor_count = info.len() as u32;
result.p_texel_buffer_view = info.as_ptr();
}
}
// Since the `DescriptorWrite` objects are built only through functions, we know for
// sure that it's impossible to have an empty descriptor write.
debug_assert!(result.descriptor_count != 0);
result
}
}

View File

@ -33,8 +33,8 @@ use std::sync::Arc;
pub struct PipelineLayout {
handle: ash::vk::PipelineLayout,
device: Arc<Device>,
descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 16]>,
push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 8]>,
descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 4]>,
push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 4]>,
}
impl PipelineLayout {
@ -50,9 +50,19 @@ impl PipelineLayout {
P: IntoIterator<Item = PipelineLayoutPcRange>,
{
let fns = device.fns();
let descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 16]> =
let descriptor_set_layouts: SmallVec<[Arc<DescriptorSetLayout>; 4]> =
descriptor_set_layouts.into_iter().collect();
let mut push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 8]> =
if descriptor_set_layouts
.iter()
.filter(|layout| layout.desc().is_push_descriptor())
.count()
> 1
{
return Err(PipelineLayoutCreationError::MultiplePushDescriptor);
}
let mut push_constant_ranges: SmallVec<[PipelineLayoutPcRange; 4]> =
push_constant_ranges.into_iter().collect();
// Check for overlapping stages
@ -89,11 +99,11 @@ impl PipelineLayout {
let layouts_ids = descriptor_set_layouts
.iter()
.map(|l| l.internal_object())
.collect::<SmallVec<[_; 16]>>();
.collect::<SmallVec<[_; 4]>>();
// Builds a list of `vkPushConstantRange` that describe the push constants.
let push_constants = {
let mut out: SmallVec<[_; 8]> = SmallVec::new();
let mut out: SmallVec<[_; 4]> = SmallVec::new();
for &PipelineLayoutPcRange {
offset,
@ -279,6 +289,8 @@ pub enum PipelineLayoutCreationError {
/// One of the push constants range didn't obey the rules. The list of stages must not be
/// empty, the size must not be 0, and the size must be a multiple or 4.
InvalidPushConstant,
/// More than one descriptor set layout was set for push descriptors.
MultiplePushDescriptor,
/// Conflict between different push constants ranges.
PushConstantsConflict {
first_range: PipelineLayoutPcRange,
@ -292,9 +304,9 @@ impl error::Error for PipelineLayoutCreationError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
PipelineLayoutCreationError::OomError(ref err) => Some(err),
PipelineLayoutCreationError::LimitsError(ref err) => Some(err),
PipelineLayoutCreationError::SetLayoutError(ref err) => Some(err),
Self::OomError(ref err) => Some(err),
Self::LimitsError(ref err) => Some(err),
Self::SetLayoutError(ref err) => Some(err),
_ => None,
}
}
@ -303,25 +315,28 @@ impl error::Error for PipelineLayoutCreationError {
impl fmt::Display for PipelineLayoutCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
PipelineLayoutCreationError::OomError(_) => "not enough memory available",
PipelineLayoutCreationError::LimitsError(_) => {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available"),
Self::LimitsError(_) => {
write!(
fmt,
"the pipeline layout description doesn't fulfill the limit requirements"
}
PipelineLayoutCreationError::InvalidPushConstant => {
"one of the push constants range didn't obey the rules"
}
PipelineLayoutCreationError::PushConstantsConflict { .. } => {
"conflict between different push constants ranges"
}
PipelineLayoutCreationError::SetLayoutError(_) => {
"one of the sets has an error"
}
)
}
)
Self::InvalidPushConstant => {
write!(fmt, "one of the push constants range didn't obey the rules")
}
Self::MultiplePushDescriptor => {
write!(
fmt,
"more than one descriptor set layout was set for push descriptors"
)
}
Self::PushConstantsConflict { .. } => {
write!(fmt, "conflict between different push constants ranges")
}
Self::SetLayoutError(_) => write!(fmt, "one of the sets has an error"),
}
}
}