mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
GPU Test Framework (#3873)
This commit is contained in:
parent
75989192a9
commit
543f921639
@ -6,7 +6,8 @@ skip-tree = [
|
|||||||
{ name = "wgpu-info" },
|
{ name = "wgpu-info" },
|
||||||
]
|
]
|
||||||
skip = [
|
skip = [
|
||||||
{ name = "wgpu" }
|
{ name = "wgpu" },
|
||||||
|
{ name = "fastrand" }
|
||||||
]
|
]
|
||||||
wildcards = "deny"
|
wildcards = "deny"
|
||||||
|
|
||||||
|
|||||||
32
.github/workflows/ci.yml
vendored
32
.github/workflows/ci.yml
vendored
@ -270,17 +270,14 @@ jobs:
|
|||||||
# Windows
|
# Windows
|
||||||
- name: Windows x86_64
|
- name: Windows x86_64
|
||||||
os: windows-2022
|
os: windows-2022
|
||||||
backends: dx12
|
|
||||||
|
|
||||||
# Mac
|
# Mac
|
||||||
- name: Mac aarch64
|
- name: Mac aarch64
|
||||||
os: [self-hosted, macOS]
|
os: [self-hosted, macOS]
|
||||||
backends: vulkan metal
|
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
- name: Linux x86_64
|
- name: Linux x86_64
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
backends: vulkan gl
|
|
||||||
|
|
||||||
name: Test ${{ matrix.name }}
|
name: Test ${{ matrix.name }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@ -294,16 +291,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tool: cargo-nextest,cargo-llvm-cov
|
tool: cargo-nextest,cargo-llvm-cov
|
||||||
|
|
||||||
- name: install swiftshader
|
- name: Install Repo MSRV toolchain
|
||||||
if: matrix.os == 'ubuntu-22.04'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
set -e
|
rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal
|
||||||
|
cargo -V
|
||||||
mkdir -p swiftshader
|
|
||||||
curl -LsSf https://github.com/gfx-rs/ci-build/releases/latest/download/swiftshader-linux-x86_64.tar.xz | tar -xf - -C swiftshader
|
|
||||||
|
|
||||||
echo "VK_ICD_FILENAMES=$PWD/swiftshader/vk_swiftshader_icd.json" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: install llvmpipe, vulkan sdk
|
- name: install llvmpipe, vulkan sdk
|
||||||
if: matrix.os == 'ubuntu-22.04'
|
if: matrix.os == 'ubuntu-22.04'
|
||||||
@ -317,8 +308,10 @@ jobs:
|
|||||||
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
|
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
|
||||||
|
|
||||||
|
sudo add-apt-repository ppa:kisak/kisak-mesa
|
||||||
|
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk
|
sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk mesa-vulkan-drivers
|
||||||
|
|
||||||
- name: disable debug
|
- name: disable debug
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -334,22 +327,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
key: test-${{ matrix.os }}-${{ env.CACHE_SUFFIX }}
|
key: test-${{ matrix.os }}-${{ env.CACHE_SUFFIX }}
|
||||||
|
|
||||||
- name: run wgpu-info
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cargo llvm-cov --no-cfg-coverage run --bin wgpu-info --no-report --features vulkan-portability
|
|
||||||
|
|
||||||
- name: run tests
|
- name: run tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
for backend in ${{ matrix.backends }}; do
|
cargo xtask test --llvm-cov
|
||||||
echo "======= NATIVE TESTS $backend ======";
|
|
||||||
WGPU_BACKEND=$backend cargo llvm-cov --no-cfg-coverage nextest --no-fail-fast --no-report --features vulkan-portability
|
|
||||||
done
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: always() # We want artifacts even if the tests fail.
|
if: always() # We want artifacts even if the tests fail.
|
||||||
@ -421,6 +404,7 @@ jobs:
|
|||||||
- name: run rustfmt
|
- name: run rustfmt
|
||||||
run: |
|
run: |
|
||||||
cargo fmt -- --check
|
cargo fmt -- --check
|
||||||
|
cargo fmt --manifest-path xtask/Cargo.toml -- --check
|
||||||
|
|
||||||
check-cts-runner:
|
check-cts-runner:
|
||||||
name: Clippy cts_runner
|
name: Clippy cts_runner
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -28,3 +28,6 @@ cts/
|
|||||||
|
|
||||||
# Readme says to put angle in working directory
|
# Readme says to put angle in working directory
|
||||||
*.dll
|
*.dll
|
||||||
|
|
||||||
|
# Cached GPU config
|
||||||
|
.gpuconfig
|
||||||
|
|||||||
551
Cargo.lock
generated
551
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
15
Cargo.toml
@ -9,6 +9,7 @@ members = [
|
|||||||
"wgpu-core",
|
"wgpu-core",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
"wgpu-info",
|
"wgpu-info",
|
||||||
|
"wgpu-macros",
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
"tests",
|
"tests",
|
||||||
]
|
]
|
||||||
@ -20,6 +21,7 @@ default-members = [
|
|||||||
"wgpu-core",
|
"wgpu-core",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
"wgpu-info",
|
"wgpu-info",
|
||||||
|
"wgpu-macros",
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
"tests"
|
"tests"
|
||||||
]
|
]
|
||||||
@ -64,21 +66,27 @@ bytemuck = { version = "1.14", features = ["derive"] }
|
|||||||
cfg_aliases = "0.1"
|
cfg_aliases = "0.1"
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
codespan-reporting = "0.11"
|
codespan-reporting = "0.11"
|
||||||
|
ctor = "0.2"
|
||||||
ddsfile = "0.5"
|
ddsfile = "0.5"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
|
flume = "0.11"
|
||||||
|
futures-lite = "1"
|
||||||
futures-intrusive = "0.5"
|
futures-intrusive = "0.5"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
glam = "0.24.2"
|
glam = "0.24.2"
|
||||||
|
heck = "0.4.0"
|
||||||
image = { version = "0.24", default-features = false, features = ["png"] }
|
image = { version = "0.24", default-features = false, features = ["png"] }
|
||||||
# libloading 0.8 switches from `winapi` to `windows-sys`; permit either
|
# libloading 0.8 switches from `winapi` to `windows-sys`; permit either
|
||||||
libloading = ">=0.7, <0.9"
|
libloading = ">=0.7, <0.9"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
libtest-mimic = "0.6"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nanorand = { version = "0.7", default-features = false, features = ["wyrand"] }
|
nanorand = { version = "0.7", default-features = false, features = ["wyrand"] }
|
||||||
nv-flip = "0.1"
|
nv-flip = "0.1"
|
||||||
num-traits = { version = "0.2" }
|
num-traits = { version = "0.2" }
|
||||||
noise = "0.8"
|
noise = "0.8"
|
||||||
obj = "0.10"
|
obj = "0.10"
|
||||||
|
once_cell = "1"
|
||||||
# parking_lot 0.12 switches from `winapi` to `windows`; permit either
|
# parking_lot 0.12 switches from `winapi` to `windows`; permit either
|
||||||
parking_lot = ">=0.11,<0.13"
|
parking_lot = ">=0.11,<0.13"
|
||||||
pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] }
|
pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] }
|
||||||
@ -96,10 +104,17 @@ thiserror = "1"
|
|||||||
wgpu = { version = "0.17.0", path = "./wgpu" }
|
wgpu = { version = "0.17.0", path = "./wgpu" }
|
||||||
wgpu-core = { version = "0.17.0", path = "./wgpu-core" }
|
wgpu-core = { version = "0.17.0", path = "./wgpu-core" }
|
||||||
wgpu-example = { version = "0.17.0", path = "./examples/common" }
|
wgpu-example = { version = "0.17.0", path = "./examples/common" }
|
||||||
|
wgpu-macros = { version = "0.17.0", path = "./wgpu-macros" }
|
||||||
wgpu-test = { version = "0.17", path = "./tests"}
|
wgpu-test = { version = "0.17", path = "./tests"}
|
||||||
wgpu-types = { version = "0.17.0", path = "./wgpu-types" }
|
wgpu-types = { version = "0.17.0", path = "./wgpu-types" }
|
||||||
winit = { version = "0.28.7", features = [ "android-native-activity" ] }
|
winit = { version = "0.28.7", features = [ "android-native-activity" ] }
|
||||||
|
|
||||||
|
# Metal dependencies
|
||||||
|
block = "0.1"
|
||||||
|
metal = "0.25.0"
|
||||||
|
objc = "0.2.5"
|
||||||
|
core-graphics-types = "0.1"
|
||||||
|
|
||||||
# Vulkan dependencies
|
# Vulkan dependencies
|
||||||
ash = "0.37.3"
|
ash = "0.37.3"
|
||||||
gpu-alloc = "0.6"
|
gpu-alloc = "0.6"
|
||||||
|
|||||||
@ -160,7 +160,7 @@ To install it, run `cargo install cargo-nextest`.
|
|||||||
To run the test suite:
|
To run the test suite:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo nextest run --no-fail-fast
|
cargo xtask test
|
||||||
```
|
```
|
||||||
|
|
||||||
To run the test suite on WebGL (currently incomplete):
|
To run the test suite on WebGL (currently incomplete):
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "boids"
|
name = "boids"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -326,16 +326,16 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// run example
|
/// run example
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("boids");
|
wgpu_example::framework::run::<Example>("boids");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn boids() {
|
name: "boids",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
// Generated on 1080ti on Vk/Windows
|
// Generated on 1080ti on Vk/Windows
|
||||||
image_path: "examples/boids/screenshot.png",
|
image_path: "examples/boids/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
@ -347,5 +347,8 @@ fn boids() {
|
|||||||
// Lots of validation errors, maybe related to https://github.com/gfx-rs/wgpu/issues/3160
|
// Lots of validation errors, maybe related to https://github.com/gfx-rs/wgpu/issues/3160
|
||||||
.expect_fail(wgpu_test::FailureCase::molten_vk()),
|
.expect_fail(wgpu_test::FailureCase::molten_vk()),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.005)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.005)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "bunnymark"
|
name = "bunnymark"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -353,16 +353,16 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("bunnymark");
|
wgpu_example::framework::run::<Example>("bunnymark");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn bunnymark() {
|
name: "bunnymark",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/bunnymark/screenshot.png",
|
image_path: "/examples/bunnymark/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
@ -376,5 +376,8 @@ fn bunnymark() {
|
|||||||
threshold: 0.05,
|
threshold: 0.05,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -7,6 +7,8 @@ use std::time::Instant;
|
|||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas};
|
use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas};
|
||||||
|
use wgpu::{WasmNotSend, WasmNotSync};
|
||||||
|
use wgpu_test::GpuTestConfiguration;
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{self, WindowEvent},
|
event::{self, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
@ -493,7 +495,9 @@ pub fn parse_url_query_string<'a>(query: &'a str, search_key: &str) -> Option<&'
|
|||||||
|
|
||||||
pub use wgpu_test::image::ComparisonType;
|
pub use wgpu_test::image::ComparisonType;
|
||||||
|
|
||||||
pub struct FrameworkRefTest {
|
#[derive(Clone)]
|
||||||
|
pub struct ExampleTestParams<E> {
|
||||||
|
pub name: &'static str,
|
||||||
// Path to the reference image, relative to the root of the repo.
|
// Path to the reference image, relative to the root of the repo.
|
||||||
pub image_path: &'static str,
|
pub image_path: &'static str,
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
@ -502,129 +506,128 @@ pub struct FrameworkRefTest {
|
|||||||
pub base_test_parameters: wgpu_test::TestParameters,
|
pub base_test_parameters: wgpu_test::TestParameters,
|
||||||
/// Comparisons against FLIP statistics that determine if the test passes or fails.
|
/// Comparisons against FLIP statistics that determine if the test passes or fails.
|
||||||
pub comparisons: &'static [ComparisonType],
|
pub comparisons: &'static [ComparisonType],
|
||||||
|
pub _phantom: std::marker::PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
impl<E: Example + WasmNotSend + WasmNotSync> From<ExampleTestParams<E>> for GpuTestConfiguration {
|
||||||
pub fn test<E: Example>(mut params: FrameworkRefTest) {
|
fn from(params: ExampleTestParams<E>) -> Self {
|
||||||
use std::mem;
|
GpuTestConfiguration::new()
|
||||||
|
.name(params.name)
|
||||||
|
.parameters({
|
||||||
|
assert_eq!(params.width % 64, 0, "width needs to be aligned 64");
|
||||||
|
|
||||||
assert_eq!(params.width % 64, 0, "width needs to be aligned 64");
|
let features = E::required_features() | params.optional_features;
|
||||||
|
|
||||||
let features = E::required_features() | params.optional_features;
|
params.base_test_parameters.clone().features(features)
|
||||||
|
})
|
||||||
wgpu_test::initialize_test(
|
.run_async(move |ctx| async move {
|
||||||
mem::take(&mut params.base_test_parameters).features(features),
|
let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|ctx| {
|
label: Some("destination"),
|
||||||
let spawner = Spawner::new();
|
size: wgpu::Extent3d {
|
||||||
|
width: params.width,
|
||||||
let dst_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
height: params.height,
|
||||||
label: Some("destination"),
|
depth_or_array_layers: 1,
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: params.width,
|
|
||||||
height: params.height,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
|
||||||
view_formats: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
|
|
||||||
let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: Some("image map buffer"),
|
|
||||||
size: params.width as u64 * params.height as u64 * 4,
|
|
||||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut example = E::init(
|
|
||||||
&wgpu::SurfaceConfiguration {
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
|
||||||
width: params.width,
|
|
||||||
height: params.height,
|
|
||||||
present_mode: wgpu::PresentMode::Fifo,
|
|
||||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
|
||||||
view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb],
|
|
||||||
},
|
|
||||||
&ctx.adapter,
|
|
||||||
&ctx.device,
|
|
||||||
&ctx.queue,
|
|
||||||
);
|
|
||||||
|
|
||||||
example.render(&dst_view, &ctx.device, &ctx.queue, &spawner);
|
|
||||||
|
|
||||||
// Handle specific case for bunnymark
|
|
||||||
#[allow(deprecated)]
|
|
||||||
if params.image_path == "/examples/bunnymark/screenshot.png" {
|
|
||||||
// Press spacebar to spawn bunnies
|
|
||||||
example.update(winit::event::WindowEvent::KeyboardInput {
|
|
||||||
input: winit::event::KeyboardInput {
|
|
||||||
scancode: 0,
|
|
||||||
state: winit::event::ElementState::Pressed,
|
|
||||||
virtual_keycode: Some(winit::event::VirtualKeyCode::Space),
|
|
||||||
modifiers: winit::event::ModifiersState::empty(),
|
|
||||||
},
|
},
|
||||||
device_id: unsafe { winit::event::DeviceId::dummy() },
|
mip_level_count: 1,
|
||||||
is_synthetic: false,
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
||||||
|
view_formats: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 3 extra frames
|
let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
for _ in 0..3 {
|
|
||||||
example.render(&dst_view, &ctx.device, &ctx.queue, &spawner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cmd_buf = ctx
|
let dst_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
.device
|
label: Some("image map buffer"),
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
size: params.width as u64 * params.height as u64 * 4,
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
cmd_buf.copy_texture_to_buffer(
|
let mut example = E::init(
|
||||||
wgpu::ImageCopyTexture {
|
&wgpu::SurfaceConfiguration {
|
||||||
texture: &dst_texture,
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
mip_level: 0,
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
origin: wgpu::Origin3d::ZERO,
|
width: params.width,
|
||||||
aspect: wgpu::TextureAspect::All,
|
height: params.height,
|
||||||
},
|
present_mode: wgpu::PresentMode::Fifo,
|
||||||
wgpu::ImageCopyBuffer {
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||||
buffer: &dst_buffer,
|
view_formats: vec![wgpu::TextureFormat::Rgba8UnormSrgb],
|
||||||
layout: wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(params.width * 4),
|
|
||||||
rows_per_image: None,
|
|
||||||
},
|
},
|
||||||
},
|
&ctx.adapter,
|
||||||
wgpu::Extent3d {
|
&ctx.device,
|
||||||
width: params.width,
|
&ctx.queue,
|
||||||
height: params.height,
|
);
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.queue.submit(Some(cmd_buf.finish()));
|
{
|
||||||
|
let spawner = Spawner::new();
|
||||||
|
example.render(&dst_view, &ctx.device, &ctx.queue, &spawner);
|
||||||
|
|
||||||
let dst_buffer_slice = dst_buffer.slice(..);
|
// Handle specific case for bunnymark
|
||||||
dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ());
|
#[allow(deprecated)]
|
||||||
ctx.device.poll(wgpu::Maintain::Wait);
|
if params.image_path == "/examples/bunnymark/screenshot.png" {
|
||||||
let bytes = dst_buffer_slice.get_mapped_range().to_vec();
|
// Press spacebar to spawn bunnies
|
||||||
|
example.update(winit::event::WindowEvent::KeyboardInput {
|
||||||
|
input: winit::event::KeyboardInput {
|
||||||
|
scancode: 0,
|
||||||
|
state: winit::event::ElementState::Pressed,
|
||||||
|
virtual_keycode: Some(winit::event::VirtualKeyCode::Space),
|
||||||
|
modifiers: winit::event::ModifiersState::empty(),
|
||||||
|
},
|
||||||
|
device_id: unsafe { winit::event::DeviceId::dummy() },
|
||||||
|
is_synthetic: false,
|
||||||
|
});
|
||||||
|
|
||||||
wgpu_test::image::compare_image_output(
|
// Step 3 extra frames
|
||||||
env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + params.image_path,
|
for _ in 0..3 {
|
||||||
&ctx.adapter_info,
|
example.render(&dst_view, &ctx.device, &ctx.queue, &spawner);
|
||||||
params.width,
|
}
|
||||||
params.height,
|
}
|
||||||
&bytes,
|
}
|
||||||
params.comparisons,
|
|
||||||
);
|
let mut cmd_buf = ctx
|
||||||
},
|
.device
|
||||||
);
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
cmd_buf.copy_texture_to_buffer(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
texture: &dst_texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
},
|
||||||
|
wgpu::ImageCopyBuffer {
|
||||||
|
buffer: &dst_buffer,
|
||||||
|
layout: wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(params.width * 4),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: params.width,
|
||||||
|
height: params.height,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.queue.submit(Some(cmd_buf.finish()));
|
||||||
|
|
||||||
|
let dst_buffer_slice = dst_buffer.slice(..);
|
||||||
|
dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ());
|
||||||
|
ctx.device.poll(wgpu::Maintain::Wait);
|
||||||
|
let bytes = dst_buffer_slice.get_mapped_range().to_vec();
|
||||||
|
|
||||||
|
wgpu_test::image::compare_image_output(
|
||||||
|
env!("CARGO_MANIFEST_DIR").to_string() + "/../../" + params.image_path,
|
||||||
|
&ctx.adapter_info,
|
||||||
|
params.width,
|
||||||
|
params.height,
|
||||||
|
&bytes,
|
||||||
|
params.comparisons,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This allows treating the framework as a standalone example,
|
|
||||||
// thus avoiding listing the example names in `Cargo.toml`.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn main() {}
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "conservative-raster"
|
name = "conservative-raster"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasm-bindgen-test.workspace = true
|
wasm-bindgen-test.workspace = true
|
||||||
|
|||||||
@ -312,21 +312,24 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("conservative-raster");
|
wgpu_example::framework::run::<Example>("conservative-raster");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn conservative_raster() {
|
name: "conservative-raster",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/conservative-raster/screenshot.png",
|
image_path: "/examples/conservative-raster/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
optional_features: wgpu::Features::default(),
|
optional_features: wgpu::Features::default(),
|
||||||
base_test_parameters: wgpu_test::TestParameters::default(),
|
base_test_parameters: wgpu_test::TestParameters::default(),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "cube"
|
name = "cube"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -405,16 +405,16 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("cube");
|
wgpu_example::framework::run::<Example>("cube");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn cube() {
|
name: "cube",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
// Generated on 1080ti on Vk/Windows
|
// Generated on 1080ti on Vk/Windows
|
||||||
image_path: "/examples/cube/screenshot.png",
|
image_path: "/examples/cube/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
@ -424,13 +424,14 @@ fn cube() {
|
|||||||
comparisons: &[
|
comparisons: &[
|
||||||
wgpu_test::ComparisonType::Mean(0.04), // Bounded by Intel 630 on Vk/Windows
|
wgpu_test::ComparisonType::Mean(0.04), // Bounded by Intel 630 on Vk/Windows
|
||||||
],
|
],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
#[wgpu_test::gpu_test]
|
||||||
fn cube_lines() {
|
static TEST_LINES: wgpu_example::framework::ExampleTestParams =
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
wgpu_example::framework::ExampleTestParams {
|
||||||
|
name: "cube-lines",
|
||||||
// Generated on 1080ti on Vk/Windows
|
// Generated on 1080ti on Vk/Windows
|
||||||
image_path: "/examples/cube/screenshot-lines.png",
|
image_path: "/examples/cube/screenshot-lines.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
@ -445,5 +446,8 @@ fn cube_lines() {
|
|||||||
threshold: 0.36,
|
threshold: 0.36,
|
||||||
}, // Bounded by 1080ti on DX12
|
}, // Bounded by 1080ti on DX12
|
||||||
],
|
],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,11 +9,12 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "hello-compute"
|
name = "hello-compute"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
futures-intrusive.workspace = true
|
flume.workspace = true
|
||||||
pollster.workspace = true
|
pollster.workspace = true
|
||||||
wgpu.workspace = true
|
wgpu.workspace = true
|
||||||
winit.workspace = true
|
winit.workspace = true
|
||||||
@ -27,4 +28,3 @@ wasm-bindgen-futures.workspace = true
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test.workspace = true
|
wasm-bindgen-test.workspace = true
|
||||||
wgpu-test.workspace = true
|
wgpu-test.workspace = true
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use wgpu::util::DeviceExt;
|
|||||||
// Indicates a u32 overflow in an intermediate Collatz value
|
// Indicates a u32 overflow in an intermediate Collatz value
|
||||||
const OVERFLOW: u32 = 0xffffffff;
|
const OVERFLOW: u32 = 0xffffffff;
|
||||||
|
|
||||||
|
#[cfg_attr(test, allow(dead_code))]
|
||||||
async fn run() {
|
async fn run() {
|
||||||
let numbers = if std::env::args().len() <= 1 {
|
let numbers = if std::env::args().len() <= 1 {
|
||||||
let default = vec![1, 2, 3, 4];
|
let default = vec![1, 2, 3, 4];
|
||||||
@ -31,6 +32,7 @@ async fn run() {
|
|||||||
log::info!("Steps: [{}]", disp_steps.join(", "));
|
log::info!("Steps: [{}]", disp_steps.join(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, allow(dead_code))]
|
||||||
async fn execute_gpu(numbers: &[u32]) -> Option<Vec<u32>> {
|
async fn execute_gpu(numbers: &[u32]) -> Option<Vec<u32>> {
|
||||||
// Instantiates instance of WebGPU
|
// Instantiates instance of WebGPU
|
||||||
let instance = wgpu::Instance::default();
|
let instance = wgpu::Instance::default();
|
||||||
@ -54,12 +56,6 @@ async fn execute_gpu(numbers: &[u32]) -> Option<Vec<u32>> {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let info = adapter.get_info();
|
|
||||||
// skip this on LavaPipe temporarily
|
|
||||||
if info.vendor == 0x10005 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
execute_gpu_inner(&device, &queue, numbers).await
|
execute_gpu_inner(&device, &queue, numbers).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +146,7 @@ async fn execute_gpu_inner(
|
|||||||
// Note that we're not calling `.await` here.
|
// Note that we're not calling `.await` here.
|
||||||
let buffer_slice = staging_buffer.slice(..);
|
let buffer_slice = staging_buffer.slice(..);
|
||||||
// Sets the buffer up for mapping, sending over the result of the mapping back to us when it is finished.
|
// Sets the buffer up for mapping, sending over the result of the mapping back to us when it is finished.
|
||||||
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
|
let (sender, receiver) = flume::bounded(1);
|
||||||
buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
|
buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
|
||||||
|
|
||||||
// Poll the device in a blocking manner so that our future resolves.
|
// Poll the device in a blocking manner so that our future resolves.
|
||||||
@ -159,7 +155,7 @@ async fn execute_gpu_inner(
|
|||||||
device.poll(wgpu::Maintain::Wait);
|
device.poll(wgpu::Maintain::Wait);
|
||||||
|
|
||||||
// Awaits until `buffer_future` can be read from
|
// Awaits until `buffer_future` can be read from
|
||||||
if let Some(Ok(())) = receiver.receive().await {
|
if let Ok(Ok(())) = receiver.recv_async().await {
|
||||||
// Gets contents of buffer
|
// Gets contents of buffer
|
||||||
let data = buffer_slice.get_mapped_range();
|
let data = buffer_slice.get_mapped_range();
|
||||||
// Since contents are got in bytes, this converts these bytes back to u32
|
// Since contents are got in bytes, this converts these bytes back to u32
|
||||||
@ -181,6 +177,7 @@ async fn execute_gpu_inner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
@ -195,5 +192,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(test, not(target_arch = "wasm32")))]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -1,122 +1,96 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use wgpu_test::{initialize_test, FailureCase, TestParameters};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[gpu_test]
|
||||||
|
static COMPUTE_1: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
#[test]
|
.parameters(
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
|
||||||
fn test_compute_1() {
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
.features(wgpu::Features::TIMESTAMP_QUERY)
|
.features(wgpu::Features::TIMESTAMP_QUERY)
|
||||||
.skip(FailureCase::adapter("V3D")),
|
.skip(FailureCase::adapter("V3D")),
|
||||||
|ctx| {
|
)
|
||||||
let input = &[1, 2, 3, 4];
|
.run_async(|ctx| {
|
||||||
|
let input = &[1, 2, 3, 4];
|
||||||
|
|
||||||
pollster::block_on(assert_execute_gpu(
|
async move { assert_execute_gpu(&ctx.device, &ctx.queue, input, &[0, 1, 7, 2]).await }
|
||||||
&ctx.device,
|
});
|
||||||
&ctx.queue,
|
|
||||||
input,
|
|
||||||
&[0, 1, 7, 2],
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
static COMPUTE_2: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn test_compute_2() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
.features(wgpu::Features::TIMESTAMP_QUERY)
|
.features(wgpu::Features::TIMESTAMP_QUERY)
|
||||||
.skip(FailureCase::adapter("V3D")),
|
.skip(FailureCase::adapter("V3D")),
|
||||||
|ctx| {
|
)
|
||||||
let input = &[5, 23, 10, 9];
|
.run_async(|ctx| {
|
||||||
|
let input = &[5, 23, 10, 9];
|
||||||
|
|
||||||
pollster::block_on(assert_execute_gpu(
|
async move { assert_execute_gpu(&ctx.device, &ctx.queue, input, &[5, 15, 6, 19]).await }
|
||||||
&ctx.device,
|
});
|
||||||
&ctx.queue,
|
|
||||||
input,
|
|
||||||
&[5, 15, 6, 19],
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
static COMPUTE_OVERFLOW: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn test_compute_overflow() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
.features(wgpu::Features::TIMESTAMP_QUERY)
|
.features(wgpu::Features::TIMESTAMP_QUERY)
|
||||||
.skip(FailureCase::adapter("V3D")),
|
.skip(FailureCase::adapter("V3D")),
|
||||||
|ctx| {
|
)
|
||||||
let input = &[77031, 837799, 8400511, 63728127];
|
.run_async(|ctx| {
|
||||||
pollster::block_on(assert_execute_gpu(
|
let input = &[77031, 837799, 8400511, 63728127];
|
||||||
|
async move {
|
||||||
|
assert_execute_gpu(
|
||||||
&ctx.device,
|
&ctx.device,
|
||||||
&ctx.queue,
|
&ctx.queue,
|
||||||
input,
|
input,
|
||||||
&[350, 524, OVERFLOW, OVERFLOW],
|
&[350, 524, OVERFLOW, OVERFLOW],
|
||||||
));
|
)
|
||||||
},
|
.await
|
||||||
);
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
#[test]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
// Wasm doesn't support threads
|
#[gpu_test]
|
||||||
fn test_multithreaded_compute() {
|
static MULTITHREADED_COMPUTE: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
.features(wgpu::Features::TIMESTAMP_QUERY)
|
.features(wgpu::Features::TIMESTAMP_QUERY)
|
||||||
.skip(FailureCase::adapter("V3D"))
|
.skip(FailureCase::adapter("V3D")),
|
||||||
// https://github.com/gfx-rs/wgpu/issues/3944
|
)
|
||||||
.skip(FailureCase::backend_adapter(
|
.run_sync(|ctx| {
|
||||||
wgpu::Backends::VULKAN,
|
use std::{sync::mpsc, sync::Arc, thread, time::Duration};
|
||||||
"swiftshader",
|
|
||||||
))
|
|
||||||
// https://github.com/gfx-rs/wgpu/issues/3250
|
|
||||||
.skip(FailureCase::backend_adapter(wgpu::Backends::GL, "llvmpipe"))
|
|
||||||
.skip(FailureCase::molten_vk()),
|
|
||||||
|ctx| {
|
|
||||||
use std::{sync::mpsc, thread, time::Duration};
|
|
||||||
|
|
||||||
let ctx = Arc::new(ctx);
|
let ctx = Arc::new(ctx);
|
||||||
|
|
||||||
let thread_count = 8;
|
let thread_count = 8;
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
for _ in 0..thread_count {
|
for _ in 0..thread_count {
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
let ctx = Arc::clone(&ctx);
|
let ctx = Arc::clone(&ctx);
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let input = &[100, 100, 100];
|
let input = &[100, 100, 100];
|
||||||
pollster::block_on(assert_execute_gpu(
|
pollster::block_on(assert_execute_gpu(
|
||||||
&ctx.device,
|
&ctx.device,
|
||||||
&ctx.queue,
|
&ctx.queue,
|
||||||
input,
|
input,
|
||||||
&[25, 25, 25],
|
&[25, 25, 25],
|
||||||
));
|
));
|
||||||
tx.send(true).unwrap();
|
tx.send(true).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..thread_count {
|
for _ in 0..thread_count {
|
||||||
rx.recv_timeout(Duration::from_secs(10))
|
rx.recv_timeout(Duration::from_secs(10))
|
||||||
.expect("A thread never completed.");
|
.expect("A thread never completed.");
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn assert_execute_gpu(
|
async fn assert_execute_gpu(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
|
|||||||
@ -13,7 +13,7 @@ path = "src/main.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
futures-intrusive.workspace = true
|
flume.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
pollster.workspace = true
|
pollster.workspace = true
|
||||||
wgpu.workspace = true
|
wgpu.workspace = true
|
||||||
|
|||||||
@ -179,10 +179,10 @@ async fn get_data<T: bytemuck::Pod>(
|
|||||||
);
|
);
|
||||||
queue.submit(Some(command_encoder.finish()));
|
queue.submit(Some(command_encoder.finish()));
|
||||||
let buffer_slice = staging_buffer.slice(..);
|
let buffer_slice = staging_buffer.slice(..);
|
||||||
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
|
let (sender, receiver) = flume::bounded(1);
|
||||||
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
|
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
|
||||||
device.poll(wgpu::Maintain::Wait);
|
device.poll(wgpu::Maintain::Wait);
|
||||||
receiver.receive().await.unwrap().unwrap();
|
receiver.recv_async().await.unwrap().unwrap();
|
||||||
output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));
|
output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));
|
||||||
staging_buffer.unmap();
|
staging_buffer.unmap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,18 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use pollster::FutureExt;
|
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};
|
||||||
use wgpu_test::{initialize_test, TestParameters};
|
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[gpu_test]
|
||||||
|
static SYNC: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
#[test]
|
.parameters(
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
|
||||||
fn hello_synchronization_test_results() {
|
|
||||||
initialize_test(
|
|
||||||
// Taken from hello-compute tests.
|
// Taken from hello-compute tests.
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(wgpu::DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(wgpu::Limits::downlevel_defaults()),
|
.limits(wgpu::Limits::downlevel_defaults()),
|
||||||
|ctx| {
|
)
|
||||||
let ExecuteResults {
|
.run_async(|ctx| async move {
|
||||||
patient_workgroup_results,
|
let ExecuteResults {
|
||||||
hasty_workgroup_results: _,
|
patient_workgroup_results,
|
||||||
} = execute(&ctx.device, &ctx.queue, ARR_SIZE).block_on();
|
hasty_workgroup_results: _,
|
||||||
assert_eq!(patient_workgroup_results, [16_u32; ARR_SIZE]);
|
} = execute(&ctx.device, &ctx.queue, ARR_SIZE).await;
|
||||||
},
|
assert_eq!(patient_workgroup_results, [16_u32; ARR_SIZE]);
|
||||||
);
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "mipmap"
|
name = "mipmap"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -506,16 +506,16 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("mipmap");
|
wgpu_example::framework::run::<Example>("mipmap");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn mipmap() {
|
name: "mipmap",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/mipmap/screenshot.png",
|
image_path: "/examples/mipmap/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
@ -523,19 +523,23 @@ fn mipmap() {
|
|||||||
base_test_parameters: wgpu_test::TestParameters::default()
|
base_test_parameters: wgpu_test::TestParameters::default()
|
||||||
.expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::GL)),
|
.expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::GL)),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
#[wgpu_test::gpu_test]
|
||||||
fn mipmap_query() {
|
static TEST_QUERY: wgpu_example::framework::ExampleTestParams =
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
wgpu_example::framework::ExampleTestParams {
|
||||||
|
name: "mipmap-query",
|
||||||
image_path: "/examples/mipmap/screenshot-query.png",
|
image_path: "/examples/mipmap/screenshot-query.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
optional_features: QUERY_FEATURES,
|
optional_features: QUERY_FEATURES,
|
||||||
base_test_parameters: wgpu_test::TestParameters::default()
|
base_test_parameters: wgpu_test::TestParameters::default()
|
||||||
.expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::GL)),
|
.expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::GL)),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.025)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "msaa-line"
|
name = "msaa-line"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -313,16 +313,16 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("msaa-line");
|
wgpu_example::framework::run::<Example>("msaa-line");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn msaa_line() {
|
name: "msaa-line",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/msaa-line/screenshot.png",
|
image_path: "/examples/msaa-line/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
@ -343,5 +343,8 @@ fn msaa_line() {
|
|||||||
threshold: 0.29,
|
threshold: 0.29,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "shadow"
|
name = "shadow"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::{borrow::Cow, f32::consts, iter, mem, ops::Range, rc::Rc};
|
use std::{borrow::Cow, f32::consts, iter, mem, ops::Range, sync::Arc};
|
||||||
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use wgpu::util::{align_to, DeviceExt};
|
use wgpu::util::{align_to, DeviceExt};
|
||||||
@ -80,8 +80,8 @@ struct Entity {
|
|||||||
mx_world: glam::Mat4,
|
mx_world: glam::Mat4,
|
||||||
rotation_speed: f32,
|
rotation_speed: f32,
|
||||||
color: wgpu::Color,
|
color: wgpu::Color,
|
||||||
vertex_buf: Rc<wgpu::Buffer>,
|
vertex_buf: Arc<wgpu::Buffer>,
|
||||||
index_buf: Rc<wgpu::Buffer>,
|
index_buf: Arc<wgpu::Buffer>,
|
||||||
index_format: wgpu::IndexFormat,
|
index_format: wgpu::IndexFormat,
|
||||||
index_count: usize,
|
index_count: usize,
|
||||||
uniform_offset: wgpu::DynamicOffset,
|
uniform_offset: wgpu::DynamicOffset,
|
||||||
@ -221,7 +221,7 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
// Create the vertex and index buffers
|
// Create the vertex and index buffers
|
||||||
let vertex_size = mem::size_of::<Vertex>();
|
let vertex_size = mem::size_of::<Vertex>();
|
||||||
let (cube_vertex_data, cube_index_data) = create_cube();
|
let (cube_vertex_data, cube_index_data) = create_cube();
|
||||||
let cube_vertex_buf = Rc::new(device.create_buffer_init(
|
let cube_vertex_buf = Arc::new(device.create_buffer_init(
|
||||||
&wgpu::util::BufferInitDescriptor {
|
&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Cubes Vertex Buffer"),
|
label: Some("Cubes Vertex Buffer"),
|
||||||
contents: bytemuck::cast_slice(&cube_vertex_data),
|
contents: bytemuck::cast_slice(&cube_vertex_data),
|
||||||
@ -229,7 +229,7 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
let cube_index_buf = Rc::new(device.create_buffer_init(
|
let cube_index_buf = Arc::new(device.create_buffer_init(
|
||||||
&wgpu::util::BufferInitDescriptor {
|
&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Cubes Index Buffer"),
|
label: Some("Cubes Index Buffer"),
|
||||||
contents: bytemuck::cast_slice(&cube_index_data),
|
contents: bytemuck::cast_slice(&cube_index_data),
|
||||||
@ -306,8 +306,8 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
mx_world: glam::Mat4::IDENTITY,
|
mx_world: glam::Mat4::IDENTITY,
|
||||||
rotation_speed: 0.0,
|
rotation_speed: 0.0,
|
||||||
color: wgpu::Color::WHITE,
|
color: wgpu::Color::WHITE,
|
||||||
vertex_buf: Rc::new(plane_vertex_buf),
|
vertex_buf: Arc::new(plane_vertex_buf),
|
||||||
index_buf: Rc::new(plane_index_buf),
|
index_buf: Arc::new(plane_index_buf),
|
||||||
index_format,
|
index_format,
|
||||||
index_count: plane_index_data.len(),
|
index_count: plane_index_data.len(),
|
||||||
uniform_offset: 0,
|
uniform_offset: 0,
|
||||||
@ -327,8 +327,8 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
mx_world,
|
mx_world,
|
||||||
rotation_speed: cube.rotation,
|
rotation_speed: cube.rotation,
|
||||||
color: wgpu::Color::GREEN,
|
color: wgpu::Color::GREEN,
|
||||||
vertex_buf: Rc::clone(&cube_vertex_buf),
|
vertex_buf: Arc::clone(&cube_vertex_buf),
|
||||||
index_buf: Rc::clone(&cube_index_buf),
|
index_buf: Arc::clone(&cube_index_buf),
|
||||||
index_format,
|
index_format,
|
||||||
index_count: cube_index_data.len(),
|
index_count: cube_index_data.len(),
|
||||||
uniform_offset: ((i + 1) * uniform_alignment as usize) as _,
|
uniform_offset: ((i + 1) * uniform_alignment as usize) as _,
|
||||||
@ -840,16 +840,16 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("shadow");
|
wgpu_example::framework::run::<Example>("shadow");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn shadow() {
|
name: "shadow",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/shadow/screenshot.png",
|
image_path: "/examples/shadow/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
@ -860,12 +860,10 @@ fn shadow() {
|
|||||||
.expect_fail(wgpu_test::FailureCase::backend_adapter(
|
.expect_fail(wgpu_test::FailureCase::backend_adapter(
|
||||||
wgpu::Backends::VULKAN,
|
wgpu::Backends::VULKAN,
|
||||||
"V3D",
|
"V3D",
|
||||||
))
|
|
||||||
// llvmpipe versions in CI are flaky: https://github.com/gfx-rs/wgpu/issues/2594
|
|
||||||
.skip(wgpu_test::FailureCase::backend_adapter(
|
|
||||||
wgpu::Backends::VULKAN,
|
|
||||||
"llvmpipe",
|
|
||||||
)),
|
)),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "skybox"
|
name = "skybox"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -52,7 +52,7 @@ impl Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Skybox {
|
pub struct Example {
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
sky_pipeline: wgpu::RenderPipeline,
|
sky_pipeline: wgpu::RenderPipeline,
|
||||||
entity_pipeline: wgpu::RenderPipeline,
|
entity_pipeline: wgpu::RenderPipeline,
|
||||||
@ -63,7 +63,7 @@ pub struct Skybox {
|
|||||||
staging_belt: wgpu::util::StagingBelt,
|
staging_belt: wgpu::util::StagingBelt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Skybox {
|
impl Example {
|
||||||
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus;
|
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus;
|
||||||
|
|
||||||
fn create_depth_texture(
|
fn create_depth_texture(
|
||||||
@ -89,7 +89,7 @@ impl Skybox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl wgpu_example::framework::Example for Skybox {
|
impl wgpu_example::framework::Example for Example {
|
||||||
fn optional_features() -> wgpu::Features {
|
fn optional_features() -> wgpu::Features {
|
||||||
wgpu::Features::TEXTURE_COMPRESSION_ASTC
|
wgpu::Features::TEXTURE_COMPRESSION_ASTC
|
||||||
| wgpu::Features::TEXTURE_COMPRESSION_ETC2
|
| wgpu::Features::TEXTURE_COMPRESSION_ETC2
|
||||||
@ -356,7 +356,7 @@ impl wgpu_example::framework::Example for Skybox {
|
|||||||
|
|
||||||
let depth_view = Self::create_depth_texture(config, device);
|
let depth_view = Self::create_depth_texture(config, device);
|
||||||
|
|
||||||
Skybox {
|
Example {
|
||||||
camera,
|
camera,
|
||||||
sky_pipeline,
|
sky_pipeline,
|
||||||
entity_pipeline,
|
entity_pipeline,
|
||||||
@ -461,16 +461,16 @@ impl wgpu_example::framework::Example for Skybox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Skybox>("skybox");
|
wgpu_example::framework::run::<Example>("skybox");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn skybox() {
|
name: "skybox",
|
||||||
wgpu_example::framework::test::<Skybox>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/skybox/screenshot.png",
|
image_path: "/examples/skybox/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
@ -479,44 +479,50 @@ fn skybox() {
|
|||||||
wgpu_test::FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"),
|
wgpu_test::FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"),
|
||||||
),
|
),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.015)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.015)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
#[wgpu_test::gpu_test]
|
||||||
fn skybox_bc1() {
|
static TEST_BCN: wgpu_example::framework::ExampleTestParams =
|
||||||
wgpu_example::framework::test::<Skybox>(wgpu_example::framework::FrameworkRefTest {
|
wgpu_example::framework::ExampleTestParams {
|
||||||
|
name: "skybox-bc1",
|
||||||
image_path: "/examples/skybox/screenshot-bc1.png",
|
image_path: "/examples/skybox/screenshot-bc1.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
optional_features: wgpu::Features::TEXTURE_COMPRESSION_BC,
|
optional_features: wgpu::Features::TEXTURE_COMPRESSION_BC,
|
||||||
base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
#[wgpu_test::gpu_test]
|
||||||
fn skybox_etc2() {
|
static TEST_ETC2: wgpu_example::framework::ExampleTestParams =
|
||||||
wgpu_example::framework::test::<Skybox>(wgpu_example::framework::FrameworkRefTest {
|
wgpu_example::framework::ExampleTestParams {
|
||||||
|
name: "skybox-etc2",
|
||||||
image_path: "/examples/skybox/screenshot-etc2.png",
|
image_path: "/examples/skybox/screenshot-etc2.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
optional_features: wgpu::Features::TEXTURE_COMPRESSION_ETC2,
|
optional_features: wgpu::Features::TEXTURE_COMPRESSION_ETC2,
|
||||||
base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.015)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.015)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
#[wgpu_test::gpu_test]
|
||||||
fn skybox_astc() {
|
static TEST_ASTC: wgpu_example::framework::ExampleTestParams =
|
||||||
wgpu_example::framework::test::<Skybox>(wgpu_example::framework::FrameworkRefTest {
|
wgpu_example::framework::ExampleTestParams {
|
||||||
|
name: "skybox-astc",
|
||||||
image_path: "/examples/skybox/screenshot-astc.png",
|
image_path: "/examples/skybox/screenshot-astc.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
optional_features: wgpu::Features::TEXTURE_COMPRESSION_ASTC,
|
optional_features: wgpu::Features::TEXTURE_COMPRESSION_ASTC,
|
||||||
base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
base_test_parameters: wgpu_test::TestParameters::default(), // https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.016)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.016)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "stencil-triangles"
|
name = "stencil-triangles"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -15,7 +15,7 @@ fn vertex(x: f32, y: f32) -> Vertex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Triangles {
|
struct Example {
|
||||||
outer_vertex_buffer: wgpu::Buffer,
|
outer_vertex_buffer: wgpu::Buffer,
|
||||||
mask_vertex_buffer: wgpu::Buffer,
|
mask_vertex_buffer: wgpu::Buffer,
|
||||||
outer_pipeline: wgpu::RenderPipeline,
|
outer_pipeline: wgpu::RenderPipeline,
|
||||||
@ -23,7 +23,7 @@ struct Triangles {
|
|||||||
stencil_buffer: wgpu::Texture,
|
stencil_buffer: wgpu::Texture,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl wgpu_example::framework::Example for Triangles {
|
impl wgpu_example::framework::Example for Example {
|
||||||
fn init(
|
fn init(
|
||||||
config: &wgpu::SurfaceConfiguration,
|
config: &wgpu::SurfaceConfiguration,
|
||||||
_adapter: &wgpu::Adapter,
|
_adapter: &wgpu::Adapter,
|
||||||
@ -155,7 +155,7 @@ impl wgpu_example::framework::Example for Triangles {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
Triangles {
|
Example {
|
||||||
outer_vertex_buffer,
|
outer_vertex_buffer,
|
||||||
mask_vertex_buffer,
|
mask_vertex_buffer,
|
||||||
outer_pipeline,
|
outer_pipeline,
|
||||||
@ -230,21 +230,24 @@ impl wgpu_example::framework::Example for Triangles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Triangles>("stencil-triangles");
|
wgpu_example::framework::run::<Example>("stencil-triangles");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn stencil_triangles() {
|
name: "stencil-triangles",
|
||||||
wgpu_example::framework::test::<Triangles>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/stencil-triangles/screenshot.png",
|
image_path: "/examples/stencil-triangles/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
optional_features: wgpu::Features::default(),
|
optional_features: wgpu::Features::default(),
|
||||||
base_test_parameters: wgpu_test::TestParameters::default(),
|
base_test_parameters: wgpu_test::TestParameters::default(),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.03)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.03)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "texture-arrays"
|
name = "texture-arrays"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -406,29 +406,44 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("texture-arrays");
|
wgpu_example::framework::run::<Example>("texture-arrays");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn texture_arrays_uniform() {
|
name: "texture-arrays",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/texture-arrays/screenshot.png",
|
image_path: "/examples/texture-arrays/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
optional_features: wgpu::Features::empty(),
|
optional_features: wgpu::Features::empty(),
|
||||||
base_test_parameters: wgpu_test::TestParameters::default(),
|
base_test_parameters: wgpu_test::TestParameters::default(),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
#[wgpu_test::gpu_test]
|
||||||
fn texture_arrays_non_uniform() {
|
static TEST_UNIFORM: wgpu_example::framework::ExampleTestParams =
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
wgpu_example::framework::ExampleTestParams {
|
||||||
|
name: "texture-arrays-uniform",
|
||||||
|
image_path: "/examples/texture-arrays/screenshot.png",
|
||||||
|
width: 1024,
|
||||||
|
height: 768,
|
||||||
|
optional_features: wgpu::Features::empty(),
|
||||||
|
base_test_parameters: wgpu_test::TestParameters::default(),
|
||||||
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
|
||||||
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
|
static TEST_NON_UNIFORM: wgpu_example::framework::ExampleTestParams =
|
||||||
|
wgpu_example::framework::ExampleTestParams {
|
||||||
|
name: "texture-arrays-non-uniform",
|
||||||
image_path: "/examples/texture-arrays/screenshot.png",
|
image_path: "/examples/texture-arrays/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
@ -436,5 +451,8 @@ fn texture_arrays_non_uniform() {
|
|||||||
wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
base_test_parameters: wgpu_test::TestParameters::default(),
|
base_test_parameters: wgpu_test::TestParameters::default(),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -419,35 +419,29 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use wgpu_test::{gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
use crate::{submit_render_and_compute_pass_with_queries, QueryResults};
|
use crate::{submit_render_and_compute_pass_with_queries, QueryResults};
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
static TIMESTAMPS_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn test_timestamps_encoder() {
|
.parameters(
|
||||||
wgpu_test::initialize_test(
|
|
||||||
wgpu_test::TestParameters::default()
|
wgpu_test::TestParameters::default()
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
.features(wgpu::Features::TIMESTAMP_QUERY),
|
.features(wgpu::Features::TIMESTAMP_QUERY),
|
||||||
|ctx| {
|
)
|
||||||
test_timestamps(ctx, false);
|
.run_sync(|ctx| test_timestamps(ctx, false));
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
static TIMESTAMPS_PASSES: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn test_timestamps_passes() {
|
.parameters(
|
||||||
wgpu_test::initialize_test(
|
|
||||||
wgpu_test::TestParameters::default()
|
wgpu_test::TestParameters::default()
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
.features(
|
.features(
|
||||||
wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
|
wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
|
||||||
),
|
),
|
||||||
|ctx| {
|
)
|
||||||
test_timestamps(ctx, true);
|
.run_sync(|ctx| test_timestamps(ctx, true));
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_timestamps(ctx: wgpu_test::TestingContext, timestamps_inside_passes: bool) {
|
fn test_timestamps(ctx: wgpu_test::TestingContext, timestamps_inside_passes: bool) {
|
||||||
let queries = submit_render_and_compute_pass_with_queries(&ctx.device, &ctx.queue);
|
let queries = submit_render_and_compute_pass_with_queries(&ctx.device, &ctx.queue);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ publish = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "water"
|
name = "water"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
|
|||||||
@ -819,16 +819,16 @@ impl wgpu_example::framework::Example for Example {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
fn main() {
|
fn main() {
|
||||||
wgpu_example::framework::run::<Example>("water");
|
wgpu_example::framework::run::<Example>("water");
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
#[cfg(test)]
|
||||||
|
#[wgpu_test::gpu_test]
|
||||||
#[test]
|
static TEST: wgpu_example::framework::ExampleTestParams =
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
wgpu_example::framework::ExampleTestParams {
|
||||||
fn water() {
|
name: "water",
|
||||||
wgpu_example::framework::test::<Example>(wgpu_example::framework::FrameworkRefTest {
|
|
||||||
image_path: "/examples/water/screenshot.png",
|
image_path: "/examples/water/screenshot.png",
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 768,
|
height: 768,
|
||||||
@ -836,5 +836,8 @@ fn water() {
|
|||||||
base_test_parameters: wgpu_test::TestParameters::default()
|
base_test_parameters: wgpu_test::TestParameters::default()
|
||||||
.downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL),
|
.downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL),
|
||||||
comparisons: &[wgpu_test::ComparisonType::Mean(0.01)],
|
comparisons: &[wgpu_test::ComparisonType::Mean(0.01)],
|
||||||
});
|
_phantom: std::marker::PhantomData::<Example>,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
wgpu_test::gpu_test_main!();
|
||||||
|
|||||||
@ -12,23 +12,37 @@ autotests = false
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "wgpu-tests"
|
name = "gpu-tests"
|
||||||
path = "tests/root.rs"
|
path = "tests/gpu.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "cpu-tests"
|
||||||
|
path = "tests/cpu.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
webgl = ["wgpu/webgl"]
|
webgl = ["wgpu/webgl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
arrayvec.workspace = true
|
||||||
bitflags.workspace = true
|
bitflags.workspace = true
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
cfg-if.workspace = true
|
cfg-if.workspace = true
|
||||||
|
ctor.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
futures-lite.workspace = true
|
||||||
|
heck.workspace = true
|
||||||
|
libtest-mimic.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
png.workspace = true
|
png.workspace = true
|
||||||
pollster.workspace = true
|
pollster.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
wgpu.workspace = true
|
wgpu.workspace = true
|
||||||
wgt.workspace = true
|
wgpu-macros.workspace = true
|
||||||
|
wgt = { workspace = true, features = ["replay"] }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
nv-flip.workspace = true
|
nv-flip.workspace = true
|
||||||
|
|||||||
113
tests/src/config.rs
Normal file
113
tests/src/config.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
use std::{future::Future, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{TestParameters, TestingContext};
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_arch = "wasm32")] {
|
||||||
|
pub type RunTestAsync = Arc<dyn Fn(TestingContext) -> Pin<Box<dyn Future<Output = ()>>>>;
|
||||||
|
|
||||||
|
// We can't use WasmNonSend and WasmNonSync here, as we need these to not require Send/Sync
|
||||||
|
// even with the `fragile-send-sync-non-atomic-wasm` enabled.
|
||||||
|
pub trait RunTestSendSync {}
|
||||||
|
impl<T> RunTestSendSync for T {}
|
||||||
|
} else {
|
||||||
|
pub type RunTestAsync = Arc<dyn Fn(TestingContext) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> + Send + Sync>;
|
||||||
|
|
||||||
|
pub trait RunTestSendSync: Send + Sync {}
|
||||||
|
impl<T> RunTestSendSync for T where T: Send + Sync {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration for a GPU test.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GpuTestConfiguration {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) params: TestParameters,
|
||||||
|
pub(crate) test: Option<RunTestAsync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpuTestConfiguration {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
name: String::new(),
|
||||||
|
params: TestParameters::default(),
|
||||||
|
test: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the name of the test. Must be unique across all tests in the binary.
|
||||||
|
pub fn name(self, name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: String::from(name),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Derives the name from a `struct S` in the function initializing the test.
|
||||||
|
///
|
||||||
|
/// Does not overwrite a given name if a name has already been set
|
||||||
|
pub fn name_from_init_function_typename<S>(self, name: &'static str) -> Self {
|
||||||
|
if !self.name.is_empty() {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
let type_name = std::any::type_name::<S>();
|
||||||
|
|
||||||
|
// We end up with a string like:
|
||||||
|
//
|
||||||
|
// module::path::we::want::test_name_initializer::S
|
||||||
|
//
|
||||||
|
// So we reverse search for the 4th colon from the end, and take everything before that.
|
||||||
|
let mut colons = 0;
|
||||||
|
let mut colon_4_index = type_name.len();
|
||||||
|
for i in (0..type_name.len()).rev() {
|
||||||
|
if type_name.as_bytes()[i] == b':' {
|
||||||
|
colons += 1;
|
||||||
|
}
|
||||||
|
if colons == 4 {
|
||||||
|
colon_4_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let full = format!("{}::{}", &type_name[..colon_4_index], name);
|
||||||
|
Self { name: full, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the parameters that the test needs to succeed.
|
||||||
|
pub fn parameters(self, parameters: TestParameters) -> Self {
|
||||||
|
Self {
|
||||||
|
params: parameters,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make the test function an synchronous function.
|
||||||
|
pub fn run_sync(
|
||||||
|
self,
|
||||||
|
test: impl Fn(TestingContext) + Copy + RunTestSendSync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
test: Some(Arc::new(move |ctx| Box::pin(async move { test(ctx) }))),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make the test function an asynchronous function/future.
|
||||||
|
pub fn run_async<F, R>(self, test: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(TestingContext) -> R + RunTestSendSync + 'static,
|
||||||
|
R: Future<Output = ()> + RunTestSendSync + 'static,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
test: Some(Arc::new(move |ctx| Box::pin(test(ctx)))),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GpuTestConfiguration {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,12 @@
|
|||||||
use std::{borrow::Cow, ffi::OsStr, io, path::Path};
|
//! Image comparison utilities
|
||||||
|
|
||||||
|
use std::{borrow::Cow, ffi::OsStr, path::Path};
|
||||||
|
|
||||||
use wgpu::util::{align_to, DeviceExt};
|
use wgpu::util::{align_to, DeviceExt};
|
||||||
use wgpu::*;
|
use wgpu::*;
|
||||||
|
|
||||||
fn read_png(path: impl AsRef<Path>, width: u32, height: u32) -> Option<Vec<u8>> {
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
async fn read_png(path: impl AsRef<Path>, width: u32, height: u32) -> Option<Vec<u8>> {
|
||||||
let data = match std::fs::read(&path) {
|
let data = match std::fs::read(&path) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -15,7 +18,7 @@ fn read_png(path: impl AsRef<Path>, width: u32, height: u32) -> Option<Vec<u8>>
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let decoder = png::Decoder::new(io::Cursor::new(data));
|
let decoder = png::Decoder::new(std::io::Cursor::new(data));
|
||||||
let mut reader = decoder.read_info().ok()?;
|
let mut reader = decoder.read_info().ok()?;
|
||||||
|
|
||||||
let mut buffer = vec![0; reader.output_buffer_size()];
|
let mut buffer = vec![0; reader.output_buffer_size()];
|
||||||
@ -40,32 +43,26 @@ fn read_png(path: impl AsRef<Path>, width: u32, height: u32) -> Option<Vec<u8>>
|
|||||||
Some(buffer)
|
Some(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn write_png(
|
async fn write_png(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
compression: png::Compression,
|
compression: png::Compression,
|
||||||
) {
|
) {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
let file = std::io::BufWriter::new(std::fs::File::create(path).unwrap());
|
||||||
{
|
|
||||||
let file = io::BufWriter::new(std::fs::File::create(path).unwrap());
|
|
||||||
|
|
||||||
let mut encoder = png::Encoder::new(file, width, height);
|
let mut encoder = png::Encoder::new(file, width, height);
|
||||||
encoder.set_color(png::ColorType::Rgba);
|
encoder.set_color(png::ColorType::Rgba);
|
||||||
encoder.set_depth(png::BitDepth::Eight);
|
encoder.set_depth(png::BitDepth::Eight);
|
||||||
encoder.set_compression(compression);
|
encoder.set_compression(compression);
|
||||||
let mut writer = encoder.write_header().unwrap();
|
let mut writer = encoder.write_header().unwrap();
|
||||||
|
|
||||||
writer.write_image_data(data).unwrap();
|
writer.write_image_data(data).unwrap();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calc_difference(lhs: u8, rhs: u8) -> u8 {
|
|
||||||
(lhs as i16 - rhs as i16).unsigned_abs() as u8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", allow(unused))]
|
||||||
fn add_alpha(input: &[u8]) -> Vec<u8> {
|
fn add_alpha(input: &[u8]) -> Vec<u8> {
|
||||||
input
|
input
|
||||||
.chunks_exact(3)
|
.chunks_exact(3)
|
||||||
@ -73,6 +70,7 @@ fn add_alpha(input: &[u8]) -> Vec<u8> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", allow(unused))]
|
||||||
fn remove_alpha(input: &[u8]) -> Vec<u8> {
|
fn remove_alpha(input: &[u8]) -> Vec<u8> {
|
||||||
input
|
input
|
||||||
.chunks_exact(4)
|
.chunks_exact(4)
|
||||||
@ -148,7 +146,8 @@ impl ComparisonType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare_image_output(
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub async fn compare_image_output(
|
||||||
path: impl AsRef<Path> + AsRef<OsStr>,
|
path: impl AsRef<Path> + AsRef<OsStr>,
|
||||||
adapter_info: &wgt::AdapterInfo,
|
adapter_info: &wgt::AdapterInfo,
|
||||||
width: u32,
|
width: u32,
|
||||||
@ -156,29 +155,47 @@ pub fn compare_image_output(
|
|||||||
test_with_alpha: &[u8],
|
test_with_alpha: &[u8],
|
||||||
checks: &[ComparisonType],
|
checks: &[ComparisonType],
|
||||||
) {
|
) {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
use std::{ffi::OsString, str::FromStr};
|
||||||
|
|
||||||
|
let reference_path = Path::new(&path);
|
||||||
|
let reference_with_alpha = read_png(&path, width, height).await;
|
||||||
|
|
||||||
|
let reference = match reference_with_alpha {
|
||||||
|
Some(v) => remove_alpha(&v),
|
||||||
|
None => {
|
||||||
|
write_png(
|
||||||
|
&path,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
test_with_alpha,
|
||||||
|
png::Compression::Best,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let test = remove_alpha(test_with_alpha);
|
||||||
|
|
||||||
|
assert_eq!(reference.len(), test.len());
|
||||||
|
|
||||||
|
let file_stem = reference_path.file_stem().unwrap().to_string_lossy();
|
||||||
|
let renderer = format!(
|
||||||
|
"{}-{}-{}",
|
||||||
|
adapter_info.backend.to_str(),
|
||||||
|
sanitize_for_path(&adapter_info.name),
|
||||||
|
sanitize_for_path(&adapter_info.driver)
|
||||||
|
);
|
||||||
|
// Determine the paths to write out the various intermediate files
|
||||||
|
let actual_path = Path::new(&path).with_file_name(
|
||||||
|
OsString::from_str(&format!("{}-{}-actual.png", file_stem, renderer)).unwrap(),
|
||||||
|
);
|
||||||
|
let difference_path = Path::new(&path).with_file_name(
|
||||||
|
OsString::from_str(&format!("{}-{}-difference.png", file_stem, renderer,)).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut all_passed;
|
||||||
|
let magma_image_with_alpha;
|
||||||
{
|
{
|
||||||
use std::{ffi::OsString, str::FromStr};
|
|
||||||
|
|
||||||
let reference_with_alpha = read_png(&path, width, height);
|
|
||||||
|
|
||||||
let reference = match reference_with_alpha {
|
|
||||||
Some(v) => remove_alpha(&v),
|
|
||||||
None => {
|
|
||||||
write_png(
|
|
||||||
&path,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
test_with_alpha,
|
|
||||||
png::Compression::Best,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let test = remove_alpha(test_with_alpha);
|
|
||||||
|
|
||||||
assert_eq!(reference.len(), test.len());
|
|
||||||
|
|
||||||
let reference_flip = nv_flip::FlipImageRgb8::with_data(width, height, &reference);
|
let reference_flip = nv_flip::FlipImageRgb8::with_data(width, height, &reference);
|
||||||
let test_flip = nv_flip::FlipImageRgb8::with_data(width, height, &test);
|
let test_flip = nv_flip::FlipImageRgb8::with_data(width, height, &test);
|
||||||
|
|
||||||
@ -189,7 +206,6 @@ pub fn compare_image_output(
|
|||||||
);
|
);
|
||||||
let mut pool = nv_flip::FlipPool::from_image(&error_map_flip);
|
let mut pool = nv_flip::FlipPool::from_image(&error_map_flip);
|
||||||
|
|
||||||
let reference_path = Path::new(&path);
|
|
||||||
println!(
|
println!(
|
||||||
"Starting image comparison test with reference image \"{}\"",
|
"Starting image comparison test with reference image \"{}\"",
|
||||||
reference_path.display()
|
reference_path.display()
|
||||||
@ -198,59 +214,57 @@ pub fn compare_image_output(
|
|||||||
print_flip(&mut pool);
|
print_flip(&mut pool);
|
||||||
|
|
||||||
// If there are no checks, we want to fail the test.
|
// If there are no checks, we want to fail the test.
|
||||||
let mut all_passed = !checks.is_empty();
|
all_passed = !checks.is_empty();
|
||||||
// We always iterate all of these, as the call to check prints
|
// We always iterate all of these, as the call to check prints
|
||||||
for check in checks {
|
for check in checks {
|
||||||
all_passed &= check.check(&mut pool);
|
all_passed &= check.check(&mut pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_stem = reference_path.file_stem().unwrap().to_string_lossy();
|
|
||||||
let renderer = format!(
|
|
||||||
"{}-{}-{}",
|
|
||||||
adapter_info.backend.to_str(),
|
|
||||||
sanitize_for_path(&adapter_info.name),
|
|
||||||
sanitize_for_path(&adapter_info.driver)
|
|
||||||
);
|
|
||||||
// Determine the paths to write out the various intermediate files
|
|
||||||
let actual_path = Path::new(&path).with_file_name(
|
|
||||||
OsString::from_str(&format!("{}-{}-actual.png", file_stem, renderer)).unwrap(),
|
|
||||||
);
|
|
||||||
let difference_path = Path::new(&path).with_file_name(
|
|
||||||
OsString::from_str(&format!("{}-{}-difference.png", file_stem, renderer,)).unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert the error values to a false color reprensentation
|
// Convert the error values to a false color reprensentation
|
||||||
let magma_image = error_map_flip
|
let magma_image = error_map_flip
|
||||||
.apply_color_lut(&nv_flip::magma_lut())
|
.apply_color_lut(&nv_flip::magma_lut())
|
||||||
.to_vec();
|
.to_vec();
|
||||||
let magma_image_with_alpha = add_alpha(&magma_image);
|
magma_image_with_alpha = add_alpha(&magma_image);
|
||||||
|
|
||||||
write_png(
|
|
||||||
actual_path,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
test_with_alpha,
|
|
||||||
png::Compression::Fast,
|
|
||||||
);
|
|
||||||
write_png(
|
|
||||||
difference_path,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
&magma_image_with_alpha,
|
|
||||||
png::Compression::Fast,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !all_passed {
|
|
||||||
panic!("Image data mismatch!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_png(
|
||||||
|
actual_path,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
test_with_alpha,
|
||||||
|
png::Compression::Fast,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
write_png(
|
||||||
|
difference_path,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
&magma_image_with_alpha,
|
||||||
|
png::Compression::Fast,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if !all_passed {
|
||||||
|
panic!("Image data mismatch!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub async fn compare_image_output(
|
||||||
|
path: impl AsRef<Path> + AsRef<OsStr>,
|
||||||
|
adapter_info: &wgt::AdapterInfo,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
test_with_alpha: &[u8],
|
||||||
|
checks: &[ComparisonType],
|
||||||
|
) {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
{
|
{
|
||||||
let _ = (path, adapter_info, width, height, test_with_alpha, checks);
|
let _ = (path, adapter_info, width, height, test_with_alpha, checks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", allow(unused))]
|
||||||
fn sanitize_for_path(s: &str) -> String {
|
fn sanitize_for_path(s: &str) -> String {
|
||||||
s.chars()
|
s.chars()
|
||||||
.map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '_' })
|
.map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '_' })
|
||||||
|
|||||||
129
tests/src/init.rs
Normal file
129
tests/src/init.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
use wgpu::{Adapter, Device, Instance, Queue};
|
||||||
|
use wgt::{Backends, Features, Limits};
|
||||||
|
|
||||||
|
/// Initialize a wgpu instance with the options from the environment.
|
||||||
|
pub fn initialize_instance() -> Instance {
|
||||||
|
// We ignore `WGPU_BACKEND` for now, merely using test filtering to only run a single backend's tests.
|
||||||
|
//
|
||||||
|
// We can potentially work support back into the test runner in the future, but as the adapters are matched up
|
||||||
|
// based on adapter index, removing some backends messes up the indexes in annoying ways.
|
||||||
|
let backends = Backends::all();
|
||||||
|
let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default();
|
||||||
|
let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default();
|
||||||
|
Instance::new(wgpu::InstanceDescriptor {
|
||||||
|
backends,
|
||||||
|
flags: wgpu::InstanceFlags::debugging().with_env(),
|
||||||
|
dx12_shader_compiler,
|
||||||
|
gles_minor_version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a wgpu adapter, taking the `n`th adapter from the instance.
|
||||||
|
pub async fn initialize_adapter(adapter_index: usize) -> (Adapter, Option<SurfaceGuard>) {
|
||||||
|
let instance = initialize_instance();
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let _surface: wgpu::Surface;
|
||||||
|
let surface_guard: Option<SurfaceGuard>;
|
||||||
|
|
||||||
|
// Create a canvas iff we need a WebGL2RenderingContext to have a working device.
|
||||||
|
#[cfg(not(all(
|
||||||
|
target_arch = "wasm32",
|
||||||
|
any(target_os = "emscripten", feature = "webgl")
|
||||||
|
)))]
|
||||||
|
{
|
||||||
|
surface_guard = None;
|
||||||
|
}
|
||||||
|
#[cfg(all(
|
||||||
|
target_arch = "wasm32",
|
||||||
|
any(target_os = "emscripten", feature = "webgl")
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
// On wasm, append a canvas to the document body for initializing the adapter
|
||||||
|
let canvas = initialize_html_canvas();
|
||||||
|
|
||||||
|
_surface = instance
|
||||||
|
.create_surface_from_canvas(canvas.clone())
|
||||||
|
.expect("could not create surface from canvas");
|
||||||
|
|
||||||
|
surface_guard = Some(SurfaceGuard { canvas });
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))] {
|
||||||
|
let adapter_iter = instance.enumerate_adapters(wgpu::Backends::all());
|
||||||
|
let adapter_count = adapter_iter.len();
|
||||||
|
let adapter = adapter_iter.into_iter()
|
||||||
|
.nth(adapter_index)
|
||||||
|
.unwrap_or_else(|| panic!("Tried to get index {adapter_index} adapter, but adapter list was only {adapter_count} long. Is .gpuconfig out of date?"));
|
||||||
|
} else {
|
||||||
|
assert_eq!(adapter_index, 0);
|
||||||
|
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Testing using adapter: {:#?}", adapter.get_info());
|
||||||
|
|
||||||
|
(adapter, surface_guard)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a wgpu device from a given adapter.
|
||||||
|
pub async fn initialize_device(
|
||||||
|
adapter: &Adapter,
|
||||||
|
features: Features,
|
||||||
|
limits: Limits,
|
||||||
|
) -> (Device, Queue) {
|
||||||
|
let bundle = adapter
|
||||||
|
.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
features,
|
||||||
|
limits,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match bundle {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(e) => panic!("Failed to initialize device: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a canvas for testing.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub fn initialize_html_canvas() -> web_sys::HtmlCanvasElement {
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
web_sys::window()
|
||||||
|
.and_then(|win| win.document())
|
||||||
|
.and_then(|doc| {
|
||||||
|
let canvas = doc.create_element("Canvas").unwrap();
|
||||||
|
canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
|
||||||
|
})
|
||||||
|
.expect("couldn't create canvas")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SurfaceGuard {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[allow(unused)]
|
||||||
|
canvas: web_sys::HtmlCanvasElement,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SurfaceGuard {
|
||||||
|
#[cfg(all(
|
||||||
|
target_arch = "wasm32",
|
||||||
|
any(target_os = "emscripten", feature = "webgl")
|
||||||
|
))]
|
||||||
|
pub(crate) fn check_for_unreported_errors(&self) -> bool {
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
self.canvas
|
||||||
|
.get_context("webgl2")
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<web_sys::WebGl2RenderingContext>()
|
||||||
|
.unwrap()
|
||||||
|
.get_error()
|
||||||
|
!= web_sys::WebGl2RenderingContext::NO_ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
624
tests/src/lib.rs
624
tests/src/lib.rs
@ -1,595 +1,27 @@
|
|||||||
//! This module contains common test-only code that needs to be shared between the examples and the tests.
|
//! Test utilities for the wgpu repository.
|
||||||
#![allow(dead_code)] // This module is used in a lot of contexts and only parts of it will be used
|
|
||||||
|
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
|
||||||
|
|
||||||
use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue, Surface};
|
|
||||||
use wgt::{Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits};
|
|
||||||
|
|
||||||
|
mod config;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
mod init;
|
||||||
mod isolation;
|
mod isolation;
|
||||||
|
pub mod native;
|
||||||
pub use self::image::ComparisonType;
|
mod params;
|
||||||
|
mod report;
|
||||||
const CANVAS_ID: &str = "test-canvas";
|
mod run;
|
||||||
|
|
||||||
async fn initialize_device(
|
|
||||||
adapter: &Adapter,
|
|
||||||
features: Features,
|
|
||||||
limits: Limits,
|
|
||||||
) -> (Device, Queue) {
|
|
||||||
let bundle = adapter
|
|
||||||
.request_device(
|
|
||||||
&DeviceDescriptor {
|
|
||||||
label: None,
|
|
||||||
features,
|
|
||||||
limits,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match bundle {
|
|
||||||
Ok(b) => b,
|
|
||||||
Err(e) => panic!("Failed to initialize device: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestingContext {
|
|
||||||
pub adapter: Adapter,
|
|
||||||
pub adapter_info: wgt::AdapterInfo,
|
|
||||||
pub adapter_downlevel_capabilities: wgt::DownlevelCapabilities,
|
|
||||||
pub device: Device,
|
|
||||||
pub device_features: wgt::Features,
|
|
||||||
pub device_limits: wgt::Limits,
|
|
||||||
pub queue: Queue,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lowest_downlevel_properties() -> DownlevelCapabilities {
|
|
||||||
DownlevelCapabilities {
|
|
||||||
flags: wgt::DownlevelFlags::empty(),
|
|
||||||
limits: wgt::DownlevelLimits {},
|
|
||||||
shader_model: wgt::ShaderModel::Sm2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Conditions under which a test should fail or be skipped.
|
|
||||||
///
|
|
||||||
/// By passing a `FailureCase` to [`TestParameters::expect_fail`], you can
|
|
||||||
/// mark a test as expected to fail under the indicated conditions. By
|
|
||||||
/// passing it to [`TestParameters::skip`], you can request that the
|
|
||||||
/// test be skipped altogether.
|
|
||||||
///
|
|
||||||
/// If a field is `None`, then that field does not restrict matches. For
|
|
||||||
/// example:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use wgpu_test::FailureCase;
|
|
||||||
/// FailureCase {
|
|
||||||
/// backends: Some(wgpu::Backends::DX11 | wgpu::Backends::DX12),
|
|
||||||
/// vendor: None,
|
|
||||||
/// adapter: Some("RTX"),
|
|
||||||
/// driver: None,
|
|
||||||
/// }
|
|
||||||
/// # ;
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// This applies to all cards with `"RTX'` in their name on either
|
|
||||||
/// Direct3D backend, no matter the vendor ID or driver name.
|
|
||||||
///
|
|
||||||
/// The strings given here need only appear as a substring in the
|
|
||||||
/// corresponding [`AdapterInfo`] fields. The comparison is
|
|
||||||
/// case-insensitive.
|
|
||||||
///
|
|
||||||
/// The default value of `FailureCase` applies to any test case. That
|
|
||||||
/// is, there are no criteria to constrain the match.
|
|
||||||
///
|
|
||||||
/// [`AdapterInfo`]: wgt::AdapterInfo
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct FailureCase {
|
|
||||||
/// Backends expected to fail, or `None` for any backend.
|
|
||||||
///
|
|
||||||
/// If this is `None`, or if the test is using one of the backends
|
|
||||||
/// in `backends`, then this `FailureCase` applies.
|
|
||||||
pub backends: Option<wgpu::Backends>,
|
|
||||||
|
|
||||||
/// Vendor expected to fail, or `None` for any vendor.
|
|
||||||
///
|
|
||||||
/// If `Some`, this must match [`AdapterInfo::device`], which is
|
|
||||||
/// usually the PCI device id. Otherwise, this `FailureCase`
|
|
||||||
/// applies regardless of vendor.
|
|
||||||
///
|
|
||||||
/// [`AdapterInfo::device`]: wgt::AdapterInfo::device
|
|
||||||
pub vendor: Option<u32>,
|
|
||||||
|
|
||||||
/// Name of adaper expected to fail, or `None` for any adapter name.
|
|
||||||
///
|
|
||||||
/// If this is `Some(s)` and `s` is a substring of
|
|
||||||
/// [`AdapterInfo::name`], then this `FailureCase` applies. If
|
|
||||||
/// this is `None`, the adapter name isn't considered.
|
|
||||||
///
|
|
||||||
/// [`AdapterInfo::name`]: wgt::AdapterInfo::name
|
|
||||||
pub adapter: Option<&'static str>,
|
|
||||||
|
|
||||||
/// Name of driver expected to fail, or `None` for any driver name.
|
|
||||||
///
|
|
||||||
/// If this is `Some(s)` and `s` is a substring of
|
|
||||||
/// [`AdapterInfo::driver`], then this `FailureCase` applies. If
|
|
||||||
/// this is `None`, the driver name isn't considered.
|
|
||||||
///
|
|
||||||
/// [`AdapterInfo::driver`]: wgt::AdapterInfo::driver
|
|
||||||
pub driver: Option<&'static str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FailureCase {
|
|
||||||
/// This case applies to all tests.
|
|
||||||
pub fn always() -> Self {
|
|
||||||
FailureCase::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This case applies to no tests.
|
|
||||||
pub fn never() -> Self {
|
|
||||||
FailureCase {
|
|
||||||
backends: Some(wgpu::Backends::empty()),
|
|
||||||
..FailureCase::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests running on any of the given backends.
|
|
||||||
pub fn backend(backends: wgpu::Backends) -> Self {
|
|
||||||
FailureCase {
|
|
||||||
backends: Some(backends),
|
|
||||||
..FailureCase::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests running on `adapter`.
|
|
||||||
///
|
|
||||||
/// For this case to apply, the `adapter` string must appear as a substring
|
|
||||||
/// of the adapter's [`AdapterInfo::name`]. The comparison is
|
|
||||||
/// case-insensitive.
|
|
||||||
///
|
|
||||||
/// [`AdapterInfo::name`]: wgt::AdapterInfo::name
|
|
||||||
pub fn adapter(adapter: &'static str) -> Self {
|
|
||||||
FailureCase {
|
|
||||||
adapter: Some(adapter),
|
|
||||||
..FailureCase::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests running on `backend` and `adapter`.
|
|
||||||
///
|
|
||||||
/// For this case to apply, the test must be using an adapter for one of the
|
|
||||||
/// given `backend` bits, and `adapter` string must appear as a substring of
|
|
||||||
/// the adapter's [`AdapterInfo::name`]. The string comparison is
|
|
||||||
/// case-insensitive.
|
|
||||||
///
|
|
||||||
/// [`AdapterInfo::name`]: wgt::AdapterInfo::name
|
|
||||||
pub fn backend_adapter(backends: wgpu::Backends, adapter: &'static str) -> Self {
|
|
||||||
FailureCase {
|
|
||||||
backends: Some(backends),
|
|
||||||
adapter: Some(adapter),
|
|
||||||
..FailureCase::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests running under WebGL.
|
|
||||||
///
|
|
||||||
/// Because of wasm's limited ability to recover from errors, we
|
|
||||||
/// usually need to skip the test altogether if it's not
|
|
||||||
/// supported, so this should be usually used with
|
|
||||||
/// [`TestParameters::skip`].
|
|
||||||
pub fn webgl2() -> Self {
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let case = FailureCase::backend(wgpu::Backends::GL);
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let case = FailureCase::never();
|
|
||||||
case
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests running on the MoltenVK Vulkan driver on macOS.
|
|
||||||
pub fn molten_vk() -> Self {
|
|
||||||
FailureCase {
|
|
||||||
backends: Some(wgpu::Backends::VULKAN),
|
|
||||||
driver: Some("MoltenVK"),
|
|
||||||
..FailureCase::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test whether `self` applies to `info`.
|
|
||||||
///
|
|
||||||
/// If it does, return a `FailureReasons` whose set bits indicate
|
|
||||||
/// why. If it doesn't, return `None`.
|
|
||||||
///
|
|
||||||
/// The caller is responsible for converting the string-valued
|
|
||||||
/// fields of `info` to lower case, to ensure case-insensitive
|
|
||||||
/// matching.
|
|
||||||
fn applies_to(&self, info: &wgt::AdapterInfo) -> Option<FailureReasons> {
|
|
||||||
let mut reasons = FailureReasons::empty();
|
|
||||||
|
|
||||||
if let Some(backends) = self.backends {
|
|
||||||
if !backends.contains(wgpu::Backends::from(info.backend)) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
reasons.set(FailureReasons::BACKEND, true);
|
|
||||||
}
|
|
||||||
if let Some(vendor) = self.vendor {
|
|
||||||
if vendor != info.vendor {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
reasons.set(FailureReasons::VENDOR, true);
|
|
||||||
}
|
|
||||||
if let Some(adapter) = self.adapter {
|
|
||||||
let adapter = adapter.to_lowercase();
|
|
||||||
if !info.name.contains(&adapter) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
reasons.set(FailureReasons::ADAPTER, true);
|
|
||||||
}
|
|
||||||
if let Some(driver) = self.driver {
|
|
||||||
let driver = driver.to_lowercase();
|
|
||||||
if !info.driver.contains(&driver) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
reasons.set(FailureReasons::DRIVER, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got this far but no specific reasons were triggered, then this
|
|
||||||
// must be a wildcard.
|
|
||||||
if reasons.is_empty() {
|
|
||||||
Some(FailureReasons::ALWAYS)
|
|
||||||
} else {
|
|
||||||
Some(reasons)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This information determines if a test should run.
|
|
||||||
pub struct TestParameters {
|
|
||||||
pub required_features: Features,
|
|
||||||
pub required_downlevel_properties: DownlevelCapabilities,
|
|
||||||
pub required_limits: Limits,
|
|
||||||
|
|
||||||
/// Conditions under which this test should be skipped.
|
|
||||||
pub skips: Vec<FailureCase>,
|
|
||||||
|
|
||||||
/// Conditions under which this test should be run, but is expected to fail.
|
|
||||||
pub failures: Vec<FailureCase>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TestParameters {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
required_features: Features::empty(),
|
|
||||||
required_downlevel_properties: lowest_downlevel_properties(),
|
|
||||||
required_limits: Limits::downlevel_webgl2_defaults(),
|
|
||||||
skips: Vec::new(),
|
|
||||||
failures: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct FailureReasons: u8 {
|
|
||||||
const BACKEND = 1 << 0;
|
|
||||||
const VENDOR = 1 << 1;
|
|
||||||
const ADAPTER = 1 << 2;
|
|
||||||
const DRIVER = 1 << 3;
|
|
||||||
const ALWAYS = 1 << 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builder pattern to make it easier
|
|
||||||
impl TestParameters {
|
|
||||||
/// Set of common features that most internal tests require for readback.
|
|
||||||
pub fn test_features_limits(self) -> Self {
|
|
||||||
self.features(Features::MAPPABLE_PRIMARY_BUFFERS | Features::VERTEX_WRITABLE_STORAGE)
|
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the list of features this test requires.
|
|
||||||
pub fn features(mut self, features: Features) -> Self {
|
|
||||||
self.required_features |= features;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self {
|
|
||||||
self.required_downlevel_properties.flags |= downlevel_flags;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the limits needed for the test.
|
|
||||||
pub fn limits(mut self, limits: Limits) -> Self {
|
|
||||||
self.required_limits = limits;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the test as always failing, but not to be skipped.
|
|
||||||
pub fn expect_fail(mut self, when: FailureCase) -> Self {
|
|
||||||
self.failures.push(when);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the test as always failing, and needing to be skipped.
|
|
||||||
pub fn skip(mut self, when: FailureCase) -> Self {
|
|
||||||
self.skips.push(when);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(TestingContext)) {
|
|
||||||
// We don't actually care if it fails
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let _ = env_logger::try_init();
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let _ = console_log::init_with_level(log::Level::Info);
|
|
||||||
|
|
||||||
let _test_guard = isolation::OneTestPerProcessGuard::new();
|
|
||||||
|
|
||||||
let (adapter, _surface_guard) = initialize_adapter();
|
|
||||||
|
|
||||||
let adapter_info = adapter.get_info();
|
|
||||||
|
|
||||||
// Produce a lower-case version of the adapter info, for comparison against
|
|
||||||
// `parameters.skips` and `parameters.failures`.
|
|
||||||
let adapter_lowercase_info = wgt::AdapterInfo {
|
|
||||||
name: adapter_info.name.to_lowercase(),
|
|
||||||
driver: adapter_info.driver.to_lowercase(),
|
|
||||||
..adapter_info.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let adapter_features = adapter.features();
|
|
||||||
let adapter_limits = adapter.limits();
|
|
||||||
let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities();
|
|
||||||
|
|
||||||
let missing_features = parameters.required_features - adapter_features;
|
|
||||||
if !missing_features.is_empty() {
|
|
||||||
log::info!("TEST SKIPPED: MISSING FEATURES {:?}", missing_features);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !parameters.required_limits.check_limits(&adapter_limits) {
|
|
||||||
log::info!("TEST SKIPPED: LIMIT TOO LOW");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let missing_downlevel_flags =
|
|
||||||
parameters.required_downlevel_properties.flags - adapter_downlevel_capabilities.flags;
|
|
||||||
if !missing_downlevel_flags.is_empty() {
|
|
||||||
log::info!(
|
|
||||||
"TEST SKIPPED: MISSING DOWNLEVEL FLAGS {:?}",
|
|
||||||
missing_downlevel_flags
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if adapter_downlevel_capabilities.shader_model
|
|
||||||
< parameters.required_downlevel_properties.shader_model
|
|
||||||
{
|
|
||||||
log::info!(
|
|
||||||
"TEST SKIPPED: LOW SHADER MODEL {:?}",
|
|
||||||
adapter_downlevel_capabilities.shader_model
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (device, queue) = pollster::block_on(initialize_device(
|
|
||||||
&adapter,
|
|
||||||
parameters.required_features,
|
|
||||||
parameters.required_limits.clone(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let context = TestingContext {
|
|
||||||
adapter,
|
|
||||||
adapter_info,
|
|
||||||
adapter_downlevel_capabilities,
|
|
||||||
device,
|
|
||||||
device_features: parameters.required_features,
|
|
||||||
device_limits: parameters.required_limits,
|
|
||||||
queue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if we should skip the test altogether.
|
|
||||||
if let Some(skip_reason) = parameters
|
|
||||||
.skips
|
|
||||||
.iter()
|
|
||||||
.find_map(|case| case.applies_to(&adapter_lowercase_info))
|
|
||||||
{
|
|
||||||
log::info!("EXPECTED TEST FAILURE SKIPPED: {:?}", skip_reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if we expect this test to fail, and if so, why.
|
|
||||||
let expected_failure_reason = parameters
|
|
||||||
.failures
|
|
||||||
.iter()
|
|
||||||
.find_map(|case| case.applies_to(&adapter_lowercase_info));
|
|
||||||
|
|
||||||
// Run the test, and catch panics (possibly due to failed assertions).
|
|
||||||
let panicked = catch_unwind(AssertUnwindSafe(|| test_function(context))).is_err();
|
|
||||||
|
|
||||||
// Check whether any validation errors were reported during the test run.
|
|
||||||
cfg_if::cfg_if!(
|
|
||||||
if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] {
|
|
||||||
let canary_set = wgpu::hal::VALIDATION_CANARY.get_and_reset();
|
|
||||||
} else {
|
|
||||||
let canary_set = _surface_guard.unwrap().check_for_unreported_errors();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Summarize reasons for actual failure, if any.
|
|
||||||
let failure_cause = match (panicked, canary_set) {
|
|
||||||
(true, true) => Some("PANIC AND VALIDATION ERROR"),
|
|
||||||
(true, false) => Some("PANIC"),
|
|
||||||
(false, true) => Some("VALIDATION ERROR"),
|
|
||||||
(false, false) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compare actual results against expectations.
|
|
||||||
match (failure_cause, expected_failure_reason) {
|
|
||||||
// The test passed, as expected.
|
|
||||||
(None, None) => {}
|
|
||||||
// The test failed unexpectedly.
|
|
||||||
(Some(cause), None) => {
|
|
||||||
panic!("UNEXPECTED TEST FAILURE DUE TO {cause}")
|
|
||||||
}
|
|
||||||
// The test passed unexpectedly.
|
|
||||||
(None, Some(reason)) => {
|
|
||||||
panic!("UNEXPECTED TEST PASS: {reason:?}");
|
|
||||||
}
|
|
||||||
// The test failed, as expected.
|
|
||||||
(Some(cause), Some(reason_expected)) => {
|
|
||||||
log::info!(
|
|
||||||
"EXPECTED FAILURE DUE TO {} (expected because of {:?})",
|
|
||||||
cause,
|
|
||||||
reason_expected
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize_adapter() -> (Adapter, Option<SurfaceGuard>) {
|
|
||||||
let instance = initialize_instance();
|
|
||||||
let surface_guard: Option<SurfaceGuard>;
|
|
||||||
let compatible_surface;
|
|
||||||
|
|
||||||
// Create a canvas iff we need a WebGL2RenderingContext to have a working device.
|
|
||||||
#[cfg(not(all(
|
|
||||||
target_arch = "wasm32",
|
|
||||||
any(target_os = "emscripten", feature = "webgl")
|
|
||||||
)))]
|
|
||||||
{
|
|
||||||
surface_guard = None;
|
|
||||||
compatible_surface = None;
|
|
||||||
}
|
|
||||||
#[cfg(all(
|
|
||||||
target_arch = "wasm32",
|
|
||||||
any(target_os = "emscripten", feature = "webgl")
|
|
||||||
))]
|
|
||||||
{
|
|
||||||
// On wasm, append a canvas to the document body for initializing the adapter
|
|
||||||
let canvas = create_html_canvas();
|
|
||||||
|
|
||||||
// We use raw_window_handle here, as create_surface_from_canvas is not implemented on emscripten.
|
|
||||||
struct WindowHandle;
|
|
||||||
unsafe impl raw_window_handle::HasRawWindowHandle for WindowHandle {
|
|
||||||
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
|
|
||||||
raw_window_handle::RawWindowHandle::Web({
|
|
||||||
let mut handle = raw_window_handle::WebWindowHandle::empty();
|
|
||||||
handle.id = 1;
|
|
||||||
handle
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsafe impl raw_window_handle::HasRawDisplayHandle for WindowHandle {
|
|
||||||
fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
|
|
||||||
raw_window_handle::RawDisplayHandle::Web(
|
|
||||||
raw_window_handle::WebDisplayHandle::empty(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let surface = unsafe {
|
|
||||||
instance
|
|
||||||
.create_surface(&WindowHandle)
|
|
||||||
.expect("could not create surface from canvas")
|
|
||||||
};
|
|
||||||
|
|
||||||
surface_guard = Some(SurfaceGuard { canvas });
|
|
||||||
|
|
||||||
compatible_surface = Some(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
let compatible_surface: Option<&Surface> = compatible_surface.as_ref();
|
|
||||||
let adapter = pollster::block_on(wgpu::util::initialize_adapter_from_env_or_default(
|
|
||||||
&instance,
|
|
||||||
compatible_surface,
|
|
||||||
))
|
|
||||||
.expect("could not find suitable adapter on the system");
|
|
||||||
|
|
||||||
(adapter, surface_guard)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize_instance() -> Instance {
|
|
||||||
let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all);
|
|
||||||
let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default();
|
|
||||||
let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default();
|
|
||||||
Instance::new(wgpu::InstanceDescriptor {
|
|
||||||
backends,
|
|
||||||
flags: wgpu::InstanceFlags::debugging().with_env(),
|
|
||||||
dx12_shader_compiler,
|
|
||||||
gles_minor_version,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public because it is used by tests of interacting with canvas
|
|
||||||
pub struct SurfaceGuard {
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
pub canvas: web_sys::HtmlCanvasElement,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SurfaceGuard {
|
|
||||||
fn check_for_unreported_errors(&self) -> bool {
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(all(target_arch = "wasm32", any(target_os = "emscripten", feature = "webgl")))] {
|
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
|
|
||||||
self.canvas
|
|
||||||
.get_context("webgl2")
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.dyn_into::<web_sys::WebGl2RenderingContext>()
|
|
||||||
.unwrap()
|
|
||||||
.get_error()
|
|
||||||
!= web_sys::WebGl2RenderingContext::NO_ERROR
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(
|
|
||||||
target_arch = "wasm32",
|
|
||||||
any(target_os = "emscripten", feature = "webgl")
|
|
||||||
))]
|
|
||||||
impl Drop for SurfaceGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
delete_html_canvas();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub fn create_html_canvas() -> web_sys::HtmlCanvasElement {
|
pub use init::initialize_html_canvas;
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
|
|
||||||
web_sys::window()
|
pub use self::image::ComparisonType;
|
||||||
.and_then(|win| win.document())
|
pub use config::GpuTestConfiguration;
|
||||||
.and_then(|doc| {
|
#[doc(hidden)]
|
||||||
let body = doc.body().unwrap();
|
pub use ctor::ctor;
|
||||||
let canvas = doc.create_element("Canvas").unwrap();
|
pub use init::{initialize_adapter, initialize_device, initialize_instance};
|
||||||
canvas.set_attribute("data-raw-handle", "1").unwrap();
|
pub use params::{FailureCase, FailureReasons, TestParameters};
|
||||||
canvas.set_id(CANVAS_ID);
|
pub use run::{execute_test, TestingContext};
|
||||||
body.append_child(&canvas).unwrap();
|
pub use wgpu_macros::gpu_test;
|
||||||
canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
|
|
||||||
})
|
|
||||||
.expect("couldn't append canvas to document body")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(
|
/// Run some code in an error scope and assert that validation fails.
|
||||||
target_arch = "wasm32",
|
|
||||||
any(target_os = "emscripten", feature = "webgl")
|
|
||||||
))]
|
|
||||||
fn delete_html_canvas() {
|
|
||||||
if let Some(document) = web_sys::window().and_then(|win| win.document()) {
|
|
||||||
if let Some(element) = document.get_element_by_id(CANVAS_ID) {
|
|
||||||
element.remove();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run some code in an error scope and assert that validation fails.
|
|
||||||
pub fn fail<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
|
pub fn fail<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
|
||||||
device.push_error_scope(wgpu::ErrorFilter::Validation);
|
device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||||
let result = callback();
|
let result = callback();
|
||||||
@ -598,7 +30,7 @@ pub fn fail<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run some code in an error scope and assert that validation succeeds.
|
/// Run some code in an error scope and assert that validation succeeds.
|
||||||
pub fn valid<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
|
pub fn valid<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
|
||||||
device.push_error_scope(wgpu::ErrorFilter::Validation);
|
device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||||
let result = callback();
|
let result = callback();
|
||||||
@ -607,8 +39,8 @@ pub fn valid<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run some code in an error scope and assert that validation succeeds or fails depending on the
|
/// Run some code in an error scope and assert that validation succeeds or fails depending on the
|
||||||
// provided `should_fail` boolean.
|
/// provided `should_fail` boolean.
|
||||||
pub fn fail_if<T>(device: &wgpu::Device, should_fail: bool, callback: impl FnOnce() -> T) -> T {
|
pub fn fail_if<T>(device: &wgpu::Device, should_fail: bool, callback: impl FnOnce() -> T) -> T {
|
||||||
if should_fail {
|
if should_fail {
|
||||||
fail(device, callback)
|
fail(device, callback)
|
||||||
@ -616,3 +48,19 @@ pub fn fail_if<T>(device: &wgpu::Device, should_fail: bool, callback: impl FnOnc
|
|||||||
valid(device, callback)
|
valid(device, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds the necissary main function for our gpu test harness.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! gpu_test_main {
|
||||||
|
() => {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn main() -> $crate::native::MainResult {
|
||||||
|
$crate::native::main()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
104
tests/src/native.rs
Normal file
104
tests/src/native.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
|
//! Infrastructure for the native, `cargo-nextest` based harness.
|
||||||
|
//!
|
||||||
|
//! This is largly used by [`gpu_test_main`](crate::gpu_test_main) and [`gpu_test`](crate::gpu_test).
|
||||||
|
|
||||||
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::GpuTestConfiguration, params::TestInfo, report::AdapterReport, run::execute_test,
|
||||||
|
};
|
||||||
|
|
||||||
|
type NativeTestFuture = Pin<Box<dyn Future<Output = ()> + Send + Sync>>;
|
||||||
|
|
||||||
|
struct NativeTest {
|
||||||
|
name: String,
|
||||||
|
future: NativeTestFuture,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NativeTest {
|
||||||
|
fn from_configuration(
|
||||||
|
config: GpuTestConfiguration,
|
||||||
|
adapter: &AdapterReport,
|
||||||
|
adapter_index: usize,
|
||||||
|
) -> Self {
|
||||||
|
let backend = adapter.info.backend;
|
||||||
|
let device_name = &adapter.info.name;
|
||||||
|
|
||||||
|
let test_info = TestInfo::from_configuration(&config, adapter);
|
||||||
|
|
||||||
|
let full_name = format!(
|
||||||
|
"[{running_msg}] [{backend:?}/{device_name}/{adapter_index}] {base_name}",
|
||||||
|
running_msg = test_info.running_msg,
|
||||||
|
base_name = config.name,
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
name: full_name,
|
||||||
|
future: Box::pin(async move {
|
||||||
|
// Enable metal validation layers if we're running on metal.
|
||||||
|
//
|
||||||
|
// This is a process-wide setting as it's via environment variable, but all
|
||||||
|
// tests are run in separate processes.
|
||||||
|
//
|
||||||
|
// We don't do this in the instance initializer as we don't want to enable
|
||||||
|
// validation layers for the entire process, or other instances.
|
||||||
|
//
|
||||||
|
// We do not enable metal validation when running on moltenvk.
|
||||||
|
let metal_validation = backend == wgpu::Backend::Metal;
|
||||||
|
|
||||||
|
let env_value = if metal_validation { "1" } else { "0" };
|
||||||
|
std::env::set_var("MTL_DEBUG_LAYER", env_value);
|
||||||
|
std::env::set_var("MTL_SHADER_VALIDATION", env_value);
|
||||||
|
|
||||||
|
execute_test(config, Some(test_info), adapter_index).await;
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_trial(self) -> libtest_mimic::Trial {
|
||||||
|
libtest_mimic::Trial::test(self.name, || {
|
||||||
|
pollster::block_on(self.future);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub static TEST_LIST: Mutex<Vec<crate::GpuTestConfiguration>> = Mutex::new(Vec::new());
|
||||||
|
|
||||||
|
/// Return value for the main function.
|
||||||
|
pub type MainResult = anyhow::Result<()>;
|
||||||
|
|
||||||
|
/// Main function that runs every gpu function once for every adapter on the system.
|
||||||
|
pub fn main() -> MainResult {
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
|
use crate::report::GpuReport;
|
||||||
|
|
||||||
|
let config_text =
|
||||||
|
&std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR")))
|
||||||
|
.context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")?;
|
||||||
|
let report = GpuReport::from_json(config_text).context("Could not pare .gpuconfig JSON")?;
|
||||||
|
|
||||||
|
let mut test_guard = TEST_LIST.lock();
|
||||||
|
execute_native(test_guard.drain(..).flat_map(|test| {
|
||||||
|
report
|
||||||
|
.devices
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(adapter_index, adapter)| {
|
||||||
|
NativeTest::from_configuration(test.clone(), adapter, adapter_index)
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_native(tests: impl IntoIterator<Item = NativeTest>) {
|
||||||
|
let args = libtest_mimic::Arguments::from_args();
|
||||||
|
let trials = tests.into_iter().map(NativeTest::into_trial).collect();
|
||||||
|
|
||||||
|
libtest_mimic::run(&args, trials).exit_if_failed();
|
||||||
|
}
|
||||||
363
tests/src/params.rs
Normal file
363
tests/src/params.rs
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use wgt::{DownlevelCapabilities, DownlevelFlags, Features, Limits};
|
||||||
|
|
||||||
|
use crate::{report::AdapterReport, GpuTestConfiguration};
|
||||||
|
|
||||||
|
/// Conditions under which a test should fail or be skipped.
|
||||||
|
///
|
||||||
|
/// By passing a `FailureCase` to [`TestParameters::expect_fail`], you can
|
||||||
|
/// mark a test as expected to fail under the indicated conditions. By
|
||||||
|
/// passing it to [`TestParameters::skip`], you can request that the
|
||||||
|
/// test be skipped altogether.
|
||||||
|
///
|
||||||
|
/// If a field is `None`, then that field does not restrict matches. For
|
||||||
|
/// example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use wgpu_test::FailureCase;
|
||||||
|
/// FailureCase {
|
||||||
|
/// backends: Some(wgpu::Backends::DX11 | wgpu::Backends::DX12),
|
||||||
|
/// vendor: None,
|
||||||
|
/// adapter: Some("RTX"),
|
||||||
|
/// driver: None,
|
||||||
|
/// }
|
||||||
|
/// # ;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This applies to all cards with `"RTX'` in their name on either
|
||||||
|
/// Direct3D backend, no matter the vendor ID or driver name.
|
||||||
|
///
|
||||||
|
/// The strings given here need only appear as a substring in the
|
||||||
|
/// corresponding [`AdapterInfo`] fields. The comparison is
|
||||||
|
/// case-insensitive.
|
||||||
|
///
|
||||||
|
/// The default value of `FailureCase` applies to any test case. That
|
||||||
|
/// is, there are no criteria to constrain the match.
|
||||||
|
///
|
||||||
|
/// [`AdapterInfo`]: wgt::AdapterInfo
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct FailureCase {
|
||||||
|
/// Backends expected to fail, or `None` for any backend.
|
||||||
|
///
|
||||||
|
/// If this is `None`, or if the test is using one of the backends
|
||||||
|
/// in `backends`, then this `FailureCase` applies.
|
||||||
|
pub backends: Option<wgpu::Backends>,
|
||||||
|
|
||||||
|
/// Vendor expected to fail, or `None` for any vendor.
|
||||||
|
///
|
||||||
|
/// If `Some`, this must match [`AdapterInfo::device`], which is
|
||||||
|
/// usually the PCI device id. Otherwise, this `FailureCase`
|
||||||
|
/// applies regardless of vendor.
|
||||||
|
///
|
||||||
|
/// [`AdapterInfo::device`]: wgt::AdapterInfo::device
|
||||||
|
pub vendor: Option<u32>,
|
||||||
|
|
||||||
|
/// Name of adaper expected to fail, or `None` for any adapter name.
|
||||||
|
///
|
||||||
|
/// If this is `Some(s)` and `s` is a substring of
|
||||||
|
/// [`AdapterInfo::name`], then this `FailureCase` applies. If
|
||||||
|
/// this is `None`, the adapter name isn't considered.
|
||||||
|
///
|
||||||
|
/// [`AdapterInfo::name`]: wgt::AdapterInfo::name
|
||||||
|
pub adapter: Option<&'static str>,
|
||||||
|
|
||||||
|
/// Name of driver expected to fail, or `None` for any driver name.
|
||||||
|
///
|
||||||
|
/// If this is `Some(s)` and `s` is a substring of
|
||||||
|
/// [`AdapterInfo::driver`], then this `FailureCase` applies. If
|
||||||
|
/// this is `None`, the driver name isn't considered.
|
||||||
|
///
|
||||||
|
/// [`AdapterInfo::driver`]: wgt::AdapterInfo::driver
|
||||||
|
pub driver: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FailureCase {
|
||||||
|
/// This case applies to all tests.
|
||||||
|
pub fn always() -> Self {
|
||||||
|
FailureCase::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This case applies to no tests.
|
||||||
|
pub fn never() -> Self {
|
||||||
|
FailureCase {
|
||||||
|
backends: Some(wgpu::Backends::empty()),
|
||||||
|
..FailureCase::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests running on any of the given backends.
|
||||||
|
pub fn backend(backends: wgpu::Backends) -> Self {
|
||||||
|
FailureCase {
|
||||||
|
backends: Some(backends),
|
||||||
|
..FailureCase::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests running on `adapter`.
|
||||||
|
///
|
||||||
|
/// For this case to apply, the `adapter` string must appear as a substring
|
||||||
|
/// of the adapter's [`AdapterInfo::name`]. The comparison is
|
||||||
|
/// case-insensitive.
|
||||||
|
///
|
||||||
|
/// [`AdapterInfo::name`]: wgt::AdapterInfo::name
|
||||||
|
pub fn adapter(adapter: &'static str) -> Self {
|
||||||
|
FailureCase {
|
||||||
|
adapter: Some(adapter),
|
||||||
|
..FailureCase::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests running on `backend` and `adapter`.
|
||||||
|
///
|
||||||
|
/// For this case to apply, the test must be using an adapter for one of the
|
||||||
|
/// given `backend` bits, and `adapter` string must appear as a substring of
|
||||||
|
/// the adapter's [`AdapterInfo::name`]. The string comparison is
|
||||||
|
/// case-insensitive.
|
||||||
|
///
|
||||||
|
/// [`AdapterInfo::name`]: wgt::AdapterInfo::name
|
||||||
|
pub fn backend_adapter(backends: wgpu::Backends, adapter: &'static str) -> Self {
|
||||||
|
FailureCase {
|
||||||
|
backends: Some(backends),
|
||||||
|
adapter: Some(adapter),
|
||||||
|
..FailureCase::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests running under WebGL.
|
||||||
|
///
|
||||||
|
/// Because of wasm's limited ability to recover from errors, we
|
||||||
|
/// usually need to skip the test altogether if it's not
|
||||||
|
/// supported, so this should be usually used with
|
||||||
|
/// [`TestParameters::skip`].
|
||||||
|
pub fn webgl2() -> Self {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let case = FailureCase::backend(wgpu::Backends::GL);
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let case = FailureCase::never();
|
||||||
|
case
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests running on the MoltenVK Vulkan driver on macOS.
|
||||||
|
pub fn molten_vk() -> Self {
|
||||||
|
FailureCase {
|
||||||
|
backends: Some(wgpu::Backends::VULKAN),
|
||||||
|
driver: Some("MoltenVK"),
|
||||||
|
..FailureCase::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test whether `self` applies to `info`.
|
||||||
|
///
|
||||||
|
/// If it does, return a `FailureReasons` whose set bits indicate
|
||||||
|
/// why. If it doesn't, return `None`.
|
||||||
|
///
|
||||||
|
/// The caller is responsible for converting the string-valued
|
||||||
|
/// fields of `info` to lower case, to ensure case-insensitive
|
||||||
|
/// matching.
|
||||||
|
pub(crate) fn applies_to(&self, info: &wgt::AdapterInfo) -> Option<FailureReasons> {
|
||||||
|
let mut reasons = FailureReasons::empty();
|
||||||
|
|
||||||
|
if let Some(backends) = self.backends {
|
||||||
|
if !backends.contains(wgpu::Backends::from(info.backend)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
reasons.set(FailureReasons::BACKEND, true);
|
||||||
|
}
|
||||||
|
if let Some(vendor) = self.vendor {
|
||||||
|
if vendor != info.vendor {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
reasons.set(FailureReasons::VENDOR, true);
|
||||||
|
}
|
||||||
|
if let Some(adapter) = self.adapter {
|
||||||
|
let adapter = adapter.to_lowercase();
|
||||||
|
if !info.name.contains(&adapter) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
reasons.set(FailureReasons::ADAPTER, true);
|
||||||
|
}
|
||||||
|
if let Some(driver) = self.driver {
|
||||||
|
let driver = driver.to_lowercase();
|
||||||
|
if !info.driver.contains(&driver) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
reasons.set(FailureReasons::DRIVER, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got this far but no specific reasons were triggered, then this
|
||||||
|
// must be a wildcard.
|
||||||
|
if reasons.is_empty() {
|
||||||
|
Some(FailureReasons::ALWAYS)
|
||||||
|
} else {
|
||||||
|
Some(reasons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOWEST_DOWNLEVEL_PROPERTIES: wgpu::DownlevelCapabilities = DownlevelCapabilities {
|
||||||
|
flags: wgt::DownlevelFlags::empty(),
|
||||||
|
limits: wgt::DownlevelLimits {},
|
||||||
|
shader_model: wgt::ShaderModel::Sm2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This information determines if a test should run.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TestParameters {
|
||||||
|
pub required_features: Features,
|
||||||
|
pub required_downlevel_caps: DownlevelCapabilities,
|
||||||
|
pub required_limits: Limits,
|
||||||
|
|
||||||
|
/// Conditions under which this test should be skipped.
|
||||||
|
pub skips: Vec<FailureCase>,
|
||||||
|
|
||||||
|
/// Conditions under which this test should be run, but is expected to fail.
|
||||||
|
pub failures: Vec<FailureCase>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TestParameters {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
required_features: Features::empty(),
|
||||||
|
required_downlevel_caps: LOWEST_DOWNLEVEL_PROPERTIES,
|
||||||
|
required_limits: Limits::downlevel_webgl2_defaults(),
|
||||||
|
skips: Vec::new(),
|
||||||
|
failures: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
/// Ways that a given test can be expected to fail.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct FailureReasons: u8 {
|
||||||
|
const BACKEND = 1 << 0;
|
||||||
|
const VENDOR = 1 << 1;
|
||||||
|
const ADAPTER = 1 << 2;
|
||||||
|
const DRIVER = 1 << 3;
|
||||||
|
const ALWAYS = 1 << 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder pattern to make it easier
|
||||||
|
impl TestParameters {
|
||||||
|
/// Set of common features that most internal tests require for readback.
|
||||||
|
pub fn test_features_limits(self) -> Self {
|
||||||
|
self.features(Features::MAPPABLE_PRIMARY_BUFFERS | Features::VERTEX_WRITABLE_STORAGE)
|
||||||
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the list of features this test requires.
|
||||||
|
pub fn features(mut self, features: Features) -> Self {
|
||||||
|
self.required_features |= features;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downlevel_flags(mut self, downlevel_flags: DownlevelFlags) -> Self {
|
||||||
|
self.required_downlevel_caps.flags |= downlevel_flags;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the limits needed for the test.
|
||||||
|
pub fn limits(mut self, limits: Limits) -> Self {
|
||||||
|
self.required_limits = limits;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the test as always failing, but not to be skipped.
|
||||||
|
pub fn expect_fail(mut self, when: FailureCase) -> Self {
|
||||||
|
self.failures.push(when);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the test as always failing, and needing to be skipped.
|
||||||
|
pub fn skip(mut self, when: FailureCase) -> Self {
|
||||||
|
self.skips.push(when);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about a test, including if if it should be skipped.
|
||||||
|
pub struct TestInfo {
|
||||||
|
pub skip: bool,
|
||||||
|
pub expected_failure_reason: Option<FailureReasons>,
|
||||||
|
pub running_msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestInfo {
|
||||||
|
pub(crate) fn from_configuration(test: &GpuTestConfiguration, adapter: &AdapterReport) -> Self {
|
||||||
|
// Figure out if we should skip the test and if so, why.
|
||||||
|
let mut skipped_reasons: ArrayVec<_, 4> = ArrayVec::new();
|
||||||
|
let missing_features = test.params.required_features - adapter.features;
|
||||||
|
if !missing_features.is_empty() {
|
||||||
|
skipped_reasons.push("Features");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !test.params.required_limits.check_limits(&adapter.limits) {
|
||||||
|
skipped_reasons.push("Limits");
|
||||||
|
}
|
||||||
|
|
||||||
|
let missing_downlevel_flags =
|
||||||
|
test.params.required_downlevel_caps.flags - adapter.downlevel_caps.flags;
|
||||||
|
if !missing_downlevel_flags.is_empty() {
|
||||||
|
skipped_reasons.push("Downlevel Flags");
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.params.required_downlevel_caps.shader_model > adapter.downlevel_caps.shader_model {
|
||||||
|
skipped_reasons.push("Shader Model");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce a lower-case version of the adapter info, for comparison against
|
||||||
|
// `parameters.skips` and `parameters.failures`.
|
||||||
|
let adapter_lowercase_info = wgt::AdapterInfo {
|
||||||
|
name: adapter.info.name.to_lowercase(),
|
||||||
|
driver: adapter.info.driver.to_lowercase(),
|
||||||
|
..adapter.info.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if we should skip the test altogether.
|
||||||
|
let skip_reason = test
|
||||||
|
.params
|
||||||
|
.skips
|
||||||
|
.iter()
|
||||||
|
.find_map(|case| case.applies_to(&adapter_lowercase_info));
|
||||||
|
|
||||||
|
let expected_failure_reason = test
|
||||||
|
.params
|
||||||
|
.failures
|
||||||
|
.iter()
|
||||||
|
.find_map(|case| case.applies_to(&adapter_lowercase_info));
|
||||||
|
|
||||||
|
let mut skip = false;
|
||||||
|
let running_msg = if let Some(reasons) = skip_reason {
|
||||||
|
skip = true;
|
||||||
|
|
||||||
|
let names: ArrayVec<_, 4> = reasons.iter_names().map(|(name, _)| name).collect();
|
||||||
|
let names_text = names.join(" | ");
|
||||||
|
|
||||||
|
format!("Skipped Failure: {}", names_text)
|
||||||
|
} else if !skipped_reasons.is_empty() {
|
||||||
|
skip = true;
|
||||||
|
format!("Skipped: {}", skipped_reasons.join(" | "))
|
||||||
|
} else if let Some(failure_resasons) = expected_failure_reason {
|
||||||
|
if cfg!(target_arch = "wasm32") {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let names: ArrayVec<_, 4> = failure_resasons
|
||||||
|
.iter_names()
|
||||||
|
.map(|(name, _)| name)
|
||||||
|
.collect();
|
||||||
|
let names_text = names.join(" | ");
|
||||||
|
|
||||||
|
format!("Executed Failure: {}", names_text)
|
||||||
|
} else {
|
||||||
|
String::from("Executed")
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
skip,
|
||||||
|
expected_failure_reason,
|
||||||
|
running_msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
tests/src/report.rs
Normal file
52
tests/src/report.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use wgpu::{
|
||||||
|
AdapterInfo, DownlevelCapabilities, Features, Limits, TextureFormat, TextureFormatFeatures,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Report specifying the capabilities of the GPUs on the system.
|
||||||
|
///
|
||||||
|
/// Must be synchronized with the definition on wgpu-info/src/report.rs.
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct GpuReport {
|
||||||
|
#[cfg_attr(target_arch = "wasm32", allow(unused))]
|
||||||
|
pub devices: Vec<AdapterReport>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpuReport {
|
||||||
|
#[cfg_attr(target_arch = "wasm32", allow(unused))]
|
||||||
|
pub(crate) fn from_json(file: &str) -> serde_json::Result<Self> {
|
||||||
|
serde_json::from_str(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single report of the capabilities of an Adapter.
|
||||||
|
///
|
||||||
|
/// Must be synchronized with the definition on wgpu-info/src/report.rs.
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(crate) struct AdapterReport {
|
||||||
|
pub info: AdapterInfo,
|
||||||
|
pub features: Features,
|
||||||
|
pub limits: Limits,
|
||||||
|
pub downlevel_caps: DownlevelCapabilities,
|
||||||
|
#[allow(unused)]
|
||||||
|
pub texture_format_features: HashMap<TextureFormat, TextureFormatFeatures>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AdapterReport {
|
||||||
|
pub(crate) fn from_adapter(adapter: &wgpu::Adapter) -> Self {
|
||||||
|
let info = adapter.get_info();
|
||||||
|
let features = adapter.features();
|
||||||
|
let limits = adapter.limits();
|
||||||
|
let downlevel_caps = adapter.get_downlevel_capabilities();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
info,
|
||||||
|
features,
|
||||||
|
limits,
|
||||||
|
downlevel_caps,
|
||||||
|
texture_format_features: HashMap::new(), // todo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
126
tests/src/run.rs
Normal file
126
tests/src/run.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use std::panic::AssertUnwindSafe;
|
||||||
|
|
||||||
|
use futures_lite::FutureExt;
|
||||||
|
use wgpu::{Adapter, Device, Queue};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
init::{initialize_adapter, initialize_device},
|
||||||
|
isolation,
|
||||||
|
params::TestInfo,
|
||||||
|
report::AdapterReport,
|
||||||
|
GpuTestConfiguration,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Parameters and resources hadned to the test function.
|
||||||
|
pub struct TestingContext {
|
||||||
|
pub adapter: Adapter,
|
||||||
|
pub adapter_info: wgpu::AdapterInfo,
|
||||||
|
pub adapter_downlevel_capabilities: wgpu::DownlevelCapabilities,
|
||||||
|
pub device: Device,
|
||||||
|
pub device_features: wgpu::Features,
|
||||||
|
pub device_limits: wgpu::Limits,
|
||||||
|
pub queue: Queue,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute the given test configuration with the given adapter index.
|
||||||
|
///
|
||||||
|
/// If test_info is specified, will use the information whether to skip the test.
|
||||||
|
/// If it is not, we'll create the test info from the adapter itself.
|
||||||
|
pub async fn execute_test(
|
||||||
|
config: GpuTestConfiguration,
|
||||||
|
test_info: Option<TestInfo>,
|
||||||
|
adapter_index: usize,
|
||||||
|
) {
|
||||||
|
// If we get information externally, skip based on that information before we do anything.
|
||||||
|
if let Some(TestInfo { skip: true, .. }) = test_info {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't actually care if it fails
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let _ = console_log::init_with_level(log::Level::Info);
|
||||||
|
|
||||||
|
let _test_guard = isolation::OneTestPerProcessGuard::new();
|
||||||
|
|
||||||
|
let (adapter, _surface_guard) = initialize_adapter(adapter_index).await;
|
||||||
|
|
||||||
|
let adapter_info = adapter.get_info();
|
||||||
|
let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities();
|
||||||
|
|
||||||
|
let test_info = test_info.unwrap_or_else(|| {
|
||||||
|
let adapter_report = AdapterReport::from_adapter(&adapter);
|
||||||
|
TestInfo::from_configuration(&config, &adapter_report)
|
||||||
|
});
|
||||||
|
|
||||||
|
// We are now guaranteed to have information about this test, so skip if we need to.
|
||||||
|
if test_info.skip {
|
||||||
|
log::info!("TEST RESULT: SKIPPED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (device, queue) = pollster::block_on(initialize_device(
|
||||||
|
&adapter,
|
||||||
|
config.params.required_features,
|
||||||
|
config.params.required_limits.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let context = TestingContext {
|
||||||
|
adapter,
|
||||||
|
adapter_info,
|
||||||
|
adapter_downlevel_capabilities,
|
||||||
|
device,
|
||||||
|
device_features: config.params.required_features,
|
||||||
|
device_limits: config.params.required_limits.clone(),
|
||||||
|
queue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run the test, and catch panics (possibly due to failed assertions).
|
||||||
|
let panicked = AssertUnwindSafe((config.test.as_ref().unwrap())(context))
|
||||||
|
.catch_unwind()
|
||||||
|
.await
|
||||||
|
.is_err();
|
||||||
|
|
||||||
|
// Check whether any validation errors were reported during the test run.
|
||||||
|
cfg_if::cfg_if!(
|
||||||
|
if #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] {
|
||||||
|
let canary_set = wgpu::hal::VALIDATION_CANARY.get_and_reset();
|
||||||
|
} else if #[cfg(all(target_arch = "wasm32", feature = "webgl"))] {
|
||||||
|
let canary_set = _surface_guard.unwrap().check_for_unreported_errors();
|
||||||
|
} else {
|
||||||
|
// TODO: WebGPU
|
||||||
|
let canary_set = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Summarize reasons for actual failure, if any.
|
||||||
|
let failure_cause = match (panicked, canary_set) {
|
||||||
|
(true, true) => Some("PANIC AND VALIDATION ERROR"),
|
||||||
|
(true, false) => Some("PANIC"),
|
||||||
|
(false, true) => Some("VALIDATION ERROR"),
|
||||||
|
(false, false) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compare actual results against expectations.
|
||||||
|
match (failure_cause, test_info.expected_failure_reason) {
|
||||||
|
// The test passed, as expected.
|
||||||
|
(None, None) => log::info!("TEST RESULT: PASSED"),
|
||||||
|
// The test failed unexpectedly.
|
||||||
|
(Some(cause), None) => {
|
||||||
|
panic!("UNEXPECTED TEST FAILURE DUE TO {cause}")
|
||||||
|
}
|
||||||
|
// The test passed unexpectedly.
|
||||||
|
(None, Some(reason)) => {
|
||||||
|
panic!("UNEXPECTED TEST PASS: {reason:?}");
|
||||||
|
}
|
||||||
|
// The test failed, as expected.
|
||||||
|
(Some(cause), Some(reason_expected)) => {
|
||||||
|
log::info!(
|
||||||
|
"TEST RESULT: EXPECTED FAILURE DUE TO {} (expected because of {:?})",
|
||||||
|
cause,
|
||||||
|
reason_expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};
|
||||||
use wgpu_test::{initialize_test, TestParameters};
|
|
||||||
|
|
||||||
const SHADER_SRC: &str = "
|
const SHADER_SRC: &str = "
|
||||||
@group(0) @binding(0) var tex: texture_storage_2d<bgra8unorm, write>;
|
@group(0) @binding(0) var tex: texture_storage_2d<bgra8unorm, write>;
|
||||||
@ -14,17 +13,17 @@ fn main(@builtin(workgroup_id) wgid: vec3<u32>) {
|
|||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn bgra8unorm_storage() {
|
.parameters(
|
||||||
let parameters = TestParameters::default()
|
TestParameters::default()
|
||||||
.limits(wgpu::Limits {
|
.limits(wgpu::Limits {
|
||||||
max_storage_textures_per_shader_stage: 1,
|
max_storage_textures_per_shader_stage: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.features(wgpu::Features::BGRA8UNORM_STORAGE);
|
.features(wgpu::Features::BGRA8UNORM_STORAGE),
|
||||||
|
)
|
||||||
initialize_test(parameters, |ctx| {
|
.run_sync(|ctx| {
|
||||||
let device = &ctx.device;
|
let device = &ctx.device;
|
||||||
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
@ -155,4 +154,3 @@ fn bgra8unorm_storage() {
|
|||||||
|
|
||||||
readback_buffer.unmap();
|
readback_buffer.unmap();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use wgpu_test::{initialize_test, TestParameters};
|
use wgpu_test::{gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn bind_group_layout_deduplication() {
|
static BIND_GROUP_LAYOUT_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
.run_sync(|ctx| {
|
||||||
let entries_1 = &[];
|
let entries_1 = &[];
|
||||||
|
|
||||||
let entries_2 = &[wgpu::BindGroupLayoutEntry {
|
let entries_2 = &[wgpu::BindGroupLayoutEntry {
|
||||||
@ -135,8 +135,7 @@ fn bind_group_layout_deduplication() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.queue.submit(Some(encoder.finish()));
|
ctx.queue.submit(Some(encoder.finish()));
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const SHADER_SRC: &str = "
|
const SHADER_SRC: &str = "
|
||||||
@vertex fn vs_main() -> @builtin(position) vec4<f32> { return vec4<f32>(1.0); }
|
@vertex fn vs_main() -> @builtin(position) vec4<f32> { return vec4<f32>(1.0); }
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use wgpu_test::{initialize_test, TestParameters, TestingContext};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext};
|
||||||
|
|
||||||
fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) {
|
fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) {
|
||||||
let r = wgpu::BufferUsages::MAP_READ;
|
let r = wgpu::BufferUsages::MAP_READ;
|
||||||
@ -80,82 +80,77 @@ fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str)
|
|||||||
ctx.device.poll(wgpu::MaintainBase::Wait);
|
ctx.device.poll(wgpu::MaintainBase::Wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[ignore]
|
static EMPTY_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn empty_buffer() {
|
.parameters(TestParameters::default().expect_fail(FailureCase::always()))
|
||||||
// TODO: Currently wgpu does not accept empty buffer slices, which
|
.run_sync(|ctx| {
|
||||||
// is what test is about.
|
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
test_empty_buffer_range(&ctx, 2048, "regular buffer");
|
test_empty_buffer_range(&ctx, 2048, "regular buffer");
|
||||||
test_empty_buffer_range(&ctx, 0, "zero-sized buffer");
|
test_empty_buffer_range(&ctx, 0, "zero-sized buffer");
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_map_offset() {
|
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
// This test writes 16 bytes at the beginning of buffer mapped mapped with
|
|
||||||
// an offset of 32 bytes. Then the buffer is copied into another buffer that
|
|
||||||
// is read back and we check that the written bytes are correctly placed at
|
|
||||||
// offset 32..48.
|
|
||||||
// The goal is to check that get_mapped_range did not accidentally double-count
|
|
||||||
// the mapped offset.
|
|
||||||
|
|
||||||
let write_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: 256,
|
|
||||||
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
let read_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: 256,
|
|
||||||
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
write_buf
|
|
||||||
.slice(32..)
|
|
||||||
.map_async(wgpu::MapMode::Write, move |result| {
|
|
||||||
result.unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.device.poll(wgpu::MaintainBase::Wait);
|
|
||||||
|
|
||||||
{
|
|
||||||
let slice = write_buf.slice(32..48);
|
|
||||||
let mut view = slice.get_mapped_range_mut();
|
|
||||||
for byte in &mut view[..] {
|
|
||||||
*byte = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write_buf.unmap();
|
|
||||||
|
|
||||||
let mut encoder = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
|
||||||
|
|
||||||
encoder.copy_buffer_to_buffer(&write_buf, 0, &read_buf, 0, 256);
|
|
||||||
|
|
||||||
ctx.queue.submit(Some(encoder.finish()));
|
|
||||||
|
|
||||||
read_buf
|
|
||||||
.slice(..)
|
|
||||||
.map_async(wgpu::MapMode::Read, Result::unwrap);
|
|
||||||
|
|
||||||
ctx.device.poll(wgpu::MaintainBase::Wait);
|
|
||||||
|
|
||||||
let slice = read_buf.slice(..);
|
|
||||||
let view = slice.get_mapped_range();
|
|
||||||
for byte in &view[0..32] {
|
|
||||||
assert_eq!(*byte, 0);
|
|
||||||
}
|
|
||||||
for byte in &view[32..48] {
|
|
||||||
assert_eq!(*byte, 2);
|
|
||||||
}
|
|
||||||
for byte in &view[48..] {
|
|
||||||
assert_eq!(*byte, 0);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
#[gpu_test]
|
||||||
|
static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
// This test writes 16 bytes at the beginning of buffer mapped mapped with
|
||||||
|
// an offset of 32 bytes. Then the buffer is copied into another buffer that
|
||||||
|
// is read back and we check that the written bytes are correctly placed at
|
||||||
|
// offset 32..48.
|
||||||
|
// The goal is to check that get_mapped_range did not accidentally double-count
|
||||||
|
// the mapped offset.
|
||||||
|
|
||||||
|
let write_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: 256,
|
||||||
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let read_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: 256,
|
||||||
|
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
write_buf
|
||||||
|
.slice(32..)
|
||||||
|
.map_async(wgpu::MapMode::Write, move |result| {
|
||||||
|
result.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.device.poll(wgpu::MaintainBase::Wait);
|
||||||
|
|
||||||
|
{
|
||||||
|
let slice = write_buf.slice(32..48);
|
||||||
|
let mut view = slice.get_mapped_range_mut();
|
||||||
|
for byte in &mut view[..] {
|
||||||
|
*byte = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_buf.unmap();
|
||||||
|
|
||||||
|
let mut encoder = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
|
|
||||||
|
encoder.copy_buffer_to_buffer(&write_buf, 0, &read_buf, 0, 256);
|
||||||
|
|
||||||
|
ctx.queue.submit(Some(encoder.finish()));
|
||||||
|
|
||||||
|
read_buf
|
||||||
|
.slice(..)
|
||||||
|
.map_async(wgpu::MapMode::Read, Result::unwrap);
|
||||||
|
|
||||||
|
ctx.device.poll(wgpu::MaintainBase::Wait);
|
||||||
|
|
||||||
|
let slice = read_buf.slice(..);
|
||||||
|
let view = slice.get_mapped_range();
|
||||||
|
for byte in &view[0..32] {
|
||||||
|
assert_eq!(*byte, 0);
|
||||||
|
}
|
||||||
|
for byte in &view[32..48] {
|
||||||
|
assert_eq!(*byte, 2);
|
||||||
|
}
|
||||||
|
for byte in &view[48..] {
|
||||||
|
assert_eq!(*byte, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@ -1,36 +1,37 @@
|
|||||||
//! Tests for buffer copy validation.
|
//! Tests for buffer copy validation.
|
||||||
|
|
||||||
use wasm_bindgen_test::wasm_bindgen_test;
|
|
||||||
use wgt::BufferAddress;
|
use wgt::BufferAddress;
|
||||||
|
|
||||||
use wgpu_test::{fail_if, initialize_test, TestParameters};
|
use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
#[test]
|
fn try_copy(
|
||||||
#[wasm_bindgen_test]
|
ctx: &wgpu_test::TestingContext,
|
||||||
fn copy_alignment() {
|
offset: BufferAddress,
|
||||||
fn try_copy(offset: BufferAddress, size: BufferAddress, should_fail: bool) {
|
size: BufferAddress,
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
should_fail: bool,
|
||||||
let buffer = ctx.device.create_buffer(&BUFFER_DESCRIPTOR);
|
) {
|
||||||
let data = vec![255; size as usize];
|
let buffer = ctx.device.create_buffer(&BUFFER_DESCRIPTOR);
|
||||||
fail_if(&ctx.device, should_fail, || {
|
let data = vec![255; size as usize];
|
||||||
ctx.queue.write_buffer(&buffer, offset, &data)
|
fail_if(&ctx.device, should_fail, || {
|
||||||
});
|
ctx.queue.write_buffer(&buffer, offset, &data)
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
try_copy(0, 0, false);
|
|
||||||
try_copy(4, 16 + 1, true);
|
|
||||||
try_copy(64, 20 + 2, true);
|
|
||||||
try_copy(256, 44 + 3, true);
|
|
||||||
try_copy(1024, 8 + 4, false);
|
|
||||||
|
|
||||||
try_copy(0, 4, false);
|
|
||||||
try_copy(4 + 1, 8, true);
|
|
||||||
try_copy(64 + 2, 12, true);
|
|
||||||
try_copy(256 + 3, 16, true);
|
|
||||||
try_copy(1024 + 4, 4, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static COPY_ALIGNMENT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
try_copy(&ctx, 0, 0, false);
|
||||||
|
try_copy(&ctx, 4, 16 + 1, true);
|
||||||
|
try_copy(&ctx, 64, 20 + 2, true);
|
||||||
|
try_copy(&ctx, 256, 44 + 3, true);
|
||||||
|
try_copy(&ctx, 1024, 8 + 4, false);
|
||||||
|
|
||||||
|
try_copy(&ctx, 0, 4, false);
|
||||||
|
try_copy(&ctx, 4 + 1, 8, true);
|
||||||
|
try_copy(&ctx, 64 + 2, 12, true);
|
||||||
|
try_copy(&ctx, 256 + 3, 16, true);
|
||||||
|
try_copy(&ctx, 1024 + 4, 4, false);
|
||||||
|
});
|
||||||
|
|
||||||
const BUFFER_SIZE: BufferAddress = 1234;
|
const BUFFER_SIZE: BufferAddress = 1234;
|
||||||
|
|
||||||
const BUFFER_DESCRIPTOR: wgpu::BufferDescriptor = wgpu::BufferDescriptor {
|
const BUFFER_DESCRIPTOR: wgpu::BufferDescriptor = wgpu::BufferDescriptor {
|
||||||
|
|||||||
@ -1,74 +1,69 @@
|
|||||||
//! Tests for buffer usages validation.
|
//! Tests for buffer usages validation.
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
use wgpu::BufferUsages as Bu;
|
||||||
use wgpu_test::{fail_if, initialize_test, TestParameters};
|
use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters};
|
||||||
use wgt::BufferAddress;
|
use wgt::BufferAddress;
|
||||||
|
|
||||||
const BUFFER_SIZE: BufferAddress = 1234;
|
const BUFFER_SIZE: BufferAddress = 1234;
|
||||||
|
|
||||||
#[test]
|
const ALWAYS_VALID: &[Bu; 4] = &[
|
||||||
#[wasm_bindgen_test]
|
Bu::MAP_READ,
|
||||||
fn buffer_usage() {
|
Bu::MAP_WRITE,
|
||||||
fn try_create(enable_mappable_primary_buffers: bool, usages: &[(bool, &[wgpu::BufferUsages])]) {
|
Bu::MAP_READ.union(Bu::COPY_DST),
|
||||||
let mut parameters = TestParameters::default();
|
Bu::MAP_WRITE.union(Bu::COPY_SRC),
|
||||||
if enable_mappable_primary_buffers {
|
];
|
||||||
parameters = parameters.features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS);
|
// MAP_READ can only be paired with COPY_DST and MAP_WRITE can only be paired with COPY_SRC
|
||||||
}
|
// (unless Features::MAPPABlE_PRIMARY_BUFFERS is enabled).
|
||||||
|
const NEEDS_MAPPABLE_PRIMARY_BUFFERS: &[Bu; 7] = &[
|
||||||
|
Bu::MAP_READ.union(Bu::COPY_DST.union(Bu::COPY_SRC)),
|
||||||
|
Bu::MAP_WRITE.union(Bu::COPY_SRC.union(Bu::COPY_DST)),
|
||||||
|
Bu::MAP_READ.union(Bu::MAP_WRITE),
|
||||||
|
Bu::MAP_WRITE.union(Bu::MAP_READ),
|
||||||
|
Bu::MAP_READ.union(Bu::COPY_DST.union(Bu::STORAGE)),
|
||||||
|
Bu::MAP_WRITE.union(Bu::COPY_SRC.union(Bu::STORAGE)),
|
||||||
|
Bu::all(),
|
||||||
|
];
|
||||||
|
const INVALID_BITS: Bu = Bu::from_bits_retain(0b1111111111111);
|
||||||
|
const ALWAYS_FAIL: &[Bu; 2] = &[Bu::empty(), INVALID_BITS];
|
||||||
|
|
||||||
initialize_test(parameters, |ctx| {
|
fn try_create(ctx: wgpu_test::TestingContext, usages: &[(bool, &[wgpu::BufferUsages])]) {
|
||||||
for (expect_validation_error, usage) in
|
for (expect_validation_error, usage) in usages
|
||||||
usages.iter().flat_map(|&(expect_error, usages)| {
|
.iter()
|
||||||
usages.iter().copied().map(move |u| (expect_error, u))
|
.flat_map(|&(expect_error, usages)| usages.iter().copied().map(move |u| (expect_error, u)))
|
||||||
})
|
{
|
||||||
{
|
fail_if(&ctx.device, expect_validation_error, || {
|
||||||
fail_if(&ctx.device, expect_validation_error, || {
|
let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
label: None,
|
||||||
label: None,
|
size: BUFFER_SIZE,
|
||||||
size: BUFFER_SIZE,
|
usage,
|
||||||
usage,
|
mapped_at_creation: false,
|
||||||
mapped_at_creation: false,
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
use wgpu::BufferUsages as Bu;
|
|
||||||
|
|
||||||
let always_valid = &[
|
|
||||||
Bu::MAP_READ,
|
|
||||||
Bu::MAP_WRITE,
|
|
||||||
Bu::MAP_READ | Bu::COPY_DST,
|
|
||||||
Bu::MAP_WRITE | Bu::COPY_SRC,
|
|
||||||
];
|
|
||||||
// MAP_READ can only be paired with COPY_DST and MAP_WRITE can only be paired with COPY_SRC
|
|
||||||
// (unless Features::MAPPABlE_PRIMARY_BUFFERS is enabled).
|
|
||||||
let needs_mappable_primary_buffers = &[
|
|
||||||
Bu::MAP_READ | Bu::COPY_DST | Bu::COPY_SRC,
|
|
||||||
Bu::MAP_WRITE | Bu::COPY_SRC | Bu::COPY_DST,
|
|
||||||
Bu::MAP_READ | Bu::MAP_WRITE,
|
|
||||||
Bu::MAP_WRITE | Bu::MAP_READ,
|
|
||||||
Bu::MAP_READ | Bu::COPY_DST | Bu::STORAGE,
|
|
||||||
Bu::MAP_WRITE | Bu::COPY_SRC | Bu::STORAGE,
|
|
||||||
Bu::all(),
|
|
||||||
];
|
|
||||||
let invalid_bits = Bu::from_bits_retain(0b1111111111111);
|
|
||||||
let always_fail = &[Bu::empty(), invalid_bits];
|
|
||||||
|
|
||||||
try_create(
|
|
||||||
false,
|
|
||||||
&[
|
|
||||||
(false, always_valid),
|
|
||||||
(true, needs_mappable_primary_buffers),
|
|
||||||
(true, always_fail),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
try_create(
|
|
||||||
true, // enable Features::MAPPABLE_PRIMARY_BUFFERS
|
|
||||||
&[
|
|
||||||
(false, always_valid),
|
|
||||||
(false, needs_mappable_primary_buffers),
|
|
||||||
(true, always_fail),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static BUFFER_USAGE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
try_create(
|
||||||
|
ctx,
|
||||||
|
&[
|
||||||
|
(false, ALWAYS_VALID),
|
||||||
|
(true, NEEDS_MAPPABLE_PRIMARY_BUFFERS),
|
||||||
|
(true, ALWAYS_FAIL),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static BUFFER_USAGE_MAPPABLE_PRIMARY_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
|
.parameters(TestParameters::default().features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS))
|
||||||
|
.run_sync(|ctx| {
|
||||||
|
try_create(
|
||||||
|
ctx,
|
||||||
|
&[
|
||||||
|
(false, ALWAYS_VALID),
|
||||||
|
(false, NEEDS_MAPPABLE_PRIMARY_BUFFERS),
|
||||||
|
(true, ALWAYS_FAIL),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use wasm_bindgen_test::*;
|
|
||||||
use wgpu_test::{
|
use wgpu_test::{
|
||||||
image::ReadbackBuffers, initialize_test, FailureCase, TestParameters, TestingContext,
|
gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters,
|
||||||
|
TestingContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
static TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT: &[wgpu::TextureFormat] = &[
|
static TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT: &[wgpu::TextureFormat] = &[
|
||||||
@ -326,81 +326,71 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static CLEAR_TEXTURE_UNCOMPRESSED_GLES: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn clear_texture_uncompressed_gles_compat() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.skip(FailureCase::webgl2())
|
.features(wgpu::Features::CLEAR_TEXTURE)
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE),
|
.skip(FailureCase::webgl2()),
|
||||||
|ctx| {
|
|
||||||
clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT);
|
||||||
|
});
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static CLEAR_TEXTURE_UNCOMPRESSED: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn clear_texture_uncompressed() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.skip(FailureCase::webgl2())
|
|
||||||
.expect_fail(FailureCase::backend(wgpu::Backends::GL))
|
.expect_fail(FailureCase::backend(wgpu::Backends::GL))
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE),
|
.features(wgpu::Features::CLEAR_TEXTURE),
|
||||||
|ctx| {
|
|
||||||
clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED);
|
||||||
|
});
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static CLEAR_TEXTURE_DEPTH: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn clear_texture_depth() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.skip(FailureCase::webgl2())
|
|
||||||
.downlevel_flags(
|
.downlevel_flags(
|
||||||
wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES
|
wgpu::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES
|
||||||
| wgpu::DownlevelFlags::COMPUTE_SHADERS,
|
| wgpu::DownlevelFlags::COMPUTE_SHADERS,
|
||||||
)
|
)
|
||||||
|
.skip(FailureCase::webgl2())
|
||||||
.limits(wgpu::Limits::downlevel_defaults())
|
.limits(wgpu::Limits::downlevel_defaults())
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE),
|
.features(wgpu::Features::CLEAR_TEXTURE),
|
||||||
|ctx| {
|
|
||||||
clear_texture_tests(&ctx, TEXTURE_FORMATS_DEPTH);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
clear_texture_tests(&ctx, TEXTURE_FORMATS_DEPTH);
|
||||||
|
});
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static CLEAR_TEXTURE_DEPTH32_STENCIL8: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn clear_texture_d32_s8() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::DEPTH32FLOAT_STENCIL8),
|
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::DEPTH32FLOAT_STENCIL8),
|
||||||
|ctx| {
|
|
||||||
clear_texture_tests(&ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
clear_texture_tests(&ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]);
|
||||||
|
});
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn clear_texture_bc() {
|
static CLEAR_TEXTURE_COMPRESSED_BCN: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_BC)
|
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_BC)
|
||||||
// https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
// https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
||||||
.expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"))
|
.expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"))
|
||||||
// compressed texture copy to buffer not yet implemented
|
// compressed texture copy to buffer not yet implemented
|
||||||
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
|
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
|
||||||
|ctx| {
|
|
||||||
clear_texture_tests(&ctx, TEXTURE_FORMATS_BC);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
clear_texture_tests(&ctx, TEXTURE_FORMATS_BC);
|
||||||
|
});
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn clear_texture_astc() {
|
static CLEAR_TEXTURE_COMPRESSED_ASTC: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ASTC)
|
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ASTC)
|
||||||
.limits(wgpu::Limits {
|
.limits(wgpu::Limits {
|
||||||
@ -411,23 +401,21 @@ fn clear_texture_astc() {
|
|||||||
.expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"))
|
.expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"))
|
||||||
// compressed texture copy to buffer not yet implemented
|
// compressed texture copy to buffer not yet implemented
|
||||||
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
|
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
|
||||||
|ctx| {
|
|
||||||
clear_texture_tests(&ctx, TEXTURE_FORMATS_ASTC);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
clear_texture_tests(&ctx, TEXTURE_FORMATS_ASTC);
|
||||||
|
});
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn clear_texture_etc2() {
|
static CLEAR_TEXTURE_COMPRESSED_ETC2: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ETC2)
|
.features(wgpu::Features::CLEAR_TEXTURE | wgpu::Features::TEXTURE_COMPRESSION_ETC2)
|
||||||
// https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
// https://bugs.chromium.org/p/angleproject/issues/detail?id=7056
|
||||||
.expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"))
|
.expect_fail(FailureCase::backend_adapter(wgpu::Backends::GL, "ANGLE"))
|
||||||
// compressed texture copy to buffer not yet implemented
|
// compressed texture copy to buffer not yet implemented
|
||||||
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
|
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
|
||||||
|ctx| {
|
|
||||||
clear_texture_tests(&ctx, TEXTURE_FORMATS_ETC2);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
clear_texture_tests(&ctx, TEXTURE_FORMATS_ETC2);
|
||||||
|
});
|
||||||
|
|||||||
1
tests/tests/cpu.rs
Normal file
1
tests/tests/cpu.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod example_wgsl;
|
||||||
@ -5,18 +5,17 @@
|
|||||||
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
#[wasm_bindgen_test::wasm_bindgen_test]
|
||||||
fn canvas_get_context_returned_null() {
|
fn canvas_get_context_returned_null() {
|
||||||
// Not using initialize_test() because that goes straight to creating the canvas for us.
|
// Not using the normal testing infrastructure because that goes straight to creating the canvas for us.
|
||||||
let instance = wgpu_test::initialize_instance();
|
let instance = wgpu_test::initialize_instance();
|
||||||
// Create canvas and cleanup on drop
|
// Create canvas
|
||||||
let canvas_g = wgpu_test::SurfaceGuard {
|
let canvas = wgpu_test::initialize_html_canvas();
|
||||||
canvas: wgpu_test::create_html_canvas(),
|
|
||||||
};
|
|
||||||
// Using a context id that is not "webgl2" or "webgpu" will render the canvas unusable by wgpu.
|
// Using a context id that is not "webgl2" or "webgpu" will render the canvas unusable by wgpu.
|
||||||
canvas_g.canvas.get_context("2d").unwrap();
|
canvas.get_context("2d").unwrap();
|
||||||
|
|
||||||
#[allow(clippy::redundant_clone)] // false positive — can't and shouldn't move out.
|
#[allow(clippy::redundant_clone)] // false positive — can't and shouldn't move out.
|
||||||
let error = instance
|
let error = instance
|
||||||
.create_surface_from_canvas(canvas_g.canvas.clone())
|
.create_surface_from_canvas(canvas.clone())
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
|
|||||||
@ -1,44 +1,30 @@
|
|||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
use wgpu_test::{fail, initialize_test, FailureCase, TestParameters};
|
#[gpu_test]
|
||||||
|
static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
|
.parameters(TestParameters::default().expect_fail(FailureCase::always()))
|
||||||
|
.run_sync(|ctx| {
|
||||||
|
// Create a bind group uisng a layout from another device. This should be a validation
|
||||||
|
// error but currently crashes.
|
||||||
|
let (device2, _) =
|
||||||
|
pollster::block_on(ctx.adapter.request_device(&Default::default(), None)).unwrap();
|
||||||
|
|
||||||
#[test]
|
{
|
||||||
#[wasm_bindgen_test]
|
let bind_group_layout =
|
||||||
fn device_initialization() {
|
device2.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
initialize_test(TestParameters::default(), |_ctx| {
|
|
||||||
// intentionally empty
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn device_mismatch() {
|
|
||||||
initialize_test(
|
|
||||||
// https://github.com/gfx-rs/wgpu/issues/3927
|
|
||||||
TestParameters::default().expect_fail(FailureCase::always()),
|
|
||||||
|ctx| {
|
|
||||||
// Create a bind group uisng a lyaout from another device. This should be a validation
|
|
||||||
// error but currently crashes.
|
|
||||||
let (device2, _) =
|
|
||||||
pollster::block_on(ctx.adapter.request_device(&Default::default(), None)).unwrap();
|
|
||||||
|
|
||||||
{
|
|
||||||
let bind_group_layout =
|
|
||||||
device2.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
entries: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let _bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: None,
|
label: None,
|
||||||
layout: &bind_group_layout,
|
|
||||||
entries: &[],
|
entries: &[],
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
ctx.device.poll(wgpu::Maintain::Poll);
|
let _bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
},
|
label: None,
|
||||||
);
|
layout: &bind_group_layout,
|
||||||
}
|
entries: &[],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.device.poll(wgpu::Maintain::Poll);
|
||||||
|
});
|
||||||
|
|
||||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
||||||
#[test]
|
#[test]
|
||||||
@ -53,7 +39,7 @@ fn request_device_error_on_native() {
|
|||||||
async fn request_device_error_message() {
|
async fn request_device_error_message() {
|
||||||
// Not using initialize_test() because that doesn't let us catch the error
|
// Not using initialize_test() because that doesn't let us catch the error
|
||||||
// nor .await anything
|
// nor .await anything
|
||||||
let (adapter, _surface_guard) = wgpu_test::initialize_adapter();
|
let (adapter, _surface_guard) = wgpu_test::initialize_adapter(0).await;
|
||||||
|
|
||||||
let device_error = adapter
|
let device_error = adapter
|
||||||
.request_device(
|
.request_device(
|
||||||
@ -91,47 +77,187 @@ async fn request_device_error_message() {
|
|||||||
assert!(device_error.contains(expected), "{device_error}");
|
assert!(device_error.contains(expected), "{device_error}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// This is a test of device behavior after device.destroy. Specifically, all operations
|
||||||
fn device_destroy_then_more() {
|
// should trigger errors since the device is lost.
|
||||||
// This is a test of device behavior after device.destroy. Specifically, all operations
|
//
|
||||||
// should trigger errors since the device is lost.
|
// On DX12 this test fails with a validation error in the very artifical actions taken
|
||||||
//
|
// after lose the device. The error is "ID3D12CommandAllocator::Reset: The command
|
||||||
// On DX12 this test fails with a validation error in the very artifical actions taken
|
// allocator cannot be reset because a command list is currently being recorded with the
|
||||||
// after lose the device. The error is "ID3D12CommandAllocator::Reset: The command
|
// allocator." That may indicate that DX12 doesn't like opened command buffers staying
|
||||||
// allocator cannot be reset because a command list is currently being recorded with the
|
// open even after they return an error. For now, this test is skipped on DX12.
|
||||||
// allocator." That may indicate that DX12 doesn't like opened command buffers staying
|
//
|
||||||
// open even after they return an error. For now, this test is skipped on DX12.
|
// The DX12 issue may be related to https://github.com/gfx-rs/wgpu/issues/3193.
|
||||||
//
|
#[gpu_test]
|
||||||
// The DX12 issue may be related to https://github.com/gfx-rs/wgpu/issues/3193.
|
static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.features(wgpu::Features::CLEAR_TEXTURE)
|
.features(wgpu::Features::CLEAR_TEXTURE)
|
||||||
.skip(FailureCase::backend(wgpu::Backends::DX12)),
|
.expect_fail(FailureCase::backend(wgpu::Backends::DX12)),
|
||||||
|ctx| {
|
)
|
||||||
// Create some resources on the device that we will attempt to use *after* losing
|
.run_sync(|ctx| {
|
||||||
// the device.
|
// Create some resources on the device that we will attempt to use *after* losing
|
||||||
|
// the device.
|
||||||
|
|
||||||
// Create some 512 x 512 2D textures.
|
// Create some 512 x 512 2D textures.
|
||||||
let texture_extent = wgpu::Extent3d {
|
let texture_extent = wgpu::Extent3d {
|
||||||
width: 512,
|
width: 512,
|
||||||
height: 512,
|
height: 512,
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
};
|
};
|
||||||
let texture_for_view = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
let texture_for_view = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: texture_extent,
|
||||||
|
mip_level_count: 2,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rg8Uint,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let target_view = texture_for_view.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
let texture_for_read = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: texture_extent,
|
||||||
|
mip_level_count: 2,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rg8Uint,
|
||||||
|
usage: wgpu::TextureUsages::COPY_SRC,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let texture_for_write = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: texture_extent,
|
||||||
|
mip_level_count: 2,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rg8Uint,
|
||||||
|
usage: wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create some buffers.
|
||||||
|
let buffer_source = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: 256,
|
||||||
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let buffer_dest = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: 256,
|
||||||
|
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let buffer_for_map = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: 256,
|
||||||
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let buffer_for_unmap = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: 256,
|
||||||
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
||||||
|
mapped_at_creation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a bind group layout.
|
||||||
|
let bind_group_layout =
|
||||||
|
ctx.device
|
||||||
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
entries: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a shader module.
|
||||||
|
let shader_module = ctx
|
||||||
|
.device
|
||||||
|
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
size: texture_extent,
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("")),
|
||||||
mip_level_count: 2,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rg8Uint,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
view_formats: &[],
|
|
||||||
});
|
});
|
||||||
let target_view = texture_for_view.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
|
|
||||||
let texture_for_read = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
// Create some command encoders.
|
||||||
|
let mut encoder_for_clear = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
let mut encoder_for_compute_pass = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
let mut encoder_for_render_pass = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
let mut encoder_for_buffer_buffer_copy = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
let mut encoder_for_buffer_texture_copy = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
let mut encoder_for_texture_buffer_copy = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
let mut encoder_for_texture_texture_copy = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
// Destroy the device. This will cause all other requests to return some variation of
|
||||||
|
// a device invalid error.
|
||||||
|
ctx.device.destroy();
|
||||||
|
|
||||||
|
// TODO: verify the following operations will return an invalid device error:
|
||||||
|
// * Run a compute or render pass
|
||||||
|
// * Finish a render bundle encoder
|
||||||
|
// * Create a texture from HAL
|
||||||
|
// * Create a buffer from HAL
|
||||||
|
// * Create a sampler
|
||||||
|
// * Validate a surface configuration
|
||||||
|
// * Start or stop capture
|
||||||
|
// * Get or set buffer sub data
|
||||||
|
|
||||||
|
// TODO: figure out how to structure a test around these operations which panic when
|
||||||
|
// the device is invalid:
|
||||||
|
// * device.features()
|
||||||
|
// * device.limits()
|
||||||
|
// * device.downlevel_properties()
|
||||||
|
// * device.create_query_set()
|
||||||
|
|
||||||
|
// TODO: change these fail calls to check for the specific errors which indicate that
|
||||||
|
// the device is not valid.
|
||||||
|
|
||||||
|
// Creating a commmand encoder should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
ctx.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Creating a buffer should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
size: texture_extent,
|
size: 256,
|
||||||
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Creating a texture should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
mip_level_count: 2,
|
mip_level_count: 2,
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
@ -139,328 +265,187 @@ fn device_destroy_then_more() {
|
|||||||
usage: wgpu::TextureUsages::COPY_SRC,
|
usage: wgpu::TextureUsages::COPY_SRC,
|
||||||
view_formats: &[],
|
view_formats: &[],
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
let texture_for_write = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
// Texture clear should fail.
|
||||||
label: None,
|
fail(&ctx.device, || {
|
||||||
size: texture_extent,
|
encoder_for_clear.clear_texture(
|
||||||
mip_level_count: 2,
|
&texture_for_write,
|
||||||
sample_count: 1,
|
&wgpu::ImageSubresourceRange {
|
||||||
dimension: wgpu::TextureDimension::D2,
|
aspect: wgpu::TextureAspect::All,
|
||||||
format: wgpu::TextureFormat::Rg8Uint,
|
base_mip_level: 0,
|
||||||
usage: wgpu::TextureUsages::COPY_DST,
|
mip_level_count: None,
|
||||||
view_formats: &[],
|
base_array_layer: 0,
|
||||||
});
|
array_layer_count: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Create some buffers.
|
// Creating a compute pass should fail.
|
||||||
let buffer_source = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
fail(&ctx.device, || {
|
||||||
|
encoder_for_compute_pass.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
size: 256,
|
timestamp_writes: None,
|
||||||
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
let buffer_dest = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: 256,
|
|
||||||
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
let buffer_for_map = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: 256,
|
|
||||||
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
let buffer_for_unmap = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: 256,
|
|
||||||
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
||||||
mapped_at_creation: true,
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Create a bind group layout.
|
// Creating a render pass should fail.
|
||||||
let bind_group_layout =
|
fail(&ctx.device, || {
|
||||||
ctx.device
|
encoder_for_render_pass.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
label: None,
|
||||||
label: None,
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
entries: &[],
|
ops: wgpu::Operations::default(),
|
||||||
});
|
resolve_target: None,
|
||||||
|
view: &target_view,
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Create a shader module.
|
// Copying a buffer to a buffer should fail.
|
||||||
let shader_module = ctx
|
fail(&ctx.device, || {
|
||||||
.device
|
encoder_for_buffer_buffer_copy.copy_buffer_to_buffer(
|
||||||
|
&buffer_source,
|
||||||
|
0,
|
||||||
|
&buffer_dest,
|
||||||
|
0,
|
||||||
|
256,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copying a buffer to a texture should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
encoder_for_buffer_texture_copy.copy_buffer_to_texture(
|
||||||
|
wgpu::ImageCopyBuffer {
|
||||||
|
buffer: &buffer_source,
|
||||||
|
layout: wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(4),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
texture_for_write.as_image_copy(),
|
||||||
|
texture_extent,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copying a texture to a buffer should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
encoder_for_texture_buffer_copy.copy_texture_to_buffer(
|
||||||
|
texture_for_read.as_image_copy(),
|
||||||
|
wgpu::ImageCopyBuffer {
|
||||||
|
buffer: &buffer_source,
|
||||||
|
layout: wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(4),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
texture_extent,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copying a texture to a texture should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
encoder_for_texture_texture_copy.copy_texture_to_texture(
|
||||||
|
texture_for_read.as_image_copy(),
|
||||||
|
texture_for_write.as_image_copy(),
|
||||||
|
texture_extent,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Creating a bind group layout should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
ctx.device
|
||||||
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
entries: &[],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Creating a bind group should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
entries: &[wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::Buffer(
|
||||||
|
buffer_source.as_entire_buffer_binding(),
|
||||||
|
),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Creating a pipeline layout should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
ctx.device
|
||||||
|
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Creating a shader module should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
ctx.device
|
||||||
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("")),
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("")),
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Create some command encoders.
|
// Creating a shader module spirv should fail.
|
||||||
let mut encoder_for_clear = ctx
|
fail(&ctx.device, || unsafe {
|
||||||
.device
|
ctx.device
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
|
||||||
|
|
||||||
let mut encoder_for_compute_pass = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
let mut encoder_for_render_pass = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
let mut encoder_for_buffer_buffer_copy = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
let mut encoder_for_buffer_texture_copy = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
let mut encoder_for_texture_buffer_copy = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
let mut encoder_for_texture_texture_copy = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
// Destroy the device. This will cause all other requests to return some variation of
|
|
||||||
// a device invalid error.
|
|
||||||
ctx.device.destroy();
|
|
||||||
|
|
||||||
// TODO: verify the following operations will return an invalid device error:
|
|
||||||
// * Run a compute or render pass
|
|
||||||
// * Finish a render bundle encoder
|
|
||||||
// * Create a texture from HAL
|
|
||||||
// * Create a buffer from HAL
|
|
||||||
// * Create a sampler
|
|
||||||
// * Validate a surface configuration
|
|
||||||
// * Start or stop capture
|
|
||||||
// * Get or set buffer sub data
|
|
||||||
|
|
||||||
// TODO: figure out how to structure a test around these operations which panic when
|
|
||||||
// the device is invalid:
|
|
||||||
// * device.features()
|
|
||||||
// * device.limits()
|
|
||||||
// * device.downlevel_properties()
|
|
||||||
// * device.create_query_set()
|
|
||||||
|
|
||||||
// TODO: change these fail calls to check for the specific errors which indicate that
|
|
||||||
// the device is not valid.
|
|
||||||
|
|
||||||
// Creating a commmand encoder should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a buffer should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
label: None,
|
||||||
size: 256,
|
source: std::borrow::Cow::Borrowed(&[]),
|
||||||
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Creating a texture should fail.
|
// Creating a render pipeline should fail.
|
||||||
fail(&ctx.device, || {
|
fail(&ctx.device, || {
|
||||||
ctx.device.create_texture(&wgpu::TextureDescriptor {
|
ctx.device
|
||||||
|
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
size: wgpu::Extent3d {
|
layout: None,
|
||||||
width: 512,
|
vertex: wgpu::VertexState {
|
||||||
height: 512,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
mip_level_count: 2,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rg8Uint,
|
|
||||||
usage: wgpu::TextureUsages::COPY_SRC,
|
|
||||||
view_formats: &[],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Texture clear should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
encoder_for_clear.clear_texture(
|
|
||||||
&texture_for_write,
|
|
||||||
&wgpu::ImageSubresourceRange {
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
base_mip_level: 0,
|
|
||||||
mip_level_count: None,
|
|
||||||
base_array_layer: 0,
|
|
||||||
array_layer_count: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a compute pass should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
encoder_for_compute_pass.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
||||||
label: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a render pass should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
encoder_for_render_pass.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,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Copying a buffer to a buffer should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
encoder_for_buffer_buffer_copy.copy_buffer_to_buffer(
|
|
||||||
&buffer_source,
|
|
||||||
0,
|
|
||||||
&buffer_dest,
|
|
||||||
0,
|
|
||||||
256,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Copying a buffer to a texture should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
encoder_for_buffer_texture_copy.copy_buffer_to_texture(
|
|
||||||
wgpu::ImageCopyBuffer {
|
|
||||||
buffer: &buffer_source,
|
|
||||||
layout: wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(4),
|
|
||||||
rows_per_image: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
texture_for_write.as_image_copy(),
|
|
||||||
texture_extent,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Copying a texture to a buffer should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
encoder_for_texture_buffer_copy.copy_texture_to_buffer(
|
|
||||||
texture_for_read.as_image_copy(),
|
|
||||||
wgpu::ImageCopyBuffer {
|
|
||||||
buffer: &buffer_source,
|
|
||||||
layout: wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(4),
|
|
||||||
rows_per_image: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
texture_extent,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Copying a texture to a texture should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
encoder_for_texture_texture_copy.copy_texture_to_texture(
|
|
||||||
texture_for_read.as_image_copy(),
|
|
||||||
texture_for_write.as_image_copy(),
|
|
||||||
texture_extent,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a bind group layout should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device
|
|
||||||
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
entries: &[],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a bind group should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: &bind_group_layout,
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::Buffer(
|
|
||||||
buffer_source.as_entire_buffer_binding(),
|
|
||||||
),
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a pipeline layout should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device
|
|
||||||
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
bind_group_layouts: &[],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a shader module should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device
|
|
||||||
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
||||||
label: None,
|
|
||||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("")),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a shader module spirv should fail.
|
|
||||||
fail(&ctx.device, || unsafe {
|
|
||||||
ctx.device
|
|
||||||
.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
|
|
||||||
label: None,
|
|
||||||
source: std::borrow::Cow::Borrowed(&[]),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a render pipeline should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device
|
|
||||||
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: None,
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &shader_module,
|
|
||||||
entry_point: "",
|
|
||||||
buffers: &[],
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState::default(),
|
|
||||||
depth_stencil: None,
|
|
||||||
multisample: wgpu::MultisampleState::default(),
|
|
||||||
fragment: None,
|
|
||||||
multiview: None,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating a compute pipeline should fail.
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
ctx.device
|
|
||||||
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: None,
|
|
||||||
module: &shader_module,
|
module: &shader_module,
|
||||||
entry_point: "",
|
entry_point: "",
|
||||||
});
|
buffers: &[],
|
||||||
});
|
},
|
||||||
|
primitive: wgpu::PrimitiveState::default(),
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
fragment: None,
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Buffer map should fail.
|
// Creating a compute pipeline should fail.
|
||||||
fail(&ctx.device, || {
|
fail(&ctx.device, || {
|
||||||
buffer_for_map
|
ctx.device
|
||||||
.slice(..)
|
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
||||||
.map_async(wgpu::MapMode::Write, |_| ());
|
label: None,
|
||||||
});
|
layout: None,
|
||||||
|
module: &shader_module,
|
||||||
|
entry_point: "",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Buffer unmap should fail.
|
// Buffer map should fail.
|
||||||
fail(&ctx.device, || {
|
fail(&ctx.device, || {
|
||||||
buffer_for_unmap.unmap();
|
buffer_for_map
|
||||||
});
|
.slice(..)
|
||||||
},
|
.map_async(wgpu::MapMode::Write, |_| ());
|
||||||
)
|
});
|
||||||
}
|
|
||||||
|
// Buffer unmap should fail.
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
buffer_for_unmap.unmap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -1,30 +1,24 @@
|
|||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
use wgpu::RenderPassDescriptor;
|
|
||||||
use wgpu_test::{fail, initialize_test, FailureCase, TestParameters};
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static DROP_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
fn drop_encoder() {
|
let encoder = ctx
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
.device
|
||||||
let encoder = ctx
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
.device
|
drop(encoder);
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
});
|
||||||
drop(encoder);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
// This test crashes on DX12 with the exception:
|
||||||
fn drop_encoder_after_error() {
|
//
|
||||||
// This test crashes on DX12 with the exception:
|
// ID3D12CommandAllocator::Reset: The command allocator cannot be reset because a
|
||||||
//
|
// command list is currently being recorded with the allocator. [ EXECUTION ERROR
|
||||||
// ID3D12CommandAllocator::Reset: The command allocator cannot be reset because a
|
// #543: COMMAND_ALLOCATOR_CANNOT_RESET]
|
||||||
// command list is currently being recorded with the allocator. [ EXECUTION ERROR
|
//
|
||||||
// #543: COMMAND_ALLOCATOR_CANNOT_RESET]
|
// For now, we mark the test as failing on DX12.
|
||||||
//
|
#[gpu_test]
|
||||||
// For now, we mark the test as failing on DX12.
|
static DROP_ENCODER_AFTER_ERROR: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
let parameters =
|
.parameters(TestParameters::default().expect_fail(FailureCase::backend(wgpu::Backends::DX12)))
|
||||||
TestParameters::default().expect_fail(FailureCase::backend(wgpu::Backends::DX12));
|
.run_sync(|ctx| {
|
||||||
initialize_test(parameters, |ctx| {
|
|
||||||
let mut encoder = ctx
|
let mut encoder = ctx
|
||||||
.device
|
.device
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
@ -45,7 +39,7 @@ fn drop_encoder_after_error() {
|
|||||||
});
|
});
|
||||||
let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());
|
let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
let mut renderpass = encoder.begin_render_pass(&RenderPassDescriptor {
|
let mut renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("renderpass"),
|
label: Some("renderpass"),
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
ops: wgpu::Operations::default(),
|
ops: wgpu::Operations::default(),
|
||||||
@ -67,5 +61,4 @@ fn drop_encoder_after_error() {
|
|||||||
// a CommandEncoder which errored out when processing a command.
|
// a CommandEncoder which errored out when processing a command.
|
||||||
// The encoder is still open!
|
// The encoder is still open!
|
||||||
drop(encoder);
|
drop(encoder);
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::{fs, path::PathBuf};
|
|||||||
|
|
||||||
/// Runs through all example shaders and ensures they are valid wgsl.
|
/// Runs through all example shaders and ensures they are valid wgsl.
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_example_wgsl() {
|
pub fn parse_example_wgsl() {
|
||||||
let read_dir = match PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
let read_dir = match PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
.join("examples")
|
.join("examples")
|
||||||
.read_dir()
|
.read_dir()
|
||||||
|
|||||||
@ -1,138 +1,137 @@
|
|||||||
#![cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
#![cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||||
|
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use wgpu::ExternalImageSource;
|
use wgpu::ExternalImageSource;
|
||||||
use wgpu_test::{fail_if, initialize_test, TestParameters};
|
use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[gpu_test]
|
||||||
async fn image_bitmap_import() {
|
static IMAGE_BITMAP_IMPORT: GpuTestConfiguration =
|
||||||
let image_encoded = include_bytes!("3x3_colors.png");
|
GpuTestConfiguration::new().run_async(|ctx| async move {
|
||||||
|
let image_encoded = include_bytes!("3x3_colors.png");
|
||||||
|
|
||||||
// Create an array-of-arrays for Blob's constructor
|
// Create an array-of-arrays for Blob's constructor
|
||||||
let array = js_sys::Array::new();
|
let array = js_sys::Array::new();
|
||||||
array.push(&js_sys::Uint8Array::from(&image_encoded[..]));
|
array.push(&js_sys::Uint8Array::from(&image_encoded[..]));
|
||||||
|
|
||||||
// We're passing an array of Uint8Arrays
|
// We're passing an array of Uint8Arrays
|
||||||
let blob = web_sys::Blob::new_with_u8_array_sequence(&array).unwrap();
|
let blob = web_sys::Blob::new_with_u8_array_sequence(&array).unwrap();
|
||||||
|
|
||||||
// Parse the image from the blob
|
// Parse the image from the blob
|
||||||
|
|
||||||
// Because we need to call the function in a way that isn't bound by
|
// Because we need to call the function in a way that isn't bound by
|
||||||
// web_sys, we need to manually construct the options struct and call
|
// web_sys, we need to manually construct the options struct and call
|
||||||
// the function.
|
// the function.
|
||||||
let image_bitmap_function: js_sys::Function = web_sys::window()
|
let image_bitmap_function: js_sys::Function = web_sys::window()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get("createImageBitmap")
|
.get("createImageBitmap")
|
||||||
.unwrap()
|
|
||||||
.dyn_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let options_arg = js_sys::Object::new();
|
|
||||||
js_sys::Reflect::set(
|
|
||||||
&options_arg,
|
|
||||||
&wasm_bindgen::JsValue::from_str("premultiplyAlpha"),
|
|
||||||
&wasm_bindgen::JsValue::from_str("none"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let image_bitmap_promise: js_sys::Promise = image_bitmap_function
|
|
||||||
.call2(&wasm_bindgen::JsValue::UNDEFINED, &blob, &options_arg)
|
|
||||||
.unwrap()
|
|
||||||
.dyn_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Wait for the parsing to be done
|
|
||||||
let image_bitmap: web_sys::ImageBitmap =
|
|
||||||
wasm_bindgen_futures::JsFuture::from(image_bitmap_promise)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.dyn_into()
|
.dyn_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Sanity checks
|
let options_arg = js_sys::Object::new();
|
||||||
assert_eq!(image_bitmap.width(), 3);
|
js_sys::Reflect::set(
|
||||||
assert_eq!(image_bitmap.height(), 3);
|
&options_arg,
|
||||||
|
&wasm_bindgen::JsValue::from_str("premultiplyAlpha"),
|
||||||
// Due to restrictions with premultiplication with ImageBitmaps, we also create an HtmlCanvasElement
|
&wasm_bindgen::JsValue::from_str("none"),
|
||||||
// by drawing the image bitmap onto the canvas.
|
)
|
||||||
let canvas: web_sys::HtmlCanvasElement = web_sys::window()
|
|
||||||
.unwrap()
|
|
||||||
.document()
|
|
||||||
.unwrap()
|
|
||||||
.create_element("canvas")
|
|
||||||
.unwrap()
|
|
||||||
.dyn_into()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
canvas.set_width(3);
|
let image_bitmap_promise: js_sys::Promise = image_bitmap_function
|
||||||
canvas.set_height(3);
|
.call2(&wasm_bindgen::JsValue::UNDEFINED, &blob, &options_arg)
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let d2_context: web_sys::CanvasRenderingContext2d = canvas
|
// Wait for the parsing to be done
|
||||||
.get_context("2d")
|
let image_bitmap: web_sys::ImageBitmap =
|
||||||
.unwrap()
|
wasm_bindgen_futures::JsFuture::from(image_bitmap_promise)
|
||||||
.unwrap()
|
.await
|
||||||
.dyn_into()
|
.unwrap()
|
||||||
.unwrap();
|
.dyn_into()
|
||||||
d2_context
|
.unwrap();
|
||||||
.draw_image_with_image_bitmap(&image_bitmap, 0.0, 0.0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Decode it cpu side
|
// Sanity checks
|
||||||
let raw_image = image::load_from_memory_with_format(image_encoded, image::ImageFormat::Png)
|
assert_eq!(image_bitmap.width(), 3);
|
||||||
.unwrap()
|
assert_eq!(image_bitmap.height(), 3);
|
||||||
.into_rgba8();
|
|
||||||
|
|
||||||
// Set of test cases to test with image import
|
// Due to restrictions with premultiplication with ImageBitmaps, we also create an HtmlCanvasElement
|
||||||
#[derive(Debug, Copy, Clone)]
|
// by drawing the image bitmap onto the canvas.
|
||||||
enum TestCase {
|
let canvas: web_sys::HtmlCanvasElement = web_sys::window()
|
||||||
// Import the image as normal
|
.unwrap()
|
||||||
Normal,
|
.document()
|
||||||
// Sets the FlipY flag. Deals with global state on GLES, so run before other tests to ensure it's reset.
|
.unwrap()
|
||||||
//
|
.create_element("canvas")
|
||||||
// Only works on canvases.
|
.unwrap()
|
||||||
FlipY,
|
.dyn_into()
|
||||||
// Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset.
|
.unwrap();
|
||||||
//
|
canvas.set_width(3);
|
||||||
// Only works on canvases.
|
canvas.set_height(3);
|
||||||
Premultiplied,
|
|
||||||
// Sets the color space to P3.
|
let d2_context: web_sys::CanvasRenderingContext2d = canvas
|
||||||
//
|
.get_context("2d")
|
||||||
// Only works on canvases.
|
.unwrap()
|
||||||
ColorSpace,
|
.unwrap()
|
||||||
// Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset.
|
.dyn_into()
|
||||||
// Set both the input offset and output offset to 1 in x, so the first column is omitted.
|
.unwrap();
|
||||||
TrimLeft,
|
d2_context
|
||||||
// Set the size to 2 in x, so the last column is omitted
|
.draw_image_with_image_bitmap(&image_bitmap, 0.0, 0.0)
|
||||||
TrimRight,
|
.unwrap();
|
||||||
// Set only the output offset to 1, so the second column gets the first column's data.
|
|
||||||
SlideRight,
|
// Decode it cpu side
|
||||||
// Try to copy from out of bounds of the source image
|
let raw_image = image::load_from_memory_with_format(image_encoded, image::ImageFormat::Png)
|
||||||
SourceOutOfBounds,
|
.unwrap()
|
||||||
// Try to copy from out of bounds of the destination image
|
.into_rgba8();
|
||||||
DestOutOfBounds,
|
|
||||||
// Try to copy more than one slice from the source
|
// Set of test cases to test with image import
|
||||||
MultiSliceCopy,
|
#[derive(Debug, Copy, Clone)]
|
||||||
// Copy into the second slice of a 2D array texture,
|
enum TestCase {
|
||||||
SecondSliceCopy,
|
// Import the image as normal
|
||||||
}
|
Normal,
|
||||||
let sources = [
|
// Sets the FlipY flag. Deals with global state on GLES, so run before other tests to ensure it's reset.
|
||||||
ExternalImageSource::ImageBitmap(image_bitmap),
|
//
|
||||||
ExternalImageSource::HTMLCanvasElement(canvas),
|
// Only works on canvases.
|
||||||
];
|
FlipY,
|
||||||
let cases = [
|
// Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset.
|
||||||
TestCase::Normal,
|
//
|
||||||
TestCase::FlipY,
|
// Only works on canvases.
|
||||||
TestCase::Premultiplied,
|
Premultiplied,
|
||||||
TestCase::ColorSpace,
|
// Sets the color space to P3.
|
||||||
TestCase::TrimLeft,
|
//
|
||||||
TestCase::TrimRight,
|
// Only works on canvases.
|
||||||
TestCase::SlideRight,
|
ColorSpace,
|
||||||
TestCase::SourceOutOfBounds,
|
// Sets the premultiplied alpha flag. Deals with global state on GLES, so run before other tests to ensure it's reset.
|
||||||
TestCase::DestOutOfBounds,
|
// Set both the input offset and output offset to 1 in x, so the first column is omitted.
|
||||||
TestCase::MultiSliceCopy,
|
TrimLeft,
|
||||||
TestCase::SecondSliceCopy,
|
// Set the size to 2 in x, so the last column is omitted
|
||||||
];
|
TrimRight,
|
||||||
|
// Set only the output offset to 1, so the second column gets the first column's data.
|
||||||
|
SlideRight,
|
||||||
|
// Try to copy from out of bounds of the source image
|
||||||
|
SourceOutOfBounds,
|
||||||
|
// Try to copy from out of bounds of the destination image
|
||||||
|
DestOutOfBounds,
|
||||||
|
// Try to copy more than one slice from the source
|
||||||
|
MultiSliceCopy,
|
||||||
|
// Copy into the second slice of a 2D array texture,
|
||||||
|
SecondSliceCopy,
|
||||||
|
}
|
||||||
|
let sources = [
|
||||||
|
ExternalImageSource::ImageBitmap(image_bitmap),
|
||||||
|
ExternalImageSource::HTMLCanvasElement(canvas),
|
||||||
|
];
|
||||||
|
let cases = [
|
||||||
|
TestCase::Normal,
|
||||||
|
TestCase::FlipY,
|
||||||
|
TestCase::Premultiplied,
|
||||||
|
TestCase::ColorSpace,
|
||||||
|
TestCase::TrimLeft,
|
||||||
|
TestCase::TrimRight,
|
||||||
|
TestCase::SlideRight,
|
||||||
|
TestCase::SourceOutOfBounds,
|
||||||
|
TestCase::DestOutOfBounds,
|
||||||
|
TestCase::MultiSliceCopy,
|
||||||
|
TestCase::SecondSliceCopy,
|
||||||
|
];
|
||||||
|
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
for source in sources {
|
for source in sources {
|
||||||
for case in cases {
|
for case in cases {
|
||||||
// Copy the data, so we can modify it for tests
|
// Copy the data, so we can modify it for tests
|
||||||
@ -346,5 +345,4 @@ async fn image_bitmap_import() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
|
||||||
|
|
||||||
mod regression {
|
mod regression {
|
||||||
mod issue_3457;
|
mod issue_3457;
|
||||||
mod issue_4024;
|
mod issue_4024;
|
||||||
@ -15,7 +13,6 @@ mod clear_texture;
|
|||||||
mod create_surface_error;
|
mod create_surface_error;
|
||||||
mod device;
|
mod device;
|
||||||
mod encoder;
|
mod encoder;
|
||||||
mod example_wgsl;
|
|
||||||
mod external_texture;
|
mod external_texture;
|
||||||
mod instance;
|
mod instance;
|
||||||
mod occlusion_query;
|
mod occlusion_query;
|
||||||
@ -36,4 +33,4 @@ mod vertex_indices;
|
|||||||
mod write_texture;
|
mod write_texture;
|
||||||
mod zero_init_texture_after_discard;
|
mod zero_init_texture_after_discard;
|
||||||
|
|
||||||
wasm_bindgen_test_configure!(run_in_browser);
|
wgpu_test::gpu_test_main!();
|
||||||
@ -1,38 +1,4 @@
|
|||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static INITIALIZE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|_ctx| {});
|
||||||
fn initialize() {
|
|
||||||
let _ = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
||||||
backends: wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all),
|
|
||||||
flags: wgpu::InstanceFlags::debugging().with_env(),
|
|
||||||
dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(),
|
|
||||||
gles_minor_version: wgpu::util::gles_minor_version_from_env().unwrap_or_default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn request_adapter_inner(power: wgt::PowerPreference) {
|
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
||||||
backends: wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all),
|
|
||||||
flags: wgpu::InstanceFlags::debugging().with_env(),
|
|
||||||
dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(),
|
|
||||||
gles_minor_version: wgpu::util::gles_minor_version_from_env().unwrap_or_default(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let _adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: power,
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
compatible_surface: None,
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn request_adapter_low_power() {
|
|
||||||
request_adapter_inner(wgt::PowerPreference::LowPower);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn request_adapter_high_power() {
|
|
||||||
request_adapter_inner(wgt::PowerPreference::HighPerformance);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use wgpu_test::{initialize_test, TestParameters};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn occlusion_query() {
|
static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
|
||||||
|
.run_sync(|ctx| {
|
||||||
// Create depth texture
|
// Create depth texture
|
||||||
let depth_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
let depth_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: Some("Depth texture"),
|
label: Some("Depth texture"),
|
||||||
@ -124,5 +125,4 @@ fn occlusion_query() {
|
|||||||
assert_ne!(query_data[0], 0);
|
assert_ne!(query_data[0], 0);
|
||||||
assert_ne!(query_data[1], 0);
|
assert_ne!(query_data[1], 0);
|
||||||
assert_eq!(query_data[2], 0);
|
assert_eq!(query_data[2], 0);
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
use std::{borrow::Cow, num::NonZeroU32};
|
use std::{borrow::Cow, num::NonZeroU32};
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{gpu_test, image::ReadbackBuffers, GpuTestConfiguration, TestParameters};
|
||||||
use wgpu_test::{image::ReadbackBuffers, initialize_test, TestParameters};
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static PARTIALLY_BOUNDED_ARRAY: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn partially_bounded_array() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.features(
|
.features(
|
||||||
wgpu::Features::TEXTURE_BINDING_ARRAY
|
wgpu::Features::TEXTURE_BINDING_ARRAY
|
||||||
@ -15,96 +13,93 @@ fn partially_bounded_array() {
|
|||||||
| wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
| wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||||
)
|
)
|
||||||
.limits(wgpu::Limits::downlevel_defaults()),
|
.limits(wgpu::Limits::downlevel_defaults()),
|
||||||
|ctx| {
|
|
||||||
let device = &ctx.device;
|
|
||||||
|
|
||||||
let texture_extent = wgpu::Extent3d {
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
};
|
|
||||||
let storage_texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: texture_extent,
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rgba32Float,
|
|
||||||
usage: wgpu::TextureUsages::TEXTURE_BINDING
|
|
||||||
| wgpu::TextureUsages::COPY_DST
|
|
||||||
| wgpu::TextureUsages::STORAGE_BINDING
|
|
||||||
| wgpu::TextureUsages::COPY_SRC,
|
|
||||||
view_formats: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let texture_view = storage_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
|
|
||||||
let bind_group_layout =
|
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("bind group layout"),
|
|
||||||
entries: &[wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::COMPUTE,
|
|
||||||
ty: wgpu::BindingType::StorageTexture {
|
|
||||||
access: wgpu::StorageTextureAccess::WriteOnly,
|
|
||||||
format: wgpu::TextureFormat::Rgba32Float,
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
},
|
|
||||||
|
|
||||||
count: NonZeroU32::new(4),
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
|
|
||||||
let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
||||||
label: None,
|
|
||||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
|
|
||||||
});
|
|
||||||
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("main"),
|
|
||||||
bind_group_layouts: &[&bind_group_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let compute_pipeline =
|
|
||||||
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: Some(&pipeline_layout),
|
|
||||||
module: &cs_module,
|
|
||||||
entry_point: "main",
|
|
||||||
});
|
|
||||||
|
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureViewArray(&[&texture_view]),
|
|
||||||
}],
|
|
||||||
layout: &bind_group_layout,
|
|
||||||
label: Some("bind group"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut encoder =
|
|
||||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
|
||||||
{
|
|
||||||
let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
||||||
label: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
cpass.set_pipeline(&compute_pipeline);
|
|
||||||
cpass.set_bind_group(0, &bind_group, &[]);
|
|
||||||
cpass.dispatch_workgroups(1, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let readback_buffers = ReadbackBuffers::new(&ctx.device, &storage_texture);
|
|
||||||
readback_buffers.copy_from(&ctx.device, &mut encoder, &storage_texture);
|
|
||||||
|
|
||||||
ctx.queue.submit(Some(encoder.finish()));
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
readback_buffers
|
|
||||||
.check_buffer_contents(device, bytemuck::bytes_of(&[4.0f32, 3.0, 2.0, 1.0])),
|
|
||||||
"texture storage values are incorrect!"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
let device = &ctx.device;
|
||||||
|
|
||||||
|
let texture_extent = wgpu::Extent3d {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
};
|
||||||
|
let storage_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: texture_extent,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba32Float,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING
|
||||||
|
| wgpu::TextureUsages::COPY_DST
|
||||||
|
| wgpu::TextureUsages::STORAGE_BINDING
|
||||||
|
| wgpu::TextureUsages::COPY_SRC,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let texture_view = storage_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("bind group layout"),
|
||||||
|
entries: &[wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::COMPUTE,
|
||||||
|
ty: wgpu::BindingType::StorageTexture {
|
||||||
|
access: wgpu::StorageTextureAccess::WriteOnly,
|
||||||
|
format: wgpu::TextureFormat::Rgba32Float,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
|
||||||
|
count: NonZeroU32::new(4),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
|
||||||
|
});
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("main"),
|
||||||
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
module: &cs_module,
|
||||||
|
entry_point: "main",
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
entries: &[wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureViewArray(&[&texture_view]),
|
||||||
|
}],
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
label: Some("bind group"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut encoder =
|
||||||
|
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
|
{
|
||||||
|
let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||||
|
label: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
});
|
||||||
|
cpass.set_pipeline(&compute_pipeline);
|
||||||
|
cpass.set_bind_group(0, &bind_group, &[]);
|
||||||
|
cpass.dispatch_workgroups(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let readback_buffers = ReadbackBuffers::new(&ctx.device, &storage_texture);
|
||||||
|
readback_buffers.copy_from(&ctx.device, &mut encoder, &storage_texture);
|
||||||
|
|
||||||
|
ctx.queue.submit(Some(encoder.finish()));
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
readback_buffers
|
||||||
|
.check_buffer_contents(device, bytemuck::bytes_of(&[4.0f32, 3.0, 2.0, 1.0])),
|
||||||
|
"texture storage values are incorrect!"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
use wgpu_test::{fail, initialize_test, FailureCase, TestParameters};
|
|
||||||
|
|
||||||
#[test]
|
// Create an invalid shader and a compute pipeline that uses it
|
||||||
#[wasm_bindgen_test]
|
// with a default bindgroup layout, and then ask for that layout.
|
||||||
fn pipeline_default_layout_bad_module() {
|
// Validation should fail, but wgpu should not panic.
|
||||||
// Create an invalid shader and a compute pipeline that uses it
|
#[gpu_test]
|
||||||
// with a default bindgroup layout, and then ask for that layout.
|
static PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
// Validation should fail, but wgpu should not panic.
|
.parameters(
|
||||||
let parameters = TestParameters::default()
|
TestParameters::default()
|
||||||
.skip(FailureCase::webgl2())
|
// https://github.com/gfx-rs/wgpu/issues/4167
|
||||||
// https://github.com/gfx-rs/wgpu/issues/4167
|
.expect_fail(FailureCase::always()),
|
||||||
.expect_fail(FailureCase::always());
|
)
|
||||||
initialize_test(parameters, |ctx| {
|
.run_sync(|ctx| {
|
||||||
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
|
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||||
|
|
||||||
fail(&ctx.device, || {
|
fail(&ctx.device, || {
|
||||||
@ -34,4 +33,3 @@ fn pipeline_default_layout_bad_module() {
|
|||||||
pipeline.get_bind_group_layout(0);
|
pipeline.get_bind_group_layout(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,130 +1,115 @@
|
|||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry,
|
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
||||||
BindingResource, BindingType, BufferBindingType, BufferDescriptor, BufferUsages, CommandBuffer,
|
BindGroupLayoutEntry, BindingResource, BindingType, Buffer, BufferBindingType,
|
||||||
CommandEncoderDescriptor, ComputePassDescriptor, Maintain, ShaderStages,
|
BufferDescriptor, BufferUsages, CommandBuffer, CommandEncoderDescriptor, ComputePassDescriptor,
|
||||||
|
Maintain, ShaderStages,
|
||||||
};
|
};
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{gpu_test, GpuTestConfiguration, TestingContext};
|
||||||
use wgpu_test::{initialize_test, FailureCase, TestParameters, TestingContext};
|
|
||||||
|
|
||||||
fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer {
|
struct DummyWorkData {
|
||||||
let buffer = ctx.device.create_buffer(&BufferDescriptor {
|
_buffer: Buffer,
|
||||||
label: None,
|
_bgl: BindGroupLayout,
|
||||||
size: 16,
|
_bg: BindGroup,
|
||||||
usage: BufferUsages::UNIFORM,
|
cmd_buf: CommandBuffer,
|
||||||
mapped_at_creation: false,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let bind_group_layout = ctx
|
impl DummyWorkData {
|
||||||
.device
|
fn new(ctx: &TestingContext) -> Self {
|
||||||
.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
let buffer = ctx.device.create_buffer(&BufferDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
entries: &[BindGroupLayoutEntry {
|
size: 16,
|
||||||
|
usage: BufferUsages::UNIFORM,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group_layout = ctx
|
||||||
|
.device
|
||||||
|
.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
entries: &[BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: ShaderStages::COMPUTE,
|
||||||
|
ty: BindingType::Buffer {
|
||||||
|
ty: BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: Some(NonZeroU64::new(16).unwrap()),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
entries: &[BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: ShaderStages::COMPUTE,
|
resource: BindingResource::Buffer(buffer.as_entire_buffer_binding()),
|
||||||
ty: BindingType::Buffer {
|
|
||||||
ty: BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: Some(NonZeroU64::new(16).unwrap()),
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
|
|
||||||
let bind_group = ctx.device.create_bind_group(&BindGroupDescriptor {
|
let mut cmd_buf = ctx
|
||||||
label: None,
|
.device
|
||||||
layout: &bind_group_layout,
|
.create_command_encoder(&CommandEncoderDescriptor::default());
|
||||||
entries: &[BindGroupEntry {
|
|
||||||
binding: 0,
|
let mut cpass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::default());
|
||||||
resource: BindingResource::Buffer(buffer.as_entire_buffer_binding()),
|
cpass.set_bind_group(0, &bind_group, &[]);
|
||||||
}],
|
drop(cpass);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
_buffer: buffer,
|
||||||
|
_bgl: bind_group_layout,
|
||||||
|
_bg: bind_group,
|
||||||
|
cmd_buf: cmd_buf.finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
let data = DummyWorkData::new(&ctx);
|
||||||
|
|
||||||
|
ctx.queue.submit(Some(data.cmd_buf));
|
||||||
|
ctx.device.poll(Maintain::Wait);
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static DOUBLE_WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
let data = DummyWorkData::new(&ctx);
|
||||||
|
|
||||||
|
ctx.queue.submit(Some(data.cmd_buf));
|
||||||
|
ctx.device.poll(Maintain::Wait);
|
||||||
|
ctx.device.poll(Maintain::Wait);
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static WAIT_ON_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
let data = DummyWorkData::new(&ctx);
|
||||||
|
|
||||||
|
let index = ctx.queue.submit(Some(data.cmd_buf));
|
||||||
|
ctx.device.poll(Maintain::WaitForSubmissionIndex(index));
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static DOUBLE_WAIT_ON_SUBMISSION: GpuTestConfiguration =
|
||||||
|
GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
let data = DummyWorkData::new(&ctx);
|
||||||
|
|
||||||
|
let index = ctx.queue.submit(Some(data.cmd_buf));
|
||||||
|
ctx.device
|
||||||
|
.poll(Maintain::WaitForSubmissionIndex(index.clone()));
|
||||||
|
ctx.device.poll(Maintain::WaitForSubmissionIndex(index));
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut cmd_buf = ctx
|
#[gpu_test]
|
||||||
.device
|
static WAIT_OUT_OF_ORDER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
.create_command_encoder(&CommandEncoderDescriptor::default());
|
let data1 = DummyWorkData::new(&ctx);
|
||||||
|
let data2 = DummyWorkData::new(&ctx);
|
||||||
|
|
||||||
let mut cpass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::default());
|
let index1 = ctx.queue.submit(Some(data1.cmd_buf));
|
||||||
cpass.set_bind_group(0, &bind_group, &[]);
|
let index2 = ctx.queue.submit(Some(data2.cmd_buf));
|
||||||
drop(cpass);
|
ctx.device.poll(Maintain::WaitForSubmissionIndex(index2));
|
||||||
|
ctx.device.poll(Maintain::WaitForSubmissionIndex(index1));
|
||||||
cmd_buf.finish()
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn wait() {
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default().skip(FailureCase::always()),
|
|
||||||
|ctx| {
|
|
||||||
let cmd_buf = generate_dummy_work(&ctx);
|
|
||||||
|
|
||||||
ctx.queue.submit(Some(cmd_buf));
|
|
||||||
ctx.device.poll(Maintain::Wait);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn double_wait() {
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default().skip(FailureCase::always()),
|
|
||||||
|ctx| {
|
|
||||||
let cmd_buf = generate_dummy_work(&ctx);
|
|
||||||
|
|
||||||
ctx.queue.submit(Some(cmd_buf));
|
|
||||||
ctx.device.poll(Maintain::Wait);
|
|
||||||
ctx.device.poll(Maintain::Wait);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn wait_on_submission() {
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default().skip(FailureCase::always()),
|
|
||||||
|ctx| {
|
|
||||||
let cmd_buf = generate_dummy_work(&ctx);
|
|
||||||
|
|
||||||
let index = ctx.queue.submit(Some(cmd_buf));
|
|
||||||
ctx.device.poll(Maintain::WaitForSubmissionIndex(index));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn double_wait_on_submission() {
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default().skip(FailureCase::always()),
|
|
||||||
|ctx| {
|
|
||||||
let cmd_buf = generate_dummy_work(&ctx);
|
|
||||||
|
|
||||||
let index = ctx.queue.submit(Some(cmd_buf));
|
|
||||||
ctx.device
|
|
||||||
.poll(Maintain::WaitForSubmissionIndex(index.clone()));
|
|
||||||
ctx.device.poll(Maintain::WaitForSubmissionIndex(index));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn wait_out_of_order() {
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default().skip(FailureCase::always()),
|
|
||||||
|ctx| {
|
|
||||||
let cmd_buf1 = generate_dummy_work(&ctx);
|
|
||||||
let cmd_buf2 = generate_dummy_work(&ctx);
|
|
||||||
|
|
||||||
let index1 = ctx.queue.submit(Some(cmd_buf1));
|
|
||||||
let index2 = ctx.queue.submit(Some(cmd_buf2));
|
|
||||||
ctx.device.poll(Maintain::WaitForSubmissionIndex(index2));
|
|
||||||
ctx.device.poll(Maintain::WaitForSubmissionIndex(index1));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
use wgpu_test::{initialize_test, FailureCase, TestParameters};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn drop_failed_timestamp_query_set() {
|
static DROP_FAILED_TIMESTAMP_QUERY_SET: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
let parameters = TestParameters::default()
|
.parameters(
|
||||||
// https://github.com/gfx-rs/wgpu/issues/4139
|
TestParameters::default()
|
||||||
.expect_fail(FailureCase::always());
|
// https://github.com/gfx-rs/wgpu/issues/4139
|
||||||
initialize_test(parameters, |ctx| {
|
.expect_fail(FailureCase::always()),
|
||||||
|
)
|
||||||
|
.run_sync(|ctx| {
|
||||||
// Enter an error scope, so the validation catch-all doesn't
|
// Enter an error scope, so the validation catch-all doesn't
|
||||||
// report the error too early.
|
// report the error too early.
|
||||||
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
|
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||||
@ -23,4 +25,3 @@ fn drop_failed_timestamp_query_set() {
|
|||||||
|
|
||||||
assert!(pollster::block_on(ctx.device.pop_error_scope()).is_some());
|
assert!(pollster::block_on(ctx.device.pop_error_scope()).is_some());
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
//! Tests for buffer copy validation.
|
//! Tests for buffer copy validation.
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{fail, gpu_test, GpuTestConfiguration};
|
||||||
use wgpu_test::{fail, initialize_test, TestParameters};
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration =
|
||||||
fn queue_write_texture_overflow() {
|
GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
size: wgpu::Extent3d {
|
size: wgpu::Extent3d {
|
||||||
@ -47,4 +45,3 @@ fn queue_write_texture_overflow() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use wgpu_test::{initialize_test, TestParameters};
|
use wgpu_test::{gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
use wasm_bindgen_test::wasm_bindgen_test;
|
|
||||||
use wgpu::*;
|
use wgpu::*;
|
||||||
|
|
||||||
/// The core issue here was that we weren't properly disabling vertex attributes on GL
|
/// The core issue here was that we weren't properly disabling vertex attributes on GL
|
||||||
@ -15,10 +14,9 @@ use wgpu::*;
|
|||||||
///
|
///
|
||||||
/// We use non-consecutive vertex attribute locations (0 and 5) in order to also test
|
/// We use non-consecutive vertex attribute locations (0 and 5) in order to also test
|
||||||
/// that we unset the correct locations (see PR #3706).
|
/// that we unset the correct locations (see PR #3706).
|
||||||
#[wasm_bindgen_test]
|
#[gpu_test]
|
||||||
#[test]
|
static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration =
|
||||||
fn pass_reset_vertex_buffer() {
|
GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
let module = ctx
|
let module = ctx
|
||||||
.device
|
.device
|
||||||
.create_shader_module(include_wgsl!("issue_3457.wgsl"));
|
.create_shader_module(include_wgsl!("issue_3457.wgsl"));
|
||||||
@ -190,5 +188,4 @@ fn pass_reset_vertex_buffer() {
|
|||||||
drop(single_rpass);
|
drop(single_rpass);
|
||||||
|
|
||||||
ctx.queue.submit(Some(encoder2.finish()));
|
ctx.queue.submit(Some(encoder2.finish()));
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use wgpu_test::{initialize_test, TestParameters};
|
use wgpu_test::{gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
use wasm_bindgen_test::wasm_bindgen_test;
|
|
||||||
use wgpu::*;
|
use wgpu::*;
|
||||||
|
|
||||||
/// The WebGPU specification has very specific requirements about the ordering of map_async
|
/// The WebGPU specification has very specific requirements about the ordering of map_async
|
||||||
@ -13,10 +12,9 @@ use wgpu::*;
|
|||||||
///
|
///
|
||||||
/// We previously immediately invoked on_submitted_work_done callbacks if there was no active submission
|
/// We previously immediately invoked on_submitted_work_done callbacks if there was no active submission
|
||||||
/// to add them to. This is incorrect, as we do not immediatley invoke map_async callbacks.
|
/// to add them to. This is incorrect, as we do not immediatley invoke map_async callbacks.
|
||||||
#[wasm_bindgen_test]
|
#[gpu_test]
|
||||||
#[test]
|
static QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn queue_submitted_callback_ordering() {
|
.run_sync(|ctx| {
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
// Create a mappable buffer
|
// Create a mappable buffer
|
||||||
let buffer = ctx.device.create_buffer(&BufferDescriptor {
|
let buffer = ctx.device.create_buffer(&BufferDescriptor {
|
||||||
label: Some("mappable buffer"),
|
label: Some("mappable buffer"),
|
||||||
@ -87,5 +85,4 @@ fn queue_submitted_callback_ordering() {
|
|||||||
assert_eq!(ordering.value_read_map_async, Some(0));
|
assert_eq!(ordering.value_read_map_async, Some(0));
|
||||||
// The queue submitted work done callback was invoked second.
|
// The queue submitted work done callback was invoked second.
|
||||||
assert_eq!(ordering.value_read_queue_submitted, Some(1));
|
assert_eq!(ordering.value_read_queue_submitted, Some(1));
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use std::{num::NonZeroU64, ops::Range};
|
use std::{num::NonZeroU64, ops::Range};
|
||||||
|
|
||||||
use wasm_bindgen_test::wasm_bindgen_test;
|
use wgpu_test::{gpu_test, GpuTestConfiguration, TestingContext};
|
||||||
use wgpu_test::{initialize_test, TestParameters, TestingContext};
|
|
||||||
|
|
||||||
fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {
|
fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {
|
||||||
let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
@ -88,10 +87,9 @@ fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {
|
|||||||
/// certain conditions. See https://github.com/gfx-rs/wgpu/issues/4122 for more information.
|
/// certain conditions. See https://github.com/gfx-rs/wgpu/issues/4122 for more information.
|
||||||
///
|
///
|
||||||
/// This test will fail on nvidia if the bug is not properly worked around.
|
/// This test will fail on nvidia if the bug is not properly worked around.
|
||||||
#[wasm_bindgen_test]
|
#[gpu_test]
|
||||||
#[test]
|
static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration =
|
||||||
fn clear_buffer_bug() {
|
GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
// This hits most of the cases in nvidia's clear buffer bug
|
// This hits most of the cases in nvidia's clear buffer bug
|
||||||
let mut succeeded = true;
|
let mut succeeded = true;
|
||||||
for power in 4..14 {
|
for power in 4..14 {
|
||||||
@ -107,4 +105,3 @@ fn clear_buffer_bug() {
|
|||||||
}
|
}
|
||||||
assert!(succeeded);
|
assert!(succeeded);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,22 +1,17 @@
|
|||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{gpu_test, GpuTestConfiguration};
|
||||||
use wgpu_test::{initialize_test, TestParameters};
|
|
||||||
|
|
||||||
/// Buffer's size and usage can be read back.
|
#[gpu_test]
|
||||||
#[test]
|
static BUFFER_SIZE_AND_USAGE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
#[wasm_bindgen_test]
|
let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
fn buffer_size_and_usage() {
|
label: None,
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
size: 1234,
|
||||||
let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST,
|
||||||
label: None,
|
mapped_at_creation: false,
|
||||||
size: 1234,
|
});
|
||||||
usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(buffer.size(), 1234);
|
assert_eq!(buffer.size(), 1234);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer.usage(),
|
buffer.usage(),
|
||||||
wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST
|
wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,57 +1,48 @@
|
|||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{fail, gpu_test, valid, GpuTestConfiguration};
|
||||||
use wgpu_test::{fail, initialize_test, valid, TestParameters};
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static BAD_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
fn bad_buffer() {
|
|
||||||
// Create a buffer with bad parameters and call a few methods.
|
// Create a buffer with bad parameters and call a few methods.
|
||||||
// Validation should fail but there should be not panic.
|
// Validation should fail but there should be not panic.
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
let buffer = fail(&ctx.device, || {
|
||||||
let buffer = fail(&ctx.device, || {
|
ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
label: None,
|
||||||
label: None,
|
size: 99999999,
|
||||||
size: 99999999,
|
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE,
|
||||||
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE,
|
mapped_at_creation: false,
|
||||||
mapped_at_creation: false,
|
})
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {})
|
|
||||||
});
|
|
||||||
fail(&ctx.device, || buffer.unmap());
|
|
||||||
valid(&ctx.device, || buffer.destroy());
|
|
||||||
valid(&ctx.device, || buffer.destroy());
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
fail(&ctx.device, || {
|
||||||
#[wasm_bindgen_test]
|
buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {})
|
||||||
fn bad_texture() {
|
|
||||||
// Create a texture with bad parameters and call a few methods.
|
|
||||||
// Validation should fail but there should be not panic.
|
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
let texture = fail(&ctx.device, || {
|
|
||||||
ctx.device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: 0,
|
|
||||||
height: 12345678,
|
|
||||||
depth_or_array_layers: 9001,
|
|
||||||
},
|
|
||||||
mip_level_count: 2000,
|
|
||||||
sample_count: 27,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
|
||||||
usage: wgpu::TextureUsages::all(),
|
|
||||||
view_formats: &[],
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
fail(&ctx.device, || {
|
|
||||||
let _ = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
});
|
|
||||||
valid(&ctx.device, || texture.destroy());
|
|
||||||
valid(&ctx.device, || texture.destroy());
|
|
||||||
});
|
});
|
||||||
}
|
fail(&ctx.device, || buffer.unmap());
|
||||||
|
valid(&ctx.device, || buffer.destroy());
|
||||||
|
valid(&ctx.device, || buffer.destroy());
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static BAD_TEXTURE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
let texture = fail(&ctx.device, || {
|
||||||
|
ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: 0,
|
||||||
|
height: 12345678,
|
||||||
|
depth_or_array_layers: 9001,
|
||||||
|
},
|
||||||
|
mip_level_count: 2000,
|
||||||
|
sample_count: 27,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::all(),
|
||||||
|
view_formats: &[],
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
let _ = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
});
|
||||||
|
valid(&ctx.device, || texture.destroy());
|
||||||
|
valid(&ctx.device, || texture.destroy());
|
||||||
|
});
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use wgpu_test::{image, initialize_test, TestParameters, TestingContext};
|
use wgpu_test::{gpu_test, image, GpuTestConfiguration, TestingContext};
|
||||||
|
|
||||||
struct Rect {
|
struct Rect {
|
||||||
x: u32,
|
x: u32,
|
||||||
@ -97,25 +97,23 @@ fn scissor_test_impl(ctx: &TestingContext, scissor_rect: Rect, expected_data: [u
|
|||||||
assert!(readback_buffer.check_buffer_contents(&ctx.device, &expected_data));
|
assert!(readback_buffer.check_buffer_contents(&ctx.device, &expected_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn scissor_test_full_rect() {
|
static SCISSOR_TEST_FULL_RECT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
scissor_test_impl(
|
||||||
scissor_test_impl(
|
&ctx,
|
||||||
&ctx,
|
Rect {
|
||||||
Rect {
|
x: 0,
|
||||||
x: 0,
|
y: 0,
|
||||||
y: 0,
|
width: TEXTURE_WIDTH,
|
||||||
width: TEXTURE_WIDTH,
|
height: TEXTURE_HEIGHT,
|
||||||
height: TEXTURE_HEIGHT,
|
},
|
||||||
},
|
[255; BUFFER_SIZE],
|
||||||
[255; BUFFER_SIZE],
|
);
|
||||||
);
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn scissor_test_empty_rect() {
|
static SCISSOR_TEST_EMPTY_RECT: GpuTestConfiguration =
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
scissor_test_impl(
|
scissor_test_impl(
|
||||||
&ctx,
|
&ctx,
|
||||||
Rect {
|
Rect {
|
||||||
@ -126,12 +124,11 @@ fn scissor_test_empty_rect() {
|
|||||||
},
|
},
|
||||||
[0; BUFFER_SIZE],
|
[0; BUFFER_SIZE],
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn scissor_test_empty_rect_with_offset() {
|
static SCISSOR_TEST_EMPTY_RECT_WITH_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
.run_sync(|ctx| {
|
||||||
scissor_test_impl(
|
scissor_test_impl(
|
||||||
&ctx,
|
&ctx,
|
||||||
Rect {
|
Rect {
|
||||||
@ -142,15 +139,15 @@ fn scissor_test_empty_rect_with_offset() {
|
|||||||
},
|
},
|
||||||
[0; BUFFER_SIZE],
|
[0; BUFFER_SIZE],
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
#[gpu_test]
|
||||||
|
static SCISSOR_TEST_CUSTOM_RECT: GpuTestConfiguration =
|
||||||
|
GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
|
let mut expected_result = [0; BUFFER_SIZE];
|
||||||
|
expected_result[((3 * BUFFER_SIZE) / 4)..][..BUFFER_SIZE / 4]
|
||||||
|
.copy_from_slice(&[255; BUFFER_SIZE / 4]);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scissor_test_custom_rect() {
|
|
||||||
let mut expected_result = [0; BUFFER_SIZE];
|
|
||||||
expected_result[((3 * BUFFER_SIZE) / 4)..][..BUFFER_SIZE / 4]
|
|
||||||
.copy_from_slice(&[255; BUFFER_SIZE / 4]);
|
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
|
||||||
scissor_test_impl(
|
scissor_test_impl(
|
||||||
&ctx,
|
&ctx,
|
||||||
Rect {
|
Rect {
|
||||||
@ -161,5 +158,4 @@ fn scissor_test_custom_rect() {
|
|||||||
},
|
},
|
||||||
expected_result,
|
expected_result,
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -15,9 +15,9 @@ use wgpu::{
|
|||||||
|
|
||||||
use wgpu_test::TestingContext;
|
use wgpu_test::TestingContext;
|
||||||
|
|
||||||
mod numeric_builtins;
|
pub mod numeric_builtins;
|
||||||
mod struct_layout;
|
pub mod struct_layout;
|
||||||
mod zero_init_workgroup_mem;
|
pub mod zero_init_workgroup_mem;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
enum InputStorageType {
|
enum InputStorageType {
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
use wasm_bindgen_test::*;
|
|
||||||
use wgpu::{DownlevelFlags, Limits};
|
use wgpu::{DownlevelFlags, Limits};
|
||||||
|
|
||||||
use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest};
|
use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest};
|
||||||
use wgpu_test::{initialize_test, TestParameters};
|
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
fn create_numeric_builtin_test() -> Vec<ShaderTest> {
|
fn create_numeric_builtin_test() -> Vec<ShaderTest> {
|
||||||
let mut tests = Vec::new();
|
let mut tests = Vec::new();
|
||||||
@ -38,19 +37,17 @@ fn create_numeric_builtin_test() -> Vec<ShaderTest> {
|
|||||||
tests
|
tests
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static NUMERIC_BUILTINS: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn numeric_builtins() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(Limits::downlevel_defaults()),
|
.limits(Limits::downlevel_defaults()),
|
||||||
|ctx| {
|
)
|
||||||
shader_input_output_test(
|
.run_sync(|ctx| {
|
||||||
ctx,
|
shader_input_output_test(
|
||||||
InputStorageType::Storage,
|
ctx,
|
||||||
create_numeric_builtin_test(),
|
InputStorageType::Storage,
|
||||||
);
|
create_numeric_builtin_test(),
|
||||||
},
|
);
|
||||||
);
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use wgpu::{Backends, DownlevelFlags, Features, Limits};
|
use wgpu::{Backends, DownlevelFlags, Features, Limits};
|
||||||
|
|
||||||
use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest, MAX_BUFFER_SIZE};
|
use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest, MAX_BUFFER_SIZE};
|
||||||
use wgpu_test::{initialize_test, FailureCase, TestParameters};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec<ShaderTest> {
|
fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec<ShaderTest> {
|
||||||
let input_values: Vec<_> = (0..(MAX_BUFFER_SIZE as u32 / 4)).collect();
|
let input_values: Vec<_> = (0..(MAX_BUFFER_SIZE as u32 / 4)).collect();
|
||||||
@ -175,46 +174,41 @@ fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec<ShaderTest>
|
|||||||
tests
|
tests
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static UNIFORM_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn uniform_input() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
||||||
// Validation errors thrown by the SPIR-V validator https://github.com/gfx-rs/naga/issues/2034
|
// Validation errors thrown by the SPIR-V validator https://github.com/gfx-rs/naga/issues/2034
|
||||||
.expect_fail(FailureCase::backend(wgpu::Backends::VULKAN))
|
.expect_fail(FailureCase::backend(wgpu::Backends::VULKAN))
|
||||||
.limits(Limits::downlevel_defaults()),
|
.limits(Limits::downlevel_defaults()),
|
||||||
|ctx| {
|
)
|
||||||
shader_input_output_test(
|
.run_sync(|ctx| {
|
||||||
ctx,
|
shader_input_output_test(
|
||||||
InputStorageType::Uniform,
|
ctx,
|
||||||
create_struct_layout_tests(InputStorageType::Uniform),
|
InputStorageType::Uniform,
|
||||||
);
|
create_struct_layout_tests(InputStorageType::Uniform),
|
||||||
},
|
);
|
||||||
);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static STORAGE_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn storage_input() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(Limits::downlevel_defaults()),
|
.limits(Limits::downlevel_defaults()),
|
||||||
|ctx| {
|
)
|
||||||
shader_input_output_test(
|
.run_sync(|ctx| {
|
||||||
ctx,
|
shader_input_output_test(
|
||||||
InputStorageType::Storage,
|
ctx,
|
||||||
create_struct_layout_tests(InputStorageType::Storage),
|
InputStorageType::Storage,
|
||||||
);
|
create_struct_layout_tests(InputStorageType::Storage),
|
||||||
},
|
);
|
||||||
);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static PUSH_CONSTANT_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn push_constant_input() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.features(Features::PUSH_CONSTANTS)
|
.features(Features::PUSH_CONSTANTS)
|
||||||
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
||||||
@ -223,12 +217,11 @@ fn push_constant_input() {
|
|||||||
..Limits::downlevel_defaults()
|
..Limits::downlevel_defaults()
|
||||||
})
|
})
|
||||||
.expect_fail(FailureCase::backend(Backends::GL)),
|
.expect_fail(FailureCase::backend(Backends::GL)),
|
||||||
|ctx| {
|
)
|
||||||
shader_input_output_test(
|
.run_sync(|ctx| {
|
||||||
ctx,
|
shader_input_output_test(
|
||||||
InputStorageType::PushConstant,
|
ctx,
|
||||||
create_struct_layout_tests(InputStorageType::PushConstant),
|
InputStorageType::PushConstant,
|
||||||
);
|
create_struct_layout_tests(InputStorageType::PushConstant),
|
||||||
},
|
);
|
||||||
);
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -8,29 +8,152 @@ use wgpu::{
|
|||||||
ShaderStages,
|
ShaderStages,
|
||||||
};
|
};
|
||||||
|
|
||||||
use wgpu_test::{initialize_test, FailureCase, TestParameters, TestingContext};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn zero_init_workgroup_mem() {
|
static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
|
||||||
.limits(Limits::downlevel_defaults())
|
.limits(Limits::downlevel_defaults())
|
||||||
// remove both of these once we get to https://github.com/gfx-rs/wgpu/issues/3193 or
|
// remove once we get to https://github.com/gfx-rs/wgpu/issues/3193
|
||||||
// https://github.com/gfx-rs/wgpu/issues/3160
|
|
||||||
.skip(FailureCase {
|
.skip(FailureCase {
|
||||||
backends: Some(Backends::DX12),
|
backends: Some(Backends::DX12),
|
||||||
vendor: Some(5140),
|
vendor: Some(5140),
|
||||||
adapter: Some("Microsoft Basic Render Driver"),
|
adapter: Some("Microsoft Basic Render Driver"),
|
||||||
..FailureCase::default()
|
..FailureCase::default()
|
||||||
})
|
})
|
||||||
.skip(FailureCase::backend_adapter(
|
.skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")),
|
||||||
Backends::VULKAN,
|
)
|
||||||
"swiftshader",
|
.run_sync(|ctx| {
|
||||||
)),
|
let bgl = ctx
|
||||||
zero_init_workgroup_mem_impl,
|
.device
|
||||||
);
|
.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
}
|
label: None,
|
||||||
|
entries: &[BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: ShaderStages::COMPUTE,
|
||||||
|
ty: BindingType::Buffer {
|
||||||
|
ty: BufferBindingType::Storage { read_only: false },
|
||||||
|
has_dynamic_offset: true,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let output_buffer = ctx.device.create_buffer(&BufferDescriptor {
|
||||||
|
label: Some("output buffer"),
|
||||||
|
size: BUFFER_SIZE,
|
||||||
|
usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC | BufferUsages::STORAGE,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor {
|
||||||
|
label: Some("mapping buffer"),
|
||||||
|
size: BUFFER_SIZE,
|
||||||
|
usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bg = ctx.device.create_bind_group(&BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &bgl,
|
||||||
|
entries: &[BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: BindingResource::Buffer(BufferBinding {
|
||||||
|
buffer: &output_buffer,
|
||||||
|
offset: 0,
|
||||||
|
size: Some(NonZeroU64::new(BUFFER_BINDING_SIZE as u64).unwrap()),
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let pll = ctx
|
||||||
|
.device
|
||||||
|
.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&bgl],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let sm = ctx
|
||||||
|
.device
|
||||||
|
.create_shader_module(include_wgsl!("zero_init_workgroup_mem.wgsl"));
|
||||||
|
|
||||||
|
let pipeline_read = ctx
|
||||||
|
.device
|
||||||
|
.create_compute_pipeline(&ComputePipelineDescriptor {
|
||||||
|
label: Some("pipeline read"),
|
||||||
|
layout: Some(&pll),
|
||||||
|
module: &sm,
|
||||||
|
entry_point: "read",
|
||||||
|
});
|
||||||
|
|
||||||
|
let pipeline_write = ctx
|
||||||
|
.device
|
||||||
|
.create_compute_pipeline(&ComputePipelineDescriptor {
|
||||||
|
label: Some("pipeline write"),
|
||||||
|
layout: None,
|
||||||
|
module: &sm,
|
||||||
|
entry_point: "write",
|
||||||
|
});
|
||||||
|
|
||||||
|
// -- Initializing data --
|
||||||
|
|
||||||
|
let output_pre_init_data = vec![1; OUTPUT_ARRAY_SIZE as usize];
|
||||||
|
ctx.queue.write_buffer(
|
||||||
|
&output_buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&output_pre_init_data),
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- Run test --
|
||||||
|
|
||||||
|
let mut encoder = ctx
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&CommandEncoderDescriptor::default());
|
||||||
|
|
||||||
|
let mut cpass = encoder.begin_compute_pass(&ComputePassDescriptor::default());
|
||||||
|
|
||||||
|
cpass.set_pipeline(&pipeline_write);
|
||||||
|
for _ in 0..NR_OF_DISPATCHES {
|
||||||
|
cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpass.set_pipeline(&pipeline_read);
|
||||||
|
for i in 0..NR_OF_DISPATCHES {
|
||||||
|
cpass.set_bind_group(0, &bg, &[i * BUFFER_BINDING_SIZE]);
|
||||||
|
cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2);
|
||||||
|
}
|
||||||
|
drop(cpass);
|
||||||
|
|
||||||
|
// -- Pulldown data --
|
||||||
|
|
||||||
|
encoder.copy_buffer_to_buffer(&output_buffer, 0, &mapping_buffer, 0, BUFFER_SIZE);
|
||||||
|
|
||||||
|
ctx.queue.submit(Some(encoder.finish()));
|
||||||
|
|
||||||
|
mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());
|
||||||
|
ctx.device.poll(Maintain::Wait);
|
||||||
|
|
||||||
|
let mapped = mapping_buffer.slice(..).get_mapped_range();
|
||||||
|
|
||||||
|
let typed: &[u32] = bytemuck::cast_slice(&mapped);
|
||||||
|
|
||||||
|
// -- Check results --
|
||||||
|
|
||||||
|
let num_disptaches_failed = typed.iter().filter(|&&res| res != 0).count();
|
||||||
|
let ratio = (num_disptaches_failed as f32 / OUTPUT_ARRAY_SIZE as f32) * 100.;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
num_disptaches_failed == 0,
|
||||||
|
"Zero-initialization of workgroup memory failed ({ratio:.0}% of disptaches failed)."
|
||||||
|
);
|
||||||
|
|
||||||
|
drop(mapped);
|
||||||
|
mapping_buffer.unmap();
|
||||||
|
});
|
||||||
|
|
||||||
const DISPATCH_SIZE: (u32, u32, u32) = (64, 64, 64);
|
const DISPATCH_SIZE: (u32, u32, u32) = (64, 64, 64);
|
||||||
const TOTAL_WORK_GROUPS: u32 = DISPATCH_SIZE.0 * DISPATCH_SIZE.1 * DISPATCH_SIZE.2;
|
const TOTAL_WORK_GROUPS: u32 = DISPATCH_SIZE.0 * DISPATCH_SIZE.1 * DISPATCH_SIZE.2;
|
||||||
@ -45,133 +168,3 @@ const NR_OF_DISPATCHES: u32 =
|
|||||||
const OUTPUT_ARRAY_SIZE: u32 = TOTAL_WORK_GROUPS * NR_OF_DISPATCHES;
|
const OUTPUT_ARRAY_SIZE: u32 = TOTAL_WORK_GROUPS * NR_OF_DISPATCHES;
|
||||||
const BUFFER_SIZE: u64 = OUTPUT_ARRAY_SIZE as u64 * 4;
|
const BUFFER_SIZE: u64 = OUTPUT_ARRAY_SIZE as u64 * 4;
|
||||||
const BUFFER_BINDING_SIZE: u32 = TOTAL_WORK_GROUPS * 4;
|
const BUFFER_BINDING_SIZE: u32 = TOTAL_WORK_GROUPS * 4;
|
||||||
|
|
||||||
fn zero_init_workgroup_mem_impl(ctx: TestingContext) {
|
|
||||||
let bgl = ctx
|
|
||||||
.device
|
|
||||||
.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
entries: &[BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: ShaderStages::COMPUTE,
|
|
||||||
ty: BindingType::Buffer {
|
|
||||||
ty: BufferBindingType::Storage { read_only: false },
|
|
||||||
has_dynamic_offset: true,
|
|
||||||
min_binding_size: None,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
|
|
||||||
let output_buffer = ctx.device.create_buffer(&BufferDescriptor {
|
|
||||||
label: Some("output buffer"),
|
|
||||||
size: BUFFER_SIZE,
|
|
||||||
usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC | BufferUsages::STORAGE,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mapping_buffer = ctx.device.create_buffer(&BufferDescriptor {
|
|
||||||
label: Some("mapping buffer"),
|
|
||||||
size: BUFFER_SIZE,
|
|
||||||
usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let bg = ctx.device.create_bind_group(&BindGroupDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: &bgl,
|
|
||||||
entries: &[BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: BindingResource::Buffer(BufferBinding {
|
|
||||||
buffer: &output_buffer,
|
|
||||||
offset: 0,
|
|
||||||
size: Some(NonZeroU64::new(BUFFER_BINDING_SIZE as u64).unwrap()),
|
|
||||||
}),
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
|
|
||||||
let pll = ctx
|
|
||||||
.device
|
|
||||||
.create_pipeline_layout(&PipelineLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
bind_group_layouts: &[&bgl],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let sm = ctx
|
|
||||||
.device
|
|
||||||
.create_shader_module(include_wgsl!("zero_init_workgroup_mem.wgsl"));
|
|
||||||
|
|
||||||
let pipeline_read = ctx
|
|
||||||
.device
|
|
||||||
.create_compute_pipeline(&ComputePipelineDescriptor {
|
|
||||||
label: Some("pipeline read"),
|
|
||||||
layout: Some(&pll),
|
|
||||||
module: &sm,
|
|
||||||
entry_point: "read",
|
|
||||||
});
|
|
||||||
|
|
||||||
let pipeline_write = ctx
|
|
||||||
.device
|
|
||||||
.create_compute_pipeline(&ComputePipelineDescriptor {
|
|
||||||
label: Some("pipeline write"),
|
|
||||||
layout: None,
|
|
||||||
module: &sm,
|
|
||||||
entry_point: "write",
|
|
||||||
});
|
|
||||||
|
|
||||||
// -- Initializing data --
|
|
||||||
|
|
||||||
let output_pre_init_data = vec![1; OUTPUT_ARRAY_SIZE as usize];
|
|
||||||
ctx.queue.write_buffer(
|
|
||||||
&output_buffer,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(&output_pre_init_data),
|
|
||||||
);
|
|
||||||
|
|
||||||
// -- Run test --
|
|
||||||
|
|
||||||
let mut encoder = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
let mut cpass = encoder.begin_compute_pass(&ComputePassDescriptor::default());
|
|
||||||
|
|
||||||
cpass.set_pipeline(&pipeline_write);
|
|
||||||
for _ in 0..NR_OF_DISPATCHES {
|
|
||||||
cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpass.set_pipeline(&pipeline_read);
|
|
||||||
for i in 0..NR_OF_DISPATCHES {
|
|
||||||
cpass.set_bind_group(0, &bg, &[i * BUFFER_BINDING_SIZE]);
|
|
||||||
cpass.dispatch_workgroups(DISPATCH_SIZE.0, DISPATCH_SIZE.1, DISPATCH_SIZE.2);
|
|
||||||
}
|
|
||||||
drop(cpass);
|
|
||||||
|
|
||||||
// -- Pulldown data --
|
|
||||||
|
|
||||||
encoder.copy_buffer_to_buffer(&output_buffer, 0, &mapping_buffer, 0, BUFFER_SIZE);
|
|
||||||
|
|
||||||
ctx.queue.submit(Some(encoder.finish()));
|
|
||||||
|
|
||||||
mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());
|
|
||||||
ctx.device.poll(Maintain::Wait);
|
|
||||||
|
|
||||||
let mapped = mapping_buffer.slice(..).get_mapped_range();
|
|
||||||
|
|
||||||
let typed: &[u32] = bytemuck::cast_slice(&mapped);
|
|
||||||
|
|
||||||
// -- Check results --
|
|
||||||
|
|
||||||
let num_disptaches_failed = typed.iter().filter(|&&res| res != 0).count();
|
|
||||||
let ratio = (num_disptaches_failed as f32 / OUTPUT_ARRAY_SIZE as f32) * 100.;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
num_disptaches_failed == 0,
|
|
||||||
"Zero-initialization of workgroup memory failed ({ratio:.0}% of disptaches failed)."
|
|
||||||
);
|
|
||||||
|
|
||||||
drop(mapped);
|
|
||||||
mapping_buffer.unmap();
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
use wasm_bindgen_test::*;
|
|
||||||
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use wgpu_test::{image, initialize_test, TestParameters, TestingContext};
|
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};
|
||||||
|
|
||||||
//
|
//
|
||||||
// These tests render two triangles to a 2x2 render target. The first triangle
|
// These tests render two triangles to a 2x2 render target. The first triangle
|
||||||
@ -37,55 +35,52 @@ use wgpu_test::{image, initialize_test, TestParameters, TestingContext};
|
|||||||
// draw_indexed() draws the triangles in the opposite order, using index
|
// draw_indexed() draws the triangles in the opposite order, using index
|
||||||
// buffer [3, 4, 5, 0, 1, 2]. This also swaps the resulting pixel colors.
|
// buffer [3, 4, 5, 0, 1, 2]. This also swaps the resulting pixel colors.
|
||||||
//
|
//
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn draw() {
|
|
||||||
//
|
|
||||||
// +-----+-----+
|
|
||||||
// |white|blue |
|
|
||||||
// +-----+-----+
|
|
||||||
// | red |white|
|
|
||||||
// +-----+-----+
|
|
||||||
//
|
|
||||||
let expected = [
|
|
||||||
255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255,
|
|
||||||
];
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
|
||||||
.test_features_limits()
|
|
||||||
.features(wgpu::Features::SHADER_PRIMITIVE_INDEX),
|
|
||||||
|ctx| {
|
|
||||||
pulling_common(ctx, &expected, |rpass| {
|
|
||||||
rpass.draw(0..6, 0..1);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static DRAW: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn draw_indexed() {
|
.parameters(
|
||||||
//
|
|
||||||
// +-----+-----+
|
|
||||||
// |white| red |
|
|
||||||
// +-----+-----+
|
|
||||||
// |blue |white|
|
|
||||||
// +-----+-----+
|
|
||||||
//
|
|
||||||
let expected = [
|
|
||||||
255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255,
|
|
||||||
];
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.test_features_limits()
|
.test_features_limits()
|
||||||
.features(wgpu::Features::SHADER_PRIMITIVE_INDEX),
|
.features(wgpu::Features::SHADER_PRIMITIVE_INDEX),
|
||||||
|ctx| {
|
)
|
||||||
pulling_common(ctx, &expected, |rpass| {
|
.run_sync(|ctx| {
|
||||||
rpass.draw_indexed(0..6, 0, 0..1);
|
//
|
||||||
})
|
// +-----+-----+
|
||||||
},
|
// |white|blue |
|
||||||
);
|
// +-----+-----+
|
||||||
}
|
// | red |white|
|
||||||
|
// +-----+-----+
|
||||||
|
//
|
||||||
|
let expected = [
|
||||||
|
255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255,
|
||||||
|
];
|
||||||
|
pulling_common(ctx, &expected, |rpass| {
|
||||||
|
rpass.draw(0..6, 0..1);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static DRAW_INDEXED: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
|
.parameters(
|
||||||
|
TestParameters::default()
|
||||||
|
.test_features_limits()
|
||||||
|
.features(wgpu::Features::SHADER_PRIMITIVE_INDEX),
|
||||||
|
)
|
||||||
|
.run_sync(|ctx| {
|
||||||
|
//
|
||||||
|
// +-----+-----+
|
||||||
|
// |white| red |
|
||||||
|
// +-----+-----+
|
||||||
|
// |blue |white|
|
||||||
|
// +-----+-----+
|
||||||
|
//
|
||||||
|
let expected = [
|
||||||
|
255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255,
|
||||||
|
];
|
||||||
|
pulling_common(ctx, &expected, |rpass| {
|
||||||
|
rpass.draw_indexed(0..6, 0, 0..1);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
fn pulling_common(
|
fn pulling_common(
|
||||||
ctx: TestingContext,
|
ctx: TestingContext,
|
||||||
@ -169,7 +164,7 @@ fn pulling_common(
|
|||||||
});
|
});
|
||||||
let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
let readback_buffer = image::ReadbackBuffers::new(&ctx.device, &color_texture);
|
let readback_buffer = wgpu_test::image::ReadbackBuffers::new(&ctx.device, &color_texture);
|
||||||
|
|
||||||
let mut encoder = ctx
|
let mut encoder = ctx
|
||||||
.device
|
.device
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat};
|
use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat};
|
||||||
use wgpu_test::{
|
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};
|
||||||
image::calc_difference, initialize_test, FailureCase, TestParameters, TestingContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn reinterpret_srgb_ness() {
|
static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
let parameters = TestParameters::default()
|
.parameters(
|
||||||
.downlevel_flags(DownlevelFlags::VIEW_FORMATS)
|
TestParameters::default()
|
||||||
.limits(Limits::downlevel_defaults())
|
.downlevel_flags(DownlevelFlags::VIEW_FORMATS)
|
||||||
.skip(FailureCase {
|
.limits(Limits::downlevel_defaults()),
|
||||||
backends: Some(wgpu::Backends::GL),
|
)
|
||||||
..FailureCase::default()
|
.run_sync(|ctx| {
|
||||||
});
|
|
||||||
initialize_test(parameters, |ctx| {
|
|
||||||
let unorm_data: [[u8; 4]; 4] = [
|
let unorm_data: [[u8; 4]; 4] = [
|
||||||
[180, 0, 0, 255],
|
[180, 0, 0, 255],
|
||||||
[0, 84, 0, 127],
|
[0, 84, 0, 127],
|
||||||
@ -58,7 +54,6 @@ fn reinterpret_srgb_ness() {
|
|||||||
&unorm_data,
|
&unorm_data,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
fn reinterpret(
|
fn reinterpret(
|
||||||
ctx: &TestingContext,
|
ctx: &TestingContext,
|
||||||
@ -193,10 +188,10 @@ fn reinterpret(
|
|||||||
let expect = expect_data[(h * size.width + w) as usize];
|
let expect = expect_data[(h * size.width + w) as usize];
|
||||||
let tolerance = tolerance_data[(h * size.width + w) as usize];
|
let tolerance = tolerance_data[(h * size.width + w) as usize];
|
||||||
let index = (w * 4 + offset) as usize;
|
let index = (w * 4 + offset) as usize;
|
||||||
if calc_difference(expect[0], data[index]) > tolerance[0]
|
if expect[0].abs_diff(data[index]) > tolerance[0]
|
||||||
|| calc_difference(expect[1], data[index + 1]) > tolerance[1]
|
|| expect[1].abs_diff(data[index + 1]) > tolerance[1]
|
||||||
|| calc_difference(expect[2], data[index + 2]) > tolerance[2]
|
|| expect[2].abs_diff(data[index + 2]) > tolerance[2]
|
||||||
|| calc_difference(expect[3], data[index + 3]) > tolerance[3]
|
|| expect[3].abs_diff(data[index + 3]) > tolerance[3]
|
||||||
{
|
{
|
||||||
panic!(
|
panic!(
|
||||||
"Reinterpret {:?} as {:?} mismatch! expect {:?} get [{}, {}, {}, {}]",
|
"Reinterpret {:?} as {:?} mismatch! expect {:?} get [{}, {}, {}, {}]",
|
||||||
|
|||||||
@ -1,33 +1,27 @@
|
|||||||
//! Tests for texture copy bounds checks.
|
//! Tests for texture copy bounds checks.
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration};
|
||||||
use wgpu_test::{fail_if, initialize_test, TestParameters};
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
fn bad_copy_origin() {
|
let try_origin = |origin, size, should_panic| {
|
||||||
fn try_origin(origin: wgpu::Origin3d, size: wgpu::Extent3d, should_panic: bool) {
|
let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR);
|
||||||
let parameters = TestParameters::default();
|
let data = vec![255; BUFFER_SIZE as usize];
|
||||||
|
|
||||||
initialize_test(parameters, |ctx| {
|
fail_if(&ctx.device, should_panic, || {
|
||||||
let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR);
|
ctx.queue.write_texture(
|
||||||
let data = vec![255; BUFFER_SIZE as usize];
|
wgpu::ImageCopyTexture {
|
||||||
|
texture: &texture,
|
||||||
fail_if(&ctx.device, should_panic, || {
|
mip_level: 0,
|
||||||
ctx.queue.write_texture(
|
origin,
|
||||||
wgpu::ImageCopyTexture {
|
aspect: wgpu::TextureAspect::All,
|
||||||
texture: &texture,
|
},
|
||||||
mip_level: 0,
|
&data,
|
||||||
origin,
|
BUFFER_COPY_LAYOUT,
|
||||||
aspect: wgpu::TextureAspect::All,
|
size,
|
||||||
},
|
)
|
||||||
&data,
|
|
||||||
BUFFER_COPY_LAYOUT,
|
|
||||||
size,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false);
|
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false);
|
||||||
try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true);
|
try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true);
|
||||||
@ -86,7 +80,7 @@ fn bad_copy_origin() {
|
|||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
const TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d {
|
const TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d {
|
||||||
width: 64,
|
width: 64,
|
||||||
|
|||||||
@ -1,69 +1,65 @@
|
|||||||
use wgpu_test::{fail, initialize_test, TestParameters};
|
use wgpu_test::{fail, gpu_test, GpuTestConfiguration};
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
fn copy_overflow_z() {
|
static COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
// A simple crash test exercising validation that used to happen a bit too
|
let mut encoder = ctx
|
||||||
// late, letting an integer overflow slip through.
|
.device
|
||||||
initialize_test(TestParameters::default(), |ctx| {
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
||||||
let mut encoder = ctx
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
||||||
|
|
||||||
let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
size: wgpu::Extent3d {
|
size: wgpu::Extent3d {
|
||||||
width: 256,
|
width: 256,
|
||||||
height: 256,
|
height: 256,
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
format: wgpu::TextureFormat::Rgba8Uint,
|
||||||
|
usage: wgpu::TextureUsages::COPY_DST,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: 256,
|
||||||
|
height: 256,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
format: wgpu::TextureFormat::Rgba8Uint,
|
||||||
|
usage: wgpu::TextureUsages::COPY_DST,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
fail(&ctx.device, || {
|
||||||
|
// Validation should catch the silly selected z layer range without panicking.
|
||||||
|
encoder.copy_texture_to_texture(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
texture: &t1,
|
||||||
|
mip_level: 1,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
},
|
},
|
||||||
format: wgpu::TextureFormat::Rgba8Uint,
|
wgpu::ImageCopyTexture {
|
||||||
usage: wgpu::TextureUsages::COPY_DST,
|
texture: &t2,
|
||||||
mip_level_count: 1,
|
mip_level: 1,
|
||||||
sample_count: 1,
|
origin: wgpu::Origin3d {
|
||||||
view_formats: &[],
|
x: 0,
|
||||||
});
|
y: 0,
|
||||||
let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
z: 3824276442,
|
||||||
label: None,
|
},
|
||||||
dimension: wgpu::TextureDimension::D2,
|
aspect: wgpu::TextureAspect::All,
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: 256,
|
|
||||||
height: 256,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
},
|
||||||
format: wgpu::TextureFormat::Rgba8Uint,
|
wgpu::Extent3d {
|
||||||
usage: wgpu::TextureUsages::COPY_DST,
|
width: 100,
|
||||||
mip_level_count: 1,
|
height: 3,
|
||||||
sample_count: 1,
|
depth_or_array_layers: 613286111,
|
||||||
view_formats: &[],
|
},
|
||||||
});
|
);
|
||||||
|
ctx.queue.submit(Some(encoder.finish()));
|
||||||
fail(&ctx.device, || {
|
});
|
||||||
// Validation should catch the silly selected z layer range without panicking.
|
});
|
||||||
encoder.copy_texture_to_texture(
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
texture: &t1,
|
|
||||||
mip_level: 1,
|
|
||||||
origin: wgpu::Origin3d::ZERO,
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
},
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
texture: &t2,
|
|
||||||
mip_level: 1,
|
|
||||||
origin: wgpu::Origin3d {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: 3824276442,
|
|
||||||
},
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
},
|
|
||||||
wgpu::Extent3d {
|
|
||||||
width: 100,
|
|
||||||
height: 3,
|
|
||||||
depth_or_array_layers: 613286111,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
ctx.queue.submit(Some(encoder.finish()));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use wgpu_test::{initialize_test, FailureCase, TestParameters, TestingContext};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext};
|
||||||
|
|
||||||
fn pulling_common(
|
fn pulling_common(
|
||||||
ctx: TestingContext,
|
ctx: TestingContext,
|
||||||
@ -134,54 +133,48 @@ fn pulling_common(
|
|||||||
assert_eq!(data, expected);
|
assert_eq!(data, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static DRAW: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn draw() {
|
.parameters(TestParameters::default().test_features_limits())
|
||||||
initialize_test(TestParameters::default().test_features_limits(), |ctx| {
|
.run_sync(|ctx| {
|
||||||
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
||||||
cmb.draw(0..6, 0..1);
|
cmb.draw(0..6, 0..1);
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static DRAW_VERTEX: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn draw_vertex_offset() {
|
.parameters(TestParameters::default().test_features_limits())
|
||||||
initialize_test(
|
.run_sync(|ctx| {
|
||||||
|
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
||||||
|
cmb.draw(0..3, 0..1);
|
||||||
|
cmb.draw(3..6, 0..1);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static DRAW_INSTANCED: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.test_features_limits()
|
.test_features_limits()
|
||||||
.expect_fail(FailureCase::backend(wgpu::Backends::DX11)),
|
.expect_fail(FailureCase::backend(wgpu::Backends::DX11)),
|
||||||
|ctx| {
|
|
||||||
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
|
||||||
cmb.draw(0..3, 0..1);
|
|
||||||
cmb.draw(3..6, 0..1);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn draw_instanced() {
|
|
||||||
initialize_test(TestParameters::default().test_features_limits(), |ctx| {
|
|
||||||
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
||||||
cmb.draw(0..3, 0..2);
|
cmb.draw(0..3, 0..2);
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static DRAW_INSTANCED_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn draw_instanced_offset() {
|
.parameters(
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.test_features_limits()
|
.test_features_limits()
|
||||||
.expect_fail(FailureCase::backend(wgpu::Backends::DX11)),
|
.expect_fail(FailureCase::backend(wgpu::Backends::DX11)),
|
||||||
|ctx| {
|
|
||||||
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
|
||||||
cmb.draw(0..3, 0..1);
|
|
||||||
cmb.draw(0..3, 1..2);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
.run_sync(|ctx| {
|
||||||
|
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
|
||||||
|
cmb.draw(0..3, 0..1);
|
||||||
|
cmb.draw(0..3, 1..2);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
//! Tests for texture copy
|
//! Tests for texture copy
|
||||||
|
|
||||||
use wgpu_test::{initialize_test, FailureCase, TestParameters};
|
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
#[gpu_test]
|
||||||
|
static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
|
.parameters(TestParameters::default().expect_fail(FailureCase::backend(wgpu::Backends::DX12)))
|
||||||
|
.run_sync(|ctx| {
|
||||||
|
let size = 256;
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn write_texture_subset_2d() {
|
|
||||||
let size = 256;
|
|
||||||
let parameters =
|
|
||||||
TestParameters::default().expect_fail(FailureCase::backend(wgpu::Backends::DX12));
|
|
||||||
initialize_test(parameters, |ctx| {
|
|
||||||
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
@ -98,15 +95,12 @@ fn write_texture_subset_2d() {
|
|||||||
assert_eq!(*byte, 0);
|
assert_eq!(*byte, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static WRITE_TEXTURE_SUBSET_3D: GpuTestConfiguration =
|
||||||
fn write_texture_subset_3d() {
|
GpuTestConfiguration::new().run_sync(|ctx| {
|
||||||
let size = 256;
|
let size = 256;
|
||||||
let depth = 4;
|
let depth = 4;
|
||||||
let parameters = TestParameters::default();
|
|
||||||
initialize_test(parameters, |ctx| {
|
|
||||||
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
dimension: wgpu::TextureDimension::D3,
|
dimension: wgpu::TextureDimension::D3,
|
||||||
@ -194,4 +188,3 @@ fn write_texture_subset_3d() {
|
|||||||
assert_eq!(*byte, 0);
|
assert_eq!(*byte, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,58 +1,81 @@
|
|||||||
use wasm_bindgen_test::*;
|
|
||||||
use wgpu::*;
|
use wgpu::*;
|
||||||
use wgpu_test::{
|
use wgpu_test::{
|
||||||
image::ReadbackBuffers, initialize_test, FailureCase, TestParameters, TestingContext,
|
gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters,
|
||||||
|
TestingContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder.
|
// Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder.
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT:
|
||||||
fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_after_submit() {
|
GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
|
||||||
TestParameters::default().skip(FailureCase::webgl2()),
|
.run_sync(|mut ctx| {
|
||||||
|mut ctx| {
|
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
|
||||||
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
|
case.create_command_encoder();
|
||||||
case.create_command_encoder();
|
case.discard();
|
||||||
case.discard();
|
case.submit_command_encoder();
|
||||||
case.submit_command_encoder();
|
|
||||||
|
|
||||||
case.create_command_encoder();
|
case.create_command_encoder();
|
||||||
case.copy_texture_to_buffer();
|
case.copy_texture_to_buffer();
|
||||||
case.submit_command_encoder();
|
case.submit_command_encoder();
|
||||||
|
|
||||||
case.assert_buffers_are_zero();
|
case.assert_buffers_are_zero();
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in the same encoder to a buffer.
|
#[gpu_test]
|
||||||
#[test]
|
static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER:
|
||||||
#[wasm_bindgen_test]
|
GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() {
|
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
|
||||||
initialize_test(
|
.run_sync(|mut ctx| {
|
||||||
TestParameters::default().skip(FailureCase::webgl2()),
|
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
|
||||||
|mut ctx| {
|
case.create_command_encoder();
|
||||||
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
|
case.discard();
|
||||||
case.create_command_encoder();
|
case.copy_texture_to_buffer();
|
||||||
case.discard();
|
case.submit_command_encoder();
|
||||||
case.copy_texture_to_buffer();
|
|
||||||
case.submit_command_encoder();
|
|
||||||
|
|
||||||
case.assert_buffers_are_zero();
|
case.assert_buffers_are_zero();
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[gpu_test]
|
||||||
#[wasm_bindgen_test]
|
static DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER:
|
||||||
fn discarding_depth_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() {
|
GpuTestConfiguration = GpuTestConfiguration::new()
|
||||||
initialize_test(
|
.parameters(
|
||||||
TestParameters::default()
|
TestParameters::default()
|
||||||
.downlevel_flags(
|
.downlevel_flags(
|
||||||
DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS,
|
DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS,
|
||||||
)
|
)
|
||||||
.limits(Limits::downlevel_defaults()),
|
.limits(Limits::downlevel_defaults()),
|
||||||
|mut ctx| {
|
)
|
||||||
|
.run_sync(|mut ctx| {
|
||||||
|
for format in [
|
||||||
|
TextureFormat::Stencil8,
|
||||||
|
TextureFormat::Depth16Unorm,
|
||||||
|
TextureFormat::Depth24Plus,
|
||||||
|
TextureFormat::Depth24PlusStencil8,
|
||||||
|
TextureFormat::Depth32Float,
|
||||||
|
] {
|
||||||
|
let mut case = TestCase::new(&mut ctx, format);
|
||||||
|
case.create_command_encoder();
|
||||||
|
case.discard();
|
||||||
|
case.copy_texture_to_buffer();
|
||||||
|
case.submit_command_encoder();
|
||||||
|
|
||||||
|
case.assert_buffers_are_zero();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[gpu_test]
|
||||||
|
static DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST: GpuTestConfiguration =
|
||||||
|
GpuTestConfiguration::new()
|
||||||
|
.parameters(
|
||||||
|
TestParameters::default()
|
||||||
|
.downlevel_flags(
|
||||||
|
DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES
|
||||||
|
| DownlevelFlags::COMPUTE_SHADERS,
|
||||||
|
)
|
||||||
|
.limits(Limits::downlevel_defaults()),
|
||||||
|
)
|
||||||
|
.run_sync(|mut ctx| {
|
||||||
for format in [
|
for format in [
|
||||||
TextureFormat::Stencil8,
|
TextureFormat::Stencil8,
|
||||||
TextureFormat::Depth16Unorm,
|
TextureFormat::Depth16Unorm,
|
||||||
@ -62,43 +85,20 @@ fn discarding_depth_target_resets_texture_init_state_check_visible_on_copy_in_sa
|
|||||||
] {
|
] {
|
||||||
let mut case = TestCase::new(&mut ctx, format);
|
let mut case = TestCase::new(&mut ctx, format);
|
||||||
case.create_command_encoder();
|
case.create_command_encoder();
|
||||||
case.discard();
|
case.discard_depth();
|
||||||
|
case.submit_command_encoder();
|
||||||
|
|
||||||
|
case.create_command_encoder();
|
||||||
|
case.discard_stencil();
|
||||||
|
case.submit_command_encoder();
|
||||||
|
|
||||||
|
case.create_command_encoder();
|
||||||
case.copy_texture_to_buffer();
|
case.copy_texture_to_buffer();
|
||||||
case.submit_command_encoder();
|
case.submit_command_encoder();
|
||||||
|
|
||||||
case.assert_buffers_are_zero();
|
case.assert_buffers_are_zero();
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn discarding_either_depth_or_stencil_aspect() {
|
|
||||||
initialize_test(
|
|
||||||
TestParameters::default()
|
|
||||||
.downlevel_flags(
|
|
||||||
DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES | DownlevelFlags::COMPUTE_SHADERS,
|
|
||||||
)
|
|
||||||
.limits(Limits::downlevel_defaults()),
|
|
||||||
|mut ctx| {
|
|
||||||
let mut case = TestCase::new(&mut ctx, TextureFormat::Depth24PlusStencil8);
|
|
||||||
case.create_command_encoder();
|
|
||||||
case.discard_depth();
|
|
||||||
case.submit_command_encoder();
|
|
||||||
|
|
||||||
case.create_command_encoder();
|
|
||||||
case.discard_stencil();
|
|
||||||
case.submit_command_encoder();
|
|
||||||
|
|
||||||
case.create_command_encoder();
|
|
||||||
case.copy_texture_to_buffer();
|
|
||||||
case.submit_command_encoder();
|
|
||||||
|
|
||||||
case.assert_buffers_are_zero();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestCase<'ctx> {
|
struct TestCase<'ctx> {
|
||||||
ctx: &'ctx mut TestingContext,
|
ctx: &'ctx mut TestingContext,
|
||||||
|
|||||||
@ -80,7 +80,8 @@ impl Instance {
|
|||||||
|
|
||||||
impl crate::Instance<Api> for Instance {
|
impl crate::Instance<Api> for Instance {
|
||||||
unsafe fn init(_desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
|
unsafe fn init(_desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
|
||||||
//TODO: enable `METAL_DEVICE_WRAPPER_TYPE` environment based on the flags?
|
// We do not enable metal validation based on the validation flags as it affects the entire
|
||||||
|
// process. Instead, we enable the validation inside the test harness itself in tests/src/native.rs.
|
||||||
Ok(Instance {
|
Ok(Instance {
|
||||||
managed_metal_layer_delegate: surface::HalManagedMetalLayerDelegate::new(),
|
managed_metal_layer_delegate: surface::HalManagedMetalLayerDelegate::new(),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -7,6 +7,9 @@ use wgpu::{
|
|||||||
|
|
||||||
use crate::texture;
|
use crate::texture;
|
||||||
|
|
||||||
|
/// Report specifying the capabilities of the GPUs on the system.
|
||||||
|
///
|
||||||
|
/// Must be synchronized with the definition on tests/src/report.rs.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct GpuReport {
|
pub struct GpuReport {
|
||||||
pub devices: Vec<AdapterReport>,
|
pub devices: Vec<AdapterReport>,
|
||||||
@ -48,6 +51,9 @@ impl GpuReport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single report of the capabilities of an Adapter.
|
||||||
|
///
|
||||||
|
/// Must be synchronized with the definition on tests/src/report.rs.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AdapterReport {
|
pub struct AdapterReport {
|
||||||
pub info: AdapterInfo,
|
pub info: AdapterInfo,
|
||||||
|
|||||||
20
wgpu-macros/Cargo.toml
Normal file
20
wgpu-macros/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "wgpu-macros"
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
description = "Macros for wgpu"
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
exclude = ["Cargo.lock"]
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
heck = "0.4"
|
||||||
|
quote = "1"
|
||||||
|
syn = { version = "2", features = ["full"] }
|
||||||
44
wgpu-macros/src/lib.rs
Normal file
44
wgpu-macros/src/lib.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use heck::ToSnakeCase;
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::Ident;
|
||||||
|
|
||||||
|
/// Creates a test that will run on all gpus on a given system.
|
||||||
|
///
|
||||||
|
/// Apply this macro to a static variable with a type that can be converted to a `GpuTestConfiguration`.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn gpu_test(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let input_static = syn::parse_macro_input!(item as syn::ItemStatic);
|
||||||
|
let expr = &input_static.expr;
|
||||||
|
let ident = &input_static.ident;
|
||||||
|
let ident_str = ident.to_string();
|
||||||
|
let ident_lower = ident_str.to_snake_case();
|
||||||
|
|
||||||
|
let register_test_name = Ident::new(&format!("{}_initializer", ident_lower), ident.span());
|
||||||
|
let test_name_webgl = Ident::new(&format!("{}_webgl", ident_lower), ident.span());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[::wgpu_test::ctor]
|
||||||
|
fn #register_test_name() {
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
::wgpu_test::native::TEST_LIST.lock().push(
|
||||||
|
// Allow any type that can be converted to a GpuTestConfiguration
|
||||||
|
::wgpu_test::GpuTestConfiguration::from(#expr).name_from_init_function_typename::<S>(#ident_lower)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[wasm_bindgen_test::wasm_bindgen_test]
|
||||||
|
async fn #test_name_webgl() {
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
// Allow any type that can be converted to a GpuTestConfiguration
|
||||||
|
let test_config = ::wgpu_test::GpuTestConfiguration::from(#expr).name_from_init_function_typename::<S>(#ident_lower);
|
||||||
|
|
||||||
|
::wgpu_test::execute_test(test_config, None, 0).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
@ -95,6 +95,8 @@ optional = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arrayvec.workspace = true
|
arrayvec.workspace = true
|
||||||
|
cfg-if.workspace = true
|
||||||
|
flume.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
profiling.workspace = true
|
profiling.workspace = true
|
||||||
@ -102,7 +104,6 @@ raw-window-handle.workspace = true
|
|||||||
serde = { workspace = true, features = ["derive"], optional = true }
|
serde = { workspace = true, features = ["derive"], optional = true }
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
static_assertions.workspace = true
|
static_assertions.workspace = true
|
||||||
cfg-if.workspace = true
|
|
||||||
|
|
||||||
[dependencies.naga]
|
[dependencies.naga]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use crate::{
|
|||||||
BufferViewMut, CommandEncoder, Device, MapMode,
|
BufferViewMut, CommandEncoder, Device, MapMode,
|
||||||
};
|
};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::{mpsc, Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
struct Chunk {
|
struct Chunk {
|
||||||
buffer: Arc<Buffer>,
|
buffer: Arc<Buffer>,
|
||||||
@ -36,9 +36,9 @@ pub struct StagingBelt {
|
|||||||
/// into `active_chunks`.
|
/// into `active_chunks`.
|
||||||
free_chunks: Vec<Chunk>,
|
free_chunks: Vec<Chunk>,
|
||||||
/// When closed chunks are mapped again, the map callback sends them here.
|
/// When closed chunks are mapped again, the map callback sends them here.
|
||||||
sender: mpsc::Sender<Chunk>,
|
sender: flume::Sender<Chunk>,
|
||||||
/// Free chunks are received here to be put on `self.free_chunks`.
|
/// Free chunks are received here to be put on `self.free_chunks`.
|
||||||
receiver: mpsc::Receiver<Chunk>,
|
receiver: flume::Receiver<Chunk>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StagingBelt {
|
impl StagingBelt {
|
||||||
@ -53,7 +53,7 @@ impl StagingBelt {
|
|||||||
/// (per [`StagingBelt::finish()`]); and
|
/// (per [`StagingBelt::finish()`]); and
|
||||||
/// * bigger is better, within these bounds.
|
/// * bigger is better, within these bounds.
|
||||||
pub fn new(chunk_size: BufferAddress) -> Self {
|
pub fn new(chunk_size: BufferAddress) -> Self {
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = flume::unbounded();
|
||||||
StagingBelt {
|
StagingBelt {
|
||||||
chunk_size,
|
chunk_size,
|
||||||
active_chunks: Vec::new(),
|
active_chunks: Vec::new(),
|
||||||
|
|||||||
16
xtask/Cargo.lock
generated
16
xtask/Cargo.lock
generated
@ -626,6 +626,21 @@ version = "0.48.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xshell"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "962c039b3a7b16cf4e9a4248397c6585c07547412e7d6a6e035389a802dcfe90"
|
||||||
|
dependencies = [
|
||||||
|
"xshell-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xshell-macros"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dbabb1cbd15a1d6d12d9ed6b35cc6777d4af87ab3ba155ea37215f20beab80c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -635,4 +650,5 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"pico-args",
|
"pico-args",
|
||||||
|
"xshell",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -12,5 +12,6 @@ cargo-run-wasm = { version = "0.3.2", git = "https://github.com/ErichDonGubler/c
|
|||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
log = "0.4.18"
|
log = "0.4.18"
|
||||||
pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] }
|
pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] }
|
||||||
|
xshell = "0.2.3"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|||||||
@ -8,6 +8,8 @@ Usage: xtask <COMMAND>
|
|||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
run-wasm
|
run-wasm
|
||||||
|
test
|
||||||
|
--llvm-cov Run tests with LLVM code coverage using the llvm-cov tool
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Print help
|
-h, --help Print help
|
||||||
@ -40,6 +42,7 @@ impl Args {
|
|||||||
|
|
||||||
pub(crate) enum Subcommand {
|
pub(crate) enum Subcommand {
|
||||||
RunWasm { args: Arguments },
|
RunWasm { args: Arguments },
|
||||||
|
Test { args: Arguments },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Subcommand {
|
impl Subcommand {
|
||||||
@ -50,6 +53,7 @@ impl Subcommand {
|
|||||||
.context("no subcommand specified; see `--help` for more details")?;
|
.context("no subcommand specified; see `--help` for more details")?;
|
||||||
match &*subcmd {
|
match &*subcmd {
|
||||||
"run-wasm" => Ok(Self::RunWasm { args }),
|
"run-wasm" => Ok(Self::RunWasm { args }),
|
||||||
|
"test" => Ok(Self::Test { args }),
|
||||||
other => {
|
other => {
|
||||||
bail!("unrecognized subcommand {other:?}; see `--help` for more details")
|
bail!("unrecognized subcommand {other:?}; see `--help` for more details")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use cli::{Args, Subcommand};
|
|||||||
use pico_args::Arguments;
|
use pico_args::Arguments;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
|
mod test;
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
env_logger::builder()
|
env_logger::builder()
|
||||||
@ -29,7 +30,8 @@ fn run(args: Args) -> anyhow::Result<()> {
|
|||||||
match subcommand {
|
match subcommand {
|
||||||
Subcommand::RunWasm { mut args } => {
|
Subcommand::RunWasm { mut args } => {
|
||||||
// Use top-level Cargo.toml instead of xtask/Cargo.toml by default
|
// Use top-level Cargo.toml instead of xtask/Cargo.toml by default
|
||||||
let manifest_path = args.value_from_str("--manifest-path")
|
let manifest_path = args
|
||||||
|
.value_from_str("--manifest-path")
|
||||||
.unwrap_or_else(|_| "../Cargo.toml".to_string());
|
.unwrap_or_else(|_| "../Cargo.toml".to_string());
|
||||||
let mut arg_vec = args.finish();
|
let mut arg_vec = args.finish();
|
||||||
arg_vec.push("--manifest-path".into());
|
arg_vec.push("--manifest-path".into());
|
||||||
@ -44,5 +46,6 @@ fn run(args: Args) -> anyhow::Result<()> {
|
|||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Subcommand::Test { args } => test::run_tests(args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
xtask/src/test.rs
Normal file
58
xtask/src/test.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use anyhow::Context;
|
||||||
|
use pico_args::Arguments;
|
||||||
|
|
||||||
|
pub fn run_tests(mut args: Arguments) -> anyhow::Result<()> {
|
||||||
|
let llvm_cov = args.contains("--llvm-cov");
|
||||||
|
let llvm_cov_flags: &[_] = if llvm_cov {
|
||||||
|
&["llvm-cov", "--no-cfg-coverage", "--no-report"]
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
};
|
||||||
|
let llvm_cov_nextest_flags: &[_] = if llvm_cov {
|
||||||
|
&["llvm-cov", "--no-cfg-coverage", "--no-report", "nextest"]
|
||||||
|
} else {
|
||||||
|
&["nextest", "run"]
|
||||||
|
};
|
||||||
|
|
||||||
|
let shell = xshell::Shell::new().context("Couldn't create xshell shell")?;
|
||||||
|
|
||||||
|
shell.change_dir(String::from(env!("CARGO_MANIFEST_DIR")) + "/..");
|
||||||
|
|
||||||
|
log::info!("Generating .gpuconfig file based on gpus on the system");
|
||||||
|
|
||||||
|
xshell::cmd!(
|
||||||
|
shell,
|
||||||
|
"cargo {llvm_cov_flags...} run --bin wgpu-info -- --json -o .gpuconfig"
|
||||||
|
)
|
||||||
|
.quiet()
|
||||||
|
.run()
|
||||||
|
.context("Failed to run wgpu-info to generate .gpuconfig")?;
|
||||||
|
|
||||||
|
let gpu_count = shell
|
||||||
|
.read_file(".gpuconfig")
|
||||||
|
.unwrap()
|
||||||
|
.lines()
|
||||||
|
.filter(|line| line.contains("name"))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"Found {} gpu{}",
|
||||||
|
gpu_count,
|
||||||
|
if gpu_count == 1 { "" } else { "s" }
|
||||||
|
);
|
||||||
|
|
||||||
|
log::info!("Running cargo tests");
|
||||||
|
|
||||||
|
xshell::cmd!(
|
||||||
|
shell,
|
||||||
|
"cargo {llvm_cov_nextest_flags...} --no-fail-fast --retries 2"
|
||||||
|
)
|
||||||
|
.args(args.finish())
|
||||||
|
.quiet()
|
||||||
|
.run()
|
||||||
|
.context("Tests failed")?;
|
||||||
|
|
||||||
|
log::info!("Finished tests");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user