Support nv12 texture format (#4573)

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
Xiaopeng Li 2023-11-29 00:11:26 +08:00 committed by GitHub
parent f9711831d2
commit a6503e59c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 484 additions and 39 deletions

View File

@ -124,6 +124,7 @@ pub fn op_webgpu_create_texture_view(
format: args.format,
dimension: args.dimension,
range: args.range,
plane: None,
};
gfx_put!(texture => instance.texture_create_view(

View File

@ -133,6 +133,7 @@ impl Example {
mip_level_count: Some(1),
base_array_layer: 0,
array_layer_count: None,
..Default::default()
})
})
.collect::<Vec<_>>();

View File

@ -398,6 +398,7 @@ impl crate::framework::Example for Example {
mip_level_count: None,
base_array_layer: i as u32,
array_layer_count: Some(1),
..Default::default()
}))
})
.collect::<Vec<_>>();

View File

@ -49,6 +49,7 @@ static BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new()
base_array_layer: 0,
mip_level_count: Some(1),
array_layer_count: Some(1),
..Default::default()
});
let readback_buffer = device.create_buffer(&wgpu::BufferDescriptor {

View File

@ -0,0 +1,250 @@
//! Tests for nv12 texture creation and sampling.
use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters};
#[gpu_test]
static NV12_TEXTURE_CREATION_SAMPLING: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12))
.run_sync(|ctx| {
let size = wgpu::Extent3d {
width: 256,
height: 256,
depth_or_array_layers: 1,
};
let target_format = wgpu::TextureFormat::Bgra8UnormSrgb;
let shader = ctx
.device
.create_shader_module(wgpu::include_wgsl!("nv12_texture.wgsl"));
let pipeline = ctx
.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("nv12 pipeline"),
layout: None,
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(target_format.into())],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
strip_index_format: Some(wgpu::IndexFormat::Uint32),
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
dimension: wgpu::TextureDimension::D2,
size,
format: wgpu::TextureFormat::NV12,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
mip_level_count: 1,
sample_count: 1,
view_formats: &[wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::Rg8Unorm],
});
let y_view = tex.create_view(&wgpu::TextureViewDescriptor {
format: Some(wgpu::TextureFormat::R8Unorm),
plane: Some(0),
..Default::default()
});
let uv_view = tex.create_view(&wgpu::TextureViewDescriptor {
format: Some(wgpu::TextureFormat::Rg8Unorm),
plane: Some(1),
..Default::default()
});
let sampler = ctx.device.create_sampler(&wgpu::SamplerDescriptor {
min_filter: wgpu::FilterMode::Linear,
mag_filter: wgpu::FilterMode::Linear,
..Default::default()
});
let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &pipeline.get_bind_group_layout(0),
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Sampler(&sampler),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&y_view),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(&uv_view),
},
],
});
let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: target_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
ops: wgpu::Operations::default(),
resolve_target: None,
view: &target_view,
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
rpass.draw(0..4, 0..1);
drop(rpass);
ctx.queue.submit(Some(encoder.finish()));
});
#[gpu_test]
static NV12_TEXTURE_CREATION_BAD_VIEW_FORMATS: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12))
.run_sync(|ctx| {
let size = wgpu::Extent3d {
width: 256,
height: 256,
depth_or_array_layers: 1,
};
fail(&ctx.device, || {
let _ = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
dimension: wgpu::TextureDimension::D2,
size,
format: wgpu::TextureFormat::NV12,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
mip_level_count: 1,
sample_count: 1,
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
});
});
});
#[gpu_test]
static NV12_TEXTURE_VIEW_PLANE_ON_NON_PLANAR_FORMAT: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12))
.run_sync(|ctx| {
let size = wgpu::Extent3d {
width: 256,
height: 256,
depth_or_array_layers: 1,
};
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
dimension: wgpu::TextureDimension::D2,
size,
format: wgpu::TextureFormat::R8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
mip_level_count: 1,
sample_count: 1,
view_formats: &[],
});
fail(&ctx.device, || {
let _ = tex.create_view(&wgpu::TextureViewDescriptor {
plane: Some(0),
..Default::default()
});
});
});
#[gpu_test]
static NV12_TEXTURE_VIEW_PLANE_OUT_OF_BOUNDS: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12))
.run_sync(|ctx| {
let size = wgpu::Extent3d {
width: 256,
height: 256,
depth_or_array_layers: 1,
};
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
dimension: wgpu::TextureDimension::D2,
size,
format: wgpu::TextureFormat::NV12,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
mip_level_count: 1,
sample_count: 1,
view_formats: &[wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::Rg8Unorm],
});
fail(&ctx.device, || {
let _ = tex.create_view(&wgpu::TextureViewDescriptor {
format: Some(wgpu::TextureFormat::R8Unorm),
plane: Some(2),
..Default::default()
});
});
});
#[gpu_test]
static NV12_TEXTURE_BAD_FORMAT_VIEW_PLANE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12))
.run_sync(|ctx| {
let size = wgpu::Extent3d {
width: 256,
height: 256,
depth_or_array_layers: 1,
};
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
dimension: wgpu::TextureDimension::D2,
size,
format: wgpu::TextureFormat::NV12,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
mip_level_count: 1,
sample_count: 1,
view_formats: &[wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::Rg8Unorm],
});
fail(&ctx.device, || {
let _ = tex.create_view(&wgpu::TextureViewDescriptor {
format: Some(wgpu::TextureFormat::Rg8Unorm),
plane: Some(0),
..Default::default()
});
});
});
#[gpu_test]
static NV12_TEXTURE_BAD_SIZE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12))
.run_sync(|ctx| {
let size = wgpu::Extent3d {
width: 255,
height: 255,
depth_or_array_layers: 1,
};
fail(&ctx.device, || {
let _ = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
dimension: wgpu::TextureDimension::D2,
size,
format: wgpu::TextureFormat::NV12,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
mip_level_count: 1,
sample_count: 1,
view_formats: &[wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::Rg8Unorm],
});
});
});

View File

@ -0,0 +1,33 @@
struct VertexOutput {
@builtin(position) pos: vec4<f32>,
@location(0) uv: vec2<f32>,
}
@vertex
fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
var output: VertexOutput;
// 0, 0
// 2, 0
// 0, 2
// 2, 2
let v_data = vec2<f32>(f32((vertexIndex << 1u) & 2u), f32(vertexIndex & 2u));
output.pos = vec4<f32>(v_data - 1.0, 0.0, 1.0);
output.uv = v_data / 2.0;
return output;
}
@group(0) @binding(0) var s: sampler;
@group(0) @binding(1) var tex_y: texture_2d<f32>;
@group(0) @binding(2) var tex_uv: texture_2d<f32>;
@fragment
fn fs_main(v_ouput: VertexOutput) -> @location(0) vec4<f32> {
let luminance = textureSample(tex_y, s, v_ouput.uv).r;
let chrominance = textureSample(tex_uv, s, v_ouput.uv).rg;
let rgb = mat3x3<f32>(
1.000000, 1.000000, 1.000000,
0.000000,-0.187324, 1.855600,
1.574800,-0.468124, 0.000000,
) * vec3<f32>(luminance, chrominance.r - 0.5, chrominance.g - 0.5);
return vec4<f32>(rgb, 1.0);
}

View File

@ -18,6 +18,7 @@ mod external_texture;
mod instance;
mod life_cycle;
mod mem_leaks;
mod nv12_texture;
mod occlusion_query;
mod partially_bounded_arrays;
mod pipeline;

View File

@ -337,6 +337,11 @@ fn clear_texture_via_buffer_copies<A: HalApi>(
hal::FormatAspects::COLOR
);
if texture_desc.format == wgt::TextureFormat::NV12 {
// TODO: Currently COPY_DST for NV12 textures is unsupported.
return;
}
// Gather list of zero_buffer copies and issue a single command then to perform them
let mut zero_buffer_copy_regions = Vec::new();
let buffer_copy_pitch = alignments.buffer_copy_pitch.get() as u32;

View File

@ -750,7 +750,8 @@ impl<A: HalApi> Device<A> {
if desc.format == *format {
continue;
}
if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
if !check_texture_view_format_compatible(desc.format, *format) {
return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));
}
hal_view_formats.push(*format);
@ -803,9 +804,11 @@ impl<A: HalApi> Device<A> {
let mut clear_views = SmallVec::new();
for mip_level in 0..desc.mip_level_count {
for array_layer in 0..desc.size.depth_or_array_layers {
macro_rules! push_clear_view {
($format:expr, $plane:expr) => {
let desc = hal::TextureViewDescriptor {
label: clear_label,
format: desc.format,
format: $format,
dimension,
usage,
range: wgt::ImageSubresourceRange {
@ -815,11 +818,21 @@ impl<A: HalApi> Device<A> {
base_array_layer: array_layer,
array_layer_count: Some(1),
},
plane: $plane,
};
clear_views.push(Some(
unsafe { self.raw().create_texture_view(&raw_texture, &desc) }
.map_err(DeviceError::from)?,
));
};
}
if desc.format == wgt::TextureFormat::NV12 {
push_clear_view!(wgt::TextureFormat::R8Unorm, Some(0));
push_clear_view!(wgt::TextureFormat::Rg8Unorm, Some(1));
} else {
push_clear_view!(desc.format, None);
}
}
}
resource::TextureClearMode::RenderPass {
@ -1014,6 +1027,8 @@ impl<A: HalApi> Device<A> {
});
};
validate_texture_view_plane(texture.desc.format, resolved_format, desc.plane)?;
// https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view
let render_extent = 'b: loop {
if !texture
@ -1105,6 +1120,7 @@ impl<A: HalApi> Device<A> {
dimension: resolved_dimension,
usage,
range: resolved_range,
plane: desc.plane,
};
let raw = unsafe {
@ -3348,3 +3364,39 @@ impl<A: HalApi> Resource<DeviceId> for Device<A> {
&mut self.info
}
}
fn check_texture_view_format_compatible(
texture_format: TextureFormat,
view_format: TextureFormat,
) -> bool {
use TextureFormat::*;
match (texture_format, view_format) {
(NV12, R8Unorm | R8Uint | Rg8Unorm | Rg8Uint) => true,
_ => texture_format.remove_srgb_suffix() == view_format.remove_srgb_suffix(),
}
}
fn validate_texture_view_plane(
texture_format: TextureFormat,
view_format: TextureFormat,
plane: Option<u32>,
) -> Result<(), resource::CreateTextureViewError> {
use TextureFormat::*;
match (texture_format, view_format, plane) {
(NV12, R8Unorm | R8Uint, Some(0)) => Ok(()),
(NV12, Rg8Unorm | Rg8Uint, Some(1)) => Ok(()),
(NV12, _, _) => {
Err(resource::CreateTextureViewError::InvalidTextureViewPlane { plane, view_format })
}
(_, _, Some(_)) => Err(
resource::CreateTextureViewError::InvalidTextureViewPlaneOnNonplanarTexture {
plane,
texture_format,
},
),
_ => Ok(()),
}
}

View File

@ -196,6 +196,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
plane: None,
};
let clear_view = unsafe {
hal::Device::create_texture_view(

View File

@ -988,6 +988,8 @@ pub struct TextureViewDescriptor<'a> {
pub dimension: Option<wgt::TextureViewDimension>,
/// Range within the texture that is accessible via this view.
pub range: wgt::ImageSubresourceRange,
/// The plane of the texture view.
pub plane: Option<u32>,
}
#[derive(Debug)]
@ -1098,6 +1100,16 @@ pub enum CreateTextureViewError {
texture: wgt::TextureFormat,
view: wgt::TextureFormat,
},
#[error("Invalid texture view plane `{plane:?}` with view format `{view_format:?}`")]
InvalidTextureViewPlane {
plane: Option<u32>,
view_format: wgt::TextureFormat,
},
#[error("Invalid texture view plane `{plane:?}` on non-planar texture `{texture_format:?}`")]
InvalidTextureViewPlaneOnNonplanarTexture {
plane: Option<u32>,
texture_format: wgt::TextureFormat,
},
}
#[derive(Clone, Debug, Error)]

View File

@ -690,6 +690,7 @@ impl NumericType {
| Tf::Depth24PlusStencil8 => {
panic!("Unexpected depth format")
}
Tf::NV12 => panic!("Unexpected nv12 format"),
Tf::Rgb9e5Ufloat => (NumericDimension::Vector(Vs::Tri), Scalar::F32),
Tf::Bc1RgbaUnorm
| Tf::Bc1RgbaUnormSrgb

View File

@ -422,6 +422,7 @@ impl<A: hal::Api> Example<A> {
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::RESOURCE,
range: wgt::ImageSubresourceRange::default(),
plane: None,
};
let texture_view = unsafe { device.create_texture_view(&texture, &view_desc).unwrap() };
@ -658,6 +659,7 @@ impl<A: hal::Api> Example<A> {
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
plane: None,
};
let surface_tex_view = unsafe {
self.device

View File

@ -142,6 +142,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
plane: None,
},
)
.unwrap()

View File

@ -62,6 +62,7 @@ pub fn map_texture_format_failable(format: wgt::TextureFormat) -> Option<dxgifor
Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT,
Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT,
Tf::Depth32FloatStencil8 => DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
Tf::NV12 => DXGI_FORMAT_NV12,
Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM,
Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB,
Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM,

View File

@ -249,7 +249,8 @@ impl super::Adapter {
| wgt::Features::PUSH_CONSTANTS
| wgt::Features::SHADER_PRIMITIVE_INDEX
| wgt::Features::RG11B10UFLOAT_RENDERABLE
| wgt::Features::DUAL_SOURCE_BLENDING;
| wgt::Features::DUAL_SOURCE_BLENDING
| wgt::Features::TEXTURE_FORMAT_NV12;
//TODO: in order to expose this, we need to run a compute shader
// that extract the necessary statistics out of the D3D12 result.

View File

@ -467,7 +467,11 @@ impl crate::Device<super::Api> for super::Device {
aspects: view_desc.aspects,
target_base: (
texture.resource.clone(),
texture.calc_subresource(desc.range.base_mip_level, desc.range.base_array_layer, 0),
texture.calc_subresource(
desc.range.base_mip_level,
desc.range.base_array_layer,
desc.plane.unwrap_or(0),
),
),
handle_srv: if desc.usage.intersects(crate::TextureUses::RESOURCE) {
let raw_desc = unsafe { view_desc.to_srv() };

View File

@ -14,6 +14,7 @@ pub(super) struct ViewDescriptor {
array_layer_count: u32,
mip_level_base: u32,
mip_level_count: u32,
plane: u32,
}
impl crate::TextureViewDescriptor<'_> {
@ -30,6 +31,7 @@ impl crate::TextureViewDescriptor<'_> {
mip_level_count: self.range.mip_level_count.unwrap_or(!0),
array_layer_base: self.range.base_array_layer,
array_layer_count: self.range.array_layer_count.unwrap_or(!0),
plane: self.plane.unwrap_or(0),
}
}
}
@ -79,7 +81,7 @@ impl ViewDescriptor {
*desc.u.Texture2D_mut() = d3d12_ty::D3D12_TEX2D_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
ResourceMinLODClamp: 0.0,
}
}
@ -103,7 +105,7 @@ impl ViewDescriptor {
MipLevels: self.mip_level_count,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
ResourceMinLODClamp: 0.0,
}
}
@ -179,7 +181,7 @@ impl ViewDescriptor {
unsafe {
*desc.u.Texture2D_mut() = d3d12_ty::D3D12_TEX2D_UAV {
MipSlice: self.mip_level_base,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}
@ -190,7 +192,7 @@ impl ViewDescriptor {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}
@ -250,7 +252,7 @@ impl ViewDescriptor {
unsafe {
*desc.u.Texture2D_mut() = d3d12_ty::D3D12_TEX2D_RTV {
MipSlice: self.mip_level_base,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}
@ -272,7 +274,7 @@ impl ViewDescriptor {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}

View File

@ -1073,6 +1073,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
| Tf::Depth32FloatStencil8
| Tf::Depth24Plus
| Tf::Depth24PlusStencil8 => depth,
Tf::NV12 => unreachable!(),
Tf::Rgb9e5Ufloat => filterable,
Tf::Bc1RgbaUnorm
| Tf::Bc1RgbaUnormSrgb

View File

@ -87,6 +87,7 @@ impl super::AdapterShared {
glow::DEPTH_STENCIL,
glow::UNSIGNED_INT_24_8,
),
Tf::NV12 => unreachable!(),
Tf::Rgb9e5Ufloat => (glow::RGB9_E5, glow::RGB, glow::UNSIGNED_INT_5_9_9_9_REV),
Tf::Bc1RgbaUnorm => (glow::COMPRESSED_RGBA_S3TC_DXT1_EXT, glow::RGBA, 0),
Tf::Bc1RgbaUnormSrgb => (glow::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, glow::RGBA, 0),

View File

@ -981,6 +981,7 @@ pub struct TextureViewDescriptor<'a> {
pub dimension: wgt::TextureViewDimension,
pub usage: TextureUses,
pub range: wgt::ImageSubresourceRange,
pub plane: Option<u32>,
}
#[derive(Clone, Debug)]

View File

@ -232,6 +232,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
}
flags
}
Tf::NV12 => return Tfc::empty(),
Tf::Rgb9e5Ufloat => {
if pc.msaa_apple3 {
all_caps
@ -1022,6 +1023,7 @@ impl super::PrivateCapabilities {
Depth32Float_Stencil8
}
}
Tf::NV12 => unreachable!(),
Tf::Rgb9e5Ufloat => RGB9E5Float,
Tf::Bc1RgbaUnorm => BC1_RGBA,
Tf::Bc1RgbaUnormSrgb => BC1_RGBA_sRGB,

View File

@ -301,6 +301,7 @@ impl PhysicalDeviceFeatures {
fn to_wgpu(
&self,
adapter_info: &wgt::AdapterInfo,
instance: &ash::Instance,
phd: vk::PhysicalDevice,
caps: &PhysicalDeviceCapabilities,
@ -535,6 +536,22 @@ impl PhysicalDeviceFeatures {
supports_bgra8unorm_storage(instance, phd, caps.device_api_version),
);
features.set(
F::TEXTURE_FORMAT_NV12,
(caps.device_api_version >= vk::API_VERSION_1_1
|| caps.supports_extension(vk::KhrSamplerYcbcrConversionFn::name()))
&& supports_format(
instance,
phd,
vk::Format::G8_B8R8_2PLANE_420_UNORM,
vk::ImageTiling::OPTIMAL,
vk::FormatFeatureFlags::SAMPLED_IMAGE
| vk::FormatFeatureFlags::TRANSFER_SRC
| vk::FormatFeatureFlags::TRANSFER_DST,
)
&& !adapter_info.driver.contains("MoltenVK"),
);
(features, dl_flags)
}
@ -969,7 +986,7 @@ impl super::Instance {
};
let (available_features, downlevel_flags) =
phd_features.to_wgpu(&self.shared.raw, phd, &phd_capabilities);
phd_features.to_wgpu(&info, &self.shared.raw, phd, &phd_capabilities);
let mut workarounds = super::Workarounds::empty();
{
// see https://github.com/gfx-rs/gfx/issues/1930
@ -1551,14 +1568,14 @@ 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).unwrap() {
wgt::TextureSampleType::Float { filterable: _ } => limits
match format.sample_type(None) {
Some(wgt::TextureSampleType::Float { filterable: _ }) => limits
.framebuffer_color_sample_counts
.min(limits.sampled_image_color_sample_counts),
wgt::TextureSampleType::Sint | wgt::TextureSampleType::Uint => {
Some(wgt::TextureSampleType::Sint) | Some(wgt::TextureSampleType::Uint) => {
limits.sampled_image_integer_sample_counts
}
_ => unreachable!(),
_ => vk::SampleCountFlags::TYPE_1,
}
};

View File

@ -74,6 +74,7 @@ impl super::PrivateCapabilities {
}
}
Tf::Depth16Unorm => F::D16_UNORM,
Tf::NV12 => F::G8_B8R8_2PLANE_420_UNORM,
Tf::Rgb9e5Ufloat => F::E5B9G9R9_UFLOAT_PACK32,
Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK,
Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK,
@ -401,11 +402,19 @@ pub fn map_vertex_format(vertex_format: wgt::VertexFormat) -> vk::Format {
}
}
pub fn map_aspects(aspects: crate::FormatAspects) -> vk::ImageAspectFlags {
pub fn map_aspects(aspects: crate::FormatAspects, plane: Option<u32>) -> vk::ImageAspectFlags {
let mut flags = vk::ImageAspectFlags::empty();
match plane {
Some(0) => flags |= vk::ImageAspectFlags::PLANE_0,
Some(1) => flags |= vk::ImageAspectFlags::PLANE_1,
Some(2) => flags |= vk::ImageAspectFlags::PLANE_2,
Some(plane) => panic!("Unexpected plane {}", plane),
None => {
if aspects.contains(crate::FormatAspects::COLOR) {
flags |= vk::ImageAspectFlags::COLOR;
}
}
}
if aspects.contains(crate::FormatAspects::DEPTH) {
flags |= vk::ImageAspectFlags::DEPTH;
}
@ -586,9 +595,10 @@ pub fn map_copy_extent(extent: &crate::CopyExtent) -> vk::Extent3D {
pub fn map_subresource_range(
range: &wgt::ImageSubresourceRange,
format: wgt::TextureFormat,
plane: Option<u32>,
) -> vk::ImageSubresourceRange {
vk::ImageSubresourceRange {
aspect_mask: map_aspects(crate::FormatAspects::new(format, range.aspect)),
aspect_mask: map_aspects(crate::FormatAspects::new(format, range.aspect), plane),
base_mip_level: range.base_mip_level,
level_count: range.mip_level_count.unwrap_or(vk::REMAINING_MIP_LEVELS),
base_array_layer: range.base_array_layer,
@ -605,7 +615,7 @@ pub(super) fn map_subresource_range_combined_aspect(
format: wgt::TextureFormat,
private_caps: &super::PrivateCapabilities,
) -> vk::ImageSubresourceRange {
let mut range = map_subresource_range(range, format);
let mut range = map_subresource_range(range, format, None);
if !private_caps.texture_s8 && format == wgt::TextureFormat::Stencil8 {
range.aspect_mask |= vk::ImageAspectFlags::DEPTH;
}
@ -621,7 +631,7 @@ pub fn map_subresource_layers(
z: base.origin.z as i32,
};
let subresource = vk::ImageSubresourceLayers {
aspect_mask: map_aspects(base.aspect),
aspect_mask: map_aspects(base.aspect, None),
mip_level: base.mip_level,
base_array_layer: base.array_layer,
layer_count: 1,

View File

@ -1062,7 +1062,7 @@ impl crate::Device<super::Api> for super::Device {
texture: &super::Texture,
desc: &crate::TextureViewDescriptor,
) -> Result<super::TextureView, crate::DeviceError> {
let subresource_range = conv::map_subresource_range(&desc.range, desc.format);
let subresource_range = conv::map_subresource_range(&desc.range, desc.format, desc.plane);
let mut vk_info = vk::ImageViewCreateInfo::builder()
.flags(vk::ImageViewCreateFlags::empty())
.image(texture.raw)

View File

@ -771,7 +771,16 @@ bitflags::bitflags! {
/// - OpenGL
const SHADER_UNUSED_VERTEX_OUTPUT = 1 << 54;
// 54..59 available
/// Allows for creation of textures of format [`TextureFormat::NV12`]
///
/// Supported platforms:
/// - DX12
/// - Vulkan
///
/// This is a native only feature.
const TEXTURE_FORMAT_NV12 = 1 << 55;
// 55..59 available
// Shader:
@ -2377,6 +2386,21 @@ pub enum TextureFormat {
/// [`Features::DEPTH32FLOAT_STENCIL8`] must be enabled to use this texture format.
Depth32FloatStencil8,
/// YUV 4:2:0 chroma subsampled format.
///
/// Contains two planes:
/// - 0: Single 8 bit channel luminance.
/// - 1: Dual 8 bit channel chrominance at half width and half height.
///
/// Valid view formats for luminance are [`TextureFormat::R8Unorm`] and [`TextureFormat::R8Uint`].
///
/// Valid view formats for chrominance are [`TextureFormat::Rg8Unorm`] and [`TextureFormat::Rg8Uint`].
///
/// Width and height must be even.
///
/// [`Features::TEXTURE_FORMAT_NV12`] must be enabled to use this texture format.
NV12,
// Compressed textures usable with `TEXTURE_COMPRESSION_BC` feature.
/// 4x4 block compressed texture. 8 bytes per block (4 bit/px). 4 color + alpha pallet. 5 bit R + 6 bit G + 5 bit B + 1 bit alpha.
/// [0, 63] ([0, 1] for alpha) converted to/from float [0, 1] in shader.
@ -2606,6 +2630,7 @@ impl<'de> Deserialize<'de> for TextureFormat {
"depth16unorm" => TextureFormat::Depth16Unorm,
"depth24plus" => TextureFormat::Depth24Plus,
"depth24plus-stencil8" => TextureFormat::Depth24PlusStencil8,
"nv12" => TextureFormat::NV12,
"rgb9e5ufloat" => TextureFormat::Rgb9e5Ufloat,
"bc1-rgba-unorm" => TextureFormat::Bc1RgbaUnorm,
"bc1-rgba-unorm-srgb" => TextureFormat::Bc1RgbaUnormSrgb,
@ -2733,6 +2758,7 @@ impl Serialize for TextureFormat {
TextureFormat::Depth32FloatStencil8 => "depth32float-stencil8",
TextureFormat::Depth24Plus => "depth24plus",
TextureFormat::Depth24PlusStencil8 => "depth24plus-stencil8",
TextureFormat::NV12 => "nv12",
TextureFormat::Rgb9e5Ufloat => "rgb9e5ufloat",
TextureFormat::Bc1RgbaUnorm => "bc1-rgba-unorm",
TextureFormat::Bc1RgbaUnormSrgb => "bc1-rgba-unorm-srgb",
@ -2927,6 +2953,8 @@ impl TextureFormat {
| Self::Depth32Float
| Self::Depth32FloatStencil8 => (1, 1),
Self::NV12 => (2, 2),
Self::Bc1RgbaUnorm
| Self::Bc1RgbaUnormSrgb
| Self::Bc2RgbaUnorm
@ -3025,6 +3053,8 @@ impl TextureFormat {
Self::Depth32FloatStencil8 => Features::DEPTH32FLOAT_STENCIL8,
Self::NV12 => Features::TEXTURE_FORMAT_NV12,
Self::R16Unorm
| Self::R16Snorm
| Self::Rg16Unorm
@ -3079,6 +3109,7 @@ impl TextureFormat {
TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING;
let attachment = basic | TextureUsages::RENDER_ATTACHMENT;
let storage = basic | TextureUsages::STORAGE_BINDING;
let binding = TextureUsages::TEXTURE_BINDING;
let all_flags = TextureUsages::all();
let rg11b10f = if device_features.contains(Features::RG11B10UFLOAT_RENDERABLE) {
attachment
@ -3140,6 +3171,9 @@ impl TextureFormat {
Self::Depth32Float => ( msaa, attachment),
Self::Depth32FloatStencil8 => ( msaa, attachment),
// We only support sampling nv12 textures until we implement transfer plane data.
Self::NV12 => ( noaa, binding),
Self::R16Unorm => ( msaa, storage),
Self::R16Snorm => ( msaa, storage),
Self::Rg16Unorm => ( msaa, storage),
@ -3247,6 +3281,8 @@ impl TextureFormat {
Some(TextureAspect::StencilOnly) => Some(uint),
},
Self::NV12 => None,
Self::R16Unorm
| Self::R16Snorm
| Self::Rg16Unorm
@ -3363,6 +3399,8 @@ impl TextureFormat {
Some(TextureAspect::StencilOnly) => Some(1),
},
Self::NV12 => None,
Self::Bc1RgbaUnorm | Self::Bc1RgbaUnormSrgb | Self::Bc4RUnorm | Self::Bc4RSnorm => {
Some(8)
}
@ -3454,6 +3492,8 @@ impl TextureFormat {
TextureAspect::DepthOnly | TextureAspect::StencilOnly => 1,
},
Self::NV12 => 3,
Self::Bc4RUnorm | Self::Bc4RSnorm => 1,
Self::Bc5RgUnorm | Self::Bc5RgSnorm => 2,
Self::Bc6hRgbUfloat | Self::Bc6hRgbFloat => 3,

View File

@ -1610,6 +1610,7 @@ impl crate::Context for Context {
base_array_layer: desc.base_array_layer,
array_layer_count: desc.array_layer_count,
},
plane: desc.plane,
};
let global = &self.0;
let (id, error) = wgc::gfx_select!(

View File

@ -1301,6 +1301,8 @@ pub struct TextureViewDescriptor<'a> {
/// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
/// If `None`, considered to include the rest of the array layers, but at least 1 in total.
pub array_layer_count: Option<u32>,
/// The index (plane slice number) of the plane to use in the texture.
pub plane: Option<u32>,
}
static_assertions::assert_impl_all!(TextureViewDescriptor<'_>: Send, Sync);