2020-07-17 03:56:47 +00:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
/*! 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`
|
2020-07-17 14:59:02 +00:00
|
|
|
* - no swapchain use
|
2020-07-17 03:56:47 +00:00
|
|
|
!*/
|
|
|
|
|
2020-11-26 19:11:20 +00:00
|
|
|
use player::{GlobalPlay, IdentityPassThroughFactory};
|
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},
|
2020-07-17 03:56:47 +00:00
|
|
|
path::{Path, PathBuf},
|
|
|
|
ptr, slice,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[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>),
|
|
|
|
File(String, usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExpectedData {
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
match self {
|
|
|
|
ExpectedData::Raw(vec) => vec.len(),
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(serde::Deserialize)]
|
2020-07-20 16:31:05 +00:00
|
|
|
struct Test<'a> {
|
2020-07-17 03:56:47 +00:00
|
|
|
features: wgt::Features,
|
|
|
|
expectations: Vec<Expectation>,
|
2020-07-20 16:31:05 +00:00
|
|
|
actions: Vec<wgc::device::trace::Action<'a>>,
|
2020-07-17 03:56:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" fn map_callback(status: wgc::resource::BufferMapAsyncStatus, _user_data: *mut u8) {
|
|
|
|
match status {
|
|
|
|
wgc::resource::BufferMapAsyncStatus::Success => (),
|
|
|
|
_ => panic!("Unable to map"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-20 16:31:05 +00:00
|
|
|
impl Test<'_> {
|
2020-07-17 14:59:02 +00:00
|
|
|
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::Dx11 => "Dx11",
|
|
|
|
wgt::Backend::Gl => "Gl",
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
let string = read_to_string(path).unwrap().replace("Empty", backend_name);
|
|
|
|
ron::de::from_str(&string).unwrap()
|
2020-07-17 03:56:47 +00:00
|
|
|
}
|
|
|
|
|
2020-07-17 14:59:02 +00:00
|
|
|
fn run(
|
|
|
|
self,
|
|
|
|
dir: &Path,
|
|
|
|
global: &wgc::hub::Global<IdentityPassThroughFactory>,
|
|
|
|
adapter: wgc::id::AdapterId,
|
2020-08-29 23:03:10 +00:00
|
|
|
test_num: u32,
|
2020-07-17 14:59:02 +00:00
|
|
|
) {
|
|
|
|
let backend = adapter.backend();
|
2020-11-21 01:28:11 +00:00
|
|
|
let device = wgc::id::TypedId::zip(test_num, 0, backend);
|
2020-11-26 19:11:20 +00:00
|
|
|
let (_, error) = wgc::gfx_select!(adapter => global.adapter_request_device(
|
2020-07-17 14:59:02 +00:00
|
|
|
adapter,
|
|
|
|
&wgt::DeviceDescriptor {
|
2020-11-21 01:28:11 +00:00
|
|
|
label: None,
|
2020-07-17 14:59:02 +00:00
|
|
|
features: self.features | wgt::Features::MAPPABLE_PRIMARY_BUFFERS,
|
|
|
|
limits: wgt::Limits::default(),
|
|
|
|
shader_validation: true,
|
|
|
|
},
|
|
|
|
None,
|
2020-11-21 01:28:11 +00:00
|
|
|
device
|
|
|
|
));
|
2020-11-21 05:05:56 +00:00
|
|
|
if let Some(e) = error {
|
|
|
|
panic!("{:?}", e);
|
|
|
|
}
|
2020-07-17 14:59:02 +00:00
|
|
|
|
|
|
|
let mut command_buffer_id_manager = wgc::hub::IdentityManager::default();
|
|
|
|
println!("\t\t\tRunning...");
|
|
|
|
for action in self.actions {
|
2020-11-26 19:11:20 +00:00
|
|
|
wgc::gfx_select!(device => global.process(device, action, dir, &mut command_buffer_id_manager));
|
2020-07-17 14:59:02 +00:00
|
|
|
}
|
|
|
|
println!("\t\t\tMapping...");
|
|
|
|
for expect in &self.expectations {
|
|
|
|
let buffer = wgc::id::TypedId::zip(expect.buffer.index, expect.buffer.epoch, backend);
|
2020-11-26 19:11:20 +00:00
|
|
|
wgc::gfx_select!(device => global.buffer_map_async(
|
2020-07-17 14:59:02 +00:00
|
|
|
buffer,
|
|
|
|
expect.offset .. expect.offset+expect.data.len() as wgt::BufferAddress,
|
|
|
|
wgc::resource::BufferMapOperation {
|
|
|
|
host: wgc::device::HostMap::Read,
|
|
|
|
callback: map_callback,
|
|
|
|
user_data: ptr::null_mut(),
|
|
|
|
}
|
2020-07-18 08:48:14 +00:00
|
|
|
))
|
|
|
|
.unwrap();
|
2020-07-17 14:59:02 +00:00
|
|
|
}
|
2020-07-17 03:56:47 +00:00
|
|
|
|
2020-07-17 14:59:02 +00:00
|
|
|
println!("\t\t\tWaiting...");
|
2020-11-26 19:11:20 +00:00
|
|
|
wgc::gfx_select!(device => global.device_poll(device, true)).unwrap();
|
2020-07-17 14:59:02 +00:00
|
|
|
|
|
|
|
for expect in self.expectations {
|
|
|
|
println!("\t\t\tChecking {}", expect.name);
|
|
|
|
let buffer = wgc::id::TypedId::zip(expect.buffer.index, expect.buffer.epoch, backend);
|
|
|
|
let ptr =
|
2020-11-26 19:11:20 +00:00
|
|
|
wgc::gfx_select!(device => global.buffer_get_mapped_range(buffer, expect.offset, None))
|
2020-07-18 08:48:14 +00:00
|
|
|
.unwrap();
|
2020-07-17 14:59:02 +00:00
|
|
|
let contents = unsafe { slice::from_raw_parts(ptr, expect.data.len()) };
|
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
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(&expected_data[..], contents);
|
2020-07-17 14:59:02 +00:00
|
|
|
}
|
2020-09-05 03:45:00 +00:00
|
|
|
|
2020-11-26 19:11:20 +00:00
|
|
|
wgc::gfx_select!(device => global.clear_backend(()));
|
2020-07-17 03:56:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-17 14:59:02 +00:00
|
|
|
#[derive(serde::Deserialize)]
|
|
|
|
struct Corpus {
|
|
|
|
backends: wgt::BackendBit,
|
|
|
|
tests: Vec<String>,
|
|
|
|
}
|
2020-07-17 03:56:47 +00:00
|
|
|
|
2020-07-17 14:59:02 +00:00
|
|
|
const BACKENDS: &[wgt::Backend] = &[
|
|
|
|
wgt::Backend::Vulkan,
|
|
|
|
wgt::Backend::Metal,
|
|
|
|
wgt::Backend::Dx12,
|
|
|
|
wgt::Backend::Dx11,
|
|
|
|
wgt::Backend::Gl,
|
|
|
|
];
|
2020-07-17 03:56:47 +00:00
|
|
|
|
2020-07-17 14:59:02 +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();
|
|
|
|
|
|
|
|
let global = wgc::hub::Global::new("test", IdentityPassThroughFactory, corpus.backends);
|
|
|
|
for &backend in BACKENDS {
|
|
|
|
if !corpus.backends.contains(backend.into()) {
|
2020-07-17 03:56:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-07-30 05:21:12 +00:00
|
|
|
let adapter = match global.request_adapter(
|
2020-07-17 14:59:02 +00:00
|
|
|
&wgc::instance::RequestAdapterOptions {
|
2020-10-12 11:17:17 +00:00
|
|
|
power_preference: wgt::PowerPreference::LowPower,
|
2020-07-17 14:59:02 +00:00
|
|
|
compatible_surface: None,
|
|
|
|
},
|
|
|
|
wgc::instance::AdapterInputs::IdSet(
|
|
|
|
&[wgc::id::TypedId::zip(0, 0, backend)],
|
|
|
|
|id| id.backend(),
|
|
|
|
),
|
|
|
|
) {
|
2020-07-30 05:21:12 +00:00
|
|
|
Ok(adapter) => adapter,
|
|
|
|
Err(_) => continue,
|
2020-07-17 14:59:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
println!("\tBackend {:?}", backend);
|
2020-07-30 05:21:12 +00:00
|
|
|
let supported_features =
|
2020-11-26 19:11:20 +00:00
|
|
|
wgc::gfx_select!(adapter => global.adapter_features(adapter)).unwrap();
|
2020-08-29 23:03:10 +00:00
|
|
|
let mut test_num = 0;
|
2020-07-17 14:59:02 +00:00
|
|
|
for test_path in &corpus.tests {
|
|
|
|
println!("\t\tTest '{:?}'", test_path);
|
|
|
|
let test = Test::load(dir.join(test_path), adapter.backend());
|
|
|
|
if !supported_features.contains(test.features) {
|
|
|
|
println!(
|
|
|
|
"\t\tSkipped due to missing features {:?}",
|
|
|
|
test.features - supported_features
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
2020-08-29 23:03:10 +00:00
|
|
|
test.run(dir, &global, adapter, test_num);
|
|
|
|
test_num += 1;
|
2020-07-17 14:59:02 +00:00
|
|
|
}
|
2020-07-17 03:56:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_api() {
|
2020-12-03 05:30:38 +00:00
|
|
|
wgpu_subscriber::initialize_default_subscriber(
|
|
|
|
std::env::var("WGPU_CHROME_TRACE")
|
|
|
|
.as_ref()
|
|
|
|
.map(Path::new)
|
|
|
|
.ok(),
|
|
|
|
);
|
|
|
|
|
2020-07-17 14:59:02 +00:00
|
|
|
Corpus::run_from(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/all.ron"))
|
2020-07-17 03:56:47 +00:00
|
|
|
}
|