Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[hlsl-out] re-order interface struct fields #1189

Merged
merged 1 commit into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 71 additions & 34 deletions src/back/hlsl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@ struct EpStructMember {
name: String,
ty: Handle<crate::Type>,
binding: Option<crate::Binding>,
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)]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<Option<EntryPointBinding>, Error> {
Ok(if !func.arguments.is_empty() {
let struct_name_prefix = match stage {
ShaderStage::Vertex => "VertexInput",
ShaderStage::Fragment => "FragmentInput",
Expand All @@ -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
Expand Down Expand Up @@ -536,18 +556,36 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
module: &Module,
handle: Handle<crate::Type>,
_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::<Vec<_>>();
//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,
Expand Down Expand Up @@ -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())?
Expand All @@ -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,
" {}",
Expand All @@ -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, "}};")?;
Expand Down
6 changes: 3 additions & 3 deletions tests/out/hlsl/interface.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions tests/out/hlsl/quad-vert.hlsl
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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()
Expand Down