wgpu/player/tests/test.rs

277 lines
8.5 KiB
Rust
Raw Normal View History

//! Tester for WebGPU
//! It enumerates the available backends on the system,
//! and run the tests through them.
//!
//! Test requirements:
//! - all IDs have the backend `Empty`
//! - all expected buffers have `MAP_READ` usage
//! - last action is `Submit`
//! - no swapchain use
#![cfg(not(target_arch = "wasm32"))]
2020-07-17 03:56:47 +00:00
use player::GlobalPlay;
2020-07-17 03:56:47 +00:00
use std::{
fs::{read_to_string, File},
2020-08-29 23:03:10 +00:00
io::{Read, Seek, SeekFrom},
mem::size_of,
2020-07-17 03:56:47 +00:00
path::{Path, PathBuf},
slice,
2020-07-17 03:56:47 +00:00
};
#[derive(serde::Deserialize)]
struct RawId {
index: u32,
epoch: u32,
}
2020-08-29 23:03:10 +00:00
#[derive(serde::Deserialize)]
enum ExpectedData {
Raw(Vec<u8>),
U64(Vec<u64>),
2020-08-29 23:03:10 +00:00
File(String, usize),
}
impl ExpectedData {
fn len(&self) -> usize {
match self {
ExpectedData::Raw(vec) => vec.len(),
ExpectedData::U64(vec) => vec.len() * size_of::<u64>(),
2020-08-29 23:03:10 +00:00
ExpectedData::File(_, size) => *size,
}
}
}
2020-07-17 03:56:47 +00:00
#[derive(serde::Deserialize)]
struct Expectation {
name: String,
buffer: RawId,
offset: wgt::BufferAddress,
2020-08-29 23:03:10 +00:00
data: ExpectedData,
2020-07-17 03:56:47 +00:00
}
struct Test<'a> {
2020-07-17 03:56:47 +00:00
features: wgt::Features,
expectations: Vec<Expectation>,
actions: Vec<wgc::device::trace::Action<'a>>,
2020-07-17 03:56:47 +00:00
}
fn map_callback(status: Result<(), wgc::resource::BufferAccessError>) {
if let Err(e) = status {
panic!("Buffer map error: {e}");
2020-07-17 03:56:47 +00:00
}
}
impl Test<'_> {
fn load(path: PathBuf, backend: wgt::Backend) -> Self {
let backend_name = match backend {
wgt::Backend::Vulkan => "Vulkan",
wgt::Backend::Metal => "Metal",
wgt::Backend::Dx12 => "Dx12",
wgt::Backend::Gl => "Gl",
_ => unreachable!(),
};
let string = read_to_string(path).unwrap().replace("Empty", backend_name);
#[derive(serde::Deserialize)]
struct SerializedTest<'a> {
features: Vec<String>,
expectations: Vec<Expectation>,
actions: Vec<wgc::device::trace::Action<'a>>,
}
let SerializedTest {
features,
expectations,
actions,
} = ron::de::from_str(&string).unwrap();
let features = features
.iter()
.map(|feature| {
wgt::Features::from_name(feature)
.unwrap_or_else(|| panic!("Invalid feature flag {feature}"))
})
.fold(wgt::Features::empty(), |a, b| a | b);
Test {
features,
expectations,
actions,
}
2020-07-17 03:56:47 +00:00
}
fn run(
self,
dir: &Path,
global: &wgc::global::Global,
adapter: wgc::id::AdapterId,
2020-08-29 23:03:10 +00:00
test_num: u32,
) {
2024-09-13 15:30:38 +00:00
let device_id = wgc::id::Id::zip(test_num, 1);
let queue_id = wgc::id::Id::zip(test_num, 1);
let res = global.adapter_request_device(
adapter,
&wgt::DeviceDescriptor {
label: None,
required_features: self.features,
required_limits: wgt::Limits::default(),
memory_hints: wgt::MemoryHints::default(),
},
None,
Some(device_id),
2024-08-06 21:17:47 +00:00
Some(queue_id),
);
if let Err(e) = res {
panic!("{e:?}");
2020-11-21 05:05:56 +00:00
}
let mut command_buffer_id_manager = wgc::identity::IdentityManager::new();
println!("\t\t\tRunning...");
for action in self.actions {
2024-08-06 21:17:47 +00:00
global.process(
device_id,
queue_id,
action,
dir,
&mut command_buffer_id_manager,
);
}
println!("\t\t\tMapping...");
for expect in &self.expectations {
2024-09-13 15:30:38 +00:00
let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch);
2024-08-06 21:17:47 +00:00
global
.buffer_map_async(
buffer,
expect.offset,
Some(expect.data.len() as u64),
wgc::resource::BufferMapOperation {
host: wgc::device::HostMap::Read,
callback: Some(wgc::resource::BufferMapCallback::from_rust(Box::new(
map_callback,
))),
},
)
.unwrap();
}
2020-07-17 03:56:47 +00:00
println!("\t\t\tWaiting...");
2024-08-06 21:17:47 +00:00
global
.device_poll(device_id, wgt::Maintain::wait())
.unwrap();
for expect in self.expectations {
println!("\t\t\tChecking {}", expect.name);
2024-09-13 15:30:38 +00:00
let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch);
2024-08-06 21:17:47 +00:00
let (ptr, size) = global
.buffer_get_mapped_range(
buffer,
expect.offset,
Some(expect.data.len() as wgt::BufferAddress),
)
.unwrap();
let contents = unsafe { slice::from_raw_parts(ptr.as_ptr(), size as usize) };
2020-08-29 23:03:10 +00:00
let expected_data = match expect.data {
ExpectedData::Raw(vec) => vec,
ExpectedData::File(name, size) => {
let mut bin = vec![0; size];
let mut file = File::open(dir.join(name)).unwrap();
file.seek(SeekFrom::Start(expect.offset)).unwrap();
file.read_exact(&mut bin[..]).unwrap();
bin
}
ExpectedData::U64(vec) => vec
.into_iter()
.flat_map(|u| u.to_ne_bytes().to_vec())
.collect::<Vec<u8>>(),
2020-08-29 23:03:10 +00:00
};
2020-12-05 16:28:16 +00:00
if &expected_data[..] != contents {
2021-02-03 22:07:54 +00:00
panic!(
"Test expectation is not met!\nBuffer content was:\n{contents:?}\nbut expected:\n{expected_data:?}"
2021-02-03 22:07:54 +00:00
);
2020-12-05 16:28:16 +00:00
}
}
2020-07-17 03:56:47 +00:00
}
}
#[derive(serde::Deserialize)]
struct Corpus {
2021-06-30 18:43:36 +00:00
backends: wgt::Backends,
tests: Vec<String>,
}
2020-07-17 03:56:47 +00:00
const BACKENDS: &[wgt::Backend] = &[
wgt::Backend::Vulkan,
wgt::Backend::Metal,
wgt::Backend::Dx12,
wgt::Backend::Gl,
];
2020-07-17 03:56:47 +00:00
impl Corpus {
fn run_from(path: PathBuf) {
println!("Corpus {path:?}");
let dir = path.parent().unwrap();
let corpus: Corpus = ron::de::from_reader(File::open(&path).unwrap()).unwrap();
for &backend in BACKENDS {
if !corpus.backends.contains(backend.into()) {
2020-07-17 03:56:47 +00:00
continue;
}
2020-08-29 23:03:10 +00:00
let mut test_num = 0;
for test_path in &corpus.tests {
println!("\t\tTest '{test_path:?}'");
let global = wgc::global::Global::new(
"test",
wgt::InstanceDescriptor {
backends: backend.into(),
flags: wgt::InstanceFlags::debugging(),
dx12_shader_compiler: wgt::Dx12Compiler::Fxc,
gles_minor_version: wgt::Gles3MinorVersion::default(),
},
);
let adapter = match global.request_adapter(
&wgc::instance::RequestAdapterOptions {
power_preference: wgt::PowerPreference::None,
force_fallback_adapter: false,
compatible_surface: None,
},
2024-09-13 15:30:38 +00:00
wgt::Backends::from(backend),
Some(wgc::id::Id::zip(0, 1)),
) {
Ok(adapter) => adapter,
Err(_) => continue,
};
println!("\tBackend {backend:?}");
let supported_features = global.adapter_features(adapter);
let downlevel_caps = global.adapter_downlevel_capabilities(adapter);
2024-09-13 15:30:38 +00:00
let test = Test::load(dir.join(test_path), backend);
if !supported_features.contains(test.features) {
println!(
"\t\tSkipped due to missing features {:?}",
test.features - supported_features
);
continue;
}
if !downlevel_caps
.flags
.contains(wgt::DownlevelFlags::COMPUTE_SHADERS)
{
println!("\t\tSkipped due to missing compute shader capability");
continue;
}
2020-08-29 23:03:10 +00:00
test.run(dir, &global, adapter, test_num);
test_num += 1;
}
2020-07-17 03:56:47 +00:00
}
}
}
#[test]
fn test_api() {
env_logger::init();
Corpus::run_from(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/all.ron"))
2020-07-17 03:56:47 +00:00
}