mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
test: remove infrastructure-dependent integration suites (#1573)
This commit is contained in:
1 parent
f88b0f69b2
commit
10fc79ab37
199 files changed
+688
-46963
No files matched your search
@@ -525,671 +525,3 @@ pub fn try_acquire_device() -> Option<(wgpu::Device, wgpu::Queue, wgpu::Instance
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use fluxer_gpu_rebuild::{GpuLossRegistry, RebuildOutcome};
|
||||
|
||||
struct GpuCtx {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
_instance: wgpu::Instance,
|
||||
}
|
||||
|
||||
fn gpu_ctx() -> Option<GpuCtx> {
|
||||
let (device, queue, instance) = try_acquire_device()?;
|
||||
Some(GpuCtx {
|
||||
device,
|
||||
queue,
|
||||
_instance: instance,
|
||||
})
|
||||
}
|
||||
|
||||
fn make_source_texture(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
width: u32,
|
||||
height: u32,
|
||||
pixels: &[u8],
|
||||
) -> wgpu::Texture {
|
||||
assert_eq!(
|
||||
pixels.len() as u64,
|
||||
u64::from(width) * u64::from(height) * 4
|
||||
);
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_source"),
|
||||
size: wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
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: &texture,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
pixels,
|
||||
wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(width * 4),
|
||||
rows_per_image: Some(height),
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
texture
|
||||
}
|
||||
|
||||
fn make_storage_buffer(device: &wgpu::Device, size: u64, label: &str) -> wgpu::Buffer {
|
||||
device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some(label),
|
||||
size,
|
||||
usage: wgpu::BufferUsages::STORAGE
|
||||
| wgpu::BufferUsages::COPY_SRC
|
||||
| wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_buffer(device: &wgpu::Device, queue: &wgpu::Queue, src: &wgpu::Buffer) -> Vec<u8> {
|
||||
let size = src.size();
|
||||
let staging = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_staging"),
|
||||
size,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_readback"),
|
||||
});
|
||||
encoder.copy_buffer_to_buffer(src, 0, &staging, 0, size);
|
||||
queue.submit(std::iter::once(encoder.finish()));
|
||||
let slice = staging.slice(..);
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
slice.map_async(wgpu::MapMode::Read, move |res| {
|
||||
let _ = tx.send(res);
|
||||
});
|
||||
let _ = device.poll(wgpu::PollType::Wait {
|
||||
submission_index: None,
|
||||
timeout: None,
|
||||
});
|
||||
let recv = rx.recv();
|
||||
assert!(recv.is_ok(), "channel must deliver map result");
|
||||
let map_result = recv.expect("map result channel");
|
||||
assert!(map_result.is_ok(), "map_async must succeed");
|
||||
let data = {
|
||||
let range = slice.get_mapped_range();
|
||||
range.to_vec()
|
||||
};
|
||||
staging.unmap();
|
||||
data
|
||||
}
|
||||
|
||||
fn pack_and_readback(
|
||||
ctx: &GpuCtx,
|
||||
packer: &Nv12Packer,
|
||||
texture: &wgpu::Texture,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> (Vec<u8>, Vec<u8>) {
|
||||
let y_size = Nv12Packer::y_plane_size(width, height);
|
||||
let uv_size = Nv12Packer::uv_plane_size(width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, y_size, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, uv_size, "uv");
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("nv12_gpu_pack.test_encoder"),
|
||||
});
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (width, height),
|
||||
});
|
||||
assert!(result.is_ok(), "pack must succeed: {result:?}");
|
||||
ctx.queue.submit(std::iter::once(encoder.finish()));
|
||||
let y = read_buffer(&ctx.device, &ctx.queue, &y_buf);
|
||||
let uv = read_buffer(&ctx.device, &ctx.queue, &uv_buf);
|
||||
(y, uv)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solid_gray_packs_to_expected_bt709_limited() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 16;
|
||||
let height: u32 = 8;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for chunk in pixels.chunks_exact_mut(4) {
|
||||
chunk[0] = 128;
|
||||
chunk[1] = 128;
|
||||
chunk[2] = 128;
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (y, uv) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
for value in &y {
|
||||
assert!(
|
||||
(124..=128).contains(value),
|
||||
"Y value {value} out of expected range for gray 128"
|
||||
);
|
||||
}
|
||||
for value in &uv {
|
||||
assert!(
|
||||
(126..=130).contains(value),
|
||||
"UV value {value} out of expected range for neutral gray"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solid_red_v_is_above_neutral() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 16;
|
||||
let height: u32 = 8;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for chunk in pixels.chunks_exact_mut(4) {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 0;
|
||||
chunk[2] = 0;
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (_y, uv) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
for pair in uv.chunks_exact(2) {
|
||||
let u = pair[0];
|
||||
let v = pair[1];
|
||||
assert!(u < 128, "expected U below neutral for pure red, got {u}");
|
||||
assert!(
|
||||
v > 200,
|
||||
"expected V well above neutral for pure red, got {v}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_dims_exceeding_max() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let max_w: u32 = 8;
|
||||
let max_h: u32 = 8;
|
||||
let packer = Nv12Packer::new(&ctx.device, max_w, max_h);
|
||||
let y_size = Nv12Packer::y_plane_size(16, 16);
|
||||
let uv_size = Nv12Packer::uv_plane_size(16, 16);
|
||||
let y_buf = make_storage_buffer(&ctx.device, y_size, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, uv_size, "uv");
|
||||
let dummy_pixels = vec![0u8; (max_w * max_h * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, max_w, max_h, &dummy_pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (16, 16),
|
||||
});
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(Nv12PackError::DimensionsExceedMax { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_odd_dims() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let packer = Nv12Packer::new(&ctx.device, 32, 32);
|
||||
let y_buf = make_storage_buffer(&ctx.device, 64, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, 64, "uv");
|
||||
let dummy_pixels = vec![0u8; (8 * 8 * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, 8, 8, &dummy_pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (7, 8),
|
||||
});
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(Nv12PackError::DimensionsNotEven { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn determinism_two_runs_same_input_same_output() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for (i, chunk) in pixels.chunks_exact_mut(4).enumerate() {
|
||||
let v = (i % 251) as u8;
|
||||
chunk[0] = v;
|
||||
chunk[1] = v.wrapping_mul(2);
|
||||
chunk[2] = v.wrapping_add(31);
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (y1, uv1) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
let (y2, uv2) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
assert_eq!(y1, y2, "Y plane must be deterministic");
|
||||
assert_eq!(uv1, uv2, "UV plane must be deterministic");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_too_small_is_rejected() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let packer = Nv12Packer::new(&ctx.device, 64, 64);
|
||||
let y_buf = make_storage_buffer(&ctx.device, 16, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, 16, "uv");
|
||||
let pixels = vec![0u8; (16 * 16 * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, 16, 16, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (16, 16),
|
||||
});
|
||||
assert!(matches!(result, Err(Nv12PackError::YBufferTooSmall { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packer_is_ready_only_when_built() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut packer = Nv12Packer::new(&ctx.device, 32, 32);
|
||||
assert!(
|
||||
packer.is_ready(),
|
||||
"freshly constructed packer must be ready"
|
||||
);
|
||||
packer.release();
|
||||
assert!(!packer.is_ready(), "released packer must not be ready");
|
||||
let rebuilt = packer.rebuild(&ctx.device, &ctx.queue);
|
||||
assert!(rebuilt.is_ok(), "rebuild on a fresh device must succeed");
|
||||
assert!(packer.is_ready(), "rebuilt packer must be ready");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pack_between_release_and_rebuild_returns_not_ready() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut packer = Nv12Packer::new(&ctx.device, 16, 16);
|
||||
packer.release();
|
||||
let y_buf = make_storage_buffer(&ctx.device, 256, "y");
|
||||
let uv_buf = make_storage_buffer(&ctx.device, 256, "uv");
|
||||
let dummy_pixels = vec![0u8; (16 * 16 * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, 16, 16, &dummy_pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: &view,
|
||||
y_out: &y_buf,
|
||||
uv_out: &uv_buf,
|
||||
dims: (16, 16),
|
||||
});
|
||||
assert!(matches!(result, Err(Nv12PackError::NotReady)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_release_then_rebuild_invokes_callback_in_order() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let packer = Box::new(Nv12Packer::new(&ctx.device, 16, 16));
|
||||
let _guard = registry.register(packer);
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 1);
|
||||
assert_eq!(report.rebuilt_count, 1);
|
||||
assert_eq!(report.failed_count, 0);
|
||||
assert!(report.is_total_success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_loss_handling_packer_can_pack_again() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 16;
|
||||
let height: u32 = 8;
|
||||
let pixels = vec![128u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let mut packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
packer.release();
|
||||
packer
|
||||
.rebuild(&ctx.device, &ctx.queue)
|
||||
.expect("rebuild must succeed after release");
|
||||
let (y, _uv) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
assert!(!y.is_empty(), "Y plane must be non-empty after rebuild");
|
||||
assert!(
|
||||
(124..=128).contains(&y[0]),
|
||||
"Y value out of range for gray 128 after rebuild: {}",
|
||||
y[0]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn determinism_across_rebuild_round_trip() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let mut pixels = vec![0u8; (width * height * 4) as usize];
|
||||
for (i, chunk) in pixels.chunks_exact_mut(4).enumerate() {
|
||||
let v = (i % 251) as u8;
|
||||
chunk[0] = v;
|
||||
chunk[1] = v.wrapping_mul(2);
|
||||
chunk[2] = v.wrapping_add(31);
|
||||
chunk[3] = 255;
|
||||
}
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let mut packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let (y1, uv1) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
packer.release();
|
||||
packer
|
||||
.rebuild(&ctx.device, &ctx.queue)
|
||||
.expect("rebuild must succeed");
|
||||
let (y2, uv2) = pack_and_readback(&ctx, &packer, &tex, width, height);
|
||||
assert_eq!(y1, y2, "Y plane must match across rebuild round-trip");
|
||||
assert_eq!(uv1, uv2, "UV plane must match across rebuild round-trip");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_rebuild_without_release_is_owner_invariant_error() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let mut packer = Nv12Packer::new(&ctx.device, 16, 16);
|
||||
let outcome = packer.rebuild(&ctx.device, &ctx.queue);
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
Err(GpuRebuildError::OwnerInvariantBroken { .. })
|
||||
));
|
||||
}
|
||||
|
||||
struct FailingRebuildOwner {
|
||||
ready: bool,
|
||||
}
|
||||
|
||||
impl FailingRebuildOwner {
|
||||
fn new() -> Self {
|
||||
Self { ready: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuLossCallback for FailingRebuildOwner {
|
||||
fn release(&mut self) {
|
||||
let was_ready = self.ready;
|
||||
assert!(
|
||||
was_ready,
|
||||
"FailingRebuildOwner release must observe ready state",
|
||||
);
|
||||
self.ready = false;
|
||||
assert!(!self.ready, "FailingRebuildOwner postcondition");
|
||||
}
|
||||
fn rebuild(
|
||||
&mut self,
|
||||
_device: &wgpu::Device,
|
||||
_queue: &wgpu::Queue,
|
||||
) -> Result<(), GpuRebuildError> {
|
||||
assert!(
|
||||
!self.ready,
|
||||
"rebuild must run after release in FailingRebuildOwner"
|
||||
);
|
||||
Err(GpuRebuildError::DeviceRejected {
|
||||
reason: "synthetic second-device failure",
|
||||
})
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.ready
|
||||
}
|
||||
fn debug_label(&self) -> &'static str {
|
||||
"test.failing_rebuild_owner"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_rebuild_surfaces_in_report_and_owner_reports_not_ready() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let _guard = registry.register(Box::new(FailingRebuildOwner::new()));
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 1);
|
||||
assert_eq!(report.failed_count, 1);
|
||||
assert_eq!(report.rebuilt_count, 0);
|
||||
assert!(!report.is_total_success());
|
||||
let failed = report
|
||||
.outcomes
|
||||
.iter()
|
||||
.filter(|o| matches!(o, RebuildOutcome::Failed { .. }))
|
||||
.count();
|
||||
assert_eq!(failed, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_packers_release_lifo_rebuild_fifo() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let registry = GpuLossRegistry::new();
|
||||
let mut guards = Vec::new();
|
||||
for _ in 0..3u32 {
|
||||
let packer = Box::new(Nv12Packer::new(&ctx.device, 16, 16));
|
||||
guards.push(registry.register(packer));
|
||||
}
|
||||
assert_eq!(registry.len(), 3);
|
||||
let report = registry.handle_device_lost(&ctx.device, &ctx.queue);
|
||||
assert_eq!(report.released_count, 3);
|
||||
assert_eq!(report.rebuilt_count, 3);
|
||||
assert_eq!(report.failed_count, 0);
|
||||
for outcome in &report.outcomes {
|
||||
assert!(matches!(outcome, RebuildOutcome::Rebuilt { .. }));
|
||||
}
|
||||
drop(guards);
|
||||
}
|
||||
|
||||
fn pack_one_pass(
|
||||
ctx: &GpuCtx,
|
||||
packer: &Nv12Packer,
|
||||
view: &wgpu::TextureView,
|
||||
y_buf: &wgpu::Buffer,
|
||||
uv_buf: &wgpu::Buffer,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) {
|
||||
let mut encoder = ctx
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("nv12_gpu_pack.cache_test_encoder"),
|
||||
});
|
||||
let result = packer.pack(PackJob {
|
||||
device: &ctx.device,
|
||||
queue: &ctx.queue,
|
||||
encoder: &mut encoder,
|
||||
source: view,
|
||||
y_out: y_buf,
|
||||
uv_out: uv_buf,
|
||||
dims: (width, height),
|
||||
});
|
||||
assert!(result.is_ok(), "pack must succeed: {result:?}");
|
||||
ctx.queue.submit(std::iter::once(encoder.finish()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_hit_when_pack_called_twice_with_same_inputs() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y");
|
||||
let uv_buf =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv");
|
||||
assert_eq!(packer.cached_bind_group_count(), 0);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_miss_when_buffers_change() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf_a =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y_a");
|
||||
let uv_buf_a = make_storage_buffer(
|
||||
&ctx.device,
|
||||
Nv12Packer::uv_plane_size(width, height),
|
||||
"uv_a",
|
||||
);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf_a, &uv_buf_a, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
let y_buf_b =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y_b");
|
||||
let uv_buf_b = make_storage_buffer(
|
||||
&ctx.device,
|
||||
Nv12Packer::uv_plane_size(width, height),
|
||||
"uv_b",
|
||||
);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf_b, &uv_buf_b, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf_a, &uv_buf_a, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_invalidated_on_release() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y");
|
||||
let uv_buf =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv");
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
packer.release();
|
||||
assert_eq!(packer.cached_bind_group_count(), 0);
|
||||
packer
|
||||
.rebuild(&ctx.device, &ctx.queue)
|
||||
.expect("rebuild must succeed");
|
||||
assert_eq!(packer.cached_bind_group_count(), 0);
|
||||
pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_replaces_when_source_view_changes() {
|
||||
let Some(ctx) = gpu_ctx() else {
|
||||
eprintln!("nv12-gpu-pack: no wgpu adapter, skipping");
|
||||
return;
|
||||
};
|
||||
let width: u32 = 32;
|
||||
let height: u32 = 16;
|
||||
let pixels = vec![64u8; (width * height * 4) as usize];
|
||||
let tex_a = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let tex_b = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels);
|
||||
let view_a = tex_a.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let view_b = tex_b.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let packer = Nv12Packer::new(&ctx.device, width, height);
|
||||
let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y");
|
||||
let uv_buf =
|
||||
make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv");
|
||||
pack_one_pass(&ctx, &packer, &view_a, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
pack_one_pass(&ctx, &packer, &view_b, &y_buf, &uv_buf, width, height);
|
||||
assert_eq!(packer.cached_bind_group_count(), 1);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user