mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-22 23:04:07 +00:00
Merge #1315
1315: Add buffer arrays r=kvark a=csnewman **Connections** https://github.com/gfx-rs/wgpu-rs/pull/850 **Description** Adds buffer array bindings **Testing** TODO <!-- Non-trivial functional changes would need to be tested through: - [wgpu-rs](https://github.com/gfx-rs/wgpu-rs) - test the examples. - [wgpu-native](https://github.com/gfx-rs/wgpu-native/) - check the generated C header for sanity. Ideally, a PR needs to link to the draft PRs in these projects with relevant modifications. See https://github.com/gfx-rs/wgpu/pull/666 for an example. If you can add a unit/integration test here in `wgpu`, that would be best. --> Co-authored-by: Chandler Newman <chandler2newman@hotmail.co.uk>
This commit is contained in:
commit
acd21eeeb1
@ -603,6 +603,7 @@ pub struct BufferBinding {
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub enum BindingResource<'a> {
|
||||
Buffer(BufferBinding),
|
||||
BufferArray(Cow<'a, [BufferBinding]>),
|
||||
Sampler(SamplerId),
|
||||
TextureView(TextureViewId),
|
||||
TextureViewArray(Cow<'a, [TextureViewId]>),
|
||||
|
@ -1183,22 +1183,34 @@ impl<B: GfxBackend> Device<B> {
|
||||
ty: wgt::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: _,
|
||||
} => (&mut desc_count.uniform_buffer, None),
|
||||
} => (
|
||||
&mut desc_count.uniform_buffer,
|
||||
Some(wgt::Features::BUFFER_BINDING_ARRAY),
|
||||
),
|
||||
Bt::Buffer {
|
||||
ty: wgt::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: _,
|
||||
} => (&mut desc_count.uniform_buffer_dynamic, None),
|
||||
} => (
|
||||
&mut desc_count.uniform_buffer_dynamic,
|
||||
Some(wgt::Features::BUFFER_BINDING_ARRAY),
|
||||
),
|
||||
Bt::Buffer {
|
||||
ty: wgt::BufferBindingType::Storage { .. },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: _,
|
||||
} => (&mut desc_count.storage_buffer, None),
|
||||
} => (
|
||||
&mut desc_count.storage_buffer,
|
||||
Some(wgt::Features::BUFFER_BINDING_ARRAY),
|
||||
),
|
||||
Bt::Buffer {
|
||||
ty: wgt::BufferBindingType::Storage { .. },
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: _,
|
||||
} => (&mut desc_count.storage_buffer_dynamic, None),
|
||||
} => (
|
||||
&mut desc_count.storage_buffer_dynamic,
|
||||
Some(wgt::Features::BUFFER_BINDING_ARRAY),
|
||||
),
|
||||
Bt::Sampler { .. } => (&mut desc_count.sampler, None),
|
||||
Bt::Texture { .. } => (
|
||||
&mut desc_count.sampled_image,
|
||||
@ -1274,6 +1286,120 @@ impl<B: GfxBackend> Device<B> {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_buffer_descriptor<'a>(
|
||||
bb: &binding_model::BufferBinding,
|
||||
binding: u32,
|
||||
decl: &wgt::BindGroupLayoutEntry,
|
||||
used_buffer_ranges: &mut Vec<MemoryInitTrackerAction<id::BufferId>>,
|
||||
dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
|
||||
used: &mut TrackerSet,
|
||||
storage: &'a Storage<resource::Buffer<B>, id::BufferId>,
|
||||
limits: &wgt::Limits,
|
||||
) -> Result<hal::pso::Descriptor<'a, B>, binding_model::CreateBindGroupError> {
|
||||
use crate::binding_model::CreateBindGroupError as Error;
|
||||
|
||||
let (binding_ty, dynamic, min_size) = match decl.ty {
|
||||
wgt::BindingType::Buffer {
|
||||
ty,
|
||||
has_dynamic_offset,
|
||||
min_binding_size,
|
||||
} => (ty, has_dynamic_offset, min_binding_size),
|
||||
_ => {
|
||||
return Err(Error::WrongBindingType {
|
||||
binding,
|
||||
actual: decl.ty,
|
||||
expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
|
||||
})
|
||||
}
|
||||
};
|
||||
let (pub_usage, internal_use, range_limit) = match binding_ty {
|
||||
wgt::BufferBindingType::Uniform => (
|
||||
wgt::BufferUsage::UNIFORM,
|
||||
resource::BufferUse::UNIFORM,
|
||||
limits.max_uniform_buffer_binding_size,
|
||||
),
|
||||
wgt::BufferBindingType::Storage { read_only } => (
|
||||
wgt::BufferUsage::STORAGE,
|
||||
if read_only {
|
||||
resource::BufferUse::STORAGE_LOAD
|
||||
} else {
|
||||
resource::BufferUse::STORAGE_STORE
|
||||
},
|
||||
limits.max_storage_buffer_binding_size,
|
||||
),
|
||||
};
|
||||
|
||||
if bb.offset % wgt::BIND_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(Error::UnalignedBufferOffset(bb.offset));
|
||||
}
|
||||
|
||||
let buffer = used
|
||||
.buffers
|
||||
.use_extend(storage, bb.buffer_id, (), internal_use)
|
||||
.map_err(|_| Error::InvalidBuffer(bb.buffer_id))?;
|
||||
check_buffer_usage(buffer.usage, pub_usage)?;
|
||||
let &(ref buffer_raw, _) = buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
.ok_or(Error::InvalidBuffer(bb.buffer_id))?;
|
||||
|
||||
let (bind_size, bind_end) = match bb.size {
|
||||
Some(size) => {
|
||||
let end = bb.offset + size.get();
|
||||
if end > buffer.size {
|
||||
return Err(Error::BindingRangeTooLarge {
|
||||
buffer: bb.buffer_id,
|
||||
range: bb.offset..end,
|
||||
size: buffer.size,
|
||||
});
|
||||
}
|
||||
(size.get(), end)
|
||||
}
|
||||
None => (buffer.size - bb.offset, buffer.size),
|
||||
};
|
||||
|
||||
if bind_size > range_limit as u64 {
|
||||
return Err(Error::BufferRangeTooLarge {
|
||||
binding,
|
||||
given: bind_size as u32,
|
||||
limit: range_limit,
|
||||
});
|
||||
}
|
||||
|
||||
// Record binding info for validating dynamic offsets
|
||||
if dynamic {
|
||||
dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
|
||||
maximum_dynamic_offset: buffer.size - bind_end,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(non_zero) = min_size {
|
||||
let min_size = non_zero.get();
|
||||
if min_size > bind_size {
|
||||
return Err(Error::BindingSizeTooSmall {
|
||||
buffer: bb.buffer_id,
|
||||
actual: bind_size,
|
||||
min: min_size,
|
||||
});
|
||||
}
|
||||
} else if bind_size == 0 {
|
||||
return Err(Error::BindingZeroSize(bb.buffer_id));
|
||||
}
|
||||
|
||||
used_buffer_ranges.push(MemoryInitTrackerAction {
|
||||
id: bb.buffer_id,
|
||||
range: bb.offset..(bb.offset + bind_size),
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
});
|
||||
|
||||
let sub_range = hal::buffer::SubRange {
|
||||
offset: bb.offset,
|
||||
size: Some(bind_size),
|
||||
};
|
||||
Ok(hal::pso::Descriptor::Buffer(buffer_raw, sub_range))
|
||||
}
|
||||
|
||||
fn create_bind_group<G: GlobalIdentityHandlerFactory>(
|
||||
&self,
|
||||
self_id: id::DeviceId,
|
||||
@ -1317,105 +1443,52 @@ impl<B: GfxBackend> Device<B> {
|
||||
.ok_or(Error::MissingBindingDeclaration(binding))?;
|
||||
let descriptors: SmallVec<[_; 1]> = match entry.resource {
|
||||
Br::Buffer(ref bb) => {
|
||||
let (binding_ty, dynamic, min_size) = match decl.ty {
|
||||
wgt::BindingType::Buffer {
|
||||
ty,
|
||||
has_dynamic_offset,
|
||||
min_binding_size,
|
||||
} => (ty, has_dynamic_offset, min_binding_size),
|
||||
_ => {
|
||||
return Err(Error::WrongBindingType {
|
||||
binding,
|
||||
actual: decl.ty,
|
||||
expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
|
||||
})
|
||||
}
|
||||
};
|
||||
let (pub_usage, internal_use, range_limit) = match binding_ty {
|
||||
wgt::BufferBindingType::Uniform => (
|
||||
wgt::BufferUsage::UNIFORM,
|
||||
resource::BufferUse::UNIFORM,
|
||||
self.limits.max_uniform_buffer_binding_size,
|
||||
),
|
||||
wgt::BufferBindingType::Storage { read_only } => (
|
||||
wgt::BufferUsage::STORAGE,
|
||||
if read_only {
|
||||
resource::BufferUse::STORAGE_LOAD
|
||||
} else {
|
||||
resource::BufferUse::STORAGE_STORE
|
||||
},
|
||||
self.limits.max_storage_buffer_binding_size,
|
||||
),
|
||||
};
|
||||
|
||||
if bb.offset % wgt::BIND_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(Error::UnalignedBufferOffset(bb.offset));
|
||||
let buffer_desc = Self::create_buffer_descriptor(
|
||||
&bb,
|
||||
binding,
|
||||
&decl,
|
||||
&mut used_buffer_ranges,
|
||||
&mut dynamic_binding_info,
|
||||
&mut used,
|
||||
&*buffer_guard,
|
||||
&self.limits,
|
||||
)?;
|
||||
SmallVec::from([buffer_desc])
|
||||
}
|
||||
Br::BufferArray(ref bindings_array) => {
|
||||
let required_feats = wgt::Features::BUFFER_BINDING_ARRAY;
|
||||
if !self.features.contains(required_feats) {
|
||||
return Err(Error::MissingFeatures(required_feats));
|
||||
}
|
||||
|
||||
let buffer = used
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, bb.buffer_id, (), internal_use)
|
||||
.map_err(|_| Error::InvalidBuffer(bb.buffer_id))?;
|
||||
check_buffer_usage(buffer.usage, pub_usage)?;
|
||||
let &(ref buffer_raw, _) = buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
.ok_or(Error::InvalidBuffer(bb.buffer_id))?;
|
||||
|
||||
let (bind_size, bind_end) = match bb.size {
|
||||
Some(size) => {
|
||||
let end = bb.offset + size.get();
|
||||
if end > buffer.size {
|
||||
return Err(Error::BindingRangeTooLarge {
|
||||
buffer: bb.buffer_id,
|
||||
range: bb.offset..end,
|
||||
size: buffer.size,
|
||||
});
|
||||
}
|
||||
(size.get(), end)
|
||||
}
|
||||
None => (buffer.size - bb.offset, buffer.size),
|
||||
};
|
||||
|
||||
if bind_size > range_limit as u64 {
|
||||
return Err(Error::BufferRangeTooLarge {
|
||||
binding,
|
||||
given: bind_size as u32,
|
||||
limit: range_limit,
|
||||
});
|
||||
}
|
||||
|
||||
// Record binding info for validating dynamic offsets
|
||||
if dynamic {
|
||||
dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
|
||||
maximum_dynamic_offset: buffer.size - bind_end,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(non_zero) = min_size {
|
||||
let min_size = non_zero.get();
|
||||
if min_size > bind_size {
|
||||
return Err(Error::BindingSizeTooSmall {
|
||||
buffer: bb.buffer_id,
|
||||
actual: bind_size,
|
||||
min: min_size,
|
||||
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 if bind_size == 0 {
|
||||
return Err(Error::BindingZeroSize(bb.buffer_id));
|
||||
} else {
|
||||
return Err(Error::SingleBindingExpected);
|
||||
}
|
||||
|
||||
used_buffer_ranges.push(MemoryInitTrackerAction {
|
||||
id: bb.buffer_id,
|
||||
range: bb.offset..(bb.offset + bind_size),
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
});
|
||||
|
||||
let sub_range = hal::buffer::SubRange {
|
||||
offset: bb.offset,
|
||||
size: Some(bind_size),
|
||||
};
|
||||
SmallVec::from([hal::pso::Descriptor::Buffer(buffer_raw, sub_range)])
|
||||
bindings_array
|
||||
.iter()
|
||||
.map(|bb| {
|
||||
Self::create_buffer_descriptor(
|
||||
&bb,
|
||||
binding,
|
||||
&decl,
|
||||
&mut used_buffer_ranges,
|
||||
&mut dynamic_binding_info,
|
||||
&mut used,
|
||||
&*buffer_guard,
|
||||
&self.limits,
|
||||
)
|
||||
})
|
||||
.collect::<Result<_, _>>()?
|
||||
}
|
||||
Br::Sampler(id) => {
|
||||
match decl.ty {
|
||||
|
@ -197,6 +197,26 @@ impl<B: GfxBackend> Adapter<B> {
|
||||
wgt::Features::CONSERVATIVE_RASTERIZATION,
|
||||
adapter_features.contains(hal::Features::CONSERVATIVE_RASTERIZATION),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::BUFFER_BINDING_ARRAY,
|
||||
adapter_features.contains(hal::Features::BUFFER_DESCRIPTOR_ARRAY),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING,
|
||||
adapter_features.contains(hal::Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||
adapter_features.contains(hal::Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING,
|
||||
adapter_features.contains(hal::Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||
adapter_features.contains(hal::Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING),
|
||||
);
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
//TODO: https://github.com/gfx-rs/gfx/issues/3346
|
||||
features.set(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true);
|
||||
@ -514,6 +534,30 @@ impl<B: GfxBackend> Adapter<B> {
|
||||
desc.features
|
||||
.contains(wgt::Features::CONSERVATIVE_RASTERIZATION),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::BUFFER_DESCRIPTOR_ARRAY,
|
||||
desc.features.contains(wgt::Features::BUFFER_BINDING_ARRAY),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING,
|
||||
desc.features
|
||||
.contains(wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING,
|
||||
desc.features
|
||||
.contains(wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING,
|
||||
desc.features
|
||||
.contains(wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING,
|
||||
desc.features
|
||||
.contains(wgt::Features::STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING),
|
||||
);
|
||||
|
||||
let family = self
|
||||
.raw
|
||||
|
@ -435,6 +435,97 @@ bitflags::bitflags! {
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const CONSERVATIVE_RASTERIZATION = 0x0000_0000_8000_0000;
|
||||
/// Allows the user to create arrays of buffers in shaders:
|
||||
///
|
||||
/// eg. `uniform myBuffer { .... } buffer_array[10]`.
|
||||
///
|
||||
/// This capability allows them to exist and to be indexed by compile time constant
|
||||
/// values.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - DX12
|
||||
/// - Vulkan
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const BUFFER_BINDING_ARRAY = 0x0000_0001_0000_0000;
|
||||
/// Allows shaders to index uniform buffer arrays with dynamically uniform values:
|
||||
///
|
||||
/// eg. `buffer_array[uniform_value]`
|
||||
///
|
||||
/// This capability means the hardware will also support BUFFER_BINDING_ARRAY.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - DX12
|
||||
/// - Vulkan's shaderUniformBufferArrayDynamicIndexing feature
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING = 0x0000_0002_0000_0000;
|
||||
/// Allows shaders to index uniform buffer arrays with dynamically non-uniform values:
|
||||
///
|
||||
/// eg. `buffer_array[vertex_data]`
|
||||
///
|
||||
/// In order to use this capability, the corresponding GLSL extension must be enabled like so:
|
||||
///
|
||||
/// `#extension GL_EXT_nonuniform_qualifier : require`
|
||||
///
|
||||
/// and then used either as `nonuniformEXT` qualifier in variable declaration:
|
||||
///
|
||||
/// eg. `layout(location = 0) nonuniformEXT flat in int vertex_data;`
|
||||
///
|
||||
/// or as `nonuniformEXT` constructor:
|
||||
///
|
||||
/// eg. `buffer_array[nonuniformEXT(vertex_data)]`
|
||||
///
|
||||
/// HLSL does not need any extension.
|
||||
///
|
||||
/// This capability means the hardware will also support UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING
|
||||
/// and BUFFER_BINDING_ARRAY.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - DX12
|
||||
/// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s shaderUniformBufferArrayNonUniformIndexing feature)
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 0x0000_0004_0000_0000;
|
||||
/// Allows shaders to index storage buffer arrays with dynamically uniform values:
|
||||
///
|
||||
/// eg. `buffer_array[uniform_value]`
|
||||
///
|
||||
/// This capability means the hardware will also support BUFFER_BINDING_ARRAY.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - DX12
|
||||
/// - Vulkan's shaderStorageBufferArrayDynamicIndexing feature
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING = 0x0000_0008_0000_0000;
|
||||
/// Allows shaders to index storage buffer arrays with dynamically non-uniform values:
|
||||
///
|
||||
/// eg. `buffer_array[vertex_data]`
|
||||
///
|
||||
/// In order to use this capability, the corresponding GLSL extension must be enabled like so:
|
||||
///
|
||||
/// `#extension GL_EXT_nonuniform_qualifier : require`
|
||||
///
|
||||
/// and then used either as `nonuniformEXT` qualifier in variable declaration:
|
||||
///
|
||||
/// eg. `layout(location = 0) nonuniformEXT flat in int vertex_data;`
|
||||
///
|
||||
/// or as `nonuniformEXT` constructor:
|
||||
///
|
||||
/// eg. `buffer_array[nonuniformEXT(vertex_data)]`
|
||||
///
|
||||
/// HLSL does not need any extension.
|
||||
///
|
||||
/// This capability means the hardware will also support STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING
|
||||
/// and BUFFER_BINDING_ARRAY.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - DX12
|
||||
/// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s shaderStorageBufferArrayNonUniformIndexing feature)
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 0x0000_0010_0000_0000;
|
||||
/// Features which are part of the upstream WebGPU standard.
|
||||
const ALL_WEBGPU = 0x0000_0000_0000_FFFF;
|
||||
/// Features that are only available when targeting native (not web).
|
||||
|
Loading…
Reference in New Issue
Block a user