Skip to content

Commit

Permalink
Support nv12 texture format (#4573)
Browse files Browse the repository at this point in the history
Co-authored-by: Connor Fitzgerald <[email protected]>
  • Loading branch information
xiaopengli89 and cwfitzgerald authored Nov 28, 2023
1 parent f971183 commit a6503e5
Show file tree
Hide file tree
Showing 28 changed files with 484 additions and 39 deletions.
1 change: 1 addition & 0 deletions deno_webgpu/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions examples/src/mipmap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ impl Example {
mip_level_count: Some(1),
base_array_layer: 0,
array_layer_count: None,
..Default::default()
})
})
.collect::<Vec<_>>();
Expand Down
1 change: 1 addition & 0 deletions examples/src/shadow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<_>>();
Expand Down
1 change: 1 addition & 0 deletions tests/tests/bgra8unorm_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
250 changes: 250 additions & 0 deletions tests/tests/nv12_texture/mod.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 33 additions & 0 deletions tests/tests/nv12_texture/nv12_texture.wgsl
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions tests/tests/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions wgpu-core/src/command/clear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit a6503e5

Please sign in to comment.