mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-22 06:44:14 +00:00
Added storage texture array support.
I also took the liberty to refactor texture format checking a bit, and tighten up sampled texture support detection for Metal as well.
This commit is contained in:
parent
ca291bfc4f
commit
52fab481e6
@ -982,7 +982,7 @@ impl<A: HalApi> Device<A> {
|
||||
Bt::Sampler { .. } => (None, false),
|
||||
Bt::Texture { .. } => (Some(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY), false),
|
||||
Bt::StorageTexture { access, .. } => (
|
||||
None,
|
||||
Some(wgt::Features::STORAGE_TEXTURE_BINDING_ARRAY),
|
||||
match access {
|
||||
wgt::StorageTextureAccess::ReadOnly => false,
|
||||
wgt::StorageTextureAccess::WriteOnly => true,
|
||||
@ -1304,104 +1304,13 @@ impl<A: HalApi> Device<A> {
|
||||
.views
|
||||
.use_extend(&*texture_view_guard, id, (), ())
|
||||
.map_err(|_| Error::InvalidTextureView(id))?;
|
||||
let format_info = view.desc.format.describe();
|
||||
let (pub_usage, internal_use) = match decl.ty {
|
||||
wgt::BindingType::Texture {
|
||||
sample_type,
|
||||
view_dimension,
|
||||
multisampled,
|
||||
} => {
|
||||
use wgt::TextureSampleType as Tst;
|
||||
if multisampled != (view.samples != 1) {
|
||||
return Err(Error::InvalidTextureMultisample {
|
||||
binding,
|
||||
layout_multisampled: multisampled,
|
||||
view_samples: view.samples,
|
||||
});
|
||||
}
|
||||
match (sample_type, format_info.sample_type, view.format_features.filterable ) {
|
||||
(Tst::Uint, Tst::Uint, ..) |
|
||||
(Tst::Sint, Tst::Sint, ..) |
|
||||
(Tst::Depth, Tst::Depth, ..) |
|
||||
// if we expect non-filterable, accept anything float
|
||||
(Tst::Float { filterable: false }, Tst::Float { .. }, ..) |
|
||||
// if we expect filterable, require it
|
||||
(Tst::Float { filterable: true }, Tst::Float { filterable: true }, ..) |
|
||||
// if we expect filterable, also accept Float that is defined as unfilterable if filterable feature is explicitly enabled
|
||||
// (only hit if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is enabled)
|
||||
(Tst::Float { filterable: true }, Tst::Float { .. }, true) |
|
||||
// if we expect float, also accept depth
|
||||
(Tst::Float { .. }, Tst::Depth, ..) => {}
|
||||
_ => {
|
||||
return Err(Error::InvalidTextureSampleType {
|
||||
binding,
|
||||
layout_sample_type: sample_type,
|
||||
view_format: view.desc.format,
|
||||
})
|
||||
}
|
||||
}
|
||||
if view_dimension != view.desc.dimension {
|
||||
return Err(Error::InvalidTextureDimension {
|
||||
binding,
|
||||
layout_dimension: view_dimension,
|
||||
view_dimension: view.desc.dimension,
|
||||
});
|
||||
}
|
||||
(wgt::TextureUsage::SAMPLED, view.sampled_internal_use)
|
||||
}
|
||||
wgt::BindingType::StorageTexture {
|
||||
access,
|
||||
format,
|
||||
view_dimension,
|
||||
} => {
|
||||
if format != view.desc.format {
|
||||
return Err(Error::InvalidStorageTextureFormat {
|
||||
binding,
|
||||
layout_format: format,
|
||||
view_format: view.desc.format,
|
||||
});
|
||||
}
|
||||
if view_dimension != view.desc.dimension {
|
||||
return Err(Error::InvalidTextureDimension {
|
||||
binding,
|
||||
layout_dimension: view_dimension,
|
||||
view_dimension: view.desc.dimension,
|
||||
});
|
||||
}
|
||||
let internal_use = match access {
|
||||
wgt::StorageTextureAccess::ReadOnly => {
|
||||
hal::TextureUse::STORAGE_LOAD
|
||||
}
|
||||
wgt::StorageTextureAccess::WriteOnly => {
|
||||
hal::TextureUse::STORAGE_STORE
|
||||
}
|
||||
wgt::StorageTextureAccess::ReadWrite => {
|
||||
if !view.format_features.flags.contains(
|
||||
wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
|
||||
) {
|
||||
return Err(Error::StorageReadWriteNotSupported(
|
||||
view.desc.format,
|
||||
));
|
||||
}
|
||||
let (pub_usage, internal_use) = Self::texture_use_parameters(
|
||||
binding,
|
||||
decl,
|
||||
view,
|
||||
"SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
|
||||
)?;
|
||||
|
||||
hal::TextureUse::STORAGE_STORE | hal::TextureUse::STORAGE_LOAD
|
||||
}
|
||||
};
|
||||
(wgt::TextureUsage::STORAGE, internal_use)
|
||||
}
|
||||
_ => return Err(Error::WrongBindingType {
|
||||
binding,
|
||||
actual: decl.ty,
|
||||
expected:
|
||||
"SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
|
||||
}),
|
||||
};
|
||||
|
||||
if hal::FormatAspect::from(view.desc.format)
|
||||
.contains(hal::FormatAspect::DEPTH | hal::FormatAspect::STENCIL)
|
||||
{
|
||||
return Err(Error::DepthStencilAspect);
|
||||
}
|
||||
match view.source {
|
||||
resource::TextureViewSource::Native(ref source_id) => {
|
||||
// Careful here: the texture may no longer have its own ref count,
|
||||
@ -1449,18 +1358,10 @@ impl<A: HalApi> Device<A> {
|
||||
.views
|
||||
.use_extend(&*texture_view_guard, id, (), ())
|
||||
.map_err(|_| Error::InvalidTextureView(id))?;
|
||||
let (pub_usage, internal_use) = match decl.ty {
|
||||
wgt::BindingType::Texture { .. } => {
|
||||
(wgt::TextureUsage::SAMPLED, view.sampled_internal_use)
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::WrongBindingType {
|
||||
binding,
|
||||
actual: decl.ty,
|
||||
expected: "SampledTextureArray",
|
||||
})
|
||||
}
|
||||
};
|
||||
let (pub_usage, internal_use) =
|
||||
Self::texture_use_parameters(binding, decl, view,
|
||||
"SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray"
|
||||
)?;
|
||||
|
||||
match view.source {
|
||||
resource::TextureViewSource::Native(ref source_id) => {
|
||||
@ -1533,6 +1434,109 @@ impl<A: HalApi> Device<A> {
|
||||
})
|
||||
}
|
||||
|
||||
fn texture_use_parameters(
|
||||
binding: u32,
|
||||
decl: &wgt::BindGroupLayoutEntry,
|
||||
view: &crate::resource::TextureView<A>,
|
||||
expected: &'static str,
|
||||
) -> Result<(wgt::TextureUsage, hal::TextureUse), binding_model::CreateBindGroupError> {
|
||||
use crate::binding_model::CreateBindGroupError as Error;
|
||||
if hal::FormatAspect::from(view.desc.format)
|
||||
.contains(hal::FormatAspect::DEPTH | hal::FormatAspect::STENCIL)
|
||||
{
|
||||
return Err(Error::DepthStencilAspect);
|
||||
}
|
||||
let format_info = view.desc.format.describe();
|
||||
match decl.ty {
|
||||
wgt::BindingType::Texture {
|
||||
sample_type,
|
||||
view_dimension,
|
||||
multisampled,
|
||||
} => {
|
||||
use wgt::TextureSampleType as Tst;
|
||||
if multisampled != (view.samples != 1) {
|
||||
return Err(Error::InvalidTextureMultisample {
|
||||
binding,
|
||||
layout_multisampled: multisampled,
|
||||
view_samples: view.samples,
|
||||
});
|
||||
}
|
||||
match (sample_type, format_info.sample_type, view.format_features.filterable) {
|
||||
(Tst::Uint, Tst::Uint, ..) |
|
||||
(Tst::Sint, Tst::Sint, ..) |
|
||||
(Tst::Depth, Tst::Depth, ..) |
|
||||
// if we expect non-filterable, accept anything float
|
||||
(Tst::Float { filterable: false }, Tst::Float { .. }, ..) |
|
||||
// if we expect filterable, require it
|
||||
(Tst::Float { filterable: true }, Tst::Float { filterable: true }, ..) |
|
||||
// if we expect filterable, also accept Float that is defined as unfilterable if filterable feature is explicitly enabled
|
||||
// (only hit if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is enabled)
|
||||
(Tst::Float { filterable: true }, Tst::Float { .. }, true) |
|
||||
// if we expect float, also accept depth
|
||||
(Tst::Float { .. }, Tst::Depth, ..) => {}
|
||||
_ => {
|
||||
return Err(Error::InvalidTextureSampleType {
|
||||
binding,
|
||||
layout_sample_type: sample_type,
|
||||
view_format: view.desc.format,
|
||||
})
|
||||
}
|
||||
}
|
||||
if view_dimension != view.desc.dimension {
|
||||
return Err(Error::InvalidTextureDimension {
|
||||
binding,
|
||||
layout_dimension: view_dimension,
|
||||
view_dimension: view.desc.dimension,
|
||||
});
|
||||
}
|
||||
Ok((wgt::TextureUsage::SAMPLED, view.sampled_internal_use))
|
||||
}
|
||||
wgt::BindingType::StorageTexture {
|
||||
access,
|
||||
format,
|
||||
view_dimension,
|
||||
} => {
|
||||
if format != view.desc.format {
|
||||
return Err(Error::InvalidStorageTextureFormat {
|
||||
binding,
|
||||
layout_format: format,
|
||||
view_format: view.desc.format,
|
||||
});
|
||||
}
|
||||
if view_dimension != view.desc.dimension {
|
||||
return Err(Error::InvalidTextureDimension {
|
||||
binding,
|
||||
layout_dimension: view_dimension,
|
||||
view_dimension: view.desc.dimension,
|
||||
});
|
||||
}
|
||||
let internal_use = match access {
|
||||
wgt::StorageTextureAccess::ReadOnly => hal::TextureUse::STORAGE_LOAD,
|
||||
wgt::StorageTextureAccess::WriteOnly => hal::TextureUse::STORAGE_STORE,
|
||||
wgt::StorageTextureAccess::ReadWrite => {
|
||||
if !view
|
||||
.format_features
|
||||
.flags
|
||||
.contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE)
|
||||
{
|
||||
return Err(Error::StorageReadWriteNotSupported(view.desc.format));
|
||||
}
|
||||
|
||||
hal::TextureUse::STORAGE_STORE | hal::TextureUse::STORAGE_LOAD
|
||||
}
|
||||
};
|
||||
Ok((wgt::TextureUsage::STORAGE, internal_use))
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::WrongBindingType {
|
||||
binding,
|
||||
actual: decl.ty,
|
||||
expected,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_pipeline_layout(
|
||||
&self,
|
||||
self_id: id::DeviceId,
|
||||
|
@ -817,6 +817,22 @@ impl super::PrivateCapabilities {
|
||||
} else {
|
||||
Self::version_at_least(major, minor, 11, 0)
|
||||
},
|
||||
supports_arrays_of_textures: Self::supports_any(
|
||||
&device,
|
||||
&[
|
||||
MTLFeatureSet::iOS_GPUFamily3_v2,
|
||||
MTLFeatureSet::iOS_GPUFamily4_v1,
|
||||
MTLFeatureSet::iOS_GPUFamily5_v1,
|
||||
MTLFeatureSet::tvOS_GPUFamily2_v1,
|
||||
MTLFeatureSet::macOS_GPUFamily1_v3,
|
||||
MTLFeatureSet::macOS_GPUFamily2_v1,
|
||||
],
|
||||
),
|
||||
supports_arrays_of_textures_write: device.supports_family(MTLGPUFamily::Apple6)
|
||||
|| device.supports_family(MTLGPUFamily::Mac1)
|
||||
|| device.supports_family(MTLGPUFamily::Mac2)
|
||||
|| device.supports_family(MTLGPUFamily::MacCatalyst1)
|
||||
|| device.supports_family(MTLGPUFamily::MacCatalyst2),
|
||||
}
|
||||
}
|
||||
|
||||
@ -830,8 +846,21 @@ impl super::PrivateCapabilities {
|
||||
| F::VERTEX_WRITABLE_STORAGE;
|
||||
|
||||
features.set(
|
||||
F::SAMPLED_TEXTURE_BINDING_ARRAY | F::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
|
||||
self.msl_version >= MTLLanguageVersion::V2_0,
|
||||
F::SAMPLED_TEXTURE_BINDING_ARRAY
|
||||
| F::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING
|
||||
| F::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
self.msl_version >= MTLLanguageVersion::V2_0 && self.supports_arrays_of_textures,
|
||||
);
|
||||
//// XXX: this is technically not true, as read-only storage images can be used in arrays
|
||||
//// on precisely the same conditions that sampled textures can. But texel fetch from a
|
||||
//// sampled texture is a thing; should we bother introducing another feature flag?
|
||||
features.set(
|
||||
F::STORAGE_TEXTURE_BINDING_ARRAY
|
||||
| F::STORAGE_TEXTURE_ARRAY_DYNAMIC_INDEXING
|
||||
| F::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
self.msl_version >= MTLLanguageVersion::V2_2
|
||||
&& self.supports_arrays_of_textures
|
||||
&& self.supports_arrays_of_textures_write,
|
||||
);
|
||||
features.set(
|
||||
F::ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
|
@ -192,6 +192,8 @@ struct PrivateCapabilities {
|
||||
can_set_maximum_drawables_count: bool,
|
||||
can_set_display_sync: bool,
|
||||
can_set_next_drawable_timeout: bool,
|
||||
supports_arrays_of_textures: bool,
|
||||
supports_arrays_of_textures_write: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -138,7 +138,11 @@ impl PhysicalDeviceFeatures {
|
||||
wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
),
|
||||
)
|
||||
//.shader_storage_image_array_non_uniform_indexing(
|
||||
.shader_storage_image_array_non_uniform_indexing(
|
||||
requested_features.contains(
|
||||
wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
),
|
||||
)
|
||||
//.shader_storage_buffer_array_non_uniform_indexing(
|
||||
.shader_uniform_buffer_array_non_uniform_indexing(
|
||||
requested_features
|
||||
@ -164,7 +168,11 @@ impl PhysicalDeviceFeatures {
|
||||
wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
),
|
||||
)
|
||||
//.shader_storage_image_array_non_uniform_indexing(
|
||||
.shader_storage_image_array_non_uniform_indexing(
|
||||
requested_features.contains(
|
||||
wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
),
|
||||
)
|
||||
//.shader_storage_buffer_array_non_uniform_indexing(
|
||||
.shader_uniform_buffer_array_non_uniform_indexing(
|
||||
requested_features
|
||||
@ -199,6 +207,7 @@ impl PhysicalDeviceFeatures {
|
||||
| F::PUSH_CONSTANTS
|
||||
| F::ADDRESS_MODE_CLAMP_TO_BORDER
|
||||
| F::SAMPLED_TEXTURE_BINDING_ARRAY
|
||||
| F::STORAGE_TEXTURE_BINDING_ARRAY
|
||||
| F::BUFFER_BINDING_ARRAY;
|
||||
let mut dl_flags = Df::all();
|
||||
|
||||
@ -244,6 +253,10 @@ impl PhysicalDeviceFeatures {
|
||||
F::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
|
||||
self.core.shader_sampled_image_array_dynamic_indexing != 0,
|
||||
);
|
||||
features.set(
|
||||
F::STORAGE_TEXTURE_ARRAY_DYNAMIC_INDEXING,
|
||||
self.core.shader_storage_image_array_dynamic_indexing != 0,
|
||||
);
|
||||
features.set(
|
||||
F::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING,
|
||||
self.core.shader_storage_buffer_array_dynamic_indexing != 0,
|
||||
@ -270,7 +283,9 @@ impl PhysicalDeviceFeatures {
|
||||
if vulkan_1_2.shader_sampled_image_array_non_uniform_indexing != 0 {
|
||||
features |= F::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING;
|
||||
}
|
||||
//if vulkan_1_2.shader_storage_image_array_non_uniform_indexing != 0 {
|
||||
if vulkan_1_2.shader_storage_image_array_non_uniform_indexing != 0 {
|
||||
features |= F::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING;
|
||||
}
|
||||
//if vulkan_1_2.shader_storage_buffer_array_non_uniform_indexing != 0 {
|
||||
if vulkan_1_2.shader_uniform_buffer_array_non_uniform_indexing != 0 {
|
||||
features |= F::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING;
|
||||
@ -289,7 +304,9 @@ impl PhysicalDeviceFeatures {
|
||||
if descriptor_indexing.shader_sampled_image_array_non_uniform_indexing != 0 {
|
||||
features |= F::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING;
|
||||
}
|
||||
//if descriptor_indexing.shader_storage_image_array_non_uniform_indexing != 0 {
|
||||
if descriptor_indexing.shader_storage_image_array_non_uniform_indexing != 0 {
|
||||
features |= F::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING;
|
||||
}
|
||||
//if descriptor_indexing.shader_storage_buffer_array_non_uniform_indexing != 0 {
|
||||
if descriptor_indexing.shader_uniform_buffer_array_non_uniform_indexing != 0 {
|
||||
features |= F::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING;
|
||||
|
@ -537,6 +537,56 @@ bitflags::bitflags! {
|
||||
///
|
||||
/// This is a native-only feature.
|
||||
const VERTEX_WRITABLE_STORAGE = 0x0000_0020_0000_0000;
|
||||
/// Allows the user to create uniform arrays of storage textures in shaders:
|
||||
///
|
||||
/// eg. `uniform image2D textures[10]`.
|
||||
///
|
||||
/// This capability allows them to exist and to be indexed by compile time constant
|
||||
/// values.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - Metal (with MSL 2.2+ on macOS 10.13+)
|
||||
/// - Vulkan
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const STORAGE_TEXTURE_BINDING_ARRAY = 0x0000_0040_0000_0000;
|
||||
/// Allows shaders to index storage texture arrays with dynamically uniform values:
|
||||
///
|
||||
/// eg. `texture_array[uniform_value]`
|
||||
///
|
||||
/// This capability means the hardware will also support STORAGE_TEXTURE_BINDING_ARRAY.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - Metal (with MSL 2.2+ on macOS 10.13+)
|
||||
/// - Vulkan's shaderSampledImageArrayDynamicIndexing feature
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const STORAGE_TEXTURE_ARRAY_DYNAMIC_INDEXING = 0x0000_0080_0000_0000;
|
||||
/// Allows shaders to index storage texture arrays with dynamically non-uniform values:
|
||||
///
|
||||
/// eg. `texture_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. `texture_array[nonuniformEXT(vertex_data)]`
|
||||
///
|
||||
/// This capability means the hardware will also support STORAGE_TEXTURE_ARRAY_DYNAMIC_INDEXING
|
||||
/// and STORAGE_TEXTURE_BINDING_ARRAY.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// - Metal (with MSL 2.2+ on macOS 10.13+)
|
||||
/// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s shaderSampledImageArrayNonUniformIndexing feature)
|
||||
///
|
||||
/// This is a native only feature.
|
||||
const STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 0x0000_0100_0000_0000;
|
||||
/// Enables clear to zero for buffers & images.
|
||||
///
|
||||
/// Supported platforms:
|
||||
|
Loading…
Reference in New Issue
Block a user