diff --git a/src/back/hlsl/writer.rs b/src/back/hlsl/writer.rs index f46e4ee9ac..5393b3eb7e 100644 --- a/src/back/hlsl/writer.rs +++ b/src/back/hlsl/writer.rs @@ -29,6 +29,24 @@ struct EpStructMember { name: String, ty: Handle, binding: Option, + index: usize, +} + +#[derive(Eq, PartialEq, PartialOrd, Ord)] +enum InterfaceKey { + Location(u32), + BuiltIn(crate::BuiltIn), + Other, +} + +impl InterfaceKey { + fn new(binding: Option<&crate::Binding>) -> Self { + match binding { + Some(&crate::Binding::Location { location, .. }) => Self::Location(location), + Some(&crate::Binding::BuiltIn(bi)) => Self::BuiltIn(bi), + None => Self::Other, + } + } } #[derive(Copy, Clone, PartialEq)] @@ -166,7 +184,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { // Write all entry points wrapped structs for ep in module.entry_points.iter() { - self.write_ep_input_struct(module, &ep.function, ep.stage, &ep.name)?; + let ep_input = self.write_ep_input_struct(module, &ep.function, ep.stage, &ep.name)?; + self.ep_inputs.push(ep_input); } // Write all regular functions @@ -300,8 +319,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { func: &crate::Function, stage: ShaderStage, entry_point_name: &str, - ) -> BackendResult { - if !func.arguments.is_empty() { + ) -> Result, Error> { + Ok(if !func.arguments.is_empty() { let struct_name_prefix = match stage { ShaderStage::Vertex => "VertexInput", ShaderStage::Fragment => "FragmentInput", @@ -310,48 +329,49 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { let struct_name = format!("{}_{}", struct_name_prefix, entry_point_name); let mut members = Vec::with_capacity(func.arguments.len()); - - write!(self.out, "struct {}", &struct_name)?; - writeln!(self.out, " {{")?; - - for arg in func.arguments.iter() { + for (index, arg) in func.arguments.iter().enumerate() { let member_name = if let Some(ref name) = arg.name { name } else { "member" }; - let member = EpStructMember { + members.push(EpStructMember { name: self.namer.call_unique(member_name), ty: arg.ty, binding: arg.binding.clone(), - }; + index, + }); + } + + // Sort the members so that first come the user-defined varyings + // in ascending locations, and then built-ins. This allows VS and FS + // interfaces to match with regards to order. + members.sort_by_key(|m| InterfaceKey::new(m.binding.as_ref())); + write!(self.out, "struct {}", &struct_name)?; + writeln!(self.out, " {{")?; + for m in members.iter() { write!(self.out, "{}", back::INDENT)?; - self.write_type(module, member.ty)?; - write!(self.out, " {}", &member.name)?; - if let Some(ref binding) = member.binding { + self.write_type(module, m.ty)?; + write!(self.out, " {}", &m.name)?; + if let Some(ref binding) = m.binding { self.write_semantic(binding, Some((stage, Io::Input)))?; } - write!(self.out, ";")?; - writeln!(self.out)?; - - members.push(member); + writeln!(self.out, ";")?; } - writeln!(self.out, "}};")?; writeln!(self.out)?; - let ep_input = EntryPointBinding { + // now bring back the old order + members.sort_by_key(|m| m.index); + + Some(EntryPointBinding { name: struct_name, members, - }; - - self.ep_inputs.push(Some(ep_input)); + }) } else { - self.ep_inputs.push(None); - } - - Ok(()) + None + }) } /// Helper method used to write global variables @@ -536,18 +556,36 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { module: &Module, handle: Handle, _block: bool, - members: &[crate::StructMember], + original_members: &[crate::StructMember], shader_stage: Option<(ShaderStage, Io)>, ) -> BackendResult { // Write struct name write!(self.out, "struct {}", self.names[&NameKey::Type(handle)])?; writeln!(self.out, " {{")?; - for (index, member) in members.iter().enumerate() { + let has_layout = original_members.iter().any(|m| m.offset != 0); + let has_binding = original_members.iter().any(|m| m.binding.is_some()); + //TODO: avoid heap allocation + let mut members = original_members + .iter() + .enumerate() + .map(|(index, m)| (index, m.ty, m.binding.clone())) + .collect::>(); + //Note: this is a bit of a hack. We need to re-order the output fields, but we can only do this + // for non-layouted structures. It may be possible for an WGSL program can use the same struct + // for both host sharing and the interface. This case isn't supported here. + if has_binding && !has_layout { + // Sort the members so that first come the user-defined varyings + // in ascending locations, and then built-ins. This allows VS and FS + // interfaces to match with regards to order. + members.sort_by_key(|&(_, _, ref binding)| InterfaceKey::new(binding.as_ref())); + } + + for (index, ty, binding) in members { // The indentation is only for readability write!(self.out, "{}", back::INDENT)?; - match module.types[member.ty].inner { + match module.types[ty].inner { TypeInner::Array { base, size, @@ -584,7 +622,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { interpolation, sampling, .. - }) = member.binding + }) = binding { if let Some(interpolation) = interpolation { write!(self.out, "{} ", interpolation.to_hlsl_str())? @@ -598,7 +636,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } // Write the member type and name - self.write_type(module, member.ty)?; + self.write_type(module, ty)?; write!( self.out, " {}", @@ -607,11 +645,10 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } } - if let Some(ref binding) = member.binding { + if let Some(ref binding) = binding { self.write_semantic(binding, shader_stage)?; }; - write!(self.out, ";")?; - writeln!(self.out)?; + writeln!(self.out, ";")?; } writeln!(self.out, "}};")?; diff --git a/tests/out/hlsl/interface.hlsl b/tests/out/hlsl/interface.hlsl index 1d6fc884b7..4a46710a80 100644 --- a/tests/out/hlsl/interface.hlsl +++ b/tests/out/hlsl/interface.hlsl @@ -11,16 +11,16 @@ struct FragmentOutput { }; struct VertexInput_vertex { - uint vertex_index1 : SV_VertexID; - uint instance_index1 : SV_InstanceID; uint color1 : LOC10; + uint instance_index1 : SV_InstanceID; + uint vertex_index1 : SV_VertexID; }; struct FragmentInput_fragment { - VertexOutput in2; bool front_facing1 : SV_IsFrontFace; uint sample_index1 : SV_SampleIndex; uint sample_mask1 : SV_Coverage; + VertexOutput in2; }; struct ComputeInput_compute { diff --git a/tests/out/hlsl/quad-vert.hlsl b/tests/out/hlsl/quad-vert.hlsl index c0f0f1fbf1..cf91b74eec 100644 --- a/tests/out/hlsl/quad-vert.hlsl +++ b/tests/out/hlsl/quad-vert.hlsl @@ -1,17 +1,17 @@ struct gl_PerVertex { float4 gl_Position : SV_Position; - float gl_PointSize : PSIZE; float gl_ClipDistance[1] : SV_ClipDistance; float gl_CullDistance[1] : SV_CullDistance; + float gl_PointSize : PSIZE; }; struct type10 { linear float2 member : LOC0; float4 gl_Position : SV_Position; - float gl_PointSize : PSIZE; float gl_ClipDistance[1] : SV_ClipDistance; float gl_CullDistance[1] : SV_CullDistance; + float gl_PointSize : PSIZE; }; static float2 v_uv = (float2)0; @@ -20,8 +20,8 @@ static gl_PerVertex perVertexStruct = { float4(0.0, 0.0, 0.0, 1.0), 1.0, float(0 static float2 a_pos1 = (float2)0; struct VertexInput_main { - float2 a_uv2 : LOC1; float2 a_pos2 : LOC0; + float2 a_uv2 : LOC1; }; void main1()