mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-29 02:03:35 +00:00
abba12ae4e
Add the following flags to `wgpu_types::Features`: - `SHADER_INT64_ATOMIC_ALL_OPS` enables all atomic operations on `atomic<i64>` and `atomic<u64>` values. - `SHADER_INT64_ATOMIC_MIN_MAX` is a subset of the above, enabling only `AtomicFunction::Min` and `AtomicFunction::Max` operations on `atomic<i64>` and `atomic<u64>` values in the `Storage` address space. These are the only 64-bit atomic operations available on Metal as of 3.1. Add corresponding flags to `naga::valid::Capabilities`. These are supported by the WGSL front end, and all Naga backends. Platform support: - On Direct3d 12, in `D3D12_FEATURE_DATA_D3D12_OPTIONS9`, if `AtomicInt64OnTypedResourceSupported` and `AtomicInt64OnGroupSharedSupported` are both available, then both wgpu features described above are available. - On Metal, `SHADER_INT64_ATOMIC_MIN_MAX` is available on Apple9 hardware, and on hardware that advertises both Apple8 and Mac2 support. This also requires Metal Shading Language 2.4 or later. Metal does not yet support the more general `SHADER_INT64_ATOMIC_ALL_OPS`. - On Vulkan, if the `VK_KHR_shader_atomic_int64` extension is available with both the `shader_buffer_int64_atomics` and `shader_shared_int64_atomics` features, then both wgpu features described above are available.
263 lines
8.0 KiB
Rust
263 lines
8.0 KiB
Rust
use naga::{valid, Expression, Function, Scalar};
|
|
|
|
/// Validation should fail if `AtomicResult` expressions are not
|
|
/// populated by `Atomic` statements.
|
|
#[test]
|
|
fn populate_atomic_result() {
|
|
use naga::{Module, Type, TypeInner};
|
|
|
|
/// 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,
|
|
) -> 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,
|
|
);
|
|
|
|
match variant {
|
|
Variant::Atomic => {
|
|
fun.body.push(
|
|
naga::Statement::Atomic {
|
|
pointer: ex_global,
|
|
fun: naga::AtomicFunction::Add,
|
|
value: ex_42,
|
|
result: Some(ex_result),
|
|
},
|
|
span,
|
|
);
|
|
}
|
|
Variant::Emit => {
|
|
fun.body.push(
|
|
naga::Statement::Emit(naga::Range::new_from_bounds(ex_result, ex_result)),
|
|
span,
|
|
);
|
|
}
|
|
Variant::None => {}
|
|
}
|
|
|
|
module.functions.append(fun, span);
|
|
|
|
valid::Validator::new(
|
|
valid::ValidationFlags::default(),
|
|
valid::Capabilities::all(),
|
|
)
|
|
.validate(&module)
|
|
}
|
|
|
|
try_variant(Variant::Atomic).expect("module should validate");
|
|
assert!(try_variant(Variant::Emit).is_err());
|
|
assert!(try_variant(Variant::None).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn populate_call_result() {
|
|
use naga::{Module, Type, TypeInner};
|
|
|
|
/// 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,
|
|
) -> 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);
|
|
|
|
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 => {}
|
|
}
|
|
|
|
module.functions.append(fun_caller, span);
|
|
|
|
valid::Validator::new(
|
|
valid::ValidationFlags::default(),
|
|
valid::Capabilities::all(),
|
|
)
|
|
.validate(&module)
|
|
}
|
|
|
|
try_variant(Variant::Call).expect("should validate");
|
|
assert!(try_variant(Variant::Emit).is_err());
|
|
assert!(try_variant(Variant::None).is_err());
|
|
}
|
|
|
|
#[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());
|
|
}
|