Skip to content

Commit

Permalink
Add feature gated 16-bit normalized texture support (#2282)
Browse files Browse the repository at this point in the history
* Add feature gated 16-bit normalized texture support

Fixes #1934

* Query format properties only once

* Prevent supports_format from erroneously reporting false if the format wasn't queried

* Assert that 16bit norm formats also support  on vulkan

* Add storage to TextureFormatInfo for 16-bit norm formats now that we check for support
  • Loading branch information
aloucks authored Dec 13, 2021
1 parent f875e32 commit 70f7c37
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 5 deletions.
3 changes: 3 additions & 0 deletions wgpu-core/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,9 @@ impl NumericType {
Tf::Rg8Sint | Tf::Rg16Sint | Tf::Rg32Sint => {
(NumericDimension::Vector(Vs::Bi), Sk::Sint)
}
Tf::R16Snorm | Tf::R16Unorm => (NumericDimension::Scalar, Sk::Float),
Tf::Rg16Snorm | Tf::Rg16Unorm => (NumericDimension::Vector(Vs::Bi), Sk::Float),
Tf::Rgba16Snorm | Tf::Rgba16Unorm => (NumericDimension::Vector(Vs::Quad), Sk::Float),
Tf::Rgba8Unorm
| Tf::Rgba8UnormSrgb
| Tf::Rgba8Snorm
Expand Down
3 changes: 2 additions & 1 deletion wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ impl super::Adapter {
| wgt::Features::VERTEX_WRITABLE_STORAGE
| wgt::Features::TIMESTAMP_QUERY
| wgt::Features::TEXTURE_COMPRESSION_BC
| wgt::Features::CLEAR_COMMANDS;
| wgt::Features::CLEAR_COMMANDS
| wgt::Features::TEXTURE_FORMAT_16BIT_NORM;
//TODO: in order to expose this, we need to run a compute shader
// that extract the necessary statistics out of the D3D12 result.
// Alternatively, we could allocate a buffer for the query set,
Expand Down
6 changes: 6 additions & 0 deletions wgpu-hal/src/dx12/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ pub(super) fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI
Tf::R8Sint => DXGI_FORMAT_R8_SINT,
Tf::R16Uint => DXGI_FORMAT_R16_UINT,
Tf::R16Sint => DXGI_FORMAT_R16_SINT,
Tf::R16Unorm => DXGI_FORMAT_R16_UNORM,
Tf::R16Snorm => DXGI_FORMAT_R16_SNORM,
Tf::R16Float => DXGI_FORMAT_R16_FLOAT,
Tf::Rg8Unorm => DXGI_FORMAT_R8G8_UNORM,
Tf::Rg8Snorm => DXGI_FORMAT_R8G8_SNORM,
Tf::Rg8Uint => DXGI_FORMAT_R8G8_UINT,
Tf::Rg8Sint => DXGI_FORMAT_R8G8_SINT,
Tf::Rg16Unorm => DXGI_FORMAT_R16G16_UNORM,
Tf::Rg16Snorm => DXGI_FORMAT_R16G16_SNORM,
Tf::R32Uint => DXGI_FORMAT_R32_UINT,
Tf::R32Sint => DXGI_FORMAT_R32_SINT,
Tf::R32Float => DXGI_FORMAT_R32_FLOAT,
Expand All @@ -40,6 +44,8 @@ pub(super) fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI
Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT,
Tf::Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT,
Tf::Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT,
Tf::Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM,
Tf::Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM,
Tf::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT,
Tf::Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT,
Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT,
Expand Down
2 changes: 2 additions & 0 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,8 @@ impl crate::Adapter<super::Api> for super::Adapter {
Tf::Rg8Uint | Tf::Rg8Sint | Tf::R32Uint | Tf::R32Sint => {
unfiltered_color | Tfc::STORAGE
}
Tf::R16Unorm | Tf::Rg16Unorm | Tf::Rgba16Unorm => filtered_color,
Tf::R16Snorm | Tf::Rg16Snorm | Tf::Rgba16Snorm => filtered_color,
Tf::R32Float => unfiltered_color,
Tf::Rg16Uint | Tf::Rg16Sint => unfiltered_color,
Tf::Rg16Float | Tf::Rgba8Unorm | Tf::Rgba8UnormSrgb => filtered_color | Tfc::STORAGE,
Expand Down
6 changes: 6 additions & 0 deletions wgpu-hal/src/gles/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ impl super::AdapterShared {
Tf::R8Sint => (glow::R8I, glow::RED_INTEGER, glow::BYTE),
Tf::R16Uint => (glow::R16UI, glow::RED_INTEGER, glow::UNSIGNED_SHORT),
Tf::R16Sint => (glow::R16I, glow::RED_INTEGER, glow::SHORT),
Tf::R16Unorm => (glow::R16, glow::RED, glow::SHORT),
Tf::R16Snorm => (glow::R16_SNORM, glow::RED, glow::SHORT),
Tf::R16Float => (glow::R16F, glow::RED, glow::HALF_FLOAT),
Tf::Rg8Unorm => (glow::RG8, glow::RG, glow::UNSIGNED_BYTE),
Tf::Rg8Snorm => (glow::RG8, glow::RG, glow::BYTE),
Expand All @@ -22,6 +24,8 @@ impl super::AdapterShared {
Tf::R32Float => (glow::R32F, glow::RED, glow::FLOAT),
Tf::Rg16Uint => (glow::RG16UI, glow::RG_INTEGER, glow::UNSIGNED_SHORT),
Tf::Rg16Sint => (glow::RG16I, glow::RG_INTEGER, glow::SHORT),
Tf::Rg16Unorm => (glow::RG16, glow::RG, glow::SHORT),
Tf::Rg16Snorm => (glow::RG16_SNORM, glow::RG, glow::SHORT),
Tf::Rg16Float => (glow::RG16F, glow::RG, glow::HALF_FLOAT),
Tf::Rgba8Unorm => (glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE),
Tf::Rgba8UnormSrgb => (glow::SRGB8_ALPHA8, glow::RGBA, glow::UNSIGNED_BYTE),
Expand All @@ -45,6 +49,8 @@ impl super::AdapterShared {
Tf::Rg32Float => (glow::RG32F, glow::RG, glow::FLOAT),
Tf::Rgba16Uint => (glow::RGBA16UI, glow::RGBA_INTEGER, glow::UNSIGNED_SHORT),
Tf::Rgba16Sint => (glow::RGBA16I, glow::RGBA_INTEGER, glow::SHORT),
Tf::Rgba16Unorm => (glow::RGBA16, glow::RGBA, glow::SHORT),
Tf::Rgba16Snorm => (glow::RGBA16_SNORM, glow::RGBA, glow::SHORT),
Tf::Rgba16Float => (glow::RGBA16F, glow::RGBA, glow::HALF_FLOAT),
Tf::Rgba32Uint => (glow::RGBA32UI, glow::RGBA_INTEGER, glow::UNSIGNED_INT),
Tf::Rgba32Sint => (glow::RGBA32I, glow::RGBA_INTEGER, glow::INT),
Expand Down
27 changes: 26 additions & 1 deletion wgpu-hal/src/metal/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ impl crate::Adapter<super::Api> for super::Adapter {
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::R16Unorm | Tf::R16Snorm => {
Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rg8Unorm | Tf::Rg8Snorm => {
Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
Expand All @@ -105,6 +111,12 @@ impl crate::Adapter<super::Api> for super::Adapter {
Tf::Rg16Uint | Tf::Rg16Sint => {
read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT
}
Tf::Rg16Unorm | Tf::Rg16Snorm => {
Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rg16Float => {
read_write_tier2_if
| Tfc::SAMPLED_LINEAR
Expand Down Expand Up @@ -159,6 +171,12 @@ impl crate::Adapter<super::Api> for super::Adapter {
Tf::Rgba16Uint | Tf::Rgba16Sint => {
read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT
}
Tf::Rgba16Unorm | Tf::Rgba16Snorm => {
Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rgba16Float => {
read_write_tier2_if
| Tfc::SAMPLED_LINEAR
Expand Down Expand Up @@ -901,7 +919,8 @@ impl super::PrivateCapabilities {
| F::VERTEX_WRITABLE_STORAGE
| F::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
| F::POLYGON_MODE_LINE
| F::CLEAR_COMMANDS;
| F::CLEAR_COMMANDS
| F::TEXTURE_FORMAT_16BIT_NORM;

features.set(F::DEPTH_CLIP_CONTROL, self.supports_depth_clip_control);

Expand Down Expand Up @@ -1000,11 +1019,15 @@ impl super::PrivateCapabilities {
Tf::R8Sint => R8Sint,
Tf::R16Uint => R16Uint,
Tf::R16Sint => R16Sint,
Tf::R16Unorm => R16Unorm,
Tf::R16Snorm => R16Snorm,
Tf::R16Float => R16Float,
Tf::Rg8Unorm => RG8Unorm,
Tf::Rg8Snorm => RG8Snorm,
Tf::Rg8Uint => RG8Uint,
Tf::Rg8Sint => RG8Sint,
Tf::Rg16Unorm => RG16Unorm,
Tf::Rg16Snorm => RG16Snorm,
Tf::R32Uint => R32Uint,
Tf::R32Sint => R32Sint,
Tf::R32Float => R32Float,
Expand All @@ -1025,6 +1048,8 @@ impl super::PrivateCapabilities {
Tf::Rg32Float => RG32Float,
Tf::Rgba16Uint => RGBA16Uint,
Tf::Rgba16Sint => RGBA16Sint,
Tf::Rgba16Unorm => RGBA16Unorm,
Tf::Rgba16Snorm => RGBA16Snorm,
Tf::Rgba16Float => RGBA16Float,
Tf::Rgba32Uint => RGBA32Uint,
Tf::Rgba32Sint => RGBA32Sint,
Expand Down
68 changes: 65 additions & 3 deletions wgpu-hal/src/vulkan/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,11 @@ impl PhysicalDeviceFeatures {
features.set(F::MULTIVIEW, multiview.multiview != 0);
}

features.set(
F::TEXTURE_FORMAT_16BIT_NORM,
is_format_16bit_norm_supported(caps),
);

(features, dl_flags)
}

Expand All @@ -534,6 +539,7 @@ pub struct PhysicalDeviceCapabilities {
properties: vk::PhysicalDeviceProperties,
vulkan_1_2: Option<vk::PhysicalDeviceVulkan12Properties>,
descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingPropertiesEXT>,
formats: Vec<vk::FormatProperties>,
}

// This is safe because the structs have `p_next: *mut c_void`, which we null out/never read.
Expand All @@ -547,6 +553,22 @@ impl PhysicalDeviceCapabilities {
.any(|ep| unsafe { CStr::from_ptr(ep.extension_name.as_ptr()) } == extension)
}

fn supports_format(
&self,
format: vk::Format,
tiling: vk::ImageTiling,
features: vk::FormatFeatureFlags,
) -> bool {
self.formats
.get(format.as_raw() as usize)
.map(|properties| match tiling {
vk::ImageTiling::LINEAR => properties.linear_tiling_features.contains(features),
vk::ImageTiling::OPTIMAL => properties.optimal_tiling_features.contains(features),
_ => false,
})
.unwrap()
}

/// Map `requested_features` to the list of Vulkan extension strings required to create the logical device.
fn get_required_extensions(&self, requested_features: wgt::Features) -> Vec<&'static CStr> {
let mut extensions = Vec::new();
Expand Down Expand Up @@ -766,6 +788,7 @@ impl super::InstanceShared {
} else {
unsafe { self.raw.get_physical_device_properties(phd) }
};
capabilities.formats = query_format_properties(&self.raw, phd);

capabilities
};
Expand Down Expand Up @@ -1307,9 +1330,10 @@ impl crate::Adapter<super::Api> for super::Adapter {
use crate::TextureFormatCapabilities as Tfc;
let vk_format = self.private_caps.map_texture_format(format);
let properties = self
.instance
.raw
.get_physical_device_format_properties(self.raw, vk_format);
.phd_capabilities
.formats
.get(vk_format.as_raw() as usize)
.unwrap();
let features = properties.optimal_tiling_features;

let mut flags = Tfc::empty();
Expand Down Expand Up @@ -1489,3 +1513,41 @@ impl crate::Adapter<super::Api> for super::Adapter {
})
}
}

/// Querys properties of all known image formats. The raw value of `vk::Format` corresponds
/// to the index of the returned Vec.
fn query_format_properties(
instance: &ash::Instance,
physical_device: vk::PhysicalDevice,
) -> Vec<vk::FormatProperties> {
// vk::Format::UNDEFINED
const FORMAT_MIN: i32 = 0;

// vk::Format::ASTC_12X12_SRGB_BLOCK
const FORMAT_MAX: i32 = 184;

debug_assert_eq!(FORMAT_MAX, vk::Format::ASTC_12X12_SRGB_BLOCK.as_raw());

(FORMAT_MIN..(FORMAT_MAX + 1))
.map(|raw| {
let image_format = vk::Format::from_raw(raw);
unsafe { instance.get_physical_device_format_properties(physical_device, image_format) }
})
.collect::<Vec<_>>()
}

fn is_format_16bit_norm_supported(caps: &PhysicalDeviceCapabilities) -> bool {
let tiling = vk::ImageTiling::OPTIMAL;
let features = vk::FormatFeatureFlags::SAMPLED_IMAGE
| vk::FormatFeatureFlags::STORAGE_IMAGE
| vk::FormatFeatureFlags::TRANSFER_SRC
| vk::FormatFeatureFlags::TRANSFER_DST;
let r16unorm = caps.supports_format(vk::Format::R16_UNORM, tiling, features);
let r16snorm = caps.supports_format(vk::Format::R16_SNORM, tiling, features);
let rg16unorm = caps.supports_format(vk::Format::R16G16_UNORM, tiling, features);
let rg16snorm = caps.supports_format(vk::Format::R16G16_SNORM, tiling, features);
let rgba16unorm = caps.supports_format(vk::Format::R16G16B16A16_UNORM, tiling, features);
let rgba16snorm = caps.supports_format(vk::Format::R16G16B16A16_SNORM, tiling, features);

r16unorm && r16snorm && rg16unorm && rg16snorm && rgba16unorm && rgba16snorm
}
6 changes: 6 additions & 0 deletions wgpu-hal/src/vulkan/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ impl super::PrivateCapabilities {
Tf::R8Sint => F::R8_SINT,
Tf::R16Uint => F::R16_UINT,
Tf::R16Sint => F::R16_SINT,
Tf::R16Unorm => F::R16_UNORM,
Tf::R16Snorm => F::R16_SNORM,
Tf::R16Float => F::R16_SFLOAT,
Tf::Rg8Unorm => F::R8G8_UNORM,
Tf::Rg8Snorm => F::R8G8_SNORM,
Tf::Rg8Uint => F::R8G8_UINT,
Tf::Rg8Sint => F::R8G8_SINT,
Tf::Rg16Unorm => F::R16G16_UNORM,
Tf::Rg16Snorm => F::R16G16_SNORM,
Tf::R32Uint => F::R32_UINT,
Tf::R32Sint => F::R32_SINT,
Tf::R32Float => F::R32_SFLOAT,
Expand All @@ -37,6 +41,8 @@ impl super::PrivateCapabilities {
Tf::Rg32Float => F::R32G32_SFLOAT,
Tf::Rgba16Uint => F::R16G16B16A16_UINT,
Tf::Rgba16Sint => F::R16G16B16A16_SINT,
Tf::Rgba16Unorm => F::R16G16B16A16_UNORM,
Tf::Rgba16Snorm => F::R16G16B16A16_SNORM,
Tf::Rgba16Float => F::R16G16B16A16_SFLOAT,
Tf::Rgba32Uint => F::R32G32B32A32_UINT,
Tf::Rgba32Sint => F::R32G32B32A32_SINT,
Expand Down
48 changes: 48 additions & 0 deletions wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,15 @@ bitflags::bitflags! {
///
/// This is a native only feature.
const MULTIVIEW = 1 << 40;
/// Enables normalized `16-bit` texture formats.
///
/// Supported platforms:
/// - Vulkan
/// - DX12
/// - Metal
///
/// This is a native only feature.
const TEXTURE_FORMAT_16BIT_NORM = 1 << 41;
}
}

Expand Down Expand Up @@ -1473,6 +1482,16 @@ pub enum TextureFormat {
/// Red channel only. 16 bit integer per channel. Signed in shader.
#[cfg_attr(feature = "serde", serde(rename = "r16sint"))]
R16Sint,
/// Red channel only. 16 bit integer per channel. [0, 65535] converted to/from float [0, 1] in shader.
///
/// [`Features::TEXTURE_FORMAT_16BIT_NORM`] must be enabled to use this texture format.
#[cfg_attr(feature = "serde", serde(rename = "r16unorm"))]
R16Unorm,
/// Red channel only. 16 bit integer per channel. [0, 65535] converted to/from float [-1, 1] in shader.
///
/// [`Features::TEXTURE_FORMAT_16BIT_NORM`] must be enabled to use this texture format.
#[cfg_attr(feature = "serde", serde(rename = "r16snorm"))]
R16Snorm,
/// Red channel only. 16 bit float per channel. Float in shader.
#[cfg_attr(feature = "serde", serde(rename = "r16float"))]
R16Float,
Expand Down Expand Up @@ -1505,6 +1524,16 @@ pub enum TextureFormat {
/// Red and green channels. 16 bit integer per channel. Signed in shader.
#[cfg_attr(feature = "serde", serde(rename = "rg16sint"))]
Rg16Sint,
/// Red and green channels. 16 bit integer per channel. [0, 65535] converted to/from float [0, 1] in shader.
///
/// [`Features::TEXTURE_FORMAT_16BIT_NORM`] must be enabled to use this texture format.
#[cfg_attr(feature = "serde", serde(rename = "rg16unorm"))]
Rg16Unorm,
/// Red and green channels. 16 bit integer per channel. [0, 65535] converted to/from float [-1, 1] in shader.
///
/// [`Features::TEXTURE_FORMAT_16BIT_NORM`] must be enabled to use this texture format.
#[cfg_attr(feature = "serde", serde(rename = "rg16snorm"))]
Rg16Snorm,
/// Red and green channels. 16 bit float per channel. Float in shader.
#[cfg_attr(feature = "serde", serde(rename = "rg16float"))]
Rg16Float,
Expand Down Expand Up @@ -1554,6 +1583,16 @@ pub enum TextureFormat {
/// Red, green, blue, and alpha channels. 16 bit integer per channel. Signed in shader.
#[cfg_attr(feature = "serde", serde(rename = "rgba16sint"))]
Rgba16Sint,
/// Red, green, blue, and alpha channels. 16 bit integer per channel. [0, 65535] converted to/from float [0, 1] in shader.
///
/// [`Features::TEXTURE_FORMAT_16BIT_NORM`] must be enabled to use this texture format.
#[cfg_attr(feature = "serde", serde(rename = "rgba16unorm"))]
Rgba16Unorm,
/// Red, green, blue, and alpha. 16 bit integer per channel. [0, 65535] converted to/from float [-1, 1] in shader.
///
/// [`Features::TEXTURE_FORMAT_16BIT_NORM`] must be enabled to use this texture format.
#[cfg_attr(feature = "serde", serde(rename = "rgba16snorm"))]
Rgba16Snorm,
/// Red, green, blue, and alpha channels. 16 bit float per channel. Float in shader.
#[cfg_attr(feature = "serde", serde(rename = "rgba16float"))]
Rgba16Float,
Expand Down Expand Up @@ -1934,6 +1973,7 @@ impl TextureFormat {
let bc = Features::TEXTURE_COMPRESSION_BC;
let etc2 = Features::TEXTURE_COMPRESSION_ETC2;
let astc_ldr = Features::TEXTURE_COMPRESSION_ASTC_LDR;
let norm16bit = Features::TEXTURE_FORMAT_16BIT_NORM;

// Sample Types
let uint = TextureSampleType::Uint;
Expand Down Expand Up @@ -2075,6 +2115,14 @@ impl TextureFormat {
Self::Astc12x10RgbaUnormSrgb => (astc_ldr, float, srgb, (12, 10), 16, basic, 4),
Self::Astc12x12RgbaUnorm => (astc_ldr, float, linear, (12, 12), 16, basic, 4),
Self::Astc12x12RgbaUnormSrgb => (astc_ldr, float, srgb, (12, 12), 16, basic, 4),

// Optional normalized 16-bit-per-channel formats
Self::R16Unorm => (norm16bit, float, linear, (1, 1), 2, storage, 1),
Self::R16Snorm => (norm16bit, float, linear, (1, 1), 2, storage, 1),
Self::Rg16Unorm => (norm16bit, float, linear, (1, 1), 4, storage, 2),
Self::Rg16Snorm => (norm16bit, float, linear, (1, 1), 4, storage, 2),
Self::Rgba16Unorm => (norm16bit, float, linear, (1, 1), 8, storage, 4),
Self::Rgba16Snorm => (norm16bit, float, linear, (1, 1), 8, storage, 4),
};

TextureFormatInfo {
Expand Down

0 comments on commit 70f7c37

Please sign in to comment.