118 lines
4.2 KiB
Rust
118 lines
4.2 KiB
Rust
//! Real-hardware test: run the RGBA→NV12 compute on the GPU and check it byte-matches
|
|
//! the CPU reference. Skips (passes) if no GPU adapter is available.
|
|
|
|
use gpu_video_encoder::nv12::{cpu_reference, nv12_len, Nv12Converter};
|
|
|
|
fn device_queue() -> Option<(wgpu::Device, wgpu::Queue)> {
|
|
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
|
backends: wgpu::Backends::VULKAN | wgpu::Backends::GL,
|
|
..Default::default()
|
|
});
|
|
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
|
power_preference: wgpu::PowerPreference::HighPerformance,
|
|
force_fallback_adapter: false,
|
|
compatible_surface: None,
|
|
}))
|
|
.ok()?;
|
|
pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
|
label: Some("nv12-test"),
|
|
required_features: wgpu::Features::empty(),
|
|
required_limits: wgpu::Limits::downlevel_defaults(),
|
|
..Default::default()
|
|
}))
|
|
.ok()
|
|
}
|
|
|
|
/// A deterministic, varied RGBA pattern so luma and 2x2 chroma subsampling are exercised.
|
|
fn pattern(w: u32, h: u32) -> Vec<u8> {
|
|
let mut v = Vec::with_capacity((w * h * 4) as usize);
|
|
for y in 0..h {
|
|
for x in 0..w {
|
|
v.push(((x * 37 + y * 11) % 256) as u8); // R
|
|
v.push(((x * 5 + y * 53) % 256) as u8); // G
|
|
v.push(((x * 97 + y * 17) % 256) as u8); // B
|
|
v.push(255);
|
|
}
|
|
}
|
|
v
|
|
}
|
|
|
|
#[test]
|
|
fn gpu_nv12_matches_cpu_reference() {
|
|
let Some((device, queue)) = device_queue() else {
|
|
eprintln!("[gpu_nv12] no GPU adapter; skipping");
|
|
return;
|
|
};
|
|
|
|
let (w, h) = (64u32, 16u32);
|
|
let rgba = pattern(w, h);
|
|
|
|
// Source RGBA texture.
|
|
let tex = device.create_texture(&wgpu::TextureDescriptor {
|
|
label: Some("src_rgba"),
|
|
size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: wgpu::TextureDimension::D2,
|
|
format: wgpu::TextureFormat::Rgba8Unorm,
|
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
|
view_formats: &[],
|
|
});
|
|
queue.write_texture(
|
|
wgpu::TexelCopyTextureInfo {
|
|
texture: &tex,
|
|
mip_level: 0,
|
|
origin: wgpu::Origin3d::ZERO,
|
|
aspect: wgpu::TextureAspect::All,
|
|
},
|
|
&rgba,
|
|
wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(w * 4), rows_per_image: Some(h) },
|
|
wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
|
|
);
|
|
let view = tex.create_view(&Default::default());
|
|
|
|
let len = nv12_len(w, h) as u64;
|
|
let out = device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: Some("nv12_out"),
|
|
size: len,
|
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
|
|
mapped_at_creation: false,
|
|
});
|
|
let staging = device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: Some("nv12_staging"),
|
|
size: len,
|
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
|
mapped_at_creation: false,
|
|
});
|
|
|
|
let conv = Nv12Converter::new(&device);
|
|
let mut enc = device.create_command_encoder(&Default::default());
|
|
conv.convert(&device, &mut enc, &view, &out, w, h);
|
|
enc.copy_buffer_to_buffer(&out, 0, &staging, 0, len);
|
|
queue.submit(Some(enc.finish()));
|
|
|
|
let slice = staging.slice(..);
|
|
slice.map_async(wgpu::MapMode::Read, |_| {});
|
|
let _ = device.poll(wgpu::PollType::wait_indefinitely());
|
|
let gpu = slice.get_mapped_range().to_vec();
|
|
|
|
let cpu = cpu_reference(&rgba, w, h);
|
|
assert_eq!(gpu.len(), cpu.len(), "length mismatch");
|
|
|
|
// Allow ±1 for rounding differences between GPU and CPU float paths.
|
|
let mut max_diff = 0i32;
|
|
let mut nbad = 0;
|
|
for (i, (g, c)) in gpu.iter().zip(cpu.iter()).enumerate() {
|
|
let d = (*g as i32 - *c as i32).abs();
|
|
max_diff = max_diff.max(d);
|
|
if d > 1 {
|
|
nbad += 1;
|
|
if nbad <= 8 {
|
|
eprintln!("[gpu_nv12] byte {i}: gpu={g} cpu={c} (diff {d})");
|
|
}
|
|
}
|
|
}
|
|
eprintln!("[gpu_nv12] {}x{} NV12, max byte diff = {max_diff}", w, h);
|
|
assert_eq!(nbad, 0, "{nbad} bytes differ from CPU reference by >1");
|
|
}
|