Support descriptor arrays shorter than binding length

This commit is contained in:
Connor Fitzgerald 2021-09-24 16:45:02 -04:00 committed by Dzmitry Malyshau
parent 8fa3ca731b
commit b679342d9e
14 changed files with 482 additions and 121 deletions

View File

@ -65,8 +65,16 @@ pub enum CreateBindGroupError {
InvalidTextureView(TextureViewId),
#[error("sampler {0:?} is invalid")]
InvalidSampler(SamplerId),
#[error("binding count declared with {expected} items, but {actual} items were provided")]
#[error(
"binding count declared with at most {expected} items, but {actual} items were provided"
)]
BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
#[error(
"binding count declared with exactly {expected} items, but {actual} items were provided"
)]
BindingArrayLengthMismatch { actual: usize, expected: usize },
#[error("array binding provided zero elements")]
BindingArrayZeroLength,
#[error("bound buffer range {range:?} does not fit in buffer of size {size}")]
BindingRangeTooLarge {
buffer: BufferId,

View File

@ -152,3 +152,12 @@ pub fn check_texture_dimension_size(
Ok(())
}
pub fn bind_group_layout_flags(features: wgt::Features) -> hal::BindGroupLayoutFlags {
let mut flags = hal::BindGroupLayoutFlags::empty();
flags.set(
hal::BindGroupLayoutFlags::PARTIALLY_BOUND,
features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY),
);
flags
}

View File

@ -19,7 +19,7 @@ use smallvec::SmallVec;
use thiserror::Error;
use wgt::{BufferAddress, TextureFormat, TextureViewDimension};
use std::{borrow::Cow, iter, marker::PhantomData, mem, ops::Range, ptr};
use std::{borrow::Cow, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, ptr};
mod life;
pub mod queue;
@ -1186,10 +1186,13 @@ impl<A: HalApi> Device<A> {
})?;
}
let bgl_flags = conv::bind_group_layout_flags(self.features);
let mut hal_bindings = entry_map.values().cloned().collect::<Vec<_>>();
hal_bindings.sort_by_key(|b| b.binding);
let hal_desc = hal::BindGroupLayoutDescriptor {
label,
flags: bgl_flags,
entries: &hal_bindings,
};
let raw = unsafe {
@ -1390,7 +1393,7 @@ impl<A: HalApi> Device<A> {
.entries
.get(&binding)
.ok_or(Error::MissingBindingDeclaration(binding))?;
let res_index = match entry.resource {
let (res_index, count) = match entry.resource {
Br::Buffer(ref bb) => {
let bb = Self::create_buffer_binding(
bb,
@ -1405,21 +1408,11 @@ impl<A: HalApi> Device<A> {
let res_index = hal_buffers.len();
hal_buffers.push(bb);
res_index
(res_index, 1)
}
Br::BufferArray(ref bindings_array) => {
if let Some(count) = decl.count {
let count = count.get() as usize;
let num_bindings = bindings_array.len();
if count != num_bindings {
return Err(Error::BindingArrayLengthMismatch {
actual: num_bindings,
expected: count,
});
}
} else {
return Err(Error::SingleBindingExpected);
}
let num_bindings = bindings_array.len();
Self::check_array_binding(self.features, decl.count, num_bindings)?;
let res_index = hal_buffers.len();
for bb in bindings_array.iter() {
@ -1435,7 +1428,7 @@ impl<A: HalApi> Device<A> {
)?;
hal_buffers.push(bb);
}
res_index
(res_index, num_bindings)
}
Br::Sampler(id) => {
match decl.ty {
@ -1467,7 +1460,7 @@ impl<A: HalApi> Device<A> {
let res_index = hal_samplers.len();
hal_samplers.push(&sampler.raw);
res_index
(res_index, 1)
}
_ => {
return Err(Error::WrongBindingType {
@ -1508,21 +1501,11 @@ impl<A: HalApi> Device<A> {
view: &view.raw,
usage: internal_use,
});
res_index
(res_index, 1)
}
Br::TextureViewArray(ref bindings_array) => {
if let Some(count) = decl.count {
let count = count.get() as usize;
let num_bindings = bindings_array.len();
if count != num_bindings {
return Err(Error::BindingArrayLengthMismatch {
actual: num_bindings,
expected: count,
});
}
} else {
return Err(Error::SingleBindingExpected);
}
let num_bindings = bindings_array.len();
Self::check_array_binding(self.features, decl.count, num_bindings)?;
let res_index = hal_textures.len();
for &id in bindings_array.iter() {
@ -1554,13 +1537,14 @@ impl<A: HalApi> Device<A> {
});
}
res_index
(res_index, num_bindings)
}
};
hal_entries.push(hal::BindGroupEntry {
binding,
resource_index: res_index as u32,
count: count as u32,
});
}
@ -1599,6 +1583,39 @@ impl<A: HalApi> Device<A> {
})
}
fn check_array_binding(
features: wgt::Features,
count: Option<NonZeroU32>,
num_bindings: usize,
) -> Result<(), super::binding_model::CreateBindGroupError> {
use super::binding_model::CreateBindGroupError as Error;
if let Some(count) = count {
let count = count.get() as usize;
if count < num_bindings {
return Err(Error::BindingArrayPartialLengthMismatch {
actual: num_bindings,
expected: count,
});
}
if count != num_bindings
&& !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
{
return Err(Error::BindingArrayLengthMismatch {
actual: num_bindings,
expected: count,
});
}
if num_bindings == 0 {
return Err(Error::BindingArrayZeroLength);
}
} else {
return Err(Error::SingleBindingExpected);
};
Ok(())
}
fn texture_use_parameters(
binding: u32,
decl: &wgt::BindGroupLayoutEntry,

View File

@ -334,10 +334,12 @@ impl<A: HalApi> Adapter<A> {
desc: &DeviceDescriptor,
trace_path: Option<&std::path::Path>,
) -> Result<Device<A>, RequestDeviceError> {
let open = unsafe { self.raw.adapter.open(desc.features) }.map_err(|err| match err {
hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory,
})?;
let open = unsafe { self.raw.adapter.open(desc.features, &desc.limits) }.map_err(
|err| match err {
hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory,
},
)?;
self.create_device_from_hal(self_id, open, desc, trace_path)
}

View File

@ -102,8 +102,11 @@ impl<A: hal::Api> Example<A> {
);
(exposed.adapter, exposed.capabilities)
};
let hal::OpenDevice { device, mut queue } =
unsafe { adapter.open(wgt::Features::empty()).unwrap() };
let hal::OpenDevice { device, mut queue } = unsafe {
adapter
.open(wgt::Features::empty(), &wgt::Limits::default())
.unwrap()
};
let window_size: (u32, u32) = window.inner_size().into();
let surface_config = hal::SurfaceConfiguration {
@ -149,6 +152,7 @@ impl<A: hal::Api> Example<A> {
let global_bgl_desc = hal::BindGroupLayoutDescriptor {
label: None,
flags: hal::BindGroupLayoutFlags::empty(),
entries: &[
wgt::BindGroupLayoutEntry {
binding: 0,
@ -186,6 +190,8 @@ impl<A: hal::Api> Example<A> {
unsafe { device.create_bind_group_layout(&global_bgl_desc).unwrap() };
let local_bgl_desc = hal::BindGroupLayoutDescriptor {
label: None,
flags: hal::BindGroupLayoutFlags::empty(),
entries: &[wgt::BindGroupLayoutEntry {
binding: 0,
visibility: wgt::ShaderStages::VERTEX,
@ -196,7 +202,6 @@ impl<A: hal::Api> Example<A> {
},
count: None,
}],
label: None,
};
let local_group_layout =
unsafe { device.create_bind_group_layout(&local_bgl_desc).unwrap() };
@ -420,14 +425,17 @@ impl<A: hal::Api> Example<A> {
hal::BindGroupEntry {
binding: 0,
resource_index: 0,
count: 1,
},
hal::BindGroupEntry {
binding: 1,
resource_index: 0,
count: 1,
},
hal::BindGroupEntry {
binding: 2,
resource_index: 0,
count: 1,
},
],
};
@ -449,6 +457,7 @@ impl<A: hal::Api> Example<A> {
entries: &[hal::BindGroupEntry {
binding: 0,
resource_index: 0,
count: 1,
}],
};
unsafe { device.create_bind_group(&local_group_desc).unwrap() }

View File

@ -262,6 +262,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
unsafe fn open(
&self,
features: wgt::Features,
_limits: &wgt::Limits,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let queue = self
.device

View File

@ -74,7 +74,11 @@ impl crate::Surface<Api> for Context {
}
impl crate::Adapter<Api> for Context {
unsafe fn open(&self, features: wgt::Features) -> DeviceResult<crate::OpenDevice<Api>> {
unsafe fn open(
&self,
features: wgt::Features,
_limits: &wgt::Limits,
) -> DeviceResult<crate::OpenDevice<Api>> {
Err(crate::DeviceError::Lost)
}
unsafe fn texture_format_capabilities(

View File

@ -419,6 +419,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
unsafe fn open(
&self,
features: wgt::Features,
_limits: &wgt::Limits,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let gl = &self.shared.context.lock();
gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);

View File

@ -188,7 +188,11 @@ pub trait Surface<A: Api>: Send + Sync {
}
pub trait Adapter<A: Api>: Send + Sync {
unsafe fn open(&self, features: wgt::Features) -> Result<OpenDevice<A>, DeviceError>;
unsafe fn open(
&self,
features: wgt::Features,
limits: &wgt::Limits,
) -> Result<OpenDevice<A>, DeviceError>;
/// Return the set of supported capabilities for a texture format.
unsafe fn texture_format_capabilities(
@ -524,6 +528,14 @@ bitflags!(
}
);
bitflags!(
/// Pipeline layout creation flags.
pub struct BindGroupLayoutFlags: u32 {
/// Allows for bind group binding arrays to be shorter than the array in the BGL.
const PARTIALLY_BOUND = 1 << 0;
}
);
bitflags!(
/// Texture format capability flags.
pub struct TextureFormatCapabilities: u32 {
@ -798,6 +810,7 @@ pub struct SamplerDescriptor<'a> {
#[derive(Clone, Debug)]
pub struct BindGroupLayoutDescriptor<'a> {
pub label: Label<'a>,
pub flags: BindGroupLayoutFlags,
pub entries: &'a [wgt::BindGroupLayoutEntry],
}
@ -847,6 +860,7 @@ impl<A: Api> Clone for TextureBinding<'_, A> {
pub struct BindGroupEntry {
pub binding: u32,
pub resource_index: u32,
pub count: u32,
}
/// BindGroup descriptor.

View File

@ -19,6 +19,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
unsafe fn open(
&self,
features: wgt::Features,
_limits: &wgt::Limits,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let queue = self
.shared

View File

@ -7,15 +7,17 @@ use std::{ffi::CStr, mem, ptr, sync::Arc};
//TODO: const fn?
fn indexing_features() -> wgt::Features {
wgt::Features::BUFFER_BINDING_ARRAY | wgt::Features::TEXTURE_BINDING_ARRAY
wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::UNSIZED_BINDING_ARRAY
}
/// Aggregate of the `vk::PhysicalDevice*Features` structs used by `gfx`.
#[derive(Debug, Default)]
pub struct PhysicalDeviceFeatures {
core: vk::PhysicalDeviceFeatures,
vulkan_1_2: Option<vk::PhysicalDeviceVulkan12Features>,
descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingFeaturesEXT>,
pub(super) vulkan_1_2: Option<vk::PhysicalDeviceVulkan12Features>,
pub(super) descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingFeaturesEXT>,
imageless_framebuffer: Option<vk::PhysicalDeviceImagelessFramebufferFeaturesKHR>,
timeline_semaphore: Option<vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR>,
image_robustness: Option<vk::PhysicalDeviceImageRobustnessFeaturesEXT>,
@ -54,7 +56,29 @@ impl PhysicalDeviceFeatures {
requested_features: wgt::Features,
downlevel_flags: wgt::DownlevelFlags,
private_caps: &super::PrivateCapabilities,
uab_types: super::UpdateAfterBindTypes,
) -> Self {
let needs_sampled_image_non_uniform = requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
);
let needs_storage_buffer_non_uniform = requested_features.contains(
wgt::Features::BUFFER_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
);
let needs_uniform_buffer_non_uniform = requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
);
let needs_storage_image_non_uniform = requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
);
let needs_partially_bound =
requested_features.intersects(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY);
Self {
// vk::PhysicalDeviceFeatures is a struct composed of Bool32's while
// Features is a bitfield so we need to map everything manually
@ -132,32 +156,30 @@ impl PhysicalDeviceFeatures {
)
.descriptor_indexing(requested_features.intersects(indexing_features()))
.shader_sampled_image_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
),
needs_sampled_image_non_uniform,
)
.shader_storage_image_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
),
needs_storage_image_non_uniform,
)
//.shader_storage_buffer_array_non_uniform_indexing(
.shader_uniform_buffer_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::BUFFER_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
),
needs_uniform_buffer_non_uniform,
)
.shader_storage_buffer_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::BUFFER_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
),
needs_storage_buffer_non_uniform,
)
.descriptor_binding_sampled_image_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::SAMPLED_TEXTURE),
)
.descriptor_binding_storage_image_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::STORAGE_TEXTURE),
)
.descriptor_binding_uniform_buffer_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::UNIFORM_BUFFER),
)
.descriptor_binding_storage_buffer_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER),
)
.descriptor_binding_partially_bound(needs_partially_bound)
.runtime_descriptor_array(
requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY),
)
@ -175,32 +197,30 @@ impl PhysicalDeviceFeatures {
Some(
vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::builder()
.shader_sampled_image_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
),
needs_sampled_image_non_uniform,
)
.shader_storage_image_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
),
needs_storage_image_non_uniform,
)
//.shader_storage_buffer_array_non_uniform_indexing(
.shader_uniform_buffer_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::BUFFER_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
),
needs_uniform_buffer_non_uniform,
)
.shader_storage_buffer_array_non_uniform_indexing(
requested_features.contains(
wgt::Features::BUFFER_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
),
needs_storage_buffer_non_uniform,
)
.descriptor_binding_sampled_image_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::SAMPLED_TEXTURE),
)
.descriptor_binding_storage_image_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::STORAGE_TEXTURE),
)
.descriptor_binding_uniform_buffer_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::UNIFORM_BUFFER),
)
.descriptor_binding_storage_buffer_update_after_bind(
uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER),
)
.descriptor_binding_partially_bound(needs_partially_bound)
.runtime_descriptor_array(
requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY),
)
@ -380,6 +400,9 @@ impl PhysicalDeviceFeatures {
if vulkan_1_2.runtime_descriptor_array != 0 {
features |= F::UNSIZED_BINDING_ARRAY;
}
if vulkan_1_2.descriptor_binding_partially_bound != 0 {
features |= F::PARTIALLY_BOUND_BINDING_ARRAY;
}
//if vulkan_1_2.sampler_mirror_clamp_to_edge != 0 {
//if vulkan_1_2.sampler_filter_minmax != 0 {
if vulkan_1_2.draw_indirect_count != 0 {
@ -419,6 +442,9 @@ impl PhysicalDeviceFeatures {
) {
features.insert(F::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING);
}
if descriptor_indexing.descriptor_binding_partially_bound != 0 {
features |= F::PARTIALLY_BOUND_BINDING_ARRAY;
}
if descriptor_indexing.runtime_descriptor_array != 0 {
features |= F::UNSIZED_BINDING_ARRAY;
}
@ -438,11 +464,18 @@ impl PhysicalDeviceFeatures {
}
/// Information gathered about a physical device capabilities.
#[derive(Default)]
pub struct PhysicalDeviceCapabilities {
supported_extensions: Vec<vk::ExtensionProperties>,
properties: vk::PhysicalDeviceProperties,
vulkan_1_2: Option<vk::PhysicalDeviceVulkan12Properties>,
descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingPropertiesEXT>,
}
// This is safe because the structs have `p_next: *mut c_void`, which we null out/never read.
unsafe impl Send for PhysicalDeviceCapabilities {}
unsafe impl Sync for PhysicalDeviceCapabilities {}
impl PhysicalDeviceCapabilities {
fn supports_extension(&self, extension: &CStr) -> bool {
self.supported_extensions
@ -498,8 +531,63 @@ impl PhysicalDeviceCapabilities {
extensions
}
fn to_wgpu_limits(&self) -> wgt::Limits {
fn to_wgpu_limits(&self, features: &PhysicalDeviceFeatures) -> wgt::Limits {
let limits = &self.properties.limits;
let uab_types = super::UpdateAfterBindTypes::from_features(features);
let max_sampled_textures =
if uab_types.contains(super::UpdateAfterBindTypes::SAMPLED_TEXTURE) {
if let Some(di) = self.descriptor_indexing {
di.max_per_stage_descriptor_update_after_bind_sampled_images
} else if let Some(vk_1_2) = self.vulkan_1_2 {
vk_1_2.max_per_stage_descriptor_update_after_bind_sampled_images
} else {
limits.max_per_stage_descriptor_sampled_images
}
} else {
limits.max_per_stage_descriptor_sampled_images
};
let max_storage_textures =
if uab_types.contains(super::UpdateAfterBindTypes::STORAGE_TEXTURE) {
if let Some(di) = self.descriptor_indexing {
di.max_per_stage_descriptor_update_after_bind_storage_images
} else if let Some(vk_1_2) = self.vulkan_1_2 {
vk_1_2.max_per_stage_descriptor_update_after_bind_storage_images
} else {
limits.max_per_stage_descriptor_storage_images
}
} else {
limits.max_per_stage_descriptor_storage_images
};
let max_uniform_buffers = if uab_types.contains(super::UpdateAfterBindTypes::UNIFORM_BUFFER)
{
if let Some(di) = self.descriptor_indexing {
di.max_per_stage_descriptor_update_after_bind_uniform_buffers
} else if let Some(vk_1_2) = self.vulkan_1_2 {
vk_1_2.max_per_stage_descriptor_update_after_bind_uniform_buffers
} else {
limits.max_per_stage_descriptor_uniform_buffers
}
} else {
limits.max_per_stage_descriptor_uniform_buffers
};
let max_storage_buffers = if uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER)
{
if let Some(di) = self.descriptor_indexing {
di.max_per_stage_descriptor_update_after_bind_storage_buffers
} else if let Some(vk_1_2) = self.vulkan_1_2 {
vk_1_2.max_per_stage_descriptor_update_after_bind_storage_buffers
} else {
limits.max_per_stage_descriptor_storage_buffers
}
} else {
limits.max_per_stage_descriptor_storage_buffers
};
wgt::Limits {
max_texture_dimension_1d: limits.max_image_dimension1_d,
max_texture_dimension_2d: limits.max_image_dimension2_d,
@ -512,11 +600,11 @@ impl PhysicalDeviceCapabilities {
.max_descriptor_set_uniform_buffers_dynamic,
max_dynamic_storage_buffers_per_pipeline_layout: limits
.max_descriptor_set_storage_buffers_dynamic,
max_sampled_textures_per_shader_stage: limits.max_per_stage_descriptor_sampled_images,
max_sampled_textures_per_shader_stage: max_sampled_textures,
max_samplers_per_shader_stage: limits.max_per_stage_descriptor_samplers,
max_storage_buffers_per_shader_stage: limits.max_per_stage_descriptor_storage_buffers,
max_storage_textures_per_shader_stage: limits.max_per_stage_descriptor_storage_images,
max_uniform_buffers_per_shader_stage: limits.max_per_stage_descriptor_uniform_buffers,
max_storage_buffers_per_shader_stage: max_storage_buffers,
max_storage_textures_per_shader_stage: max_storage_textures,
max_uniform_buffers_per_shader_stage: max_uniform_buffers,
max_uniform_buffer_binding_size: limits.max_uniform_buffer_range,
max_storage_buffer_binding_size: limits.max_storage_buffer_range,
max_vertex_buffers: limits
@ -547,11 +635,46 @@ impl super::InstanceShared {
&self,
phd: vk::PhysicalDevice,
) -> (PhysicalDeviceCapabilities, PhysicalDeviceFeatures) {
let capabilities = unsafe {
PhysicalDeviceCapabilities {
supported_extensions: self.raw.enumerate_device_extension_properties(phd).unwrap(),
properties: self.raw.get_physical_device_properties(phd),
}
let capabilities = {
let mut capabilities = PhysicalDeviceCapabilities::default();
capabilities.supported_extensions =
unsafe { self.raw.enumerate_device_extension_properties(phd).unwrap() };
capabilities.properties = if let Some(ref get_device_properties) =
self.get_physical_device_properties
{
let core = vk::PhysicalDeviceProperties::builder().build();
let mut properites2 = vk::PhysicalDeviceProperties2::builder()
.properties(core)
.build();
if capabilities.properties.api_version >= vk::API_VERSION_1_2 {
capabilities.vulkan_1_2 =
Some(vk::PhysicalDeviceVulkan12Properties::builder().build());
let mut_ref = capabilities.vulkan_1_2.as_mut().unwrap();
mut_ref.p_next =
mem::replace(&mut properites2.p_next, mut_ref as *mut _ as *mut _);
}
if capabilities.supports_extension(vk::ExtDescriptorIndexingFn::name()) {
capabilities.descriptor_indexing =
Some(vk::PhysicalDeviceDescriptorIndexingPropertiesEXT::builder().build());
let mut_ref = capabilities.descriptor_indexing.as_mut().unwrap();
mut_ref.p_next =
mem::replace(&mut properites2.p_next, mut_ref as *mut _ as *mut _);
}
unsafe {
get_device_properties
.get_physical_device_properties2_khr(phd, &mut properites2);
}
properites2.properties
} else {
unsafe { self.raw.get_physical_device_properties(phd) }
};
capabilities
};
let mut features = PhysicalDeviceFeatures::default();
@ -645,6 +768,8 @@ impl super::Instance {
&self,
phd: vk::PhysicalDevice,
) -> Option<crate::ExposedAdapter<super::Api>> {
use crate::auxil::db;
let (phd_capabilities, phd_features) = self.shared.inspect(phd);
let info = wgt::AdapterInfo {
@ -670,7 +795,6 @@ impl super::Instance {
let (available_features, downlevel_flags) = phd_features.to_wgpu(&phd_capabilities);
let mut workarounds = super::Workarounds::empty();
{
use crate::auxil::db;
// see https://github.com/gfx-rs/gfx/issues/1930
let _is_windows_intel_dual_src_bug = cfg!(windows)
&& phd_capabilities.properties.vendor_id == db::intel::VENDOR
@ -752,7 +876,7 @@ impl super::Instance {
};
let capabilities = crate::Capabilities {
limits: phd_capabilities.to_wgpu_limits(),
limits: phd_capabilities.to_wgpu_limits(&phd_features),
alignments: phd_capabilities.to_hal_alignments(),
downlevel: wgt::DownlevelCapabilities {
flags: downlevel_flags,
@ -809,6 +933,7 @@ impl super::Adapter {
&self,
enabled_extensions: &[&'static CStr],
features: wgt::Features,
uab_types: super::UpdateAfterBindTypes,
) -> PhysicalDeviceFeatures {
PhysicalDeviceFeatures::from_extensions_and_requested_features(
self.phd_capabilities.properties.api_version,
@ -816,6 +941,7 @@ impl super::Adapter {
features,
self.downlevel_flags,
&self.private_caps,
uab_types,
)
}
@ -824,11 +950,13 @@ impl super::Adapter {
/// - `raw_device` must be created from this adapter.
/// - `raw_device` must be created using `family_index`, `enabled_extensions` and `physical_device_features()`
/// - `enabled_extensions` must be a superset of `required_device_extensions()`.
#[allow(clippy::too_many_arguments)]
pub unsafe fn device_from_raw(
&self,
raw_device: ash::Device,
handle_is_owned: bool,
enabled_extensions: &[&'static CStr],
uab_types: super::UpdateAfterBindTypes,
family_index: u32,
queue_index: u32,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
@ -934,10 +1062,11 @@ impl super::Adapter {
timeline_semaphore: timeline_semaphore_fn,
},
vendor_id: self.phd_capabilities.properties.vendor_id,
timestamp_period: self.phd_capabilities.properties.limits.timestamp_period,
uab_types,
downlevel_flags: self.downlevel_flags,
private_caps: self.private_caps.clone(),
workarounds: self.workarounds,
timestamp_period: self.phd_capabilities.properties.limits.timestamp_period,
render_passes: Mutex::new(Default::default()),
framebuffers: Mutex::new(Default::default()),
});
@ -979,7 +1108,15 @@ impl super::Adapter {
};
gpu_alloc::GpuAllocator::new(config, properties)
};
let desc_allocator = gpu_descriptor::DescriptorAllocator::new(0);
let desc_allocator = gpu_descriptor::DescriptorAllocator::new(
if let Some(vk_12) = self.phd_capabilities.vulkan_1_2 {
vk_12.max_update_after_bind_descriptors_in_all_pools
} else if let Some(di) = self.phd_capabilities.descriptor_indexing {
di.max_update_after_bind_descriptors_in_all_pools
} else {
0
},
);
let device = super::Device {
shared,
@ -999,9 +1136,14 @@ impl crate::Adapter<super::Api> for super::Adapter {
unsafe fn open(
&self,
features: wgt::Features,
limits: &wgt::Limits,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let phd_limits = &self.phd_capabilities.properties.limits;
let uab_types = super::UpdateAfterBindTypes::from_limits(limits, phd_limits);
let enabled_extensions = self.required_device_extensions(features);
let mut enabled_phd_features = self.physical_device_features(&enabled_extensions, features);
let mut enabled_phd_features =
self.physical_device_features(&enabled_extensions, features, uab_types);
let family_index = 0; //TODO
let family_info = vk::DeviceQueueCreateInfo::builder()
@ -1030,6 +1172,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
raw_device,
true,
&enabled_extensions,
uab_types,
family_info.queue_family_index,
0,
)

View File

@ -377,7 +377,12 @@ impl
})
.collect::<ArrayVec<_, 8>>();
let mut vk_flags = vk::DescriptorPoolCreateFlags::empty();
let mut vk_flags =
if flags.contains(gpu_descriptor::DescriptorPoolCreateFlags::UPDATE_AFTER_BIND) {
vk::DescriptorPoolCreateFlags::UPDATE_AFTER_BIND
} else {
vk::DescriptorPoolCreateFlags::empty()
};
if flags.contains(gpu_descriptor::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET) {
vk_flags |= vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET;
}
@ -1029,9 +1034,69 @@ impl crate::Device<super::Api> for super::Device {
})
.collect::<Vec<_>>();
let vk_info = vk::DescriptorSetLayoutCreateInfo::builder()
.flags(vk::DescriptorSetLayoutCreateFlags::empty())
.bindings(&vk_bindings);
let vk_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&vk_bindings);
let mut binding_flag_info;
let binding_flag_vec;
let mut requires_update_after_bind = false;
let partially_bound = desc
.flags
.contains(crate::BindGroupLayoutFlags::PARTIALLY_BOUND);
let vk_info = if !self.shared.uab_types.is_empty() || partially_bound {
binding_flag_vec = desc
.entries
.iter()
.map(|entry| {
let mut flags = vk::DescriptorBindingFlags::empty();
if partially_bound && entry.count.is_some() {
flags |= vk::DescriptorBindingFlags::PARTIALLY_BOUND;
}
let uab_type = match entry.ty {
wgt::BindingType::Buffer {
ty: wgt::BufferBindingType::Uniform,
..
} => super::UpdateAfterBindTypes::UNIFORM_BUFFER,
wgt::BindingType::Buffer {
ty: wgt::BufferBindingType::Storage { .. },
..
} => super::UpdateAfterBindTypes::STORAGE_BUFFER,
wgt::BindingType::Texture { .. } => {
super::UpdateAfterBindTypes::SAMPLED_TEXTURE
}
wgt::BindingType::StorageTexture { .. } => {
super::UpdateAfterBindTypes::STORAGE_TEXTURE
}
_ => super::UpdateAfterBindTypes::empty(),
};
if !uab_type.is_empty() && self.shared.uab_types.contains(uab_type) {
flags |= vk::DescriptorBindingFlags::UPDATE_AFTER_BIND;
requires_update_after_bind = true;
}
flags
})
.collect::<Vec<_>>();
binding_flag_info = vk::DescriptorSetLayoutBindingFlagsCreateInfo::builder()
.binding_flags(&binding_flag_vec);
vk_info.push_next(&mut binding_flag_info)
} else {
vk_info
};
let dsl_create_flags = if requires_update_after_bind {
vk::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL
} else {
vk::DescriptorSetLayoutCreateFlags::empty()
};
let vk_info = vk_info.flags(dsl_create_flags);
let raw = self
.shared
@ -1047,6 +1112,7 @@ impl crate::Device<super::Api> for super::Device {
raw,
desc_count,
types: types.into_boxed_slice(),
requires_update_after_bind,
})
}
unsafe fn destroy_bind_group_layout(&self, bg_layout: super::BindGroupLayout) {
@ -1102,7 +1168,11 @@ impl crate::Device<super::Api> for super::Device {
let mut vk_sets = self.desc_allocator.lock().allocate(
&*self.shared,
&desc.layout.raw,
gpu_descriptor::DescriptorSetLayoutCreateFlags::empty(),
if desc.layout.requires_update_after_bind {
gpu_descriptor::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND
} else {
gpu_descriptor::DescriptorSetLayoutCreateFlags::empty()
},
&desc.layout.desc_count,
1,
)?;
@ -1139,7 +1209,7 @@ impl crate::Device<super::Api> for super::Device {
vk::DescriptorType::SAMPLED_IMAGE | vk::DescriptorType::STORAGE_IMAGE => {
let index = image_infos.len();
let start = entry.resource_index;
let end = start + size;
let end = start + entry.count;
image_infos.extend(desc.textures[start as usize..end as usize].iter().map(
|binding| {
let layout =
@ -1158,7 +1228,7 @@ impl crate::Device<super::Api> for super::Device {
| vk::DescriptorType::STORAGE_BUFFER_DYNAMIC => {
let index = buffer_infos.len();
let start = entry.resource_index;
let end = start + size;
let end = start + entry.count;
buffer_infos.extend(desc.buffers[start as usize..end as usize].iter().map(
|binding| {
vk::DescriptorBufferInfo::builder()

View File

@ -227,6 +227,82 @@ struct FramebufferKey {
sample_count: u32,
}
bitflags::bitflags! {
pub struct UpdateAfterBindTypes: u8 {
const UNIFORM_BUFFER = 0x1;
const STORAGE_BUFFER = 0x2;
const SAMPLED_TEXTURE = 0x4;
const STORAGE_TEXTURE = 0x8;
}
}
impl UpdateAfterBindTypes {
fn from_limits(limits: &wgt::Limits, phd_limits: &vk::PhysicalDeviceLimits) -> Self {
let mut uab_types = UpdateAfterBindTypes::empty();
uab_types.set(
UpdateAfterBindTypes::UNIFORM_BUFFER,
limits.max_uniform_buffers_per_shader_stage
> phd_limits.max_per_stage_descriptor_uniform_buffers,
);
uab_types.set(
UpdateAfterBindTypes::STORAGE_BUFFER,
limits.max_storage_buffers_per_shader_stage
> phd_limits.max_per_stage_descriptor_storage_buffers,
);
uab_types.set(
UpdateAfterBindTypes::SAMPLED_TEXTURE,
limits.max_sampled_textures_per_shader_stage
> phd_limits.max_per_stage_descriptor_sampled_images,
);
uab_types.set(
UpdateAfterBindTypes::STORAGE_TEXTURE,
limits.max_storage_textures_per_shader_stage
> phd_limits.max_per_stage_descriptor_storage_images,
);
uab_types
}
fn from_features(features: &adapter::PhysicalDeviceFeatures) -> Self {
let mut uab_types = UpdateAfterBindTypes::empty();
if let Some(vk_12) = features.vulkan_1_2 {
uab_types.set(
UpdateAfterBindTypes::UNIFORM_BUFFER,
vk_12.descriptor_binding_uniform_buffer_update_after_bind != 0,
);
uab_types.set(
UpdateAfterBindTypes::STORAGE_BUFFER,
vk_12.descriptor_binding_storage_buffer_update_after_bind != 0,
);
uab_types.set(
UpdateAfterBindTypes::SAMPLED_TEXTURE,
vk_12.descriptor_binding_sampled_image_update_after_bind != 0,
);
uab_types.set(
UpdateAfterBindTypes::STORAGE_TEXTURE,
vk_12.descriptor_binding_storage_image_update_after_bind != 0,
);
} else if let Some(di) = features.descriptor_indexing {
uab_types.set(
UpdateAfterBindTypes::UNIFORM_BUFFER,
di.descriptor_binding_uniform_buffer_update_after_bind != 0,
);
uab_types.set(
UpdateAfterBindTypes::STORAGE_BUFFER,
di.descriptor_binding_storage_buffer_update_after_bind != 0,
);
uab_types.set(
UpdateAfterBindTypes::SAMPLED_TEXTURE,
di.descriptor_binding_sampled_image_update_after_bind != 0,
);
uab_types.set(
UpdateAfterBindTypes::STORAGE_TEXTURE,
di.descriptor_binding_storage_image_update_after_bind != 0,
);
}
uab_types
}
}
struct DeviceShared {
raw: ash::Device,
handle_is_owned: bool,
@ -234,6 +310,7 @@ struct DeviceShared {
extension_fns: DeviceExtensionFunctions,
vendor_id: u32,
timestamp_period: f32,
uab_types: UpdateAfterBindTypes,
downlevel_flags: wgt::DownlevelFlags,
private_caps: PrivateCapabilities,
workarounds: Workarounds,
@ -314,6 +391,7 @@ pub struct BindGroupLayout {
raw: vk::DescriptorSetLayout,
desc_count: gpu_descriptor::DescriptorTotalCount,
types: Box<[(vk::DescriptorType, u32)]>,
requires_update_after_bind: bool,
}
#[derive(Debug)]

View File

@ -330,6 +330,10 @@ bitflags::bitflags! {
///
/// This is a native only feature.
const UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 1 << 21;
/// Allows the user to create bind groups continaing arrays with less bindings than the BindGroupLayout.
///
/// This is a native only feature.
const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 22;
/// Allows the user to create unsized uniform arrays of bindings:
///
/// eg. `uniform texture2D textures[]`.
@ -339,7 +343,7 @@ bitflags::bitflags! {
/// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s runtimeDescriptorArray feature
///
/// This is a native only feature.
const UNSIZED_BINDING_ARRAY = 1 << 22;
const UNSIZED_BINDING_ARRAY = 1 << 23;
/// Allows the user to call [`RenderPass::multi_draw_indirect`] and [`RenderPass::multi_draw_indexed_indirect`].
///
/// Allows multiple indirect calls to be dispatched from a single buffer.
@ -349,7 +353,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const MULTI_DRAW_INDIRECT = 1 << 23;
const MULTI_DRAW_INDIRECT = 1 << 24;
/// Allows the user to call [`RenderPass::multi_draw_indirect_count`] and [`RenderPass::multi_draw_indexed_indirect_count`].
///
/// This allows the use of a buffer containing the actual number of draw calls.
@ -359,7 +363,7 @@ bitflags::bitflags! {
/// - Vulkan 1.2+ (or VK_KHR_draw_indirect_count)
///
/// This is a native only feature.
const MULTI_DRAW_INDIRECT_COUNT = 1 << 24;
const MULTI_DRAW_INDIRECT_COUNT = 1 << 25;
/// Allows the use of push constants: small, fast bits of memory that can be updated
/// inside a [`RenderPass`].
///
@ -376,7 +380,7 @@ bitflags::bitflags! {
/// - OpenGL (emulated with uniforms)
///
/// This is a native only feature.
const PUSH_CONSTANTS = 1 << 25;
const PUSH_CONSTANTS = 1 << 26;
/// Allows the use of [`AddressMode::ClampToBorder`].
///
/// Supported platforms:
@ -387,7 +391,7 @@ bitflags::bitflags! {
/// - OpenGL
///
/// This is a web and native feature.
const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 26;
const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 27;
/// Allows the user to set [`PolygonMode::Line`] in [`PrimitiveState::polygon_mode`]
///
/// This allows drawing polygons/triangles as lines (wireframe) instead of filled
@ -398,7 +402,7 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a native only feature.
const POLYGON_MODE_LINE= 1 << 27;
const POLYGON_MODE_LINE= 1 << 28;
/// Allows the user to set [`PolygonMode::Point`] in [`PrimitiveState::polygon_mode`]
///
/// This allows only drawing the vertices of polygons/triangles instead of filled
@ -408,7 +412,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const POLYGON_MODE_POINT = 1 << 28;
const POLYGON_MODE_POINT = 1 << 29;
/// Enables ETC family of compressed textures. All ETC textures use 4x4 pixel blocks.
/// ETC2 RGB and RGBA1 are 8 bytes per block. RTC2 RGBA8 and EAC are 16 bytes per block.
///
@ -423,7 +427,7 @@ bitflags::bitflags! {
/// - Mobile (some)
///
/// This is a native-only feature.
const TEXTURE_COMPRESSION_ETC2 = 1 << 29;
const TEXTURE_COMPRESSION_ETC2 = 1 << 30;
/// Enables ASTC family of compressed textures. ASTC textures use pixel blocks varying from 4x4 to 12x12.
/// Blocks are always 16 bytes.
///
@ -438,7 +442,7 @@ bitflags::bitflags! {
/// - Mobile (some)
///
/// This is a native-only feature.
const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 30;
const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 31;
/// Enables device specific texture format features.
///
/// See `TextureFormatFeatures` for a listing of the features in question.
@ -450,7 +454,7 @@ bitflags::bitflags! {
/// This extension does not enable additional formats.
///
/// This is a native-only feature.
const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 31;
const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 32;
/// Enables 64-bit floating point types in SPIR-V shaders.
///
/// Note: even when supported by GPU hardware, 64-bit floating point operations are
@ -460,7 +464,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native-only feature.
const SHADER_FLOAT64 = 1 << 32;
const SHADER_FLOAT64 = 1 << 33;
/// Enables using 64-bit types for vertex attributes.
///
/// Requires SHADER_FLOAT64.
@ -468,7 +472,7 @@ bitflags::bitflags! {
/// Supported Platforms: N/A
///
/// This is a native-only feature.
const VERTEX_ATTRIBUTE_64BIT = 1 << 33;
const VERTEX_ATTRIBUTE_64BIT = 1 << 34;
/// Allows the user to set a overestimation-conservative-rasterization in [`PrimitiveState::conservative`]
///
/// Processing of degenerate triangles/lines is hardware specific.
@ -478,7 +482,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const CONSERVATIVE_RASTERIZATION = 1 << 34;
const CONSERVATIVE_RASTERIZATION = 1 << 35;
/// Enables bindings of writable storage buffers and textures visible to vertex shaders.
///
/// Note: some (tiled-based) platforms do not support vertex shaders with any side-effects.
@ -487,14 +491,14 @@ bitflags::bitflags! {
/// - All
///
/// This is a native-only feature.
const VERTEX_WRITABLE_STORAGE = 1 << 35;
const VERTEX_WRITABLE_STORAGE = 1 << 36;
/// Enables clear to zero for buffers & textures.
///
/// Supported platforms:
/// - All
///
/// This is a native only feature.
const CLEAR_COMMANDS = 1 << 36;
const CLEAR_COMMANDS = 1 << 37;
/// Enables creating shader modules from SPIR-V binary data (unsafe).
///
/// SPIR-V data is not parsed or interpreted in any way; you can use
@ -506,7 +510,7 @@ bitflags::bitflags! {
/// Vulkan implementation.
///
/// This is a native only feature.
const SPIRV_SHADER_PASSTHROUGH = 1 << 37;
const SPIRV_SHADER_PASSTHROUGH = 1 << 38;
/// Enables `builtin(primitive_index)` in fragment shaders.
///
/// Note: enables geometry processing for pipelines using the builtin.
@ -517,7 +521,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const SHADER_PRIMITIVE_INDEX = 1 << 38;
const SHADER_PRIMITIVE_INDEX = 1 << 39;
}
}