Add feature float32-filterable (#4759)

This commit is contained in:
Almar Klein 2023-12-04 14:23:13 +01:00 committed by GitHub
parent dd7e33250b
commit 32c5a22293
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 191 additions and 30 deletions

View File

@ -53,6 +53,7 @@ Previously, `DeviceExt::create_texture_with_data` only allowed data to be provid
#### General
- Added `DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW` to know if `@builtin(vertex_index)` and `@builtin(instance_index)` will respect the `first_vertex` / `first_instance` in indirect calls. If this is not present, both will always start counting from 0. Currently enabled on all backends except DX12. By @cwfitzgerald in [#4722](https://github.com/gfx-rs/wgpu/pull/4722)
- No longer validate surfaces against their allowed extent range on configure. This caused warnings that were almost impossible to avoid. As before, the resulting behavior depends on the compositor. By @wumpf in [#????](https://github.com/gfx-rs/wgpu/pull/????)
- Added support for the float32-filterable feature. By @almarklein in [#4759](https://github.com/gfx-rs/wgpu/pull/4759)
#### OpenGL
- `@builtin(instance_index)` now properly reflects the range provided in the draw call instead of always counting from 0. By @cwfitzgerald in [#4722](https://github.com/gfx-rs/wgpu/pull/4722).

View File

@ -4980,6 +4980,7 @@ webidl.converters["GPUFeatureName"] = webidl.createEnumConverter(
"texture-compression-astc",
"rg11b10ufloat-renderable",
"bgra8unorm-storage",
"float32-filterable",
// extended from spec

View File

@ -266,6 +266,9 @@ fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> {
if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) {
return_features.push("bgra8unorm-storage");
}
if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) {
return_features.push("float32-filterable");
}
// extended from spec
@ -498,6 +501,10 @@ impl From<GpuRequiredFeatures> for wgpu_types::Features {
wgpu_types::Features::BGRA8UNORM_STORAGE,
required_features.0.contains("bgra8unorm-storage"),
);
features.set(
wgpu_types::Features::FLOAT32_FILTERABLE,
required_features.0.contains("float32-filterable"),
);
// extended from spec

View File

@ -103,6 +103,7 @@ enum GPUFeatureName {
"shader-f16",
"rg11b10ufloat-renderable",
"bgra8unorm-storage",
"float32-filterable",
// extended from spec

View File

@ -1,4 +1,4 @@
//! Tests for texture copy bounds checks.
//! Tests for BGRA8UNORM_STORAGE feature
use std::borrow::Cow;

View File

@ -0,0 +1,75 @@
//! Tests for FLOAT32_FILTERABLE feature.
use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters};
fn create_texture_binding(device: &wgpu::Device, format: wgpu::TextureFormat, filterable: bool) {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 256,
height: 256,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable },
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
},
count: None,
}],
});
let _bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &bgl,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
}],
});
}
#[gpu_test]
static FLOAT32_FILTERABLE_WITHOUT_FEATURE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_sync(|ctx| {
let device = &ctx.device;
// Unorm textures are always filterable
create_texture_binding(device, wgpu::TextureFormat::R8Unorm, true);
create_texture_binding(device, wgpu::TextureFormat::R8Unorm, false);
// As are float16 textures
create_texture_binding(device, wgpu::TextureFormat::R16Float, true);
create_texture_binding(device, wgpu::TextureFormat::R16Float, false);
// Float 32 textures can be used as non-filterable only
create_texture_binding(device, wgpu::TextureFormat::R32Float, false);
// This is supposed to fail, since we have not activated the feature
fail(&ctx.device, || {
create_texture_binding(device, wgpu::TextureFormat::R32Float, true);
});
});
#[gpu_test]
static FLOAT32_FILTERABLE_WITH_FEATURE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().features(wgpu::Features::FLOAT32_FILTERABLE))
.run_sync(|ctx| {
let device = &ctx.device;
// With the feature enabled, it does work!
create_texture_binding(device, wgpu::TextureFormat::R32Float, true);
create_texture_binding(device, wgpu::TextureFormat::Rg32Float, true);
create_texture_binding(device, wgpu::TextureFormat::Rgba32Float, true);
});

View File

@ -15,6 +15,7 @@ mod create_surface_error;
mod device;
mod encoder;
mod external_texture;
mod float32_filterable;
mod instance;
mod life_cycle;
mod mem_leaks;

View File

@ -2064,7 +2064,7 @@ impl<A: HalApi> Device<A> {
.views
.add_single(&*texture_view_guard, id)
.ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) = Self::texture_use_parameters(
let (pub_usage, internal_use) = self.texture_use_parameters(
binding,
decl,
view,
@ -2095,7 +2095,7 @@ impl<A: HalApi> Device<A> {
.add_single(&*texture_view_guard, id)
.ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) =
Self::texture_use_parameters(binding, decl, view,
self.texture_use_parameters(binding, decl, view,
"SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?;
Self::create_texture_binding(
view,
@ -2197,6 +2197,7 @@ impl<A: HalApi> Device<A> {
}
pub(crate) fn texture_use_parameters(
self: &Arc<Self>,
binding: u32,
decl: &wgt::BindGroupLayoutEntry,
view: &TextureView<A>,
@ -2227,7 +2228,7 @@ impl<A: HalApi> Device<A> {
let compat_sample_type = view
.desc
.format
.sample_type(Some(view.desc.range.aspect))
.sample_type(Some(view.desc.range.aspect), Some(self.features))
.unwrap();
match (sample_type, compat_sample_type) {
(Tst::Uint, Tst::Uint) |
@ -3198,6 +3199,24 @@ impl<A: HalApi> Device<A> {
Ok(pipeline)
}
pub(crate) fn get_texture_format_features(
&self,
adapter: &Adapter<A>,
format: TextureFormat,
) -> wgt::TextureFormatFeatures {
// Variant of adapter.get_texture_format_features that takes device features into account
use wgt::TextureFormatFeatureFlags as tfsc;
let mut format_features = adapter.get_texture_format_features(format);
if (format == TextureFormat::R32Float
|| format == TextureFormat::Rg32Float
|| format == TextureFormat::Rgba32Float)
&& !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
{
format_features.flags.set(tfsc::FILTERABLE, false);
}
format_features
}
pub(crate) fn describe_format_features(
&self,
adapter: &Adapter<A>,
@ -3213,7 +3232,7 @@ impl<A: HalApi> Device<A> {
let downlevel = !self.downlevel.is_webgpu_compliant();
if using_device_features || downlevel {
Ok(adapter.get_texture_format_features(format))
Ok(self.get_texture_format_features(adapter, format))
} else {
Ok(format.guaranteed_format_features(self.features))
}

View File

@ -155,6 +155,10 @@ impl super::Adapter {
// bgra8unorm-storage is never supported on dx11 according to:
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/format-support-for-direct3d-11-0-feature-level-hardware#dxgi_format_b8g8r8a8_unormfcs-87
// float32-filterable should always be available on dx11
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/format-support-for-direct3d-11-0-feature-level-hardware#dxgi_format_r32g32b32a32_floatfcs-2
features.set(wgt::Features::FLOAT32_FILTERABLE, true);
//
// Fill out limits and alignments
//

View File

@ -294,6 +294,9 @@ impl super::Adapter {
bgra8unorm_storage_supported,
);
// float32-filterable should always be available on d3d12
features.set(wgt::Features::FLOAT32_FILTERABLE, true);
// TODO: Determine if IPresentationManager is supported
let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi();

View File

@ -549,6 +549,13 @@ impl super::Adapter {
);
}
features.set(
wgt::Features::FLOAT32_FILTERABLE,
extensions.contains("GL_ARB_color_buffer_float")
|| extensions.contains("GL_EXT_color_buffer_float")
|| extensions.contains("OES_texture_float_linear"),
);
// We *might* be able to emulate bgra8unorm-storage but currently don't attempt to.
let mut private_caps = super::PrivateCapabilities::empty();
@ -594,14 +601,6 @@ impl super::Adapter {
super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
color_buffer_float,
);
private_caps.set(
super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR,
if full_ver.is_some() {
color_buffer_float
} else {
extensions.contains("OES_texture_float_linear")
},
);
private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
private_caps.set(
@ -1022,8 +1021,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
| Tfc::MULTISAMPLE_RESOLVE,
);
let texture_float_linear =
private_caps_fn(super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR, filterable);
let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
match format {
Tf::R8Unorm => filterable_renderable,

View File

@ -605,7 +605,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
if !cat.ops.contains(crate::AttachmentOps::LOAD) {
let c = &cat.clear_value;
self.cmd_buffer.commands.push(
match cat.target.view.format.sample_type(None).unwrap() {
match cat.target.view.format.sample_type(None, None).unwrap() {
wgt::TextureSampleType::Float { .. } => C::ClearColorF {
draw_buffer: i as u32,
color: [c.r as f32, c.g as f32, c.b as f32, c.a as f32],

View File

@ -746,7 +746,7 @@ impl crate::Device<super::Api> for super::Device {
unsafe { gl.bind_texture(target, Some(raw)) };
//Note: this has to be done before defining the storage!
match desc.format.sample_type(None) {
match desc.format.sample_type(None, Some(self.shared.features)) {
Some(
wgt::TextureSampleType::Float { filterable: false }
| wgt::TextureSampleType::Uint

View File

@ -186,8 +186,6 @@ bitflags::bitflags! {
const COLOR_BUFFER_HALF_FLOAT = 1 << 8;
/// Supports `f11/f10` and `f32` color buffers
const COLOR_BUFFER_FLOAT = 1 << 9;
/// Supports linear flitering `f32` textures.
const TEXTURE_FLOAT_LINEAR = 1 << 10;
/// Supports query buffer objects.
const QUERY_BUFFERS = 1 << 11;
/// Supports 64 bit queries via `glGetQueryObjectui64v`

View File

@ -604,6 +604,9 @@ impl super::PrivateCapabilities {
function_specialization: Self::supports_any(device, FUNCTION_SPECIALIZATION_SUPPORT),
depth_clip_mode: Self::supports_any(device, DEPTH_CLIP_MODE),
texture_cube_array: Self::supports_any(device, TEXTURE_CUBE_ARRAY_SUPPORT),
supports_float_filtering: os_is_mac
|| (version.at_least((11, 0), (14, 0), os_is_mac)
&& device.supports_32bit_float_filtering()),
format_depth24_stencil8: os_is_mac && device.d24_s8_supported(),
format_depth32_stencil8_filter: os_is_mac,
format_depth32_stencil8_none: !os_is_mac,
@ -821,6 +824,7 @@ impl super::PrivateCapabilities {
| F::DEPTH32FLOAT_STENCIL8
| F::BGRA8UNORM_STORAGE;
features.set(F::FLOAT32_FILTERABLE, self.supports_float_filtering);
features.set(
F::INDIRECT_FIRST_INSTANCE | F::MULTI_DRAW_INDIRECT,
self.indirect_draw_dispatch,

View File

@ -191,6 +191,7 @@ struct PrivateCapabilities {
function_specialization: bool,
depth_clip_mode: bool,
texture_cube_array: bool,
supports_float_filtering: bool,
format_depth24_stencil8: bool,
format_depth32_stencil8_filter: bool,
format_depth32_stencil8_none: bool,

View File

@ -536,6 +536,10 @@ impl PhysicalDeviceFeatures {
supports_bgra8unorm_storage(instance, phd, caps.device_api_version),
);
features.set(
F::FLOAT32_FILTERABLE,
is_float32_filterable_supported(instance, phd),
);
features.set(
F::TEXTURE_FORMAT_NV12,
(caps.device_api_version >= vk::API_VERSION_1_1
@ -1568,8 +1572,8 @@ impl crate::Adapter<super::Api> for super::Adapter {
.framebuffer_stencil_sample_counts
.min(limits.sampled_image_stencil_sample_counts)
} else {
match format.sample_type(None) {
Some(wgt::TextureSampleType::Float { filterable: _ }) => limits
match format.sample_type(None, None) {
Some(wgt::TextureSampleType::Float { .. }) => limits
.framebuffer_color_sample_counts
.min(limits.sampled_image_color_sample_counts),
Some(wgt::TextureSampleType::Sint) | Some(wgt::TextureSampleType::Uint) => {
@ -1760,6 +1764,21 @@ fn is_format_16bit_norm_supported(instance: &ash::Instance, phd: vk::PhysicalDev
r16unorm && r16snorm && rg16unorm && rg16snorm && rgba16unorm && rgba16snorm
}
fn is_float32_filterable_supported(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool {
let tiling = vk::ImageTiling::OPTIMAL;
let features = vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR;
let r_float = supports_format(instance, phd, vk::Format::R32_SFLOAT, tiling, features);
let rg_float = supports_format(instance, phd, vk::Format::R32G32_SFLOAT, tiling, features);
let rgba_float = supports_format(
instance,
phd,
vk::Format::R32G32B32A32_SFLOAT,
tiling,
features,
);
r_float && rg_float && rgba_float
}
fn supports_format(
instance: &ash::Instance,
phd: vk::PhysicalDevice,

View File

@ -200,7 +200,7 @@ impl crate::ColorAttachment<'_, super::Api> {
.view
.attachment
.view_format
.sample_type(None)
.sample_type(None, None)
.unwrap()
{
wgt::TextureSampleType::Float { .. } => vk::ClearColorValue {

View File

@ -337,7 +337,18 @@ bitflags::bitflags! {
// ? const NORM16_FILTERABLE = 1 << 17; (https://github.com/gpuweb/gpuweb/issues/3839)
// ? const NORM16_RESOLVE = 1 << 18; (https://github.com/gpuweb/gpuweb/issues/3839)
// TODO const FLOAT32_FILTERABLE = 1 << 19;
/// Allows textures with formats "r32float", "rg32float", and "rgba32float" to be filterable.
///
/// Supported Platforms:
/// - Vulkan (mainly on Desktop GPUs)
/// - DX12
/// - Metal on macOS or Apple9+ GPUs, optional on iOS/iPadOS with Apple7/8 GPUs
/// - GL with one of `GL_ARB_color_buffer_float`/`GL_EXT_color_buffer_float`/`OES_texture_float_linear`
///
/// This is a web and native feature.
const FLOAT32_FILTERABLE = 1 << 19;
// ? const FLOAT32_BLENDABLE = 1 << 20; (https://github.com/gpuweb/gpuweb/issues/3556)
// ? const 32BIT_FORMAT_MULTISAMPLE = 1 << 21; (https://github.com/gpuweb/gpuweb/issues/3844)
// ? const 32BIT_FORMAT_RESOLVE = 1 << 22; (https://github.com/gpuweb/gpuweb/issues/3844)
@ -3218,10 +3229,16 @@ impl TextureFormat {
Self::Astc { .. } => ( noaa, basic),
};
let is_filterable =
self.sample_type(None) == Some(TextureSampleType::Float { filterable: true });
// Get whether the format is filterable, taking features into account
let sample_type1 = self.sample_type(None, Some(device_features));
let is_filterable = sample_type1 == Some(TextureSampleType::Float { filterable: true });
// Features that enable filtering don't affect blendability
let sample_type2 = self.sample_type(None, None);
let is_blendable = sample_type2 == Some(TextureSampleType::Float { filterable: true });
flags.set(TextureFormatFeatureFlags::FILTERABLE, is_filterable);
flags.set(TextureFormatFeatureFlags::BLENDABLE, is_filterable);
flags.set(TextureFormatFeatureFlags::BLENDABLE, is_blendable);
TextureFormatFeatures {
allowed_usages,
@ -3233,9 +3250,17 @@ impl TextureFormat {
///
/// Returns `None` only if the format is combined depth-stencil
/// and `TextureAspect::All` or no `aspect` was provided
pub fn sample_type(&self, aspect: Option<TextureAspect>) -> Option<TextureSampleType> {
pub fn sample_type(
&self,
aspect: Option<TextureAspect>,
device_features: Option<Features>,
) -> Option<TextureSampleType> {
let float = TextureSampleType::Float { filterable: true };
let unfilterable_float = TextureSampleType::Float { filterable: false };
let float32_sample_type = TextureSampleType::Float {
filterable: device_features
.unwrap_or(Features::empty())
.contains(Features::FLOAT32_FILTERABLE),
};
let depth = TextureSampleType::Depth;
let uint = TextureSampleType::Uint;
let sint = TextureSampleType::Sint;
@ -3256,7 +3281,7 @@ impl TextureFormat {
| Self::Rgb10a2Unorm
| Self::Rg11b10Float => Some(float),
Self::R32Float | Self::Rg32Float | Self::Rgba32Float => Some(unfilterable_float),
Self::R32Float | Self::Rg32Float | Self::Rgba32Float => Some(float32_sample_type),
Self::R8Uint
| Self::Rg8Uint

View File

@ -661,7 +661,7 @@ fn map_map_mode(mode: crate::MapMode) -> u32 {
}
}
const FEATURES_MAPPING: [(wgt::Features, web_sys::GpuFeatureName); 10] = [
const FEATURES_MAPPING: [(wgt::Features, web_sys::GpuFeatureName); 11] = [
//TODO: update the name
(
wgt::Features::DEPTH_CLIP_CONTROL,
@ -703,6 +703,10 @@ const FEATURES_MAPPING: [(wgt::Features, web_sys::GpuFeatureName); 10] = [
wgt::Features::BGRA8UNORM_STORAGE,
web_sys::GpuFeatureName::Bgra8unormStorage,
),
(
wgt::Features::FLOAT32_FILTERABLE,
web_sys::GpuFeatureName::Float32Filterable,
),
];
fn map_wgt_features(supported_features: web_sys::GpuSupportedFeatures) -> wgt::Features {