mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-22 06:44:14 +00:00
Support nv12 texture format (#4573)
Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
parent
f9711831d2
commit
a6503e59c9
@ -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(
|
||||
|
@ -133,6 +133,7 @@ impl Example {
|
||||
mip_level_count: Some(1),
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
..Default::default()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -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<_>>();
|
||||
|
@ -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 {
|
||||
|
250
tests/tests/nv12_texture/mod.rs
Normal file
250
tests/tests/nv12_texture/mod.rs
Normal 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],
|
||||
});
|
||||
});
|
||||
});
|
33
tests/tests/nv12_texture/nv12_texture.wgsl
Normal file
33
tests/tests/nv12_texture/nv12_texture.wgsl
Normal 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);
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,23 +804,35 @@ 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 {
|
||||
let desc = hal::TextureViewDescriptor {
|
||||
label: clear_label,
|
||||
format: desc.format,
|
||||
dimension,
|
||||
usage,
|
||||
range: wgt::ImageSubresourceRange {
|
||||
aspect: wgt::TextureAspect::All,
|
||||
base_mip_level: mip_level,
|
||||
mip_level_count: Some(1),
|
||||
base_array_layer: array_layer,
|
||||
array_layer_count: Some(1),
|
||||
},
|
||||
};
|
||||
clear_views.push(Some(
|
||||
unsafe { self.raw().create_texture_view(&raw_texture, &desc) }
|
||||
.map_err(DeviceError::from)?,
|
||||
));
|
||||
macro_rules! push_clear_view {
|
||||
($format:expr, $plane:expr) => {
|
||||
let desc = hal::TextureViewDescriptor {
|
||||
label: clear_label,
|
||||
format: $format,
|
||||
dimension,
|
||||
usage,
|
||||
range: wgt::ImageSubresourceRange {
|
||||
aspect: wgt::TextureAspect::All,
|
||||
base_mip_level: mip_level,
|
||||
mip_level_count: Some(1),
|
||||
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(()),
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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)]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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() };
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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,10 +402,18 @@ 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();
|
||||
if aspects.contains(crate::FormatAspects::COLOR) {
|
||||
flags |= vk::ImageAspectFlags::COLOR;
|
||||
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,
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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!(
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user