2024-05-24 23:25:36 +00:00
|
|
|
use naga::{valid, Expression, Function, Scalar};
|
|
|
|
|
2024-06-03 14:59:25 +00:00
|
|
|
/// Validation should fail if `AtomicResult` expressions are not
|
|
|
|
/// populated by `Atomic` statements.
|
2024-05-24 23:25:36 +00:00
|
|
|
#[test]
|
2024-06-03 14:59:25 +00:00
|
|
|
fn populate_atomic_result() {
|
2024-05-24 23:25:36 +00:00
|
|
|
use naga::{Module, Type, TypeInner};
|
|
|
|
|
2024-06-03 14:59:25 +00:00
|
|
|
/// Different variants of the test case that we want to exercise.
|
|
|
|
enum Variant {
|
|
|
|
/// An `AtomicResult` expression with an `Atomic` statement
|
|
|
|
/// that populates it: valid.
|
|
|
|
Atomic,
|
|
|
|
|
|
|
|
/// An `AtomicResult` expression visited by an `Emit`
|
|
|
|
/// statement: invalid.
|
|
|
|
Emit,
|
|
|
|
|
|
|
|
/// An `AtomicResult` expression visited by no statement at
|
|
|
|
/// all: invalid
|
|
|
|
None,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looking at uses of `variant` should make it easy to identify
|
|
|
|
// the differences between the test cases.
|
|
|
|
fn try_variant(
|
|
|
|
variant: Variant,
|
2024-05-24 23:25:36 +00:00
|
|
|
) -> Result<naga::valid::ModuleInfo, naga::WithSpan<naga::valid::ValidationError>> {
|
|
|
|
let span = naga::Span::default();
|
|
|
|
let mut module = Module::default();
|
|
|
|
let ty_u32 = module.types.insert(
|
|
|
|
Type {
|
|
|
|
name: Some("u32".into()),
|
|
|
|
inner: TypeInner::Scalar(Scalar::U32),
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
let ty_atomic_u32 = module.types.insert(
|
|
|
|
Type {
|
|
|
|
name: Some("atomic<u32>".into()),
|
|
|
|
inner: TypeInner::Atomic(Scalar::U32),
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
let var_atomic = module.global_variables.append(
|
|
|
|
naga::GlobalVariable {
|
|
|
|
name: Some("atomic_global".into()),
|
|
|
|
space: naga::AddressSpace::WorkGroup,
|
|
|
|
binding: None,
|
|
|
|
ty: ty_atomic_u32,
|
|
|
|
init: None,
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut fun = Function::default();
|
|
|
|
let ex_global = fun
|
|
|
|
.expressions
|
|
|
|
.append(Expression::GlobalVariable(var_atomic), span);
|
|
|
|
let ex_42 = fun
|
|
|
|
.expressions
|
|
|
|
.append(Expression::Literal(naga::Literal::U32(42)), span);
|
|
|
|
let ex_result = fun.expressions.append(
|
|
|
|
Expression::AtomicResult {
|
|
|
|
ty: ty_u32,
|
|
|
|
comparison: false,
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
|
2024-06-03 14:59:25 +00:00
|
|
|
match variant {
|
|
|
|
Variant::Atomic => {
|
|
|
|
fun.body.push(
|
|
|
|
naga::Statement::Atomic {
|
|
|
|
pointer: ex_global,
|
|
|
|
fun: naga::AtomicFunction::Add,
|
|
|
|
value: ex_42,
|
2024-06-09 00:21:25 +00:00
|
|
|
result: Some(ex_result),
|
2024-06-03 14:59:25 +00:00
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Variant::Emit => {
|
|
|
|
fun.body.push(
|
|
|
|
naga::Statement::Emit(naga::Range::new_from_bounds(ex_result, ex_result)),
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Variant::None => {}
|
2024-05-24 23:25:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.functions.append(fun, span);
|
|
|
|
|
|
|
|
valid::Validator::new(
|
|
|
|
valid::ValidationFlags::default(),
|
|
|
|
valid::Capabilities::all(),
|
|
|
|
)
|
|
|
|
.validate(&module)
|
|
|
|
}
|
|
|
|
|
2024-06-03 14:59:25 +00:00
|
|
|
try_variant(Variant::Atomic).expect("module should validate");
|
|
|
|
assert!(try_variant(Variant::Emit).is_err());
|
|
|
|
assert!(try_variant(Variant::None).is_err());
|
2024-05-24 23:25:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2024-06-03 14:59:25 +00:00
|
|
|
fn populate_call_result() {
|
2024-05-24 23:25:36 +00:00
|
|
|
use naga::{Module, Type, TypeInner};
|
|
|
|
|
2024-06-03 14:59:25 +00:00
|
|
|
/// Different variants of the test case that we want to exercise.
|
|
|
|
enum Variant {
|
|
|
|
/// A `CallResult` expression with an `Call` statement that
|
|
|
|
/// populates it: valid.
|
|
|
|
Call,
|
|
|
|
|
|
|
|
/// A `CallResult` expression visited by an `Emit` statement:
|
|
|
|
/// invalid.
|
|
|
|
Emit,
|
|
|
|
|
|
|
|
/// A `CallResult` expression visited by no statement at all:
|
|
|
|
/// invalid
|
|
|
|
None,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looking at uses of `variant` should make it easy to identify
|
|
|
|
// the differences between the test cases.
|
|
|
|
fn try_variant(
|
|
|
|
variant: Variant,
|
2024-05-24 23:25:36 +00:00
|
|
|
) -> Result<naga::valid::ModuleInfo, naga::WithSpan<naga::valid::ValidationError>> {
|
|
|
|
let span = naga::Span::default();
|
|
|
|
let mut module = Module::default();
|
|
|
|
let ty_u32 = module.types.insert(
|
|
|
|
Type {
|
|
|
|
name: Some("u32".into()),
|
|
|
|
inner: TypeInner::Scalar(Scalar::U32),
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut fun_callee = Function {
|
|
|
|
result: Some(naga::FunctionResult {
|
|
|
|
ty: ty_u32,
|
|
|
|
binding: None,
|
|
|
|
}),
|
|
|
|
..Function::default()
|
|
|
|
};
|
|
|
|
let ex_42 = fun_callee
|
|
|
|
.expressions
|
|
|
|
.append(Expression::Literal(naga::Literal::U32(42)), span);
|
|
|
|
fun_callee
|
|
|
|
.body
|
|
|
|
.push(naga::Statement::Return { value: Some(ex_42) }, span);
|
|
|
|
let fun_callee = module.functions.append(fun_callee, span);
|
|
|
|
|
|
|
|
let mut fun_caller = Function::default();
|
|
|
|
let ex_result = fun_caller
|
|
|
|
.expressions
|
|
|
|
.append(Expression::CallResult(fun_callee), span);
|
|
|
|
|
2024-06-03 14:59:25 +00:00
|
|
|
match variant {
|
|
|
|
Variant::Call => {
|
|
|
|
fun_caller.body.push(
|
|
|
|
naga::Statement::Call {
|
|
|
|
function: fun_callee,
|
|
|
|
arguments: vec![],
|
|
|
|
result: Some(ex_result),
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Variant::Emit => {
|
|
|
|
fun_caller.body.push(
|
|
|
|
naga::Statement::Emit(naga::Range::new_from_bounds(ex_result, ex_result)),
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Variant::None => {}
|
2024-05-24 23:25:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.functions.append(fun_caller, span);
|
|
|
|
|
|
|
|
valid::Validator::new(
|
|
|
|
valid::ValidationFlags::default(),
|
|
|
|
valid::Capabilities::all(),
|
|
|
|
)
|
|
|
|
.validate(&module)
|
|
|
|
}
|
|
|
|
|
2024-06-03 14:59:25 +00:00
|
|
|
try_variant(Variant::Call).expect("should validate");
|
|
|
|
assert!(try_variant(Variant::Emit).is_err());
|
|
|
|
assert!(try_variant(Variant::None).is_err());
|
2024-05-24 23:25:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn emit_workgroup_uniform_load_result() {
|
|
|
|
use naga::{Module, Type, TypeInner};
|
|
|
|
|
|
|
|
// We want to ensure that the *only* problem with the code is the
|
|
|
|
// use of an `Emit` statement instead of an `Atomic` statement. So
|
|
|
|
// validate two versions of the module varying only in that
|
|
|
|
// aspect.
|
|
|
|
//
|
|
|
|
// Looking at uses of the `wg_load` makes it easy to identify the
|
|
|
|
// differences between the two variants.
|
|
|
|
fn variant(
|
|
|
|
wg_load: bool,
|
|
|
|
) -> Result<naga::valid::ModuleInfo, naga::WithSpan<naga::valid::ValidationError>> {
|
|
|
|
let span = naga::Span::default();
|
|
|
|
let mut module = Module::default();
|
|
|
|
let ty_u32 = module.types.insert(
|
|
|
|
Type {
|
|
|
|
name: Some("u32".into()),
|
|
|
|
inner: TypeInner::Scalar(Scalar::U32),
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
let var_workgroup = module.global_variables.append(
|
|
|
|
naga::GlobalVariable {
|
|
|
|
name: Some("workgroup_global".into()),
|
|
|
|
space: naga::AddressSpace::WorkGroup,
|
|
|
|
binding: None,
|
|
|
|
ty: ty_u32,
|
|
|
|
init: None,
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut fun = Function::default();
|
|
|
|
let ex_global = fun
|
|
|
|
.expressions
|
|
|
|
.append(Expression::GlobalVariable(var_workgroup), span);
|
|
|
|
let ex_result = fun
|
|
|
|
.expressions
|
|
|
|
.append(Expression::WorkGroupUniformLoadResult { ty: ty_u32 }, span);
|
|
|
|
|
|
|
|
if wg_load {
|
|
|
|
fun.body.push(
|
|
|
|
naga::Statement::WorkGroupUniformLoad {
|
|
|
|
pointer: ex_global,
|
|
|
|
result: ex_result,
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
fun.body.push(
|
|
|
|
naga::Statement::Emit(naga::Range::new_from_bounds(ex_result, ex_result)),
|
|
|
|
span,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
module.functions.append(fun, span);
|
|
|
|
|
|
|
|
valid::Validator::new(
|
|
|
|
valid::ValidationFlags::default(),
|
|
|
|
valid::Capabilities::all(),
|
|
|
|
)
|
|
|
|
.validate(&module)
|
|
|
|
}
|
|
|
|
|
|
|
|
variant(true).expect("module should validate");
|
|
|
|
assert!(variant(false).is_err());
|
|
|
|
}
|
2024-08-27 18:09:00 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "wgsl-in")]
|
|
|
|
#[test]
|
|
|
|
fn bad_cross_builtin_args() {
|
|
|
|
let cases = [
|
|
|
|
(
|
|
|
|
"vec2(0., 1.)",
|
|
|
|
"\
|
|
|
|
error: Entry point main at Compute is invalid
|
|
|
|
┌─ wgsl:3:13
|
|
|
|
│
|
|
|
|
3 │ let a = cross(vec2(0., 1.), vec2(0., 1.));
|
|
|
|
│ ^^^^^ naga::Expression [6]
|
|
|
|
│
|
|
|
|
= Expression [6] is invalid
|
|
|
|
= Argument [0] to Cross as expression [2] has an invalid type.
|
|
|
|
|
|
|
|
",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"vec4(0., 1., 2., 3.)",
|
|
|
|
"\
|
|
|
|
error: Entry point main at Compute is invalid
|
|
|
|
┌─ wgsl:3:13
|
|
|
|
│
|
|
|
|
3 │ let a = cross(vec4(0., 1., 2., 3.), vec4(0., 1., 2., 3.));
|
|
|
|
│ ^^^^^ naga::Expression [10]
|
|
|
|
│
|
|
|
|
= Expression [10] is invalid
|
|
|
|
= Argument [0] to Cross as expression [4] has an invalid type.
|
|
|
|
|
|
|
|
",
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
for (invalid_arg, expected_err) in cases {
|
|
|
|
let source = format!(
|
|
|
|
"\
|
|
|
|
@compute @workgroup_size(1)
|
|
|
|
fn main() {{
|
|
|
|
let a = cross({invalid_arg}, {invalid_arg});
|
|
|
|
}}
|
|
|
|
"
|
|
|
|
);
|
|
|
|
let module = naga::front::wgsl::parse_str(&source).unwrap();
|
|
|
|
let err = valid::Validator::new(Default::default(), valid::Capabilities::all())
|
|
|
|
.validate_no_overrides(&module)
|
|
|
|
.expect_err("module should be invalid");
|
|
|
|
assert_eq!(err.emit_to_string(&source), expected_err);
|
|
|
|
}
|
|
|
|
}
|