Skip to content

Commit a834739

Browse files
committed
use threadgroup pointers instead of references
1 parent 56e4a38 commit a834739

16 files changed

Lines changed: 326 additions & 203 deletions

naga/src/back/msl/mesh_shader.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ impl<W: core::fmt::Write> super::Writer<W> {
217217
writeln!(self.out, ") {{")?;
218218

219219
// Function body
220-
if ep.stage == crate::ShaderStage::Mesh {
220+
if ep.stage == crate::ShaderStage::Mesh || ep.stage == crate::ShaderStage::Task {
221221
for (handle, var) in module.global_variables.iter() {
222222
if var.space != crate::AddressSpace::WorkGroup || fun_info[handle].is_empty() {
223223
continue;
@@ -252,7 +252,7 @@ impl<W: core::fmt::Write> super::Writer<W> {
252252
is_first = false;
253253
write!(self.out, "{}", arg.name)?;
254254
}
255-
if ep.stage == crate::ShaderStage::Mesh {
255+
if ep.stage == crate::ShaderStage::Mesh || ep.stage == crate::ShaderStage::Task {
256256
for (handle, var) in module.global_variables.iter() {
257257
if var.space != crate::AddressSpace::WorkGroup || fun_info[handle].is_empty() {
258258
continue;
@@ -261,7 +261,7 @@ impl<W: core::fmt::Write> super::Writer<W> {
261261
write!(self.out, ", ")?;
262262
}
263263
let name = &self.names[&NameKey::GlobalVariable(handle)];
264-
write!(self.out, "{name}")?;
264+
write!(self.out, "&{name}")?;
265265
}
266266
}
267267
}

naga/src/back/msl/writer.rs

Lines changed: 153 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ impl TypedGlobalVariable<'_> {
442442
};
443443
let (coherent, space, access, reference) = match (var.space.to_msl_name(), var.space) {
444444
(Some(space), crate::AddressSpace::WorkGroup) => {
445-
("", space, access, if self.reference { "&" } else { "" })
445+
("", space, access, if self.reference { "*" } else { "" })
446446
}
447447
(Some(space), _) if self.reference => {
448448
let coherent = if var
@@ -3143,6 +3143,31 @@ impl<W: Write> Writer<W> {
31433143
Ok(check_written)
31443144
}
31453145

3146+
fn is_root_workgroup_pointer(
3147+
&self,
3148+
chain: Handle<crate::Expression>,
3149+
context: &ExpressionContext,
3150+
) -> bool {
3151+
match context.function.expressions[chain] {
3152+
crate::Expression::GlobalVariable(handle) => {
3153+
let var = &context.module.global_variables[handle];
3154+
var.space == crate::AddressSpace::WorkGroup
3155+
}
3156+
crate::Expression::FunctionArgument(index) => {
3157+
let arg = &context.function.arguments[index as usize];
3158+
let type_inner = &context.module.types[arg.ty].inner;
3159+
matches!(
3160+
type_inner,
3161+
crate::TypeInner::Pointer {
3162+
space: crate::AddressSpace::WorkGroup,
3163+
..
3164+
}
3165+
)
3166+
}
3167+
_ => false,
3168+
}
3169+
}
3170+
31463171
/// Write the access chain `chain`.
31473172
///
31483173
/// `chain` is a subtree of [`Access`] and [`AccessIndex`] expressions,
@@ -3201,13 +3226,22 @@ impl<W: Write> Writer<W> {
32013226
// indexing a struct with an expression.
32023227
match *base_ty {
32033228
crate::TypeInner::Struct { .. } => {
3229+
let is_workgroup = self.is_root_workgroup_pointer(base, context);
3230+
let op = if is_workgroup { "->" } else { "." };
32043231
let base_ty = base_ty_handle.unwrap();
32053232
self.put_access_chain(base, policy, context)?;
32063233
let name = &self.names[&NameKey::StructMember(base_ty, index)];
3207-
write!(self.out, ".{name}")?;
3234+
write!(self.out, "{op}{name}")?;
32083235
}
32093236
crate::TypeInner::ValuePointer { .. } | crate::TypeInner::Vector { .. } => {
3237+
let is_workgroup_ptr = self.is_root_workgroup_pointer(base, context);
3238+
if is_workgroup_ptr {
3239+
write!(self.out, "(*")?;
3240+
}
32103241
self.put_access_chain(base, policy, context)?;
3242+
if is_workgroup_ptr {
3243+
write!(self.out, ")")?;
3244+
}
32113245
// Prior to Metal v2.1 component access for packed vectors wasn't available
32123246
// however array indexing is
32133247
if context.get_packed_vec_kind(base).is_some() {
@@ -3267,9 +3301,18 @@ impl<W: Write> Writer<W> {
32673301
let accessing_wrapped_binding_array =
32683302
matches!(*base_ty, crate::TypeInner::BindingArray { .. });
32693303

3304+
let is_workgroup = self.is_root_workgroup_pointer(base, context);
3305+
3306+
if is_workgroup && !accessing_wrapped_array {
3307+
write!(self.out, "(*")?;
3308+
}
32703309
self.put_access_chain(base, policy, context)?;
3310+
if is_workgroup && !accessing_wrapped_array {
3311+
write!(self.out, ")")?;
3312+
}
32713313
if accessing_wrapped_array {
3272-
write!(self.out, ".{WRAPPED_ARRAY_FIELD}")?;
3314+
let op = if is_workgroup { "->" } else { "." };
3315+
write!(self.out, "{op}{WRAPPED_ARRAY_FIELD}")?;
32733316
}
32743317
write!(self.out, "[")?;
32753318

@@ -3350,16 +3393,21 @@ impl<W: Write> Writer<W> {
33503393
.is_atomic_pointer(&context.module.types);
33513394

33523395
if is_atomic_pointer {
3353-
write!(
3354-
self.out,
3355-
"{NAMESPACE}::atomic_load_explicit({ATOMIC_REFERENCE}"
3356-
)?;
3396+
write!(self.out, "{NAMESPACE}::atomic_load_explicit(")?;
3397+
let is_workgroup_ptr = self.is_root_workgroup_pointer(pointer, context);
3398+
if !is_workgroup_ptr {
3399+
write!(self.out, "{ATOMIC_REFERENCE}")?;
3400+
}
33573401
self.put_access_chain(pointer, policy, context)?;
33583402
write!(self.out, ", {NAMESPACE}::memory_order_relaxed)")?;
33593403
} else {
33603404
// We don't do any dereferencing with `*` here as pointer arguments to functions
33613405
// are done by `&` references and not `*` pointers. These do not need to be
3362-
// dereferenced.
3406+
// dereferenced, except for workgroups pointers.
3407+
let is_workgroup_ptr = self.is_root_workgroup_pointer(pointer, context);
3408+
if is_workgroup_ptr {
3409+
write!(self.out, "*")?;
3410+
}
33633411
self.put_access_chain(pointer, policy, context)?;
33643412
}
33653413

@@ -4006,9 +4054,13 @@ impl<W: Write> Writer<W> {
40064054
}
40074055

40084056
// Put the atomic function invocation.
4057+
let is_workgroup_atomic = self.is_root_workgroup_pointer(pointer, context);
40094058
match *fun {
40104059
crate::AtomicFunction::Exchange { compare: Some(cmp) } => {
4011-
write!(self.out, "{ATOMIC_COMP_EXCH_FUNCTION}({ATOMIC_REFERENCE}")?;
4060+
write!(self.out, "{ATOMIC_COMP_EXCH_FUNCTION}(")?;
4061+
if !is_workgroup_atomic {
4062+
write!(self.out, "{ATOMIC_REFERENCE}")?;
4063+
}
40124064
self.put_access_chain(pointer, policy, context)?;
40134065
write!(self.out, ", ")?;
40144066
self.put_expression(cmp, context, true)?;
@@ -4017,10 +4069,10 @@ impl<W: Write> Writer<W> {
40174069
write!(self.out, ")")?;
40184070
}
40194071
_ => {
4020-
write!(
4021-
self.out,
4022-
"{NAMESPACE}::atomic_{fun_key}_explicit({ATOMIC_REFERENCE}"
4023-
)?;
4072+
write!(self.out, "{NAMESPACE}::atomic_{fun_key}_explicit(")?;
4073+
if !is_workgroup_atomic {
4074+
write!(self.out, "{ATOMIC_REFERENCE}")?;
4075+
}
40244076
self.put_access_chain(pointer, policy, context)?;
40254077
write!(self.out, ", ")?;
40264078
self.put_expression(value, context, true)?;
@@ -4274,16 +4326,21 @@ impl<W: Write> Writer<W> {
42744326
.is_atomic_pointer(&context.expression.module.types);
42754327

42764328
if is_atomic_pointer {
4277-
write!(
4278-
self.out,
4279-
"{level}{NAMESPACE}::atomic_store_explicit({ATOMIC_REFERENCE}"
4280-
)?;
4329+
write!(self.out, "{level}{NAMESPACE}::atomic_store_explicit(")?;
4330+
let is_workgroup_atomic = self.is_root_workgroup_pointer(pointer, &context.expression);
4331+
if !is_workgroup_atomic {
4332+
write!(self.out, "{ATOMIC_REFERENCE}")?;
4333+
}
42814334
self.put_access_chain(pointer, policy, &context.expression)?;
42824335
write!(self.out, ", ")?;
42834336
self.put_expression(value, &context.expression, true)?;
42844337
writeln!(self.out, ", {NAMESPACE}::memory_order_relaxed);")?;
42854338
} else {
42864339
write!(self.out, "{level}")?;
4340+
let is_workgroup_ptr = self.is_root_workgroup_pointer(pointer, &context.expression);
4341+
if is_workgroup_ptr {
4342+
write!(self.out, "*")?;
4343+
}
42874344
self.put_access_chain(pointer, policy, &context.expression)?;
42884345
write!(self.out, " = ")?;
42894346
self.put_expression(value, &context.expression, true)?;
@@ -7449,7 +7506,8 @@ template <typename A>
74497506
}
74507507
_ => {
74517508
if var.space == crate::AddressSpace::WorkGroup
7452-
&& ep.stage == crate::ShaderStage::Mesh
7509+
&& (ep.stage == crate::ShaderStage::Mesh
7510+
|| ep.stage == crate::ShaderStage::Task)
74537511
{
74547512
continue;
74557513
}
@@ -7548,7 +7606,7 @@ template <typename A>
75487606
}
75497607
writeln!(self.out)?;
75507608
}
7551-
if ep.stage == crate::ShaderStage::Mesh {
7609+
if ep.stage == crate::ShaderStage::Mesh || ep.stage == crate::ShaderStage::Task {
75527610
for (handle, var) in module.global_variables.iter() {
75537611
if var.space != crate::AddressSpace::WorkGroup || fun_info[handle].is_empty() {
75547612
continue;
@@ -7567,7 +7625,7 @@ template <typename A>
75677625
};
75687626
writeln!(
75697627
self.out,
7570-
"threadgroup {ty_context}& {}",
7628+
"threadgroup {ty_context}* {}",
75717629
self.names[&NameKey::GlobalVariable(handle)]
75727630
)?;
75737631
}
@@ -8043,19 +8101,54 @@ mod workgroup_mem_init {
80438101
}
80448102

80458103
impl Access {
8104+
fn is_pointer_type(&self, module: &crate::Module) -> bool {
8105+
match *self {
8106+
Access::GlobalVariable(handle) => {
8107+
let var = &module.global_variables[handle];
8108+
// workgroup variables are passed as pointers to the kernel
8109+
var.space == crate::AddressSpace::WorkGroup
8110+
|| matches!(module.types[var.ty].inner, crate::TypeInner::Pointer { .. })
8111+
}
8112+
Access::StructMember(struct_handle, member_index) => {
8113+
// check if the member's type is a pointer
8114+
if let crate::TypeInner::Struct { ref members, .. } =
8115+
module.types[struct_handle].inner
8116+
{
8117+
if let Some(member) = members.get(member_index as usize) {
8118+
matches!(
8119+
module.types[member.ty].inner,
8120+
crate::TypeInner::Pointer { .. }
8121+
)
8122+
} else {
8123+
false
8124+
}
8125+
} else {
8126+
false
8127+
}
8128+
}
8129+
Access::Array(_) => false,
8130+
}
8131+
}
8132+
80468133
fn write<W: Write>(
80478134
&self,
80488135
writer: &mut W,
80498136
names: &FastHashMap<NameKey, String>,
8137+
op: &str,
80508138
) -> Result<(), core::fmt::Error> {
80518139
match *self {
80528140
Access::GlobalVariable(handle) => {
80538141
write!(writer, "{}", &names[&NameKey::GlobalVariable(handle)])
80548142
}
80558143
Access::StructMember(handle, index) => {
8056-
write!(writer, ".{}", &names[&NameKey::StructMember(handle, index)])
8144+
write!(
8145+
writer,
8146+
"{}{}",
8147+
op,
8148+
&names[&NameKey::StructMember(handle, index)]
8149+
)
80578150
}
8058-
Access::Array(depth) => write!(writer, ".{WRAPPED_ARRAY_FIELD}[__i{depth}]"),
8151+
Access::Array(depth) => write!(writer, "{}{}[__i{depth}]", op, WRAPPED_ARRAY_FIELD),
80598152
}
80608153
}
80618154
}
@@ -8094,12 +8187,33 @@ mod workgroup_mem_init {
80948187
&self,
80958188
writer: &mut W,
80968189
names: &FastHashMap<NameKey, String>,
8190+
module: &crate::Module,
80978191
) -> Result<(), core::fmt::Error> {
8098-
for next in self.stack.iter() {
8099-
next.write(writer, names)?;
8192+
for (i, next) in self.stack.iter().enumerate() {
8193+
let op = if i == 0 {
8194+
// root item doesn't get an operator prefix
8195+
""
8196+
} else {
8197+
// check if the previous item is a pointer to determine the operator for this item
8198+
let prev = &self.stack[i - 1];
8199+
if prev.is_pointer_type(module) {
8200+
"->"
8201+
} else {
8202+
"."
8203+
}
8204+
};
8205+
next.write(writer, names, op)?;
81008206
}
81018207
Ok(())
81028208
}
8209+
8210+
fn root_is_workgroup_pointer(&self, module: &crate::Module) -> bool {
8211+
if let Some(Access::GlobalVariable(handle)) = self.stack.first().as_deref() {
8212+
let var = &module.global_variables[*handle];
8213+
return var.space == crate::AddressSpace::WorkGroup;
8214+
}
8215+
false
8216+
}
81038217
}
81048218

81058219
impl<W: Write> Writer<W> {
@@ -8174,16 +8288,25 @@ mod workgroup_mem_init {
81748288
) -> BackendResult {
81758289
if module_info[ty].contains(valid::TypeFlags::CONSTRUCTIBLE) {
81768290
write!(self.out, "{level}")?;
8177-
access_stack.write(&mut self.out, &self.names)?;
8291+
// workgroup variables are always pointers; add * to dereference at root level.
8292+
// Nested accesses use -> operator from the access stack.
8293+
let is_root_workgroup = access_stack.root_is_workgroup_pointer(module);
8294+
let is_nested = access_stack.stack.len() > 1;
8295+
if is_root_workgroup && !is_nested {
8296+
write!(self.out, "*")?;
8297+
}
8298+
access_stack.write(&mut self.out, &self.names, module)?;
81788299
writeln!(self.out, " = {{}};")?;
81798300
} else {
81808301
match module.types[ty].inner {
81818302
crate::TypeInner::Atomic { .. } => {
8182-
write!(
8183-
self.out,
8184-
"{level}{NAMESPACE}::atomic_store_explicit({ATOMIC_REFERENCE}"
8185-
)?;
8186-
access_stack.write(&mut self.out, &self.names)?;
8303+
write!(self.out, "{level}{NAMESPACE}::atomic_store_explicit(")?;
8304+
// only skip & for direct access to workgroup atomic
8305+
let is_nested = access_stack.stack.len() > 1;
8306+
if !access_stack.root_is_workgroup_pointer(module) || is_nested {
8307+
write!(self.out, "{ATOMIC_REFERENCE}")?;
8308+
}
8309+
access_stack.write(&mut self.out, &self.names, module)?;
81878310
writeln!(self.out, ", 0, {NAMESPACE}::memory_order_relaxed);")?;
81888311
}
81898312
crate::TypeInner::Array { base, size, .. } => {

naga/tests/out/msl/wgsl-8820-multiple-local-invocation-index-id.metal

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ struct compute1_Input {
1414
kernel void compute1_(
1515
metal::uint3 local_invocation_id [[thread_position_in_threadgroup]]
1616
, uint local_invocation_index [[thread_index_in_threadgroup]]
17-
, threadgroup uint& wg_var
17+
, threadgroup uint* wg_var
1818
) {
1919
if (local_invocation_index == 0u) {
20-
wg_var = {};
20+
*wg_var = {};
2121
}
2222
metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);
2323
const Input input = { local_invocation_id, local_invocation_index };
24-
wg_var = input.local_invocation_index * 2u;
25-
uint _e6 = wg_var;
26-
wg_var = _e6 + input.local_invocation_id[0];
24+
*wg_var = input.local_invocation_index * 2u;
25+
uint _e6 = *wg_var;
26+
*wg_var = _e6 + input.local_invocation_id[0];
2727
return;
2828
}

naga/tests/out/msl/wgsl-abstract-types-operators.metal

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,18 @@ void wgpu_4445_(
100100
}
101101

102102
void wgpu_4435_(
103-
threadgroup type_3& a
103+
threadgroup type_3* a
104104
) {
105-
uint y = a.inner[as_type<int>(as_type<uint>(1) - as_type<uint>(1))];
105+
uint y = a->inner[as_type<int>(as_type<uint>(1) - as_type<uint>(1))];
106106
return;
107107
}
108108

109109
kernel void main_(
110110
uint __local_invocation_index [[thread_index_in_threadgroup]]
111-
, threadgroup type_3& a
111+
, threadgroup type_3* a
112112
) {
113113
if (__local_invocation_index == 0u) {
114-
a = {};
114+
*a = {};
115115
}
116116
metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup);
117117
runtime_values();

0 commit comments

Comments
 (0)