mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
Update shader bencher to share some logic with snapshots (#8108)
Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
parent
d31d944ed5
commit
54ce9f7f98
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -2531,6 +2531,7 @@ dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"libm",
|
||||
"log",
|
||||
"naga-test",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"petgraph 0.8.2",
|
||||
@ -2542,7 +2543,6 @@ dependencies = [
|
||||
"spirv",
|
||||
"strum 0.27.2",
|
||||
"thiserror 2.0.16",
|
||||
"toml 0.9.5",
|
||||
"unicode-ident",
|
||||
"walkdir",
|
||||
]
|
||||
@ -2570,6 +2570,22 @@ dependencies = [
|
||||
"naga",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga-test"
|
||||
version = "26.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.3",
|
||||
"env_logger",
|
||||
"naga",
|
||||
"ron",
|
||||
"rspirv",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"spirv",
|
||||
"toml 0.9.5",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga-xtask"
|
||||
version = "0.1.0"
|
||||
@ -4946,6 +4962,7 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"criterion",
|
||||
"naga",
|
||||
"naga-test",
|
||||
"nanorand 0.8.0",
|
||||
"pollster",
|
||||
"profiling",
|
||||
|
||||
@ -10,6 +10,7 @@ members = [
|
||||
"examples/standalone/*",
|
||||
"lock-analyzer",
|
||||
"naga-cli",
|
||||
"naga-test",
|
||||
"naga",
|
||||
"naga/fuzz",
|
||||
"naga/hlsl-snapshots",
|
||||
@ -32,6 +33,7 @@ default-members = [
|
||||
"examples/standalone/*",
|
||||
"lock-analyzer",
|
||||
"naga-cli",
|
||||
"naga-test",
|
||||
"naga",
|
||||
"naga/fuzz",
|
||||
"naga/hlsl-snapshots",
|
||||
@ -64,6 +66,7 @@ authors = ["gfx-rs developers"]
|
||||
|
||||
[workspace.dependencies]
|
||||
naga = { version = "26.0.0", path = "./naga" }
|
||||
naga-test = { version = "26.0.0", path = "./naga-test" }
|
||||
wgpu = { version = "26.0.0", path = "./wgpu", default-features = false, features = [
|
||||
"serde",
|
||||
"wgsl",
|
||||
|
||||
@ -41,6 +41,7 @@ naga = { workspace = true, features = [
|
||||
"glsl-out",
|
||||
"wgsl-out",
|
||||
] }
|
||||
naga-test = { workspace = true, features = [] }
|
||||
nanorand.workspace = true
|
||||
pollster.workspace = true
|
||||
profiling.workspace = true
|
||||
|
||||
@ -1,57 +1,56 @@
|
||||
use criterion::*;
|
||||
use std::{fs, path::PathBuf, process::Command};
|
||||
use std::{fs, process::Command};
|
||||
|
||||
struct Input {
|
||||
filename: String,
|
||||
size: u64,
|
||||
const DIR_IN: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../naga/tests/in");
|
||||
|
||||
use naga_test::*;
|
||||
|
||||
struct InputWithInfo {
|
||||
inner: Input,
|
||||
data: Vec<u8>,
|
||||
string: Option<String>,
|
||||
options: Parameters,
|
||||
module: Option<naga::Module>,
|
||||
module_info: Option<naga::valid::ModuleInfo>,
|
||||
}
|
||||
impl From<Input> for InputWithInfo {
|
||||
fn from(value: Input) -> Self {
|
||||
let mut options = value.read_parameters(DIR_IN);
|
||||
options.targets = Some(options.targets.unwrap_or(Targets::all()));
|
||||
Self {
|
||||
options,
|
||||
inner: value,
|
||||
data: Vec::new(),
|
||||
string: None,
|
||||
module: None,
|
||||
module_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl InputWithInfo {
|
||||
fn filename(&self) -> &str {
|
||||
self.inner.file_name.file_name().unwrap().to_str().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct Inputs {
|
||||
inner: Vec<Input>,
|
||||
inner: Vec<InputWithInfo>,
|
||||
}
|
||||
|
||||
impl Inputs {
|
||||
#[track_caller]
|
||||
fn from_dir(folder: &str, extension: &str) -> Self {
|
||||
let mut inputs = Vec::new();
|
||||
let read_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join(folder)
|
||||
.read_dir()
|
||||
.unwrap();
|
||||
|
||||
for file_entry in read_dir {
|
||||
match file_entry {
|
||||
Ok(entry) => match entry.path().extension() {
|
||||
Some(ostr) if ostr == extension => {
|
||||
let path = entry.path();
|
||||
|
||||
inputs.push(Input {
|
||||
filename: path.to_string_lossy().into_owned(),
|
||||
size: entry.metadata().unwrap().len(),
|
||||
string: None,
|
||||
data: vec![],
|
||||
module: None,
|
||||
module_info: None,
|
||||
});
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Skipping file: {e:?}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
let inputs: Vec<InputWithInfo> = Input::files_in_dir(folder, &[extension], DIR_IN)
|
||||
.map(|a| a.into())
|
||||
.collect();
|
||||
|
||||
Self { inner: inputs }
|
||||
}
|
||||
|
||||
fn bytes(&self) -> u64 {
|
||||
self.inner.iter().map(|input| input.size).sum()
|
||||
self.inner
|
||||
.iter()
|
||||
.map(|input| input.inner.bytes(DIR_IN))
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn load(&mut self) {
|
||||
@ -60,7 +59,7 @@ impl Inputs {
|
||||
continue;
|
||||
}
|
||||
|
||||
input.data = fs::read(&input.filename).unwrap_or_default();
|
||||
input.data = fs::read(input.inner.input_path(DIR_IN)).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +84,8 @@ impl Inputs {
|
||||
continue;
|
||||
}
|
||||
|
||||
parser.set_options((&input.options.wgsl_in).into());
|
||||
|
||||
input.module = Some(parser.parse(input.string.as_ref().unwrap()).unwrap());
|
||||
}
|
||||
}
|
||||
@ -122,22 +123,22 @@ fn parse_glsl(stage: naga::ShaderStage, inputs: &Inputs) {
|
||||
};
|
||||
for input in &inputs.inner {
|
||||
parser
|
||||
.parse(&options, input.string.as_deref().unwrap())
|
||||
.parse(&options, &input.inner.read_source(DIR_IN, false))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_wgsl_inputs() -> Inputs {
|
||||
let mut inputs = Inputs::from_dir("../naga/tests/in/wgsl", "wgsl");
|
||||
let mut inputs: Vec<InputWithInfo> = Input::files_in_dir("wgsl", &["wgsl"], DIR_IN)
|
||||
.map(|a| a.into())
|
||||
.collect();
|
||||
|
||||
// remove "large-source" tests, they skew the results
|
||||
inputs
|
||||
.inner
|
||||
.retain(|input| !input.filename.contains("large-source"));
|
||||
inputs.retain(|input| !input.filename().contains("large-source"));
|
||||
|
||||
assert!(!inputs.is_empty());
|
||||
|
||||
inputs
|
||||
Inputs { inner: inputs }
|
||||
}
|
||||
|
||||
fn frontends(c: &mut Criterion) {
|
||||
@ -178,19 +179,20 @@ fn frontends(c: &mut Criterion) {
|
||||
let mut frontend = naga::front::wgsl::Frontend::new();
|
||||
b.iter(|| {
|
||||
for input in &inputs_wgsl.inner {
|
||||
frontend.set_options((&input.options.wgsl_in).into());
|
||||
frontend.parse(input.string.as_ref().unwrap()).unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let inputs_spirv = Inputs::from_dir("../naga/tests/in/spv", "spvasm");
|
||||
let inputs_spirv = Inputs::from_dir("spv", "spvasm");
|
||||
assert!(!inputs_spirv.is_empty());
|
||||
|
||||
// Assemble all the SPIR-V assembly.
|
||||
let mut assembled_spirv = Vec::<Vec<u32>>::new();
|
||||
'spirv: for input in &inputs_spirv.inner {
|
||||
let output = match Command::new("spirv-as")
|
||||
.arg(&input.filename)
|
||||
.arg(input.inner.input_path(DIR_IN))
|
||||
.arg("-o")
|
||||
.arg("-")
|
||||
.output()
|
||||
@ -220,19 +222,32 @@ fn frontends(c: &mut Criterion) {
|
||||
|
||||
let total_bytes = assembled_spirv.iter().map(|spv| spv.len() as u64).sum();
|
||||
|
||||
assert!(assembled_spirv.len() == inputs_spirv.inner.len() || assembled_spirv.is_empty());
|
||||
|
||||
group.throughput(Throughput::Bytes(total_bytes));
|
||||
group.bench_function("shader: spv-in", |b| {
|
||||
b.iter(|| {
|
||||
let options = naga::front::spv::Options::default();
|
||||
for input in &assembled_spirv {
|
||||
let parser = naga::front::spv::Frontend::new(input.iter().cloned(), &options);
|
||||
for (i, input) in assembled_spirv.iter().enumerate() {
|
||||
let params = &inputs_spirv.inner[i].options;
|
||||
let SpirvInParameters {
|
||||
adjust_coordinate_space,
|
||||
} = params.spv_in;
|
||||
|
||||
let parser = naga::front::spv::Frontend::new(
|
||||
input.iter().cloned(),
|
||||
&naga::front::spv::Options {
|
||||
adjust_coordinate_space,
|
||||
strict_capabilities: true,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
parser.parse().unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let mut inputs_vertex = Inputs::from_dir("../naga/tests/in/glsl", "vert");
|
||||
let mut inputs_fragment = Inputs::from_dir("../naga/tests/in/glsl", "frag");
|
||||
let mut inputs_vertex = Inputs::from_dir("glsl", "vert");
|
||||
let mut inputs_fragment = Inputs::from_dir("glsl", "frag");
|
||||
assert!(!inputs_vertex.is_empty());
|
||||
assert!(!inputs_fragment.is_empty());
|
||||
// let mut inputs_compute = Inputs::from_dir("../naga/tests/in/glsl", "comp");
|
||||
@ -312,14 +327,16 @@ fn backends(c: &mut Criterion) {
|
||||
group.bench_function("shader: wgsl-out", |b| {
|
||||
b.iter(|| {
|
||||
let mut string = String::new();
|
||||
let flags = naga::back::wgsl::WriterFlags::empty();
|
||||
for input in &inputs.inner {
|
||||
let mut writer = naga::back::wgsl::Writer::new(&mut string, flags);
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
);
|
||||
string.clear();
|
||||
if input.options.targets.unwrap().contains(Targets::WGSL) {
|
||||
let mut writer =
|
||||
naga::back::wgsl::Writer::new(&mut string, (&input.options.wgsl).into());
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
);
|
||||
string.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -327,21 +344,28 @@ fn backends(c: &mut Criterion) {
|
||||
group.bench_function("shader: spv-out", |b| {
|
||||
b.iter(|| {
|
||||
let mut data = Vec::new();
|
||||
let options = naga::back::spv::Options::default();
|
||||
let mut writer = naga::back::spv::Writer::new(&Default::default()).unwrap();
|
||||
for input in &inputs.inner {
|
||||
if input.filename.contains("pointer-function-arg") {
|
||||
// These fail due to https://github.com/gfx-rs/wgpu/issues/7315
|
||||
continue;
|
||||
if input.options.targets.unwrap().contains(Targets::SPIRV) {
|
||||
if input.filename().contains("pointer-function-arg") {
|
||||
// These fail due to https://github.com/gfx-rs/wgpu/issues/7315
|
||||
continue;
|
||||
}
|
||||
let opt = input
|
||||
.options
|
||||
.spv
|
||||
.to_options(input.options.bounds_check_policies, None);
|
||||
if writer.set_options(&opt).is_ok() {
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
None,
|
||||
&None,
|
||||
&mut data,
|
||||
);
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
let mut writer = naga::back::spv::Writer::new(&options).unwrap();
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
None,
|
||||
&None,
|
||||
&mut data,
|
||||
);
|
||||
data.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -350,25 +374,27 @@ fn backends(c: &mut Criterion) {
|
||||
let mut data = Vec::new();
|
||||
let options = naga::back::spv::Options::default();
|
||||
for input in &inputs.inner {
|
||||
if input.filename.contains("pointer-function-arg") {
|
||||
// These fail due to https://github.com/gfx-rs/wgpu/issues/7315
|
||||
continue;
|
||||
}
|
||||
let mut writer = naga::back::spv::Writer::new(&options).unwrap();
|
||||
let module = input.module.as_ref().unwrap();
|
||||
for ep in module.entry_points.iter() {
|
||||
let pipeline_options = naga::back::spv::PipelineOptions {
|
||||
shader_stage: ep.stage,
|
||||
entry_point: ep.name.clone(),
|
||||
};
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
Some(&pipeline_options),
|
||||
&None,
|
||||
&mut data,
|
||||
);
|
||||
data.clear();
|
||||
if input.options.targets.unwrap().contains(Targets::SPIRV) {
|
||||
if input.filename().contains("pointer-function-arg") {
|
||||
// These fail due to https://github.com/gfx-rs/wgpu/issues/7315
|
||||
continue;
|
||||
}
|
||||
let mut writer = naga::back::spv::Writer::new(&options).unwrap();
|
||||
let module = input.module.as_ref().unwrap();
|
||||
for ep in module.entry_points.iter() {
|
||||
let pipeline_options = naga::back::spv::PipelineOptions {
|
||||
shader_stage: ep.stage,
|
||||
entry_point: ep.name.clone(),
|
||||
};
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
Some(&pipeline_options),
|
||||
&None,
|
||||
&mut data,
|
||||
);
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -379,15 +405,17 @@ fn backends(c: &mut Criterion) {
|
||||
let mut string = String::new();
|
||||
let options = naga::back::msl::Options::default();
|
||||
for input in &inputs.inner {
|
||||
let pipeline_options = naga::back::msl::PipelineOptions::default();
|
||||
let mut writer = naga::back::msl::Writer::new(&mut string);
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
&options,
|
||||
&pipeline_options,
|
||||
);
|
||||
string.clear();
|
||||
if input.options.targets.unwrap().contains(Targets::METAL) {
|
||||
let pipeline_options = naga::back::msl::PipelineOptions::default();
|
||||
let mut writer = naga::back::msl::Writer::new(&mut string);
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
&options,
|
||||
&pipeline_options,
|
||||
);
|
||||
string.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -397,15 +425,17 @@ fn backends(c: &mut Criterion) {
|
||||
let options = naga::back::hlsl::Options::default();
|
||||
let mut string = String::new();
|
||||
for input in &inputs.inner {
|
||||
let pipeline_options = Default::default();
|
||||
let mut writer =
|
||||
naga::back::hlsl::Writer::new(&mut string, &options, &pipeline_options);
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
None,
|
||||
); // may fail on unimplemented things
|
||||
string.clear();
|
||||
if input.options.targets.unwrap().contains(Targets::HLSL) {
|
||||
let pipeline_options = Default::default();
|
||||
let mut writer =
|
||||
naga::back::hlsl::Writer::new(&mut string, &options, &pipeline_options);
|
||||
let _ = writer.write(
|
||||
input.module.as_ref().unwrap(),
|
||||
input.module_info.as_ref().unwrap(),
|
||||
None,
|
||||
); // may fail on unimplemented things
|
||||
string.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -420,6 +450,9 @@ fn backends(c: &mut Criterion) {
|
||||
zero_initialize_workgroup_memory: true,
|
||||
};
|
||||
for input in &inputs.inner {
|
||||
if !input.options.targets.unwrap().contains(Targets::GLSL) {
|
||||
continue;
|
||||
}
|
||||
let module = input.module.as_ref().unwrap();
|
||||
let info = input.module_info.as_ref().unwrap();
|
||||
for ep in module.entry_points.iter() {
|
||||
|
||||
39
naga-test/Cargo.toml
Normal file
39
naga-test/Cargo.toml
Normal file
@ -0,0 +1,39 @@
|
||||
[package]
|
||||
name = "naga-test"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
description = "common code for naga tests"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish = false
|
||||
|
||||
|
||||
[features]
|
||||
|
||||
[dependencies]
|
||||
naga = { workspace = true, features = [
|
||||
"serialize",
|
||||
"deserialize",
|
||||
"glsl-in",
|
||||
"glsl-out",
|
||||
"spv-in",
|
||||
"spv-out",
|
||||
"wgsl-in",
|
||||
"wgsl-out",
|
||||
"msl-out",
|
||||
"dot-out",
|
||||
"hlsl-out",
|
||||
] }
|
||||
spirv = { workspace = true, features = ["deserialize"] }
|
||||
rspirv.workspace = true
|
||||
ron.workspace = true
|
||||
toml.workspace = true
|
||||
bitflags.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
walkdir.workspace = true
|
||||
env_logger.workspace = true
|
||||
452
naga-test/src/lib.rs
Normal file
452
naga-test/src/lib.rs
Normal file
@ -0,0 +1,452 @@
|
||||
// A lot of the code can be unused based on configuration flags,
|
||||
// the corresponding warnings aren't helpful.
|
||||
#![allow(dead_code, unused_imports)]
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use naga::compact::KeepUnused;
|
||||
use ron::de;
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Clone, Copy, serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Targets: u32 {
|
||||
/// A serialization of the `naga::Module`, in RON format.
|
||||
const IR = 1;
|
||||
|
||||
/// A serialization of the `naga::valid::ModuleInfo`, in RON format.
|
||||
const ANALYSIS = 1 << 1;
|
||||
|
||||
const SPIRV = 1 << 2;
|
||||
const METAL = 1 << 3;
|
||||
const GLSL = 1 << 4;
|
||||
const DOT = 1 << 5;
|
||||
const HLSL = 1 << 6;
|
||||
const WGSL = 1 << 7;
|
||||
const NO_VALIDATION = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
impl Targets {
|
||||
/// Defaults for `spv` and `glsl` snapshots.
|
||||
pub fn non_wgsl_default() -> Self {
|
||||
Targets::WGSL
|
||||
}
|
||||
|
||||
/// Defaults for `wgsl` snapshots.
|
||||
pub fn wgsl_default() -> Self {
|
||||
Targets::HLSL | Targets::SPIRV | Targets::GLSL | Targets::METAL | Targets::WGSL
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct SpvOutVersion(pub u8, pub u8);
|
||||
impl Default for SpvOutVersion {
|
||||
fn default() -> Self {
|
||||
SpvOutVersion(1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct BindingMapSerialization {
|
||||
pub resource_binding: naga::ResourceBinding,
|
||||
pub bind_target: naga::back::spv::BindingInfo,
|
||||
}
|
||||
|
||||
pub fn deserialize_binding_map<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<naga::back::spv::BindingMap, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::Deserialize;
|
||||
|
||||
let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;
|
||||
let mut map = naga::back::spv::BindingMap::default();
|
||||
for item in vec {
|
||||
map.insert(item.resource_binding, item.bind_target);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct WgslInParameters {
|
||||
pub parse_doc_comments: bool,
|
||||
}
|
||||
impl From<&WgslInParameters> for naga::front::wgsl::Options {
|
||||
fn from(value: &WgslInParameters) -> Self {
|
||||
Self {
|
||||
parse_doc_comments: value.parse_doc_comments,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct SpirvInParameters {
|
||||
pub adjust_coordinate_space: bool,
|
||||
}
|
||||
impl From<&SpirvInParameters> for naga::front::spv::Options {
|
||||
fn from(value: &SpirvInParameters) -> Self {
|
||||
Self {
|
||||
adjust_coordinate_space: value.adjust_coordinate_space,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct SpirvOutParameters {
|
||||
pub version: SpvOutVersion,
|
||||
pub capabilities: naga::FastHashSet<spirv::Capability>,
|
||||
pub debug: bool,
|
||||
pub adjust_coordinate_space: bool,
|
||||
pub force_point_size: bool,
|
||||
pub clamp_frag_depth: bool,
|
||||
pub separate_entry_points: bool,
|
||||
#[serde(deserialize_with = "deserialize_binding_map")]
|
||||
pub binding_map: naga::back::spv::BindingMap,
|
||||
pub use_storage_input_output_16: bool,
|
||||
}
|
||||
impl Default for SpirvOutParameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: SpvOutVersion::default(),
|
||||
capabilities: naga::FastHashSet::default(),
|
||||
debug: false,
|
||||
adjust_coordinate_space: false,
|
||||
force_point_size: false,
|
||||
clamp_frag_depth: false,
|
||||
separate_entry_points: false,
|
||||
use_storage_input_output_16: true,
|
||||
binding_map: naga::back::spv::BindingMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SpirvOutParameters {
|
||||
pub fn to_options<'a>(
|
||||
&'a self,
|
||||
bounds_check_policies: naga::proc::BoundsCheckPolicies,
|
||||
debug_info: Option<naga::back::spv::DebugInfo<'a>>,
|
||||
) -> naga::back::spv::Options<'a> {
|
||||
use naga::back::spv;
|
||||
let mut flags = spv::WriterFlags::LABEL_VARYINGS;
|
||||
flags.set(spv::WriterFlags::DEBUG, self.debug);
|
||||
flags.set(
|
||||
spv::WriterFlags::ADJUST_COORDINATE_SPACE,
|
||||
self.adjust_coordinate_space,
|
||||
);
|
||||
flags.set(spv::WriterFlags::FORCE_POINT_SIZE, self.force_point_size);
|
||||
flags.set(spv::WriterFlags::CLAMP_FRAG_DEPTH, self.clamp_frag_depth);
|
||||
naga::back::spv::Options {
|
||||
lang_version: (self.version.0, self.version.1),
|
||||
flags,
|
||||
capabilities: if self.capabilities.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.capabilities.clone())
|
||||
},
|
||||
bounds_check_policies,
|
||||
binding_map: self.binding_map.clone(),
|
||||
zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::Polyfill,
|
||||
force_loop_bounding: true,
|
||||
debug_info,
|
||||
use_storage_input_output_16: self.use_storage_input_output_16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct WgslOutParameters {
|
||||
pub explicit_types: bool,
|
||||
}
|
||||
impl From<&WgslOutParameters> for naga::back::wgsl::WriterFlags {
|
||||
fn from(value: &WgslOutParameters) -> Self {
|
||||
let mut flags = Self::empty();
|
||||
flags.set(Self::EXPLICIT_TYPES, value.explicit_types);
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
pub struct FragmentModule {
|
||||
pub path: String,
|
||||
pub entry_point: String,
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Parameters {
|
||||
// -- GOD MODE --
|
||||
pub god_mode: bool,
|
||||
|
||||
// -- wgsl-in options --
|
||||
#[serde(rename = "wgsl-in")]
|
||||
pub wgsl_in: WgslInParameters,
|
||||
|
||||
// -- spirv-in options --
|
||||
#[serde(rename = "spv-in")]
|
||||
pub spv_in: SpirvInParameters,
|
||||
|
||||
// -- SPIR-V options --
|
||||
pub spv: SpirvOutParameters,
|
||||
|
||||
/// Defaults to [`Targets::non_wgsl_default()`] for `spv` and `glsl` snapshots,
|
||||
/// and [`Targets::wgsl_default()`] for `wgsl` snapshots.
|
||||
pub targets: Option<Targets>,
|
||||
|
||||
// -- MSL options --
|
||||
pub msl: naga::back::msl::Options,
|
||||
#[serde(default)]
|
||||
pub msl_pipeline: naga::back::msl::PipelineOptions,
|
||||
|
||||
// -- GLSL options --
|
||||
pub glsl: naga::back::glsl::Options,
|
||||
pub glsl_exclude_list: naga::FastHashSet<String>,
|
||||
pub glsl_multiview: Option<core::num::NonZeroU32>,
|
||||
|
||||
// -- HLSL options --
|
||||
pub hlsl: naga::back::hlsl::Options,
|
||||
|
||||
// -- WGSL options --
|
||||
pub wgsl: WgslOutParameters,
|
||||
|
||||
// -- General options --
|
||||
|
||||
// Allow backends to be aware of the fragment module.
|
||||
// Is the name of a WGSL file in the same directory as the test file.
|
||||
pub fragment_module: Option<FragmentModule>,
|
||||
|
||||
pub bounds_check_policies: naga::proc::BoundsCheckPolicies,
|
||||
pub pipeline_constants: naga::back::PipelineConstants,
|
||||
}
|
||||
|
||||
/// Information about a shader input file.
|
||||
#[derive(Debug)]
|
||||
pub struct Input {
|
||||
/// The subdirectory of `tests/in` to which this input belongs, if any.
|
||||
///
|
||||
/// If the subdirectory is omitted, we assume that the output goes
|
||||
/// to "wgsl".
|
||||
pub subdirectory: PathBuf,
|
||||
|
||||
/// The input filename name, without a directory.
|
||||
pub file_name: PathBuf,
|
||||
|
||||
/// True if output filenames should add the output extension on top of
|
||||
/// `file_name`'s existing extension, rather than replacing it.
|
||||
///
|
||||
/// This is used by `convert_snapshots_glsl`, which wants to take input files
|
||||
/// like `210-bevy-2d-shader.frag` and just add `.wgsl` to it, producing
|
||||
/// `210-bevy-2d-shader.frag.wgsl`.
|
||||
pub keep_input_extension: bool,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
/// Read an input file and its corresponding parameters file.
|
||||
///
|
||||
/// Given `input`, the relative path of a shader input file, return
|
||||
/// a `Source` value containing its path, code, and parameters.
|
||||
///
|
||||
/// The `input` path is interpreted relative to the `BASE_DIR_IN`
|
||||
/// subdirectory of the directory given by the `CARGO_MANIFEST_DIR`
|
||||
/// environment variable.
|
||||
pub fn new(subdirectory: &str, name: &str, extension: &str) -> Input {
|
||||
Input {
|
||||
subdirectory: PathBuf::from(subdirectory),
|
||||
// Don't wipe out any extensions on `name`, as
|
||||
// `with_extension` would do.
|
||||
file_name: PathBuf::from(format!("{name}.{extension}")),
|
||||
keep_input_extension: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator that produces an `Input` for each entry in `subdirectory`.
|
||||
pub fn files_in_dir<'a>(
|
||||
subdirectory: &'a str,
|
||||
file_extensions: &'a [&'a str],
|
||||
dir_in: &str,
|
||||
) -> impl Iterator<Item = Input> + 'a {
|
||||
let input_directory = Path::new(dir_in).join(subdirectory);
|
||||
|
||||
let entries = match std::fs::read_dir(&input_directory) {
|
||||
Ok(entries) => entries,
|
||||
Err(err) => panic!(
|
||||
"Error opening directory '{}': {}",
|
||||
input_directory.display(),
|
||||
err
|
||||
),
|
||||
};
|
||||
|
||||
entries.filter_map(move |result| {
|
||||
let entry = result.expect("error reading directory");
|
||||
if !entry.file_type().unwrap().is_file() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let file_name = PathBuf::from(entry.file_name());
|
||||
let extension = file_name
|
||||
.extension()
|
||||
.expect("all files in snapshot input directory should have extensions");
|
||||
|
||||
if !file_extensions.contains(&extension.to_str().unwrap()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Ok(pat) = std::env::var("NAGA_SNAPSHOT") {
|
||||
if !file_name.to_string_lossy().contains(&pat) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let input = Input::new(
|
||||
subdirectory,
|
||||
file_name.file_stem().unwrap().to_str().unwrap(),
|
||||
extension.to_str().unwrap(),
|
||||
);
|
||||
Some(input)
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the path to the input directory.
|
||||
pub fn input_directory(&self, dir_in: &str) -> PathBuf {
|
||||
Path::new(dir_in).join(&self.subdirectory)
|
||||
}
|
||||
|
||||
/// Return the path to the output directory.
|
||||
pub fn output_directory(subdirectory: &str, dir_out: &str) -> PathBuf {
|
||||
Path::new(dir_out).join(subdirectory)
|
||||
}
|
||||
|
||||
/// Return the path to the input file.
|
||||
pub fn input_path(&self, dir_in: &str) -> PathBuf {
|
||||
let mut input = self.input_directory(dir_in);
|
||||
input.push(&self.file_name);
|
||||
input
|
||||
}
|
||||
|
||||
pub fn output_path(&self, subdirectory: &str, extension: &str, dir_out: &str) -> PathBuf {
|
||||
let mut output = Self::output_directory(subdirectory, dir_out);
|
||||
if self.keep_input_extension {
|
||||
let file_name = format!(
|
||||
"{}-{}.{}",
|
||||
self.subdirectory.display(),
|
||||
self.file_name.display(),
|
||||
extension
|
||||
);
|
||||
|
||||
output.push(&file_name);
|
||||
} else {
|
||||
let file_name = format!(
|
||||
"{}-{}",
|
||||
self.subdirectory.display(),
|
||||
self.file_name.display()
|
||||
);
|
||||
|
||||
output.push(&file_name);
|
||||
output.set_extension(extension);
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
/// Return the contents of the input file as a string.
|
||||
pub fn read_source(&self, dir_in: &str, print: bool) -> String {
|
||||
if print {
|
||||
println!("Processing '{}'", self.file_name.display());
|
||||
}
|
||||
let input_path = self.input_path(dir_in);
|
||||
match fs::read_to_string(&input_path) {
|
||||
Ok(source) => source,
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Couldn't read shader input file `{}`: {}",
|
||||
input_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the contents of the input file as a vector of bytes.
|
||||
pub fn read_bytes(&self, dir_in: &str, print: bool) -> Vec<u8> {
|
||||
if print {
|
||||
println!("Processing '{}'", self.file_name.display());
|
||||
}
|
||||
let input_path = self.input_path(dir_in);
|
||||
match fs::read(&input_path) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Couldn't read shader input file `{}`: {}",
|
||||
input_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes(&self, dir_in: &str) -> u64 {
|
||||
let input_path = self.input_path(dir_in);
|
||||
std::fs::metadata(input_path).unwrap().len()
|
||||
}
|
||||
|
||||
/// Return this input's parameter file, parsed.
|
||||
pub fn read_parameters(&self, dir_in: &str) -> Parameters {
|
||||
let mut param_path = self.input_path(dir_in);
|
||||
param_path.set_extension("toml");
|
||||
let mut params = match fs::read_to_string(¶m_path) {
|
||||
Ok(string) => match toml::de::from_str(&string) {
|
||||
Ok(params) => params,
|
||||
Err(e) => panic!(
|
||||
"Couldn't parse param file: {} due to: {e}",
|
||||
param_path.display()
|
||||
),
|
||||
},
|
||||
Err(_) => Parameters::default(),
|
||||
};
|
||||
|
||||
if params.targets.is_none() {
|
||||
match self
|
||||
.input_path(dir_in)
|
||||
.extension()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
{
|
||||
"wgsl" => params.targets = Some(Targets::wgsl_default()),
|
||||
"spvasm" => params.targets = Some(Targets::non_wgsl_default()),
|
||||
"vert" | "frag" | "comp" => params.targets = Some(Targets::non_wgsl_default()),
|
||||
e => {
|
||||
panic!("Unknown extension: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params
|
||||
}
|
||||
|
||||
/// Write `data` to a file corresponding to this input file in
|
||||
/// `subdirectory`, with `extension`.
|
||||
pub fn write_output_file(
|
||||
&self,
|
||||
subdirectory: &str,
|
||||
extension: &str,
|
||||
data: impl AsRef<[u8]>,
|
||||
dir_out: &str,
|
||||
) {
|
||||
let output_path = self.output_path(subdirectory, extension, dir_out);
|
||||
fs::create_dir_all(output_path.parent().unwrap()).unwrap();
|
||||
if let Err(err) = fs::write(&output_path, data) {
|
||||
panic!("Error writing {}: {}", output_path.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,12 +113,17 @@ env_logger.workspace = true
|
||||
hashbrown = { workspace = true, features = ["serde"] }
|
||||
hlsl-snapshots.workspace = true
|
||||
itertools.workspace = true
|
||||
naga-test.workspace = true
|
||||
ron.workspace = true
|
||||
rspirv.workspace = true
|
||||
# So we don't actually need this, however if we remove this, it
|
||||
# brakes calling `--features spirv` at the workspace level. I think
|
||||
# this is because there is a `dep:spirv` in the regular feature set,
|
||||
# so cargo tries to match the feature against that, fails as it's a optional dep,
|
||||
# and then refuses to build instead of ignoring it.
|
||||
spirv.workspace = true
|
||||
serde = { workspace = true, features = ["default", "derive"] }
|
||||
spirv = { workspace = true, features = ["deserialize"] }
|
||||
strum = { workspace = true }
|
||||
toml.workspace = true
|
||||
walkdir.workspace = true
|
||||
|
||||
[lints.clippy]
|
||||
|
||||
@ -99,6 +99,24 @@ impl Writer {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_options(&mut self, options: &Options) -> Result<(), Error> {
|
||||
let (major, minor) = options.lang_version;
|
||||
if major != 1 {
|
||||
return Err(Error::UnsupportedVersion(major, minor));
|
||||
}
|
||||
self.physical_layout = PhysicalLayout::new(major, minor);
|
||||
self.capabilities_available = options.capabilities.clone();
|
||||
self.flags = options.flags;
|
||||
self.bounds_check_policies = options.bounds_check_policies;
|
||||
self.zero_initialize_workgroup_memory = options.zero_initialize_workgroup_memory;
|
||||
self.force_loop_bounding = options.force_loop_bounding;
|
||||
self.use_storage_input_output_16 = options.use_storage_input_output_16;
|
||||
self.binding_map = options.binding_map.clone();
|
||||
self.io_f16_polyfills =
|
||||
super::f16_polyfill::F16IoPolyfill::new(options.use_storage_input_output_16);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `(major, minor)` of the SPIR-V language version.
|
||||
pub const fn lang_version(&self) -> (u8, u8) {
|
||||
self.physical_layout.lang_version()
|
||||
|
||||
@ -50,6 +50,9 @@ impl Frontend {
|
||||
options,
|
||||
}
|
||||
}
|
||||
pub fn set_options(&mut self, options: Options) {
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, source: &str) -> core::result::Result<crate::Module, ParseError> {
|
||||
self.inner(source).map_err(|x| x.as_parse_error(source))
|
||||
|
||||
@ -1,407 +1,12 @@
|
||||
// A lot of the code can be unused based on configuration flags,
|
||||
// the corresponding warnings aren't helpful.
|
||||
#![allow(dead_code, unused_imports)]
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use naga::compact::KeepUnused;
|
||||
use ron::de;
|
||||
use naga_test::*;
|
||||
|
||||
const CRATE_ROOT: &str = env!("CARGO_MANIFEST_DIR");
|
||||
const BASE_DIR_IN: &str = "tests/in";
|
||||
const BASE_DIR_OUT: &str = "tests/out";
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Clone, Copy, serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct Targets: u32 {
|
||||
/// A serialization of the `naga::Module`, in RON format.
|
||||
const IR = 1;
|
||||
|
||||
/// A serialization of the `naga::valid::ModuleInfo`, in RON format.
|
||||
const ANALYSIS = 1 << 1;
|
||||
|
||||
const SPIRV = 1 << 2;
|
||||
const METAL = 1 << 3;
|
||||
const GLSL = 1 << 4;
|
||||
const DOT = 1 << 5;
|
||||
const HLSL = 1 << 6;
|
||||
const WGSL = 1 << 7;
|
||||
const NO_VALIDATION = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
impl Targets {
|
||||
/// Defaults for `spv` and `glsl` snapshots.
|
||||
fn non_wgsl_default() -> Self {
|
||||
Targets::WGSL
|
||||
}
|
||||
|
||||
/// Defaults for `wgsl` snapshots.
|
||||
fn wgsl_default() -> Self {
|
||||
Targets::HLSL | Targets::SPIRV | Targets::GLSL | Targets::METAL | Targets::WGSL
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct SpvOutVersion(u8, u8);
|
||||
impl Default for SpvOutVersion {
|
||||
fn default() -> Self {
|
||||
SpvOutVersion(1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "deserialize", spv_out))]
|
||||
#[derive(serde::Deserialize)]
|
||||
struct BindingMapSerialization {
|
||||
resource_binding: naga::ResourceBinding,
|
||||
bind_target: naga::back::spv::BindingInfo,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "deserialize", spv_out))]
|
||||
fn deserialize_binding_map<'de, D>(deserializer: D) -> Result<naga::back::spv::BindingMap, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::Deserialize;
|
||||
|
||||
let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;
|
||||
let mut map = naga::back::spv::BindingMap::default();
|
||||
for item in vec {
|
||||
map.insert(item.resource_binding, item.bind_target);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
struct WgslInParameters {
|
||||
parse_doc_comments: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
struct SpirvInParameters {
|
||||
adjust_coordinate_space: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
struct SpirvOutParameters {
|
||||
version: SpvOutVersion,
|
||||
capabilities: naga::FastHashSet<spirv::Capability>,
|
||||
debug: bool,
|
||||
adjust_coordinate_space: bool,
|
||||
force_point_size: bool,
|
||||
clamp_frag_depth: bool,
|
||||
separate_entry_points: bool,
|
||||
use_storage_input_output_16: bool,
|
||||
#[cfg(all(feature = "deserialize", spv_out))]
|
||||
#[serde(deserialize_with = "deserialize_binding_map")]
|
||||
binding_map: naga::back::spv::BindingMap,
|
||||
}
|
||||
|
||||
impl Default for SpirvOutParameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: SpvOutVersion::default(),
|
||||
capabilities: naga::FastHashSet::default(),
|
||||
debug: false,
|
||||
adjust_coordinate_space: false,
|
||||
force_point_size: false,
|
||||
clamp_frag_depth: false,
|
||||
separate_entry_points: false,
|
||||
use_storage_input_output_16: true,
|
||||
#[cfg(all(feature = "deserialize", spv_out))]
|
||||
binding_map: naga::back::spv::BindingMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
struct WgslOutParameters {
|
||||
explicit_types: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
struct FragmentModule {
|
||||
path: String,
|
||||
entry_point: String,
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
struct Parameters {
|
||||
// -- GOD MODE --
|
||||
god_mode: bool,
|
||||
|
||||
// -- wgsl-in options --
|
||||
#[serde(rename = "wgsl-in")]
|
||||
wgsl_in: WgslInParameters,
|
||||
|
||||
// -- spirv-in options --
|
||||
#[serde(rename = "spv-in")]
|
||||
spv_in: SpirvInParameters,
|
||||
|
||||
// -- SPIR-V options --
|
||||
spv: SpirvOutParameters,
|
||||
|
||||
/// Defaults to [`Targets::non_wgsl_default()`] for `spv` and `glsl` snapshots,
|
||||
/// and [`Targets::wgsl_default()`] for `wgsl` snapshots.
|
||||
targets: Option<Targets>,
|
||||
|
||||
// -- MSL options --
|
||||
#[cfg(all(feature = "deserialize", msl_out))]
|
||||
msl: naga::back::msl::Options,
|
||||
#[cfg(all(feature = "deserialize", msl_out))]
|
||||
#[serde(default)]
|
||||
msl_pipeline: naga::back::msl::PipelineOptions,
|
||||
|
||||
// -- GLSL options --
|
||||
#[cfg(all(feature = "deserialize", glsl_out))]
|
||||
glsl: naga::back::glsl::Options,
|
||||
glsl_exclude_list: naga::FastHashSet<String>,
|
||||
#[cfg(all(feature = "deserialize", glsl_out))]
|
||||
glsl_multiview: Option<core::num::NonZeroU32>,
|
||||
|
||||
// -- HLSL options --
|
||||
#[cfg(all(feature = "deserialize", hlsl_out))]
|
||||
hlsl: naga::back::hlsl::Options,
|
||||
|
||||
// -- WGSL options --
|
||||
wgsl: WgslOutParameters,
|
||||
|
||||
// -- General options --
|
||||
|
||||
// Allow backends to be aware of the fragment module.
|
||||
// Is the name of a WGSL file in the same directory as the test file.
|
||||
fragment_module: Option<FragmentModule>,
|
||||
|
||||
#[cfg(feature = "deserialize")]
|
||||
bounds_check_policies: naga::proc::BoundsCheckPolicies,
|
||||
|
||||
#[cfg(all(feature = "deserialize", any(hlsl_out, msl_out, spv_out, glsl_out)))]
|
||||
pipeline_constants: naga::back::PipelineConstants,
|
||||
}
|
||||
|
||||
/// Information about a shader input file.
|
||||
#[derive(Debug)]
|
||||
struct Input {
|
||||
/// The subdirectory of `tests/in` to which this input belongs, if any.
|
||||
///
|
||||
/// If the subdirectory is omitted, we assume that the output goes
|
||||
/// to "wgsl".
|
||||
subdirectory: PathBuf,
|
||||
|
||||
/// The input filename name, without a directory.
|
||||
file_name: PathBuf,
|
||||
|
||||
/// True if output filenames should add the output extension on top of
|
||||
/// `file_name`'s existing extension, rather than replacing it.
|
||||
///
|
||||
/// This is used by `convert_snapshots_glsl`, which wants to take input files
|
||||
/// like `210-bevy-2d-shader.frag` and just add `.wgsl` to it, producing
|
||||
/// `210-bevy-2d-shader.frag.wgsl`.
|
||||
keep_input_extension: bool,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
/// Read an input file and its corresponding parameters file.
|
||||
///
|
||||
/// Given `input`, the relative path of a shader input file, return
|
||||
/// a `Source` value containing its path, code, and parameters.
|
||||
///
|
||||
/// The `input` path is interpreted relative to the `BASE_DIR_IN`
|
||||
/// subdirectory of the directory given by the `CARGO_MANIFEST_DIR`
|
||||
/// environment variable.
|
||||
fn new(subdirectory: &str, name: &str, extension: &str) -> Input {
|
||||
Input {
|
||||
subdirectory: PathBuf::from(subdirectory),
|
||||
// Don't wipe out any extensions on `name`, as
|
||||
// `with_extension` would do.
|
||||
file_name: PathBuf::from(format!("{name}.{extension}")),
|
||||
keep_input_extension: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator that produces an `Input` for each entry in `subdirectory`.
|
||||
fn files_in_dir(
|
||||
subdirectory: &'static str,
|
||||
file_extensions: &'static [&'static str],
|
||||
) -> impl Iterator<Item = Input> + 'static {
|
||||
let input_directory = Path::new(CRATE_ROOT).join(BASE_DIR_IN).join(subdirectory);
|
||||
|
||||
let entries = match std::fs::read_dir(&input_directory) {
|
||||
Ok(entries) => entries,
|
||||
Err(err) => panic!(
|
||||
"Error opening directory '{}': {}",
|
||||
input_directory.display(),
|
||||
err
|
||||
),
|
||||
};
|
||||
|
||||
entries.filter_map(move |result| {
|
||||
let entry = result.expect("error reading directory");
|
||||
if !entry.file_type().unwrap().is_file() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let file_name = PathBuf::from(entry.file_name());
|
||||
let extension = file_name
|
||||
.extension()
|
||||
.expect("all files in snapshot input directory should have extensions");
|
||||
|
||||
if !file_extensions.contains(&extension.to_str().unwrap()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Ok(pat) = std::env::var("NAGA_SNAPSHOT") {
|
||||
if !file_name.to_string_lossy().contains(&pat) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let input = Input::new(
|
||||
subdirectory,
|
||||
file_name.file_stem().unwrap().to_str().unwrap(),
|
||||
extension.to_str().unwrap(),
|
||||
);
|
||||
Some(input)
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the path to the input directory.
|
||||
fn input_directory(&self) -> PathBuf {
|
||||
let mut dir = Path::new(CRATE_ROOT).join(BASE_DIR_IN);
|
||||
dir.push(&self.subdirectory);
|
||||
dir
|
||||
}
|
||||
|
||||
/// Return the path to the output directory.
|
||||
fn output_directory(subdirectory: &str) -> PathBuf {
|
||||
let mut dir = Path::new(CRATE_ROOT).join(BASE_DIR_OUT);
|
||||
dir.push(subdirectory);
|
||||
dir
|
||||
}
|
||||
|
||||
/// Return the path to the input file.
|
||||
fn input_path(&self) -> PathBuf {
|
||||
let mut input = self.input_directory();
|
||||
input.push(&self.file_name);
|
||||
input
|
||||
}
|
||||
|
||||
fn output_path(&self, subdirectory: &str, extension: &str) -> PathBuf {
|
||||
let mut output = Self::output_directory(subdirectory);
|
||||
if self.keep_input_extension {
|
||||
let file_name = format!(
|
||||
"{}-{}.{}",
|
||||
self.subdirectory.display(),
|
||||
self.file_name.display(),
|
||||
extension
|
||||
);
|
||||
|
||||
output.push(&file_name);
|
||||
} else {
|
||||
let file_name = format!(
|
||||
"{}-{}",
|
||||
self.subdirectory.display(),
|
||||
self.file_name.display()
|
||||
);
|
||||
|
||||
output.push(&file_name);
|
||||
output.set_extension(extension);
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
/// Return the contents of the input file as a string.
|
||||
fn read_source(&self) -> String {
|
||||
println!("Processing '{}'", self.file_name.display());
|
||||
let input_path = self.input_path();
|
||||
match fs::read_to_string(&input_path) {
|
||||
Ok(source) => source,
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Couldn't read shader input file `{}`: {}",
|
||||
input_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the contents of the input file as a vector of bytes.
|
||||
fn read_bytes(&self) -> Vec<u8> {
|
||||
println!("Processing '{}'", self.file_name.display());
|
||||
let input_path = self.input_path();
|
||||
match fs::read(&input_path) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Couldn't read shader input file `{}`: {}",
|
||||
input_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return this input's parameter file, parsed.
|
||||
fn read_parameters(&self) -> Parameters {
|
||||
let mut param_path = self.input_path();
|
||||
param_path.set_extension("toml");
|
||||
let mut params = match fs::read_to_string(¶m_path) {
|
||||
Ok(string) => match toml::de::from_str(&string) {
|
||||
Ok(params) => params,
|
||||
Err(e) => panic!(
|
||||
"Couldn't parse param file: {} due to: {e}",
|
||||
param_path.display()
|
||||
),
|
||||
},
|
||||
Err(_) => Parameters::default(),
|
||||
};
|
||||
|
||||
if params.targets.is_none() {
|
||||
match self.input_path().extension().unwrap().to_str().unwrap() {
|
||||
"wgsl" => params.targets = Some(Targets::wgsl_default()),
|
||||
"spvasm" => params.targets = Some(Targets::non_wgsl_default()),
|
||||
"vert" | "frag" | "comp" => params.targets = Some(Targets::non_wgsl_default()),
|
||||
e => {
|
||||
panic!("Unknown extension: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params
|
||||
}
|
||||
|
||||
/// Write `data` to a file corresponding to this input file in
|
||||
/// `subdirectory`, with `extension`.
|
||||
fn write_output_file(&self, subdirectory: &str, extension: &str, data: impl AsRef<[u8]>) {
|
||||
let output_path = self.output_path(subdirectory, extension);
|
||||
fs::create_dir_all(output_path.parent().unwrap()).unwrap();
|
||||
if let Err(err) = fs::write(&output_path, data) {
|
||||
panic!("Error writing {}: {}", output_path.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(hlsl_out)]
|
||||
type FragmentEntryPoint<'a> = naga::back::hlsl::FragmentEntryPoint<'a>;
|
||||
#[cfg(not(hlsl_out))]
|
||||
type FragmentEntryPoint<'a> = ();
|
||||
const DIR_IN: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/in");
|
||||
const DIR_OUT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/out");
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<&str>) {
|
||||
let params = input.read_parameters();
|
||||
let params = input.read_parameters(DIR_IN);
|
||||
let name = &input.file_name;
|
||||
|
||||
let targets = params.targets.unwrap();
|
||||
@ -420,12 +25,11 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<&
|
||||
)
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
{
|
||||
if targets.contains(Targets::IR) {
|
||||
let config = ron::ser::PrettyConfig::default().new_line("\n".to_string());
|
||||
let string = ron::ser::to_string_pretty(module, config).unwrap();
|
||||
input.write_output_file("ir", "ron", string);
|
||||
input.write_output_file("ir", "ron", string, DIR_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,12 +60,11 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<&
|
||||
// snapshots makes the output independent of unused arena entries.
|
||||
naga::compact::compact(module, KeepUnused::No);
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
{
|
||||
if targets.contains(Targets::IR) {
|
||||
let config = ron::ser::PrettyConfig::default().new_line("\n".to_string());
|
||||
let string = ron::ser::to_string_pretty(module, config).unwrap();
|
||||
input.write_output_file("ir", "compact.ron", string);
|
||||
input.write_output_file("ir", "compact.ron", string, DIR_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -478,130 +81,112 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<&
|
||||
})
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
{
|
||||
if targets.contains(Targets::ANALYSIS) {
|
||||
let config = ron::ser::PrettyConfig::default().new_line("\n".to_string());
|
||||
let string = ron::ser::to_string_pretty(&info, config).unwrap();
|
||||
input.write_output_file("analysis", "info.ron", string);
|
||||
input.write_output_file("analysis", "info.ron", string, DIR_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "deserialize", spv_out))]
|
||||
{
|
||||
if targets.contains(Targets::SPIRV) {
|
||||
let mut debug_info = None;
|
||||
if let Some(source_code) = source_code {
|
||||
debug_info = Some(naga::back::spv::DebugInfo {
|
||||
source_code,
|
||||
file_name: name.as_path().into(),
|
||||
// wgpu#6266: we technically know all the information here to
|
||||
// produce the valid language but it's not too important for
|
||||
// validation purposes
|
||||
language: naga::back::spv::SourceLanguage::Unknown,
|
||||
})
|
||||
}
|
||||
if targets.contains(Targets::SPIRV) {
|
||||
let mut debug_info = None;
|
||||
if let Some(source_code) = source_code {
|
||||
debug_info = Some(naga::back::spv::DebugInfo {
|
||||
source_code,
|
||||
file_name: name.as_path().into(),
|
||||
// wgpu#6266: we technically know all the information here to
|
||||
// produce the valid language but it's not too important for
|
||||
// validation purposes
|
||||
language: naga::back::spv::SourceLanguage::Unknown,
|
||||
})
|
||||
}
|
||||
|
||||
write_output_spv(
|
||||
write_output_spv(
|
||||
input,
|
||||
module,
|
||||
&info,
|
||||
debug_info,
|
||||
¶ms.spv,
|
||||
params.bounds_check_policies,
|
||||
¶ms.pipeline_constants,
|
||||
);
|
||||
}
|
||||
|
||||
if targets.contains(Targets::METAL) {
|
||||
write_output_msl(
|
||||
input,
|
||||
module,
|
||||
&info,
|
||||
¶ms.msl,
|
||||
¶ms.msl_pipeline,
|
||||
params.bounds_check_policies,
|
||||
¶ms.pipeline_constants,
|
||||
);
|
||||
}
|
||||
|
||||
if targets.contains(Targets::GLSL) {
|
||||
for ep in module.entry_points.iter() {
|
||||
if params.glsl_exclude_list.contains(&ep.name) {
|
||||
continue;
|
||||
}
|
||||
write_output_glsl(
|
||||
input,
|
||||
module,
|
||||
&info,
|
||||
debug_info,
|
||||
¶ms.spv,
|
||||
ep.stage,
|
||||
&ep.name,
|
||||
¶ms.glsl,
|
||||
params.bounds_check_policies,
|
||||
params.glsl_multiview,
|
||||
¶ms.pipeline_constants,
|
||||
);
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "deserialize", msl_out))]
|
||||
{
|
||||
if targets.contains(Targets::METAL) {
|
||||
write_output_msl(
|
||||
input,
|
||||
module,
|
||||
&info,
|
||||
¶ms.msl,
|
||||
¶ms.msl_pipeline,
|
||||
params.bounds_check_policies,
|
||||
¶ms.pipeline_constants,
|
||||
|
||||
if targets.contains(Targets::DOT) {
|
||||
let string = naga::back::dot::write(module, Some(&info), Default::default()).unwrap();
|
||||
input.write_output_file("dot", "dot", string, DIR_OUT);
|
||||
}
|
||||
|
||||
if targets.contains(Targets::HLSL) {
|
||||
let frag_module;
|
||||
let mut frag_ep = None;
|
||||
if let Some(ref module_spec) = params.fragment_module {
|
||||
let full_path = input.input_directory(DIR_IN).join(&module_spec.path);
|
||||
|
||||
assert_eq!(
|
||||
full_path.extension().unwrap().to_string_lossy(),
|
||||
"wgsl",
|
||||
"Currently all fragment modules must be in WGSL"
|
||||
);
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "deserialize", glsl_out))]
|
||||
{
|
||||
if targets.contains(Targets::GLSL) {
|
||||
for ep in module.entry_points.iter() {
|
||||
if params.glsl_exclude_list.contains(&ep.name) {
|
||||
continue;
|
||||
}
|
||||
write_output_glsl(
|
||||
input,
|
||||
module,
|
||||
&info,
|
||||
ep.stage,
|
||||
&ep.name,
|
||||
¶ms.glsl,
|
||||
params.bounds_check_policies,
|
||||
params.glsl_multiview,
|
||||
¶ms.pipeline_constants,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(dot_out)]
|
||||
{
|
||||
if targets.contains(Targets::DOT) {
|
||||
let string = naga::back::dot::write(module, Some(&info), Default::default()).unwrap();
|
||||
input.write_output_file("dot", "dot", string);
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "deserialize", hlsl_out))]
|
||||
{
|
||||
if targets.contains(Targets::HLSL) {
|
||||
let frag_module;
|
||||
let mut frag_ep = None;
|
||||
if let Some(ref module_spec) = params.fragment_module {
|
||||
let full_path = input.input_directory().join(&module_spec.path);
|
||||
|
||||
assert_eq!(
|
||||
full_path.extension().unwrap().to_string_lossy(),
|
||||
"wgsl",
|
||||
"Currently all fragment modules must be in WGSL"
|
||||
);
|
||||
let frag_src = std::fs::read_to_string(full_path).unwrap();
|
||||
|
||||
let frag_src = fs::read_to_string(full_path).unwrap();
|
||||
frag_module =
|
||||
naga::front::wgsl::parse_str(&frag_src).expect("Failed to parse fragment module");
|
||||
|
||||
frag_module = naga::front::wgsl::parse_str(&frag_src)
|
||||
.expect("Failed to parse fragment module");
|
||||
|
||||
frag_ep = Some(
|
||||
naga::back::hlsl::FragmentEntryPoint::new(
|
||||
&frag_module,
|
||||
&module_spec.entry_point,
|
||||
)
|
||||
frag_ep = Some(
|
||||
naga::back::hlsl::FragmentEntryPoint::new(&frag_module, &module_spec.entry_point)
|
||||
.expect("Could not find fragment entry point"),
|
||||
);
|
||||
}
|
||||
|
||||
write_output_hlsl(
|
||||
input,
|
||||
module,
|
||||
&info,
|
||||
¶ms.hlsl,
|
||||
¶ms.pipeline_constants,
|
||||
frag_ep,
|
||||
);
|
||||
}
|
||||
|
||||
write_output_hlsl(
|
||||
input,
|
||||
module,
|
||||
&info,
|
||||
¶ms.hlsl,
|
||||
¶ms.pipeline_constants,
|
||||
frag_ep,
|
||||
);
|
||||
}
|
||||
#[cfg(all(feature = "deserialize", wgsl_out))]
|
||||
{
|
||||
if targets.contains(Targets::WGSL) {
|
||||
write_output_wgsl(input, module, &info, ¶ms.wgsl);
|
||||
}
|
||||
|
||||
if targets.contains(Targets::WGSL) {
|
||||
write_output_wgsl(input, module, &info, ¶ms.wgsl);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(spv_out)]
|
||||
fn write_output_spv(
|
||||
input: &Input,
|
||||
module: &naga::Module,
|
||||
@ -612,32 +197,8 @@ fn write_output_spv(
|
||||
pipeline_constants: &naga::back::PipelineConstants,
|
||||
) {
|
||||
use naga::back::spv;
|
||||
use rspirv::binary::Disassemble;
|
||||
|
||||
let mut flags = spv::WriterFlags::LABEL_VARYINGS;
|
||||
flags.set(spv::WriterFlags::DEBUG, params.debug);
|
||||
flags.set(
|
||||
spv::WriterFlags::ADJUST_COORDINATE_SPACE,
|
||||
params.adjust_coordinate_space,
|
||||
);
|
||||
flags.set(spv::WriterFlags::FORCE_POINT_SIZE, params.force_point_size);
|
||||
flags.set(spv::WriterFlags::CLAMP_FRAG_DEPTH, params.clamp_frag_depth);
|
||||
|
||||
let options = spv::Options {
|
||||
lang_version: (params.version.0, params.version.1),
|
||||
flags,
|
||||
capabilities: if params.capabilities.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(params.capabilities.clone())
|
||||
},
|
||||
bounds_check_policies,
|
||||
binding_map: params.binding_map.clone(),
|
||||
zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::Polyfill,
|
||||
force_loop_bounding: true,
|
||||
use_storage_input_output_16: params.use_storage_input_output_16,
|
||||
debug_info,
|
||||
};
|
||||
let options = params.to_options(bounds_check_policies, debug_info);
|
||||
|
||||
let (module, info) =
|
||||
naga::back::pipeline_constants::process_overrides(module, info, None, pipeline_constants)
|
||||
@ -663,7 +224,6 @@ fn write_output_spv(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(spv_out)]
|
||||
fn write_output_spv_inner(
|
||||
input: &Input,
|
||||
module: &naga::Module,
|
||||
@ -686,10 +246,9 @@ fn write_output_spv_inner(
|
||||
} else {
|
||||
dis
|
||||
};
|
||||
input.write_output_file("spv", extension, dis);
|
||||
input.write_output_file("spv", extension, dis, DIR_OUT);
|
||||
}
|
||||
|
||||
#[cfg(msl_out)]
|
||||
fn write_output_msl(
|
||||
input: &Input,
|
||||
module: &naga::Module,
|
||||
@ -718,10 +277,9 @@ fn write_output_msl(
|
||||
}
|
||||
}
|
||||
|
||||
input.write_output_file("msl", "msl", string);
|
||||
input.write_output_file("msl", "msl", string, DIR_OUT);
|
||||
}
|
||||
|
||||
#[cfg(glsl_out)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn write_output_glsl(
|
||||
input: &Input,
|
||||
@ -760,10 +318,9 @@ fn write_output_glsl(
|
||||
writer.write().expect("GLSL write failed");
|
||||
|
||||
let extension = format!("{ep_name}.{stage:?}.glsl");
|
||||
input.write_output_file("glsl", &extension, buffer);
|
||||
input.write_output_file("glsl", &extension, buffer, DIR_OUT);
|
||||
}
|
||||
|
||||
#[cfg(hlsl_out)]
|
||||
fn write_output_hlsl(
|
||||
input: &Input,
|
||||
module: &naga::Module,
|
||||
@ -772,7 +329,6 @@ fn write_output_hlsl(
|
||||
pipeline_constants: &naga::back::PipelineConstants,
|
||||
frag_ep: Option<naga::back::hlsl::FragmentEntryPoint>,
|
||||
) {
|
||||
use core::fmt::Write as _;
|
||||
use naga::back::hlsl;
|
||||
|
||||
println!("generating HLSL");
|
||||
@ -788,7 +344,7 @@ fn write_output_hlsl(
|
||||
.write(&module, &info, frag_ep.as_ref())
|
||||
.expect("HLSL write failed");
|
||||
|
||||
input.write_output_file("hlsl", "hlsl", buffer);
|
||||
input.write_output_file("hlsl", "hlsl", buffer, DIR_OUT);
|
||||
|
||||
// We need a config file for validation script
|
||||
// This file contains an info about profiles (shader stages) contains inside generated shader
|
||||
@ -815,10 +371,11 @@ fn write_output_hlsl(
|
||||
});
|
||||
}
|
||||
|
||||
config.to_file(input.output_path("hlsl", "ron")).unwrap();
|
||||
config
|
||||
.to_file(input.output_path("hlsl", "ron", DIR_OUT))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(wgsl_out)]
|
||||
fn write_output_wgsl(
|
||||
input: &Input,
|
||||
module: &naga::Module,
|
||||
@ -829,44 +386,37 @@ fn write_output_wgsl(
|
||||
|
||||
println!("generating WGSL");
|
||||
|
||||
let mut flags = wgsl::WriterFlags::empty();
|
||||
flags.set(wgsl::WriterFlags::EXPLICIT_TYPES, params.explicit_types);
|
||||
let string = wgsl::write_string(module, info, params.into()).expect("WGSL write failed");
|
||||
|
||||
let string = wgsl::write_string(module, info, flags).expect("WGSL write failed");
|
||||
|
||||
input.write_output_file("wgsl", "wgsl", string);
|
||||
input.write_output_file("wgsl", "wgsl", string, DIR_OUT);
|
||||
}
|
||||
|
||||
// While we _can_ run this test under miri, it is extremely slow (>5 minutes),
|
||||
// and naga isn't the primary target for miri testing, so we disable it.
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
#[test]
|
||||
fn convert_snapshots_wgsl() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
for input in Input::files_in_dir("wgsl", &["wgsl"]) {
|
||||
let source = input.read_source();
|
||||
for input in Input::files_in_dir("wgsl", &["wgsl"], DIR_IN) {
|
||||
let source = input.read_source(DIR_IN, true);
|
||||
// crlf will make the large split output different on different platform
|
||||
let source = source.replace('\r', "");
|
||||
|
||||
let params = input.read_parameters();
|
||||
let WgslInParameters { parse_doc_comments } = params.wgsl_in;
|
||||
let params = input.read_parameters(DIR_IN);
|
||||
|
||||
let options = naga::front::wgsl::Options { parse_doc_comments };
|
||||
let mut frontend = naga::front::wgsl::Frontend::new_with_options(options);
|
||||
let mut frontend = naga::front::wgsl::Frontend::new_with_options((¶ms.wgsl_in).into());
|
||||
match frontend.parse(&source) {
|
||||
Ok(mut module) => check_targets(&input, &mut module, Some(&source)),
|
||||
Err(e) => panic!(
|
||||
"{}",
|
||||
e.emit_to_string_with_path(&source, input.input_path())
|
||||
e.emit_to_string_with_path(&source, input.input_path(DIR_IN))
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// miri doesn't allow us to shell out to `spirv-as`
|
||||
#[cfg(feature = "spv-in")]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
#[test]
|
||||
fn convert_snapshots_spv() {
|
||||
@ -874,11 +424,11 @@ fn convert_snapshots_spv() {
|
||||
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
for input in Input::files_in_dir("spv", &["spvasm"]) {
|
||||
for input in Input::files_in_dir("spv", &["spvasm"], DIR_IN) {
|
||||
println!("Assembling '{}'", input.file_name.display());
|
||||
|
||||
let command = Command::new("spirv-as")
|
||||
.arg(input.input_path())
|
||||
.arg(input.input_path(DIR_IN))
|
||||
.arg("-o")
|
||||
.arg("-")
|
||||
.output()
|
||||
@ -897,20 +447,10 @@ fn convert_snapshots_spv() {
|
||||
);
|
||||
}
|
||||
|
||||
let params = input.read_parameters();
|
||||
let SpirvInParameters {
|
||||
adjust_coordinate_space,
|
||||
} = params.spv_in;
|
||||
let params = input.read_parameters(DIR_IN);
|
||||
|
||||
let mut module = naga::front::spv::parse_u8_slice(
|
||||
&command.stdout,
|
||||
&naga::front::spv::Options {
|
||||
adjust_coordinate_space,
|
||||
strict_capabilities: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let mut module =
|
||||
naga::front::spv::parse_u8_slice(&command.stdout, &(¶ms.spv_in).into()).unwrap();
|
||||
|
||||
check_targets(&input, &mut module, None);
|
||||
}
|
||||
@ -918,14 +458,13 @@ fn convert_snapshots_spv() {
|
||||
|
||||
// While we _can_ run this test under miri, it is extremely slow (>5 minutes),
|
||||
// and naga isn't the primary target for miri testing, so we disable it.
|
||||
#[cfg(feature = "glsl-in")]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
#[allow(unused_variables)]
|
||||
#[test]
|
||||
fn convert_snapshots_glsl() {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
for input in Input::files_in_dir("glsl", &["vert", "frag", "comp"]) {
|
||||
for input in Input::files_in_dir("glsl", &["vert", "frag", "comp"], DIR_IN) {
|
||||
let input = Input {
|
||||
keep_input_extension: true,
|
||||
..input
|
||||
@ -946,7 +485,7 @@ fn convert_snapshots_glsl() {
|
||||
stage,
|
||||
defines: Default::default(),
|
||||
},
|
||||
&input.read_source(),
|
||||
&input.read_source(DIR_IN, true),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ Test SPIR-V backend capability checks.
|
||||
|
||||
use spirv::Capability as Ca;
|
||||
|
||||
#[cfg(spv_out)]
|
||||
use rspirv::binary::Disassemble;
|
||||
|
||||
fn capabilities_used(source: &str) -> naga::FastIndexSet<Ca> {
|
||||
@ -276,7 +275,6 @@ fn f16_io_capabilities() {
|
||||
assert!(caps_polyfill.contains(&Ca::Float16));
|
||||
}
|
||||
|
||||
#[cfg(spv_out)]
|
||||
#[test]
|
||||
fn f16_io_polyfill_codegen() {
|
||||
let source = r#"
|
||||
|
||||
@ -361,7 +361,6 @@ fn builtin_cross_product_args() {
|
||||
assert!(variant(VectorSize::Quad, 2).is_err());
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn incompatible_interpolation_and_sampling_types() {
|
||||
use dummy_interpolation_shader::DummyInterpolationShader;
|
||||
@ -443,7 +442,6 @@ fn incompatible_interpolation_and_sampling_types() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wgsl-in", feature = "glsl-out"))]
|
||||
#[test]
|
||||
fn no_flat_first_in_glsl() {
|
||||
use dummy_interpolation_shader::DummyInterpolationShader;
|
||||
@ -484,13 +482,11 @@ fn no_flat_first_in_glsl() {
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "wgsl-in"))]
|
||||
mod dummy_interpolation_shader {
|
||||
pub struct DummyInterpolationShader {
|
||||
pub source: String,
|
||||
pub module: naga::Module,
|
||||
pub interpolate_attr: String,
|
||||
#[cfg_attr(not(feature = "glsl-out"), expect(dead_code))]
|
||||
pub entry_point: &'static str,
|
||||
}
|
||||
|
||||
@ -656,7 +652,6 @@ fn binding_arrays_cannot_hold_scalars() {
|
||||
assert!(t.validator.validate(&t.module).is_err());
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn validation_error_messages() {
|
||||
let cases = [(
|
||||
@ -693,7 +688,6 @@ error: Function [1] 'main' is invalid
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn bad_texture_dimensions_level() {
|
||||
fn validate(level: &str) -> Result<ModuleInfo, naga::valid::ValidationError> {
|
||||
@ -790,7 +784,6 @@ fn arity_check() {
|
||||
assert!(validate(Mf::Pow, &[3]).is_err());
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn global_use_scalar() {
|
||||
let source = "
|
||||
@ -815,7 +808,6 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn global_use_array() {
|
||||
let source = "
|
||||
@ -840,7 +832,6 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn global_use_array_index() {
|
||||
let source = "
|
||||
@ -865,7 +856,6 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn global_use_phony() {
|
||||
let source = "
|
||||
@ -890,7 +880,6 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn global_use_unreachable() {
|
||||
// We should allow statements after `return`, and such statements should
|
||||
@ -924,7 +913,6 @@ fn main() {
|
||||
/// Parse and validate the module defined in `source`.
|
||||
///
|
||||
/// Panics if unsuccessful.
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
fn parse_validate(source: &str) -> (Module, ModuleInfo) {
|
||||
let module = naga::front::wgsl::parse_str(source).expect("module should parse");
|
||||
let info = valid::Validator::new(Default::default(), valid::Capabilities::all())
|
||||
@ -948,7 +936,6 @@ fn parse_validate(source: &str) -> (Module, ModuleInfo) {
|
||||
///
|
||||
/// The optional `unused_body` can introduce additional objects to the module,
|
||||
/// to verify that they are adjusted correctly by compaction.
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
fn override_test(test_case: &str, unused_body: Option<&str>) {
|
||||
use hashbrown::HashMap;
|
||||
use naga::back::pipeline_constants::PipelineConstantError;
|
||||
@ -1004,7 +991,6 @@ fn unused() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn override_in_workgroup_size() {
|
||||
override_test(
|
||||
@ -1017,7 +1003,6 @@ fn used() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn override_in_workgroup_size_nested() {
|
||||
// Initializer for override used in workgroup size refers to another
|
||||
@ -1034,7 +1019,6 @@ fn used() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn override_in_function() {
|
||||
override_test(
|
||||
@ -1052,7 +1036,6 @@ fn used() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn override_in_entrypoint() {
|
||||
override_test(
|
||||
@ -1070,7 +1053,6 @@ fn used() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn override_in_array_size() {
|
||||
override_test(
|
||||
@ -1086,7 +1068,6 @@ fn used() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn override_in_global_init() {
|
||||
override_test(
|
||||
@ -1102,7 +1083,6 @@ fn used() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn override_with_multiple_globals() {
|
||||
// Test that when compaction of the `unused` entrypoint removes `arr1`, the
|
||||
|
||||
@ -4098,7 +4098,6 @@ fn invalid_clip_distances() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn max_type_size_large_array() {
|
||||
// The total size of an array is not resolved until validation. Type aliases
|
||||
@ -4114,7 +4113,6 @@ fn max_type_size_large_array() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn max_type_size_array_of_arrays() {
|
||||
// If the size of the base type of an array is oversize, the error is raised
|
||||
@ -4129,7 +4127,6 @@ fn max_type_size_array_of_arrays() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn max_type_size_override_array() {
|
||||
// The validation that occurs after override processing should reject any
|
||||
@ -4166,7 +4163,6 @@ fn max_type_size_override_array() {
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn max_type_size_array_in_struct() {
|
||||
// If a struct member is oversize, the error is raised during lowering.
|
||||
@ -4189,7 +4185,6 @@ fn max_type_size_array_in_struct() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn max_type_size_two_arrays_in_struct() {
|
||||
// The total size of a struct is checked during lowering. For a struct,
|
||||
@ -4215,7 +4210,6 @@ fn max_type_size_two_arrays_in_struct() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn max_type_size_array_of_structs() {
|
||||
// The total size of an array is not resolved until validation. Type aliases
|
||||
@ -4236,7 +4230,6 @@ fn max_type_size_array_of_structs() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgsl-in")]
|
||||
#[test]
|
||||
fn source_with_control_char() {
|
||||
check(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user