mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-21 22:33:49 +00:00
spv-in parse more atomic ops (#5824)
* add parsing for spirv::Op::AtomicLoad and spirv::Op::AtomicStore * spv-in parse AtomicExchange and AtomicCompareExchange * add atomic i decrement * bookend atomic store statement with emmitter.finish/emitter.start to suppress a double load expression bookend atomic result expressions with emitter.finish/start to prevent double defs * add atomic iadd, isub, smin, umin, smax, umax, and, or, xor * parse atomic flag test and set, parse atomic flag clear * remove atomic compare exchange work * changelog * moved spirv tests into front/spv/mod.rs * feature gate atomic spv tests because they require wgsl-[in,out] * BlockContext::get_contained_global_variable returns Result * Generate spans covering the entire instruction. Granted, there is pre-existing code in the SPIR-V front end that gets this wrong, but: It doesn't make sense to read `self.data_offset`, and then immediately pass that to `self.span_from_with_op`. The point of that function is to make the span cover the entire instruction, operands included. * Move `From` implementation into spv front end * doc comments, minor cleanups * remove parsing of OpAtomicFlagClear and OpAtomicFlagTestAndSet * sync atomic spvasm files --------- Co-authored-by: Jim Blandy <jimb@red-bean.com>
This commit is contained in:
parent
434f197410
commit
fc85e4f970
@ -80,6 +80,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
|
|||||||
|
|
||||||
- Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101).
|
- Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101).
|
||||||
- Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181).
|
- Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181).
|
||||||
|
- Support for more atomic ops in the SPIR-V frontend. By @schell in [#5824](https://github.com/gfx-rs/wgpu/pull/5824).
|
||||||
|
|
||||||
#### Vulkan
|
#### Vulkan
|
||||||
|
|
||||||
|
@ -44,12 +44,8 @@ pub enum Error {
|
|||||||
MultiMemberStruct,
|
MultiMemberStruct,
|
||||||
#[error("encountered unsupported global initializer in an atomic variable")]
|
#[error("encountered unsupported global initializer in an atomic variable")]
|
||||||
GlobalInitUnsupported,
|
GlobalInitUnsupported,
|
||||||
}
|
#[error("expected to find a global variable")]
|
||||||
|
GlobalVariableMissing,
|
||||||
impl From<Error> for crate::front::spv::Error {
|
|
||||||
fn from(source: Error) -> Self {
|
|
||||||
crate::front::spv::Error::AtomicUpgradeError(source)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -159,3 +159,9 @@ impl Error {
|
|||||||
String::from_utf8(writer.into_inner()).unwrap()
|
String::from_utf8(writer.into_inner()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<atomic_upgrade::Error> for Error {
|
||||||
|
fn from(source: atomic_upgrade::Error) -> Self {
|
||||||
|
crate::front::spv::Error::AtomicUpgradeError(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -565,11 +565,15 @@ impl<'a> BlockContext<'a> {
|
|||||||
/// Descend into the expression with the given handle, locating a contained
|
/// Descend into the expression with the given handle, locating a contained
|
||||||
/// global variable.
|
/// global variable.
|
||||||
///
|
///
|
||||||
|
/// If the expression doesn't actually refer to something in a global
|
||||||
|
/// variable, we can't upgrade its type in a way that Naga validation would
|
||||||
|
/// pass, so reject the input instead.
|
||||||
|
///
|
||||||
/// This is used to track atomic upgrades.
|
/// This is used to track atomic upgrades.
|
||||||
fn get_contained_global_variable(
|
fn get_contained_global_variable(
|
||||||
&self,
|
&self,
|
||||||
mut handle: Handle<crate::Expression>,
|
mut handle: Handle<crate::Expression>,
|
||||||
) -> Option<Handle<crate::GlobalVariable>> {
|
) -> Result<Handle<crate::GlobalVariable>, Error> {
|
||||||
log::debug!("\t\tlocating global variable in {handle:?}");
|
log::debug!("\t\tlocating global variable in {handle:?}");
|
||||||
loop {
|
loop {
|
||||||
match self.expressions[handle] {
|
match self.expressions[handle] {
|
||||||
@ -583,14 +587,16 @@ impl<'a> BlockContext<'a> {
|
|||||||
}
|
}
|
||||||
crate::Expression::GlobalVariable(h) => {
|
crate::Expression::GlobalVariable(h) => {
|
||||||
log::debug!("\t\t found {h:?}");
|
log::debug!("\t\t found {h:?}");
|
||||||
return Some(h);
|
return Ok(h);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
Err(Error::AtomicUpgradeError(
|
||||||
|
crate::front::atomic_upgrade::Error::GlobalVariableMissing,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1323,6 +1329,109 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`].
|
||||||
|
///
|
||||||
|
/// Return a [`Handle`] for a Naga [`Expression`] that holds the value of
|
||||||
|
/// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it
|
||||||
|
/// is a pointer.
|
||||||
|
///
|
||||||
|
/// This may entail spilling `pointer_id`'s value to a temporary:
|
||||||
|
/// see [`get_expr_handle`]'s documentation.
|
||||||
|
///
|
||||||
|
/// [`Expression`]: crate::Expression
|
||||||
|
/// [`Type`]: crate::Type
|
||||||
|
/// [`Handle`]: crate::Handle
|
||||||
|
/// [`get_expr_handle`]: Frontend::get_expr_handle
|
||||||
|
fn get_exp_and_base_ty_handles(
|
||||||
|
&self,
|
||||||
|
pointer_id: spirv::Word,
|
||||||
|
ctx: &mut BlockContext,
|
||||||
|
emitter: &mut crate::proc::Emitter,
|
||||||
|
block: &mut crate::Block,
|
||||||
|
body_idx: usize,
|
||||||
|
) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {
|
||||||
|
log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id);
|
||||||
|
let p_lexp_handle;
|
||||||
|
let p_lexp_ty_id;
|
||||||
|
{
|
||||||
|
let lexp = self.lookup_expression.lookup(pointer_id)?;
|
||||||
|
p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);
|
||||||
|
p_lexp_ty_id = lexp.type_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
log::trace!("\t\t\tlooking up pointer type {pointer_id:?}");
|
||||||
|
let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
|
||||||
|
let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
|
||||||
|
|
||||||
|
log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}");
|
||||||
|
let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
|
||||||
|
|
||||||
|
Ok((p_lexp_handle, p_base_ty.handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn parse_atomic_expr_with_value(
|
||||||
|
&mut self,
|
||||||
|
inst: Instruction,
|
||||||
|
emitter: &mut crate::proc::Emitter,
|
||||||
|
ctx: &mut BlockContext,
|
||||||
|
block: &mut crate::Block,
|
||||||
|
block_id: spirv::Word,
|
||||||
|
body_idx: usize,
|
||||||
|
atomic_function: crate::AtomicFunction,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
inst.expect(7)?;
|
||||||
|
let start = self.data_offset;
|
||||||
|
let result_type_id = self.next()?;
|
||||||
|
let result_id = self.next()?;
|
||||||
|
let pointer_id = self.next()?;
|
||||||
|
let _scope_id = self.next()?;
|
||||||
|
let _memory_semantics_id = self.next()?;
|
||||||
|
let value_id = self.next()?;
|
||||||
|
let span = self.span_from_with_op(start);
|
||||||
|
|
||||||
|
let (p_lexp_handle, p_base_ty_handle) =
|
||||||
|
self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;
|
||||||
|
|
||||||
|
log::trace!("\t\t\tlooking up value expr {value_id:?}");
|
||||||
|
let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;
|
||||||
|
|
||||||
|
block.extend(emitter.finish(ctx.expressions));
|
||||||
|
// Create an expression for our result
|
||||||
|
let r_lexp_handle = {
|
||||||
|
let expr = crate::Expression::AtomicResult {
|
||||||
|
ty: p_base_ty_handle,
|
||||||
|
comparison: false,
|
||||||
|
};
|
||||||
|
let handle = ctx.expressions.append(expr, span);
|
||||||
|
self.lookup_expression.insert(
|
||||||
|
result_id,
|
||||||
|
LookupExpression {
|
||||||
|
handle,
|
||||||
|
type_id: result_type_id,
|
||||||
|
block_id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
handle
|
||||||
|
};
|
||||||
|
emitter.start(ctx.expressions);
|
||||||
|
|
||||||
|
// Create a statement for the op itself
|
||||||
|
let stmt = crate::Statement::Atomic {
|
||||||
|
pointer: p_lexp_handle,
|
||||||
|
fun: atomic_function,
|
||||||
|
value: v_lexp_handle,
|
||||||
|
result: Some(r_lexp_handle),
|
||||||
|
};
|
||||||
|
block.push(stmt, span);
|
||||||
|
|
||||||
|
// Store any associated global variables so we can upgrade their types later
|
||||||
|
self.upgrade_atomics
|
||||||
|
.insert(ctx.get_contained_global_variable(p_lexp_handle)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Add the next SPIR-V block's contents to `block_ctx`.
|
/// Add the next SPIR-V block's contents to `block_ctx`.
|
||||||
///
|
///
|
||||||
/// Except for the function's entry block, `block_id` should be the label of
|
/// Except for the function's entry block, `block_id` should be the label of
|
||||||
@ -3985,35 +4094,91 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||||||
);
|
);
|
||||||
emitter.start(ctx.expressions);
|
emitter.start(ctx.expressions);
|
||||||
}
|
}
|
||||||
Op::AtomicIIncrement => {
|
Op::AtomicLoad => {
|
||||||
inst.expect(6)?;
|
inst.expect(6)?;
|
||||||
let start = self.data_offset;
|
let start = self.data_offset;
|
||||||
let span = self.span_from_with_op(start);
|
|
||||||
let result_type_id = self.next()?;
|
let result_type_id = self.next()?;
|
||||||
let result_id = self.next()?;
|
let result_id = self.next()?;
|
||||||
let pointer_id = self.next()?;
|
let pointer_id = self.next()?;
|
||||||
let _scope_id = self.next()?;
|
let _scope_id = self.next()?;
|
||||||
let _memory_semantics_id = self.next()?;
|
let _memory_semantics_id = self.next()?;
|
||||||
|
let span = self.span_from_with_op(start);
|
||||||
|
|
||||||
log::trace!("\t\t\tlooking up expr {:?}", pointer_id);
|
log::trace!("\t\t\tlooking up expr {:?}", pointer_id);
|
||||||
let (p_lexp_handle, p_lexp_ty_id) = {
|
let p_lexp_handle =
|
||||||
let lexp = self.lookup_expression.lookup(pointer_id)?;
|
get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
|
||||||
let handle = get_expr_handle!(pointer_id, &lexp);
|
|
||||||
(handle, lexp.type_id)
|
// Create an expression for our result
|
||||||
|
let expr = crate::Expression::Load {
|
||||||
|
pointer: p_lexp_handle,
|
||||||
};
|
};
|
||||||
|
let handle = ctx.expressions.append(expr, span);
|
||||||
|
self.lookup_expression.insert(
|
||||||
|
result_id,
|
||||||
|
LookupExpression {
|
||||||
|
handle,
|
||||||
|
type_id: result_type_id,
|
||||||
|
block_id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
log::trace!("\t\t\tlooking up type {pointer_id:?}");
|
// Store any associated global variables so we can upgrade their types later
|
||||||
let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
|
self.upgrade_atomics
|
||||||
let p_ty_base_id =
|
.insert(ctx.get_contained_global_variable(p_lexp_handle)?);
|
||||||
p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
|
}
|
||||||
|
Op::AtomicStore => {
|
||||||
|
inst.expect(5)?;
|
||||||
|
let start = self.data_offset;
|
||||||
|
let pointer_id = self.next()?;
|
||||||
|
let _scope_id = self.next()?;
|
||||||
|
let _memory_semantics_id = self.next()?;
|
||||||
|
let value_id = self.next()?;
|
||||||
|
let span = self.span_from_with_op(start);
|
||||||
|
|
||||||
log::trace!("\t\t\tlooking up base type {p_ty_base_id:?} of {p_ty:?}");
|
log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id);
|
||||||
let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
|
let p_lexp_handle =
|
||||||
|
get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
|
||||||
|
|
||||||
|
log::trace!("\t\t\tlooking up value expr {:?}", pointer_id);
|
||||||
|
let v_lexp_handle =
|
||||||
|
get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
|
||||||
|
|
||||||
|
block.extend(emitter.finish(ctx.expressions));
|
||||||
|
// Create a statement for the op itself
|
||||||
|
let stmt = crate::Statement::Store {
|
||||||
|
pointer: p_lexp_handle,
|
||||||
|
value: v_lexp_handle,
|
||||||
|
};
|
||||||
|
block.push(stmt, span);
|
||||||
|
emitter.start(ctx.expressions);
|
||||||
|
|
||||||
|
// Store any associated global variables so we can upgrade their types later
|
||||||
|
self.upgrade_atomics
|
||||||
|
.insert(ctx.get_contained_global_variable(p_lexp_handle)?);
|
||||||
|
}
|
||||||
|
Op::AtomicIIncrement | Op::AtomicIDecrement => {
|
||||||
|
inst.expect(6)?;
|
||||||
|
let start = self.data_offset;
|
||||||
|
let result_type_id = self.next()?;
|
||||||
|
let result_id = self.next()?;
|
||||||
|
let pointer_id = self.next()?;
|
||||||
|
let _scope_id = self.next()?;
|
||||||
|
let _memory_semantics_id = self.next()?;
|
||||||
|
let span = self.span_from_with_op(start);
|
||||||
|
|
||||||
|
let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
|
||||||
|
pointer_id,
|
||||||
|
ctx,
|
||||||
|
&mut emitter,
|
||||||
|
&mut block,
|
||||||
|
body_idx,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
block.extend(emitter.finish(ctx.expressions));
|
||||||
// Create an expression for our result
|
// Create an expression for our result
|
||||||
let r_lexp_handle = {
|
let r_lexp_handle = {
|
||||||
let expr = crate::Expression::AtomicResult {
|
let expr = crate::Expression::AtomicResult {
|
||||||
ty: p_base_ty.handle,
|
ty: p_base_ty_h,
|
||||||
comparison: false,
|
comparison: false,
|
||||||
};
|
};
|
||||||
let handle = ctx.expressions.append(expr, span);
|
let handle = ctx.expressions.append(expr, span);
|
||||||
@ -4027,22 +4192,26 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||||||
);
|
);
|
||||||
handle
|
handle
|
||||||
};
|
};
|
||||||
|
emitter.start(ctx.expressions);
|
||||||
|
|
||||||
// Create a literal "1" since WGSL lacks an increment operation
|
// Create a literal "1" to use as our value
|
||||||
let one_lexp_handle = make_index_literal(
|
let one_lexp_handle = make_index_literal(
|
||||||
ctx,
|
ctx,
|
||||||
1,
|
1,
|
||||||
&mut block,
|
&mut block,
|
||||||
&mut emitter,
|
&mut emitter,
|
||||||
p_base_ty.handle,
|
p_base_ty_h,
|
||||||
p_lexp_ty_id,
|
result_type_id,
|
||||||
span,
|
span,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Create a statement for the op itself
|
// Create a statement for the op itself
|
||||||
let stmt = crate::Statement::Atomic {
|
let stmt = crate::Statement::Atomic {
|
||||||
pointer: p_lexp_handle,
|
pointer: p_exp_h,
|
||||||
fun: crate::AtomicFunction::Add,
|
fun: match inst.op {
|
||||||
|
Op::AtomicIIncrement => crate::AtomicFunction::Add,
|
||||||
|
_ => crate::AtomicFunction::Subtract,
|
||||||
|
},
|
||||||
value: one_lexp_handle,
|
value: one_lexp_handle,
|
||||||
result: Some(r_lexp_handle),
|
result: Some(r_lexp_handle),
|
||||||
};
|
};
|
||||||
@ -4050,8 +4219,38 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||||||
|
|
||||||
// Store any associated global variables so we can upgrade their types later
|
// Store any associated global variables so we can upgrade their types later
|
||||||
self.upgrade_atomics
|
self.upgrade_atomics
|
||||||
.extend(ctx.get_contained_global_variable(p_lexp_handle));
|
.insert(ctx.get_contained_global_variable(p_exp_h)?);
|
||||||
}
|
}
|
||||||
|
Op::AtomicExchange
|
||||||
|
| Op::AtomicIAdd
|
||||||
|
| Op::AtomicISub
|
||||||
|
| Op::AtomicSMin
|
||||||
|
| Op::AtomicUMin
|
||||||
|
| Op::AtomicSMax
|
||||||
|
| Op::AtomicUMax
|
||||||
|
| Op::AtomicAnd
|
||||||
|
| Op::AtomicOr
|
||||||
|
| Op::AtomicXor => self.parse_atomic_expr_with_value(
|
||||||
|
inst,
|
||||||
|
&mut emitter,
|
||||||
|
ctx,
|
||||||
|
&mut block,
|
||||||
|
block_id,
|
||||||
|
body_idx,
|
||||||
|
match inst.op {
|
||||||
|
Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None },
|
||||||
|
Op::AtomicIAdd => crate::AtomicFunction::Add,
|
||||||
|
Op::AtomicISub => crate::AtomicFunction::Subtract,
|
||||||
|
Op::AtomicSMin => crate::AtomicFunction::Min,
|
||||||
|
Op::AtomicUMin => crate::AtomicFunction::Min,
|
||||||
|
Op::AtomicSMax => crate::AtomicFunction::Max,
|
||||||
|
Op::AtomicUMax => crate::AtomicFunction::Max,
|
||||||
|
Op::AtomicAnd => crate::AtomicFunction::And,
|
||||||
|
Op::AtomicOr => crate::AtomicFunction::InclusiveOr,
|
||||||
|
_ => crate::AtomicFunction::ExclusiveOr,
|
||||||
|
},
|
||||||
|
)?,
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnsupportedInstruction(self.state, inst.op));
|
return Err(Error::UnsupportedInstruction(self.state, inst.op));
|
||||||
}
|
}
|
||||||
@ -5709,33 +5908,48 @@ mod test {
|
|||||||
];
|
];
|
||||||
let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
|
let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "wgsl-in", wgsl_out))]
|
#[cfg(all(test, feature = "wgsl-in", wgsl_out))]
|
||||||
#[test]
|
mod test_atomic {
|
||||||
fn atomic_i_inc() {
|
fn atomic_test(bytes: &[u8]) {
|
||||||
let _ = env_logger::builder().is_test(true).try_init();
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
let bytes = include_bytes!("../../../tests/in/spv/atomic_i_increment.spv");
|
let m = crate::front::spv::parse_u8_slice(bytes, &Default::default()).unwrap();
|
||||||
let m = super::parse_u8_slice(bytes, &Default::default()).unwrap();
|
|
||||||
let mut validator = crate::valid::Validator::new(
|
let mut wgsl = String::new();
|
||||||
|
let mut should_panic = false;
|
||||||
|
|
||||||
|
for vflags in [
|
||||||
|
crate::valid::ValidationFlags::all(),
|
||||||
crate::valid::ValidationFlags::empty(),
|
crate::valid::ValidationFlags::empty(),
|
||||||
Default::default(),
|
] {
|
||||||
);
|
let mut validator = crate::valid::Validator::new(vflags, Default::default());
|
||||||
let info = match validator.validate(&m) {
|
match validator.validate(&m) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("{}", e.emit_to_string(""));
|
log::error!("SPIR-V validation {}", e.emit_to_string(""));
|
||||||
return;
|
should_panic = true;
|
||||||
}
|
}
|
||||||
Ok(i) => i,
|
Ok(i) => {
|
||||||
};
|
wgsl = crate::back::wgsl::write_string(
|
||||||
let wgsl =
|
&m,
|
||||||
crate::back::wgsl::write_string(&m, &info, crate::back::wgsl::WriterFlags::empty())
|
&i,
|
||||||
.unwrap();
|
crate::back::wgsl::WriterFlags::empty(),
|
||||||
log::info!("atomic_i_increment:\n{wgsl}");
|
)
|
||||||
|
.unwrap();
|
||||||
|
log::info!("wgsl-out:\n{wgsl}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_panic {
|
||||||
|
panic!("validation error");
|
||||||
|
}
|
||||||
|
|
||||||
let m = match crate::front::wgsl::parse_str(&wgsl) {
|
let m = match crate::front::wgsl::parse_str(&wgsl) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("{}", e.emit_to_string(&wgsl));
|
log::error!("round trip WGSL validation {}", e.emit_to_string(&wgsl));
|
||||||
panic!("invalid module");
|
panic!("invalid module");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -5746,4 +5960,35 @@ mod test {
|
|||||||
panic!("invalid generated wgsl");
|
panic!("invalid generated wgsl");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atomic_i_inc() {
|
||||||
|
atomic_test(include_bytes!(
|
||||||
|
"../../../tests/in/spv/atomic_i_increment.spv"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atomic_load_and_store() {
|
||||||
|
atomic_test(include_bytes!(
|
||||||
|
"../../../tests/in/spv/atomic_load_and_store.spv"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atomic_exchange() {
|
||||||
|
atomic_test(include_bytes!("../../../tests/in/spv/atomic_exchange.spv"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atomic_i_decrement() {
|
||||||
|
atomic_test(include_bytes!(
|
||||||
|
"../../../tests/in/spv/atomic_i_decrement.spv"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atomic_i_add_and_sub() {
|
||||||
|
atomic_test(include_bytes!("../../../tests/in/spv/atomic_i_add_sub.spv"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
naga/tests/in/spv/atomic_exchange.spv
Normal file
BIN
naga/tests/in/spv/atomic_exchange.spv
Normal file
Binary file not shown.
88
naga/tests/in/spv/atomic_exchange.spvasm
Normal file
88
naga/tests/in/spv/atomic_exchange.spvasm
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
; SPIR-V
|
||||||
|
; Version: 1.5
|
||||||
|
; Generator: Google rspirv; 0
|
||||||
|
; Bound: 63
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpCapability VulkanMemoryModel
|
||||||
|
OpMemoryModel Logical Vulkan
|
||||||
|
OpEntryPoint GLCompute %1 "stage::test_atomic_exchange" %2 %3
|
||||||
|
OpExecutionMode %1 LocalSize 32 1 1
|
||||||
|
OpMemberDecorate %_struct_11 0 Offset 0
|
||||||
|
OpMemberDecorate %_struct_11 1 Offset 4
|
||||||
|
OpDecorate %_struct_12 Block
|
||||||
|
OpMemberDecorate %_struct_12 0 Offset 0
|
||||||
|
OpDecorate %2 Binding 0
|
||||||
|
OpDecorate %2 DescriptorSet 0
|
||||||
|
OpDecorate %3 NonWritable
|
||||||
|
OpDecorate %3 Binding 1
|
||||||
|
OpDecorate %3 DescriptorSet 0
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%15 = OpTypeFunction %void
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%uint_2 = OpConstant %uint 2
|
||||||
|
%false = OpConstantFalse %bool
|
||||||
|
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||||
|
%uint_1 = OpConstant %uint 1
|
||||||
|
%_struct_11 = OpTypeStruct %uint %uint
|
||||||
|
%22 = OpUndef %_struct_11
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%true = OpConstantTrue %bool
|
||||||
|
%_struct_12 = OpTypeStruct %uint
|
||||||
|
%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12
|
||||||
|
%2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer
|
||||||
|
%3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer
|
||||||
|
%26 = OpUndef %uint
|
||||||
|
%1 = OpFunction %void None %15
|
||||||
|
%27 = OpLabel
|
||||||
|
%28 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0
|
||||||
|
%29 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0
|
||||||
|
%30 = OpLoad %uint %29
|
||||||
|
%31 = OpCompositeConstruct %_struct_11 %uint_0 %30
|
||||||
|
OpBranch %32
|
||||||
|
%32 = OpLabel
|
||||||
|
%33 = OpPhi %_struct_11 %31 %27 %34 %35
|
||||||
|
%36 = OpPhi %uint %uint_0 %27 %37 %35
|
||||||
|
OpLoopMerge %38 %35 None
|
||||||
|
OpBranch %39
|
||||||
|
%39 = OpLabel
|
||||||
|
%40 = OpCompositeExtract %uint %33 0
|
||||||
|
%41 = OpCompositeExtract %uint %33 1
|
||||||
|
%42 = OpULessThan %bool %40 %41
|
||||||
|
OpSelectionMerge %43 None
|
||||||
|
OpBranchConditional %42 %44 %45
|
||||||
|
%44 = OpLabel
|
||||||
|
%47 = OpIAdd %uint %40 %uint_1
|
||||||
|
%49 = OpCompositeInsert %_struct_11 %47 %33 0
|
||||||
|
%50 = OpCompositeConstruct %_struct_11 %uint_1 %40
|
||||||
|
OpBranch %43
|
||||||
|
%45 = OpLabel
|
||||||
|
%51 = OpCompositeInsert %_struct_11 %uint_0 %22 0
|
||||||
|
OpBranch %43
|
||||||
|
%43 = OpLabel
|
||||||
|
%52 = OpPhi %_struct_11 %49 %44 %33 %45
|
||||||
|
%53 = OpPhi %_struct_11 %50 %44 %51 %45
|
||||||
|
%54 = OpCompositeExtract %uint %53 0
|
||||||
|
%55 = OpBitcast %int %54
|
||||||
|
OpSelectionMerge %56 None
|
||||||
|
OpSwitch %55 %57 0 %58 1 %59
|
||||||
|
%57 = OpLabel
|
||||||
|
OpBranch %56
|
||||||
|
%58 = OpLabel
|
||||||
|
OpBranch %56
|
||||||
|
%59 = OpLabel
|
||||||
|
%60 = OpAtomicExchange %uint %28 %uint_2 %uint_0 %36
|
||||||
|
%61 = OpIAdd %uint %36 %60
|
||||||
|
OpBranch %56
|
||||||
|
%56 = OpLabel
|
||||||
|
%62 = OpPhi %bool %false %57 %false %58 %true %59
|
||||||
|
%34 = OpPhi %_struct_11 %22 %57 %22 %58 %52 %59
|
||||||
|
%37 = OpPhi %uint %26 %57 %26 %58 %61 %59
|
||||||
|
OpBranch %35
|
||||||
|
%35 = OpLabel
|
||||||
|
OpBranchConditional %62 %32 %38
|
||||||
|
%38 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
BIN
naga/tests/in/spv/atomic_i_add_sub.spv
Normal file
BIN
naga/tests/in/spv/atomic_i_add_sub.spv
Normal file
Binary file not shown.
51
naga/tests/in/spv/atomic_i_add_sub.spvasm
Normal file
51
naga/tests/in/spv/atomic_i_add_sub.spvasm
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
; SPIR-V
|
||||||
|
; Version: 1.5
|
||||||
|
; Generator: Google rspirv; 0
|
||||||
|
; Bound: 30
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpCapability VulkanMemoryModel
|
||||||
|
OpMemoryModel Logical Vulkan
|
||||||
|
OpEntryPoint GLCompute %1 "stage::test_atomic_i_add_sub" %2 %3
|
||||||
|
OpExecutionMode %1 LocalSize 32 1 1
|
||||||
|
OpDecorate %_runtimearr_uint ArrayStride 4
|
||||||
|
OpDecorate %_struct_7 Block
|
||||||
|
OpMemberDecorate %_struct_7 0 Offset 0
|
||||||
|
OpDecorate %_struct_8 Block
|
||||||
|
OpMemberDecorate %_struct_8 0 Offset 0
|
||||||
|
OpDecorate %2 Binding 0
|
||||||
|
OpDecorate %2 DescriptorSet 0
|
||||||
|
OpDecorate %3 Binding 1
|
||||||
|
OpDecorate %3 DescriptorSet 0
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%11 = OpTypeFunction %void
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%_runtimearr_uint = OpTypeRuntimeArray %uint
|
||||||
|
%_struct_7 = OpTypeStruct %_runtimearr_uint
|
||||||
|
%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%uint_2 = OpConstant %uint 2
|
||||||
|
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||||
|
%_struct_8 = OpTypeStruct %uint
|
||||||
|
%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8
|
||||||
|
%2 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer
|
||||||
|
%3 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer
|
||||||
|
%1 = OpFunction %void None %11
|
||||||
|
%19 = OpLabel
|
||||||
|
%20 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0
|
||||||
|
%22 = OpArrayLength %uint %3 0
|
||||||
|
%23 = OpAtomicIAdd %uint %20 %uint_2 %uint_0 %uint_2
|
||||||
|
%24 = OpAtomicISub %uint %20 %uint_2 %uint_0 %23
|
||||||
|
%25 = OpULessThan %bool %23 %22
|
||||||
|
OpSelectionMerge %26 None
|
||||||
|
OpBranchConditional %25 %27 %28
|
||||||
|
%27 = OpLabel
|
||||||
|
%29 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 %23
|
||||||
|
OpStore %29 %24
|
||||||
|
OpBranch %26
|
||||||
|
%28 = OpLabel
|
||||||
|
OpBranch %26
|
||||||
|
%26 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
BIN
naga/tests/in/spv/atomic_i_decrement.spv
Normal file
BIN
naga/tests/in/spv/atomic_i_decrement.spv
Normal file
Binary file not shown.
64
naga/tests/in/spv/atomic_i_decrement.spvasm
Normal file
64
naga/tests/in/spv/atomic_i_decrement.spvasm
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
; SPIR-V
|
||||||
|
; Version: 1.5
|
||||||
|
; Generator: Google rspirv; 0
|
||||||
|
; Bound: 42
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpCapability VulkanMemoryModel
|
||||||
|
OpMemoryModel Logical Vulkan
|
||||||
|
OpEntryPoint GLCompute %1 "stage::test_atomic_i_decrement" %2 %3
|
||||||
|
OpExecutionMode %1 LocalSize 32 1 1
|
||||||
|
OpDecorate %_runtimearr_uint ArrayStride 4
|
||||||
|
OpDecorate %_struct_7 Block
|
||||||
|
OpMemberDecorate %_struct_7 0 Offset 0
|
||||||
|
OpDecorate %_struct_8 Block
|
||||||
|
OpMemberDecorate %_struct_8 0 Offset 0
|
||||||
|
OpDecorate %2 Binding 0
|
||||||
|
OpDecorate %2 DescriptorSet 0
|
||||||
|
OpDecorate %3 Binding 1
|
||||||
|
OpDecorate %3 DescriptorSet 0
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%11 = OpTypeFunction %void
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%_runtimearr_uint = OpTypeRuntimeArray %uint
|
||||||
|
%_struct_7 = OpTypeStruct %_runtimearr_uint
|
||||||
|
%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%uint_2 = OpConstant %uint 2
|
||||||
|
%false = OpConstantFalse %bool
|
||||||
|
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||||
|
%true = OpConstantTrue %bool
|
||||||
|
%_struct_8 = OpTypeStruct %uint
|
||||||
|
%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8
|
||||||
|
%2 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer
|
||||||
|
%3 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer
|
||||||
|
%1 = OpFunction %void None %11
|
||||||
|
%21 = OpLabel
|
||||||
|
%22 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0
|
||||||
|
%24 = OpArrayLength %uint %3 0
|
||||||
|
OpBranch %25
|
||||||
|
%25 = OpLabel
|
||||||
|
OpLoopMerge %26 %27 None
|
||||||
|
OpBranch %28
|
||||||
|
%28 = OpLabel
|
||||||
|
%29 = OpAtomicIDecrement %uint %22 %uint_2 %uint_0
|
||||||
|
%30 = OpULessThan %bool %29 %24
|
||||||
|
OpSelectionMerge %31 None
|
||||||
|
OpBranchConditional %30 %32 %33
|
||||||
|
%32 = OpLabel
|
||||||
|
%34 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 %29
|
||||||
|
OpStore %34 %29
|
||||||
|
%35 = OpIEqual %bool %29 %uint_0
|
||||||
|
%41 = OpSelect %bool %35 %false %true
|
||||||
|
OpBranch %31
|
||||||
|
%33 = OpLabel
|
||||||
|
OpBranch %31
|
||||||
|
%31 = OpLabel
|
||||||
|
%40 = OpPhi %bool %41 %32 %false %33
|
||||||
|
OpBranch %27
|
||||||
|
%27 = OpLabel
|
||||||
|
OpBranchConditional %40 %25 %26
|
||||||
|
%26 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
@ -59,4 +59,3 @@
|
|||||||
%26 = OpLabel
|
%26 = OpLabel
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
||||||
|
BIN
naga/tests/in/spv/atomic_load_and_store.spv
Normal file
BIN
naga/tests/in/spv/atomic_load_and_store.spv
Normal file
Binary file not shown.
86
naga/tests/in/spv/atomic_load_and_store.spvasm
Normal file
86
naga/tests/in/spv/atomic_load_and_store.spvasm
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
; SPIR-V
|
||||||
|
; Version: 1.5
|
||||||
|
; Generator: Google rspirv; 0
|
||||||
|
; Bound: 60
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpCapability VulkanMemoryModel
|
||||||
|
OpMemoryModel Logical Vulkan
|
||||||
|
OpEntryPoint GLCompute %1 "stage::test_atomic_load_and_store" %2 %3
|
||||||
|
OpExecutionMode %1 LocalSize 32 1 1
|
||||||
|
OpMemberDecorate %_struct_11 0 Offset 0
|
||||||
|
OpMemberDecorate %_struct_11 1 Offset 4
|
||||||
|
OpDecorate %_struct_12 Block
|
||||||
|
OpMemberDecorate %_struct_12 0 Offset 0
|
||||||
|
OpDecorate %2 Binding 0
|
||||||
|
OpDecorate %2 DescriptorSet 0
|
||||||
|
OpDecorate %3 NonWritable
|
||||||
|
OpDecorate %3 Binding 1
|
||||||
|
OpDecorate %3 DescriptorSet 0
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%15 = OpTypeFunction %void
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%uint_2 = OpConstant %uint 2
|
||||||
|
%false = OpConstantFalse %bool
|
||||||
|
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||||
|
%uint_1 = OpConstant %uint 1
|
||||||
|
%_struct_11 = OpTypeStruct %uint %uint
|
||||||
|
%22 = OpUndef %_struct_11
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%true = OpConstantTrue %bool
|
||||||
|
%_struct_12 = OpTypeStruct %uint
|
||||||
|
%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12
|
||||||
|
%2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer
|
||||||
|
%3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer
|
||||||
|
%1 = OpFunction %void None %15
|
||||||
|
%26 = OpLabel
|
||||||
|
%27 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0
|
||||||
|
%28 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0
|
||||||
|
%29 = OpLoad %uint %28
|
||||||
|
%30 = OpCompositeConstruct %_struct_11 %uint_0 %29
|
||||||
|
OpBranch %31
|
||||||
|
%31 = OpLabel
|
||||||
|
%32 = OpPhi %_struct_11 %30 %26 %33 %34
|
||||||
|
OpLoopMerge %35 %34 None
|
||||||
|
OpBranch %36
|
||||||
|
%36 = OpLabel
|
||||||
|
%37 = OpCompositeExtract %uint %32 0
|
||||||
|
%38 = OpCompositeExtract %uint %32 1
|
||||||
|
%39 = OpULessThan %bool %37 %38
|
||||||
|
OpSelectionMerge %40 None
|
||||||
|
OpBranchConditional %39 %41 %42
|
||||||
|
%41 = OpLabel
|
||||||
|
%44 = OpIAdd %uint %37 %uint_1
|
||||||
|
%46 = OpCompositeInsert %_struct_11 %44 %32 0
|
||||||
|
%47 = OpCompositeConstruct %_struct_11 %uint_1 %37
|
||||||
|
OpBranch %40
|
||||||
|
%42 = OpLabel
|
||||||
|
%48 = OpCompositeInsert %_struct_11 %uint_0 %22 0
|
||||||
|
OpBranch %40
|
||||||
|
%40 = OpLabel
|
||||||
|
%49 = OpPhi %_struct_11 %46 %41 %32 %42
|
||||||
|
%50 = OpPhi %_struct_11 %47 %41 %48 %42
|
||||||
|
%51 = OpCompositeExtract %uint %50 0
|
||||||
|
%52 = OpBitcast %int %51
|
||||||
|
OpSelectionMerge %53 None
|
||||||
|
OpSwitch %52 %54 0 %55 1 %56
|
||||||
|
%54 = OpLabel
|
||||||
|
OpBranch %53
|
||||||
|
%55 = OpLabel
|
||||||
|
OpBranch %53
|
||||||
|
%56 = OpLabel
|
||||||
|
%57 = OpAtomicLoad %uint %27 %uint_2 %uint_0
|
||||||
|
%58 = OpIAdd %uint %57 %uint_2
|
||||||
|
OpAtomicStore %27 %uint_2 %uint_0 %58
|
||||||
|
OpBranch %53
|
||||||
|
%53 = OpLabel
|
||||||
|
%59 = OpPhi %bool %false %54 %false %55 %true %56
|
||||||
|
%33 = OpPhi %_struct_11 %22 %54 %22 %55 %49 %56
|
||||||
|
OpBranch %34
|
||||||
|
%34 = OpLabel
|
||||||
|
OpBranchConditional %59 %31 %35
|
||||||
|
%35 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
@ -216,10 +216,6 @@
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
reject: [
|
reject: [
|
||||||
Emit((
|
|
||||||
start: 13,
|
|
||||||
end: 14,
|
|
||||||
)),
|
|
||||||
Atomic(
|
Atomic(
|
||||||
pointer: 7,
|
pointer: 7,
|
||||||
fun: Add,
|
fun: Add,
|
||||||
|
@ -241,10 +241,6 @@
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
reject: [
|
reject: [
|
||||||
Emit((
|
|
||||||
start: 14,
|
|
||||||
end: 15,
|
|
||||||
)),
|
|
||||||
Atomic(
|
Atomic(
|
||||||
pointer: 8,
|
pointer: 8,
|
||||||
fun: Add,
|
fun: Add,
|
||||||
|
Loading…
Reference in New Issue
Block a user