diff --git a/naga/src/back/msl/writer.rs b/naga/src/back/msl/writer.rs index 62e1b986a6..ada50ee412 100644 --- a/naga/src/back/msl/writer.rs +++ b/naga/src/back/msl/writer.rs @@ -86,6 +86,39 @@ const fn scalar_is_int(scalar: crate::Scalar) -> bool { /// Prefix for cached clamped level-of-detail values for `ImageLoad` expressions. const CLAMPED_LOD_LOAD_PREFIX: &str = "clamped_lod_e"; +/// Wrapper for identifier names for clamped level-of-detail values +/// +/// Values of this type implement [`std::fmt::Display`], formatting as +/// the name of the variable used to hold the cached clamped +/// level-of-detail value for an `ImageLoad` expression. +struct ClampedLod(Handle); + +impl Display for ClampedLod { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.write_prefixed(f, CLAMPED_LOD_LOAD_PREFIX) + } +} + +/// Wrapper for generating `struct _mslBufferSizes` member names for +/// runtime-sized array lengths. +/// +/// On Metal, `wgpu_hal` passes the element counts for all runtime-sized arrays +/// as an argument to the entry point. This argument's type in the MSL is +/// `struct _mslBufferSizes`, a Naga-synthesized struct with a `uint` member for +/// each global variable containing a runtime-sized array. +/// +/// If `global` is a [`Handle`] for a [`GlobalVariable`] that contains a +/// runtime-sized array, then the value `ArraySize(global)` implements +/// [`std::fmt::Display`], formatting as the name of the struct member carrying +/// the number of elements in that runtime-sized array. +struct ArraySizeMember(Handle); + +impl Display for ArraySizeMember { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.write_prefixed(f, "size") + } +} + struct TypeContext<'a> { handle: Handle, gctx: proc::GlobalCtx<'a>, @@ -677,9 +710,7 @@ impl Writer { ) -> BackendResult { match level { LevelOfDetail::Direct(expr) => self.put_expression(expr, context, true)?, - LevelOfDetail::Restricted(load) => { - write!(self.out, "{}{}", CLAMPED_LOD_LOAD_PREFIX, load.index())? - } + LevelOfDetail::Restricted(load) => write!(self.out, "{}", ClampedLod(load))?, } Ok(()) } @@ -1146,8 +1177,8 @@ impl Writer { // prevent that. write!( self.out, - "(_buffer_sizes.size{idx} - {offset} - {size}) / {stride}", - idx = handle.index(), + "(_buffer_sizes.{member} - {offset} - {size}) / {stride}", + member = ArraySizeMember(handle), offset = offset, size = size, stride = stride, @@ -2778,13 +2809,7 @@ impl Writer { return Ok(()); } - write!( - self.out, - "{}uint {}{} = ", - indent, - CLAMPED_LOD_LOAD_PREFIX, - load.index(), - )?; + write!(self.out, "{}uint {} = ", indent, ClampedLod(load),)?; self.put_restricted_scalar_image_index( image, level_of_detail, @@ -3444,24 +3469,30 @@ impl Writer { writeln!(self.out)?; { - let mut indices = vec![]; - for (handle, var) in module.global_variables.iter() { - if needs_array_length(var.ty, &module.types) { - let idx = handle.index(); - indices.push(idx); - } - } + // Make a `Vec` of all the `GlobalVariable`s that contain + // runtime-sized arrays. + let globals: Vec> = module + .global_variables + .iter() + .filter(|&(_, var)| needs_array_length(var.ty, &module.types)) + .map(|(handle, _)| handle) + .collect(); let mut buffer_indices = vec![]; for vbm in &pipeline_options.vertex_buffer_mappings { buffer_indices.push(vbm.id); } - if !indices.is_empty() || !buffer_indices.is_empty() { + if !globals.is_empty() || !buffer_indices.is_empty() { writeln!(self.out, "struct _mslBufferSizes {{")?; - for idx in indices { - writeln!(self.out, "{}uint size{};", back::INDENT, idx)?; + for global in globals { + writeln!( + self.out, + "{}uint {};", + back::INDENT, + ArraySizeMember(global) + )?; } for idx in buffer_indices {