HLSL: rewrite handling of interface matching rules (#1276)

* [hlsl-out] flatten the entry point inputs

Previously, the logic was re-ordering the inputs according to the binding.
This breaks if one of the inputs is a struct. With this change, the struct
fields are also flattened into the fake entry point struct. We also
construct the original arguments at the beginning of the function.

* hlsl-out: completely separate the flattened IO structs from the original IR structs

Previously, we had heuristics to detect if a particular struct needs the fields
to be re-ordered. We'd re-order interface structs without layout, and the detection
was very fragile and easily wrong.
The new logic is spawning separate struct types if we need any re-ordering to happen.
It's solid, there are no heuristics.
This commit is contained in:
Dzmitry Malyshau 2021-08-22 22:40:31 -04:00 committed by GitHub
parent 63e58f2022
commit 81f4ff032f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 423 additions and 263 deletions

View File

@ -164,8 +164,8 @@ pub struct Writer<'a, W> {
namer: proc::Namer,
/// HLSL backend options
options: &'a Options,
/// Information about entry point arguments wrapped into structure
ep_inputs: Vec<Option<writer::EntryPointBinding>>,
/// Information about entry point arguments and result types.
entry_point_io: Vec<writer::EntryPointInterface>,
/// Set of expressions that have associated temporary variables
named_expressions: crate::NamedExpressions,
wrapped_array_lengths: crate::FastHashSet<help::WrappedArrayLength>,

View File

@ -17,20 +17,36 @@ const SPECIAL_BASE_VERTEX: &str = "base_vertex";
const SPECIAL_BASE_INSTANCE: &str = "base_instance";
const SPECIAL_OTHER: &str = "other";
struct EpStructMember {
name: String,
ty: Handle<crate::Type>,
// technically, this should always be `Some`
binding: Option<crate::Binding>,
index: u32,
}
/// Structure contains information required for generating
/// wrapped structure of all entry points arguments
pub(super) struct EntryPointBinding {
struct EntryPointBinding {
/// Name of the fake EP argument that contains the struct
/// with all the flattened input data.
arg_name: String,
/// Generated structure name
name: String,
ty_name: String,
/// Members of generated structure
members: Vec<EpStructMember>,
}
struct EpStructMember {
name: String,
ty: Handle<crate::Type>,
binding: Option<crate::Binding>,
index: usize,
pub(super) struct EntryPointInterface {
/// If `Some`, the input of an entry point is gathered in a special
/// struct with members sorted by binding.
/// The `EntryPointBinding::members` array is sorted by index,
/// so that we can walk it in `write_ep_arguments_initialization`.
input: Option<EntryPointBinding>,
/// If `Some`, the output of an entry point is flattened.
/// The `EntryPointBinding::members` array is sorted by binding,
/// So that we can walk it in `Statement::Return` handler.
output: Option<EntryPointBinding>,
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
@ -50,19 +66,6 @@ impl InterfaceKey {
}
}
// Returns true for structures that need their members permuted,
// 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.
fn needs_permutation(members: &[crate::StructMember]) -> bool {
//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.
let has_layout = members.iter().any(|m| m.offset != 0);
let has_binding = members.iter().any(|m| m.binding.is_some());
has_binding && !has_layout
}
#[derive(Copy, Clone, PartialEq)]
enum Io {
Input,
@ -76,7 +79,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
names: crate::FastHashMap::default(),
namer: proc::Namer::default(),
options,
ep_inputs: Vec::new(),
entry_point_io: Vec::new(),
named_expressions: crate::NamedExpressions::default(),
wrapped_array_lengths: crate::FastHashSet::default(),
wrapped_image_queries: crate::FastHashSet::default(),
@ -88,7 +91,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
self.names.clear();
self.namer
.reset(module, super::keywords::RESERVED, &[], &mut self.names);
self.ep_inputs.clear();
self.entry_point_io.clear();
self.named_expressions.clear();
self.wrapped_array_lengths.clear();
self.wrapped_image_queries.clear();
@ -199,8 +202,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
// Write all entry points wrapped structs
for ep in module.entry_points.iter() {
let ep_input = self.write_ep_input_struct(module, &ep.function, ep.stage, &ep.name)?;
self.ep_inputs.push(ep_input);
let ep_io = self.write_ep_interface(module, &ep.function, ep.stage, &ep.name)?;
self.entry_point_io.push(ep_io);
}
// Write all regular functions
@ -306,6 +309,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
Ok(super::ReflectionInfo { entry_point_names })
}
//TODO: we could force fragment outputs to always go through `entry_point_io.output` path
// if they are struct, so that the `stage` argument here could be omitted.
fn write_semantic(
&mut self,
binding: &crate::Binding,
@ -328,67 +333,209 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
Ok(())
}
fn write_interface_struct(
&mut self,
module: &Module,
shader_stage: (ShaderStage, Io),
struct_name: String,
mut members: Vec<EpStructMember>,
) -> Result<EntryPointBinding, Error> {
// 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, m.ty)?;
write!(self.out, " {}", &m.name)?;
if let Some(ref binding) = m.binding {
self.write_semantic(binding, Some(shader_stage))?;
}
writeln!(self.out, ";")?;
}
writeln!(self.out, "}};")?;
writeln!(self.out)?;
match shader_stage.1 {
Io::Input => {
// bring back the original order
members.sort_by_key(|m| m.index);
}
Io::Output => {
// keep it sorted by binding
}
}
Ok(EntryPointBinding {
arg_name: self.namer.call_unique(struct_name.to_lowercase().as_str()),
ty_name: struct_name,
members,
})
}
/// Flatten all entry point arguments into a single struct.
/// This is needed since we need to re-order them: first placing user locations,
/// then built-ins.
fn write_ep_input_struct(
&mut self,
module: &Module,
func: &crate::Function,
stage: ShaderStage,
entry_point_name: &str,
) -> Result<Option<EntryPointBinding>, Error> {
Ok(if !func.arguments.is_empty() {
let struct_name_prefix = match stage {
ShaderStage::Vertex => "VertexInput",
ShaderStage::Fragment => "FragmentInput",
ShaderStage::Compute => "ComputeInput",
};
let struct_name = format!("{}_{}", struct_name_prefix, entry_point_name);
) -> Result<EntryPointBinding, Error> {
let struct_name = format!("{:?}Input_{}", stage, entry_point_name);
let mut members = Vec::with_capacity(func.arguments.len());
for (index, arg) in func.arguments.iter().enumerate() {
let member_name = if let Some(ref name) = arg.name {
name
} else {
"member"
};
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, m.ty)?;
write!(self.out, " {}", &m.name)?;
if let Some(ref binding) = m.binding {
self.write_semantic(binding, Some((stage, Io::Input)))?;
let mut fake_members = Vec::new();
for arg in func.arguments.iter() {
match module.types[arg.ty].inner {
TypeInner::Struct { ref members, .. } => {
for member in members.iter() {
let member_name = if let Some(ref name) = member.name {
name
} else {
"member"
};
let index = fake_members.len() as u32;
fake_members.push(EpStructMember {
name: self.namer.call_unique(member_name),
ty: member.ty,
binding: member.binding.clone(),
index,
});
}
}
_ => {
let member_name = if let Some(ref name) = arg.name {
name
} else {
"member"
};
let index = fake_members.len() as u32;
fake_members.push(EpStructMember {
name: self.namer.call_unique(member_name),
ty: arg.ty,
binding: arg.binding.clone(),
index,
});
}
writeln!(self.out, ";")?;
}
writeln!(self.out, "}};")?;
writeln!(self.out)?;
}
// now bring back the old order
members.sort_by_key(|m| m.index);
self.write_interface_struct(module, (stage, Io::Input), struct_name, fake_members)
}
Some(EntryPointBinding {
name: struct_name,
members,
})
} else {
None
/// Flatten all entry point results into a single struct.
/// This is needed since we need to re-order them: first placing user locations,
/// then built-ins.
fn write_ep_output_struct(
&mut self,
module: &Module,
result: &crate::FunctionResult,
stage: ShaderStage,
entry_point_name: &str,
) -> Result<EntryPointBinding, Error> {
let struct_name = format!("{:?}Output_{}", stage, entry_point_name);
let mut fake_members = Vec::new();
let empty = [];
let members = match module.types[result.ty].inner {
TypeInner::Struct { ref members, .. } => members,
ref other => {
log::error!("Unexpected {:?} output type without a binding", other);
&empty[..]
}
};
for member in members.iter() {
let member_name = if let Some(ref name) = member.name {
name
} else {
"member"
};
let index = fake_members.len() as u32;
fake_members.push(EpStructMember {
name: self.namer.call_unique(member_name),
ty: member.ty,
binding: member.binding.clone(),
index,
});
}
self.write_interface_struct(module, (stage, Io::Output), struct_name, fake_members)
}
/// Writes special interface structures for an entry point. The special structures have
/// all the fields flattened into them and sorted by binding. They are only needed for
/// VS outputs and FS inputs, so that these interfaces match.
fn write_ep_interface(
&mut self,
module: &Module,
func: &crate::Function,
stage: ShaderStage,
ep_name: &str,
) -> Result<EntryPointInterface, Error> {
Ok(EntryPointInterface {
input: if !func.arguments.is_empty() && stage == ShaderStage::Fragment {
Some(self.write_ep_input_struct(module, func, stage, ep_name)?)
} else {
None
},
output: match func.result {
Some(ref fr) if fr.binding.is_none() && stage == ShaderStage::Vertex => {
Some(self.write_ep_output_struct(module, fr, stage, ep_name)?)
}
_ => None,
},
})
}
/// Write an entry point preface that initializes the arguments as specified in IR.
fn write_ep_arguments_initialization(
&mut self,
module: &Module,
func: &crate::Function,
ep_index: u16,
) -> BackendResult {
let ep_input = match self.entry_point_io[ep_index as usize].input.take() {
Some(ep_input) => ep_input,
None => return Ok(()),
};
let mut fake_iter = ep_input.members.iter();
for (arg_index, arg) in func.arguments.iter().enumerate() {
write!(self.out, "{}", back::INDENT)?;
self.write_type(module, arg.ty)?;
let arg_name = &self.names[&NameKey::EntryPointArgument(ep_index, arg_index as u32)];
write!(self.out, " {}", arg_name)?;
match module.types[arg.ty].inner {
TypeInner::Array { size, .. } => {
self.write_array_size(module, size)?;
let fake_member = fake_iter.next().unwrap();
writeln!(self.out, " = {}.{};", ep_input.arg_name, fake_member.name)?;
}
TypeInner::Struct { ref members, .. } => {
write!(self.out, " = {{ ")?;
for index in 0..members.len() {
if index != 0 {
write!(self.out, ", ")?;
}
let fake_member = fake_iter.next().unwrap();
write!(self.out, "{}.{}", ep_input.arg_name, fake_member.name)?;
}
writeln!(self.out, " }};")?;
}
_ => {
let fake_member = fake_iter.next().unwrap();
writeln!(self.out, " = {}.{};", ep_input.arg_name, fake_member.name)?;
}
}
}
assert!(fake_iter.next().is_none());
Ok(())
}
/// Helper method used to write global variables
/// # Notes
/// Always adds a newline
@ -571,28 +718,18 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
module: &Module,
handle: Handle<crate::Type>,
_block: bool,
original_members: &[crate::StructMember],
members: &[crate::StructMember],
shader_stage: Option<(ShaderStage, Io)>,
) -> BackendResult {
// Write struct name
write!(self.out, "struct {}", self.names[&NameKey::Type(handle)])?;
writeln!(self.out, " {{")?;
let struct_name = &self.names[&NameKey::Type(handle)];
writeln!(self.out, "struct {} {{", struct_name)?;
//TODO: avoid heap allocation
let mut members = original_members
.iter()
.enumerate()
.map(|(index, m)| (index, m.ty, m.binding.clone()))
.collect::<Vec<_>>();
if needs_permutation(original_members) {
members.sort_by_key(|&(_, _, ref binding)| InterfaceKey::new(binding.as_ref()));
}
for (index, ty, binding) in members {
for (index, member) in members.iter().enumerate() {
// The indentation is only for readability
write!(self.out, "{}", back::INDENT)?;
match module.types[ty].inner {
match module.types[member.ty].inner {
TypeInner::Array {
base,
size,
@ -629,7 +766,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
interpolation,
sampling,
..
}) = binding
}) = member.binding
{
if let Some(interpolation) = interpolation {
write!(self.out, "{} ", interpolation.to_hlsl_str())?
@ -642,12 +779,12 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
}
}
if let TypeInner::Matrix { .. } = module.types[ty].inner {
if let TypeInner::Matrix { .. } = module.types[member.ty].inner {
write!(self.out, "row_major ")?;
}
// Write the member type and name
self.write_type(module, ty)?;
self.write_type(module, member.ty)?;
write!(
self.out,
" {}",
@ -656,7 +793,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
}
}
if let Some(ref binding) = binding {
if let Some(ref binding) = member.binding {
self.write_semantic(binding, shader_stage)?;
};
writeln!(self.out, ";")?;
@ -760,7 +897,18 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
) -> BackendResult {
// Function Declaration Syntax - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-function-syntax
if let Some(ref result) = func.result {
self.write_type(module, result.ty)?;
match func_ctx.ty {
back::FunctionType::Function(_) => {
self.write_type(module, result.ty)?;
}
back::FunctionType::EntryPoint(index) => {
if let Some(ref ep_output) = self.entry_point_io[index as usize].output {
write!(self.out, "{}", ep_output.ty_name)?;
} else {
self.write_type(module, result.ty)?;
}
}
}
} else {
write!(self.out, "void")?;
}
@ -772,6 +920,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
match func_ctx.ty {
back::FunctionType::Function(handle) => {
for (index, arg) in func.arguments.iter().enumerate() {
if index != 0 {
write!(self.out, ", ")?;
}
// Write argument type
let arg_ty = match module.types[arg.ty].inner {
// pointers in function arguments are expected and resolve to `inout`
@ -792,31 +943,30 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
if let TypeInner::Array { size, .. } = module.types[arg.ty].inner {
self.write_array_size(module, size)?;
}
if index < func.arguments.len() - 1 {
// Add a separator between args
write!(self.out, ", ")?;
}
}
}
back::FunctionType::EntryPoint(index) => {
// EntryPoint arguments wrapped into structure
// We need to ensure that entry points have arguments too.
// For the case when we working with multiple entry points
// for example vertex shader with arguments and fragment shader without arguments.
if !self.ep_inputs.is_empty()
&& !module.entry_points[index as usize]
.function
.arguments
.is_empty()
{
if let Some(ref ep_input) = self.ep_inputs[index as usize] {
write!(
self.out,
"{} {}",
ep_input.name,
self.namer
.call_unique(ep_input.name.to_lowercase().as_str())
)?;
back::FunctionType::EntryPoint(ep_index) => {
if let Some(ref ep_input) = self.entry_point_io[ep_index as usize].input {
write!(self.out, "{} {}", ep_input.ty_name, ep_input.arg_name,)?;
} else {
let stage = module.entry_points[ep_index as usize].stage;
for (index, arg) in func.arguments.iter().enumerate() {
if index != 0 {
write!(self.out, ", ")?;
}
self.write_type(module, arg.ty)?;
let argument_name =
&self.names[&NameKey::EntryPointArgument(ep_index, index as u32)];
write!(self.out, " {}", argument_name)?;
if let TypeInner::Array { size, .. } = module.types[arg.ty].inner {
self.write_array_size(module, size)?;
}
if let Some(ref binding) = arg.binding {
self.write_semantic(binding, Some((stage, Io::Input)))?;
}
}
}
}
@ -825,21 +975,25 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
write!(self.out, ")")?;
// Write semantic if it present
let stage = match func_ctx.ty {
back::FunctionType::EntryPoint(index) => {
Some(module.entry_points[index as usize].stage)
}
_ => None,
};
if let Some(ref result) = func.result {
if let Some(ref binding) = result.binding {
self.write_semantic(binding, stage.map(|s| (s, Io::Output)))?;
if let back::FunctionType::EntryPoint(index) = func_ctx.ty {
let stage = module.entry_points[index as usize].stage;
if let Some(crate::FunctionResult {
binding: Some(ref binding),
..
}) = func.result
{
self.write_semantic(binding, Some((stage, Io::Output)))?;
}
}
// Function body start
writeln!(self.out)?;
writeln!(self.out, "{{")?;
if let back::FunctionType::EntryPoint(index) = func_ctx.ty {
self.write_ep_arguments_initialization(module, func, index)?;
}
// Write function local variables
for (handle, local) in func.local_variables.iter() {
// Write indentation (only for readability)
@ -982,22 +1136,47 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
// We can safery unwrap here, since we now we working with struct
let ty = base_ty_res.handle().unwrap();
let struct_name = &self.names[&NameKey::Type(ty)];
let variable_name = self.namer.call_unique(struct_name.as_str()).to_lowercase();
let variable_name = self.namer.call(&struct_name.to_lowercase());
write!(
self.out,
"{}const {} {} = ",
INDENT.repeat(indent),
struct_name,
variable_name
variable_name,
)?;
self.write_expr(module, expr, func_ctx)?;
writeln!(self.out, ";")?;
writeln!(
self.out,
"{}return {};",
INDENT.repeat(indent),
variable_name
)?;
// for entry point returns, we may need to reshuffle the outputs into a different struct
let ep_output = match func_ctx.ty {
back::FunctionType::Function(_) => None,
back::FunctionType::EntryPoint(index) => {
self.entry_point_io[index as usize].output.as_ref()
}
};
let final_name = match ep_output {
Some(ep_output) => {
let final_name = self.namer.call_unique(&variable_name);
write!(
self.out,
"{}const {} {} = {{ ",
INDENT.repeat(indent),
ep_output.ty_name,
final_name,
)?;
for (index, m) in ep_output.members.iter().enumerate() {
if index != 0 {
write!(self.out, ", ")?;
}
let member_name = &self.names[&NameKey::StructMember(ty, m.index)];
write!(self.out, "{}.{}", variable_name, member_name)?;
}
writeln!(self.out, " }};")?;
final_name
}
None => variable_name,
};
writeln!(self.out, "{}return {};", INDENT.repeat(indent), final_name)?;
} else {
write!(self.out, "{}return ", INDENT.repeat(indent))?;
self.write_expr(module, expr, func_ctx)?;
@ -1325,24 +1504,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
match *expression {
Expression::Constant(constant) => self.write_constant(module, constant)?,
Expression::Compose { ty, ref components } => {
let (braces_init, permutation) = match module.types[ty].inner {
TypeInner::Struct { ref members, .. } => {
let permutation = if needs_permutation(members) {
//TODO: avoid heap allocation. We can pre-compute this at the module leve.
let mut permutation = members
.iter()
.enumerate()
.map(|(index, m)| (index, InterfaceKey::new(m.binding.as_ref())))
.collect::<Vec<_>>();
permutation.sort_by_key(|&(_, ref key)| key.clone());
Some(permutation)
} else {
None
};
(true, permutation)
}
TypeInner::Array { .. } => (true, None),
_ => (false, None),
let braces_init = match module.types[ty].inner {
TypeInner::Struct { .. } | TypeInner::Array { .. } => true,
_ => false,
};
if braces_init {
@ -1352,16 +1516,12 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
write!(self.out, "(")?;
}
for index in 0..components.len() {
for (index, &component) in components.iter().enumerate() {
if index != 0 {
// The leading space is for readability only
write!(self.out, ", ")?;
}
let comp_index = match permutation {
Some(ref perm) => perm[index].0,
None => index,
};
self.write_expr(module, components[comp_index], func_ctx)?;
self.write_expr(module, component, func_ctx)?;
}
if braces_init {
@ -1456,24 +1616,14 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
}
}
Expression::FunctionArgument(pos) => {
match func_ctx.ty {
back::FunctionType::Function(handle) => {
let name = &self.names[&NameKey::FunctionArgument(handle, pos)];
write!(self.out, "{}", name)?;
}
let key = match func_ctx.ty {
back::FunctionType::Function(handle) => NameKey::FunctionArgument(handle, pos),
back::FunctionType::EntryPoint(index) => {
// EntryPoint arguments wrapped into structure
// We can safery unwrap here, because if we write function arguments it means, that ep_input struct already exists
let ep_input = self.ep_inputs[index as usize].as_ref().unwrap();
let member_name = &ep_input.members[pos as usize].name;
write!(
self.out,
"{}.{}",
&ep_input.name.to_lowercase(),
member_name
)?
NameKey::EntryPointArgument(index, pos)
}
};
let name = &self.names[&key];
write!(self.out, "{}", name)?;
}
Expression::ImageSample {
image,

View File

@ -1,10 +1,6 @@
RWByteAddressBuffer bar : register(u0);
struct VertexInput_foo {
uint vi1 : SV_VertexID;
};
uint NagaBufferLengthRW(RWByteAddressBuffer buffer)
{
uint ret;
@ -12,7 +8,7 @@ uint NagaBufferLengthRW(RWByteAddressBuffer buffer)
return ret;
}
float4 foo(VertexInput_foo vertexinput_foo) : SV_Position
float4 foo(uint vi : SV_VertexID) : SV_Position
{
float foo1 = 0.0;
int c[5] = {(int)0,(int)0,(int)0,(int)0,(int)0};
@ -41,8 +37,8 @@ float4 foo(VertexInput_foo vertexinput_foo) : SV_Position
int _result[5]={ a, int(b), 3, 4, 5 };
for(int _i=0; _i<5; ++_i) c[_i] = _result[_i];
}
c[(vertexinput_foo.vi1 + 1u)] = 42;
int value = c[vertexinput_foo.vi1];
c[(vi + 1u)] = 42;
int value = c[vi];
return mul(float4(int4(value.xxxx)), matrix1);
}

View File

@ -19,12 +19,8 @@ cbuffer params : register(b0) { SimParams params; }
ByteAddressBuffer particlesSrc : register(t1);
RWByteAddressBuffer particlesDst : register(u2);
struct ComputeInput_main {
uint3 global_invocation_id1 : SV_DispatchThreadID;
};
[numthreads(64, 1, 1)]
void main(ComputeInput_main computeinput_main)
void main(uint3 global_invocation_id : SV_DispatchThreadID)
{
float2 vPos = (float2)0;
float2 vVel = (float2)0;
@ -37,7 +33,7 @@ void main(ComputeInput_main computeinput_main)
float2 vel = (float2)0;
uint i = 0u;
uint index = computeinput_main.global_invocation_id1.x;
uint index = global_invocation_id.x;
if ((index >= NUM_PARTICLES)) {
return;
}

View File

@ -1,10 +1,6 @@
RWByteAddressBuffer v_indices : register(u0);
struct ComputeInput_main {
uint3 global_id1 : SV_DispatchThreadID;
};
uint collatz_iterations(uint n_base)
{
uint n = (uint)0;
@ -32,10 +28,10 @@ uint collatz_iterations(uint n_base)
}
[numthreads(1, 1, 1)]
void main(ComputeInput_main computeinput_main)
void main(uint3 global_id : SV_DispatchThreadID)
{
uint _expr8 = asuint(v_indices.Load(computeinput_main.global_id1.x*4+0));
uint _expr8 = asuint(v_indices.Load(global_id.x*4+0));
const uint _e9 = collatz_iterations(_expr8);
v_indices.Store(computeinput_main.global_id1.x*4+0, asuint(_e9));
v_indices.Store(global_id.x*4+0, asuint(_e9));
return;
}

View File

@ -1,10 +1,6 @@
struct ComputeInput_main {
uint3 global_id1 : SV_DispatchThreadID;
};
[numthreads(1, 1, 1)]
void main(ComputeInput_main computeinput_main)
void main(uint3 global_id : SV_DispatchThreadID)
{
int pos = (int)0;

View File

@ -16,10 +16,6 @@ SamplerState sampler_reg : register(s0, space1);
SamplerComparisonState sampler_cmp : register(s1, space1);
Texture2D<float> image_2d_depth : register(t2, space1);
struct ComputeInput_main {
uint3 local_id1 : SV_GroupThreadID;
};
int2 NagaRWDimensions2D(RWTexture2D<uint4> texture)
{
uint4 ret;
@ -28,15 +24,15 @@ int2 NagaRWDimensions2D(RWTexture2D<uint4> texture)
}
[numthreads(16, 1, 1)]
void main(ComputeInput_main computeinput_main)
void main(uint3 local_id : SV_GroupThreadID)
{
int2 dim = NagaRWDimensions2D(image_storage_src);
int2 itc = ((dim * int2(computeinput_main.local_id1.xy)) % int2(10, 20));
uint4 value1_ = image_mipmapped_src.Load(int3(itc, int(computeinput_main.local_id1.z)));
uint4 value2_ = image_multisampled_src.Load(itc, int(computeinput_main.local_id1.z));
float value3_ = image_depth_multisampled_src.Load(itc, int(computeinput_main.local_id1.z)).x;
int2 itc = ((dim * int2(local_id.xy)) % int2(10, 20));
uint4 value1_ = image_mipmapped_src.Load(int3(itc, int(local_id.z)));
uint4 value2_ = image_multisampled_src.Load(itc, int(local_id.z));
float value3_ = image_depth_multisampled_src.Load(itc, int(local_id.z)).x;
uint4 value4_ = image_storage_src.Load(itc);
uint4 value5_ = image_array_src.Load(int4(itc, int(computeinput_main.local_id1.z), (int(computeinput_main.local_id1.z) + 1)));
uint4 value5_ = image_array_src.Load(int4(itc, int(local_id.z), (int(local_id.z) + 1)));
image_dst[itc.x] = ((((value1_ + value2_) + uint4(uint(value3_).xxxx)) + value4_) + value5_);
return;
}

View File

@ -18,45 +18,42 @@ struct FragmentOutput {
groupshared uint output[1];
struct VertexInput_vertex {
uint color1 : LOC10;
uint instance_index1 : SV_InstanceID;
uint vertex_index1 : SV_VertexID;
struct VertexOutput_vertex {
float varying : LOC1;
float4 position : SV_Position;
};
struct FragmentInput_fragment {
float varying : LOC1;
float4 position : SV_Position;
bool front_facing1 : SV_IsFrontFace;
uint sample_index1 : SV_SampleIndex;
uint sample_mask1 : SV_Coverage;
VertexOutput in2;
};
struct ComputeInput_compute {
uint3 global_id1 : SV_DispatchThreadID;
uint3 local_id1 : SV_GroupThreadID;
uint local_index1 : SV_GroupIndex;
uint3 wg_id1 : SV_GroupID;
uint3 num_wgs1 : SV_GroupID;
};
VertexOutput vertex(VertexInput_vertex vertexinput_vertex)
VertexOutput_vertex vertex(uint vertex_index : SV_VertexID, uint instance_index : SV_InstanceID, uint color : LOC10)
{
uint tmp = (((_NagaConstants.base_vertex + vertexinput_vertex.vertex_index1) + (_NagaConstants.base_instance + vertexinput_vertex.instance_index1)) + vertexinput_vertex.color1);
const VertexOutput vertexoutput1 = { float4(1.0.xxxx), float(tmp) };
uint tmp = (((_NagaConstants.base_vertex + vertex_index) + (_NagaConstants.base_instance + instance_index)) + color);
const VertexOutput vertexoutput = { float4(1.0.xxxx), float(tmp) };
const VertexOutput_vertex vertexoutput1 = { vertexoutput.varying, vertexoutput.position };
return vertexoutput1;
}
FragmentOutput fragment(FragmentInput_fragment fragmentinput_fragment)
{
uint mask = (fragmentinput_fragment.sample_mask1 & (1u << fragmentinput_fragment.sample_index1));
float color2 = (fragmentinput_fragment.front_facing1 ? 1.0 : 0.0);
const FragmentOutput fragmentoutput1 = { fragmentinput_fragment.in2.varying, mask, color2 };
return fragmentoutput1;
VertexOutput in1 = { fragmentinput_fragment.position, fragmentinput_fragment.varying };
bool front_facing = fragmentinput_fragment.front_facing1;
uint sample_index = fragmentinput_fragment.sample_index1;
uint sample_mask = fragmentinput_fragment.sample_mask1;
uint mask = (sample_mask & (1u << sample_index));
float color1 = (front_facing ? 1.0 : 0.0);
const FragmentOutput fragmentoutput = { in1.varying, mask, color1 };
return fragmentoutput;
}
[numthreads(1, 1, 1)]
void compute(ComputeInput_compute computeinput_compute)
void compute(uint3 global_id : SV_DispatchThreadID, uint3 local_id : SV_GroupThreadID, uint local_index : SV_GroupIndex, uint3 wg_id : SV_GroupID, uint3 num_wgs : SV_GroupID)
{
output[0] = ((((computeinput_compute.global_id1.x + computeinput_compute.local_id1.x) + computeinput_compute.local_index1) + computeinput_compute.wg_id1.x) + uint3(_NagaConstants.base_vertex, _NagaConstants.base_instance, _NagaConstants.other).x);
output[0] = ((((global_id.x + local_id.x) + local_index) + wg_id.x) + uint3(_NagaConstants.base_vertex, _NagaConstants.base_instance, _NagaConstants.other).x);
return;
}

View File

@ -10,11 +10,29 @@ struct FragmentInput {
linear sample float perspective_sample : LOC6;
};
struct FragmentInput_main {
FragmentInput val1;
struct VertexOutput_main {
uint flat : LOC0;
float linear1 : LOC1;
float2 linear_centroid : LOC2;
float3 linear_sample : LOC3;
float4 perspective : LOC4;
float perspective_centroid : LOC5;
float perspective_sample : LOC6;
float4 position : SV_Position;
};
FragmentInput main()
struct FragmentInput_main {
uint flat : LOC0;
float linear2 : LOC1;
float2 linear_centroid : LOC2;
float3 linear_sample : LOC3;
float4 perspective : LOC4;
float perspective_centroid : LOC5;
float perspective_sample : LOC6;
float4 position : SV_Position;
};
VertexOutput_main main()
{
FragmentInput out1 = (FragmentInput)0;
@ -27,11 +45,13 @@ FragmentInput main()
out1.perspective_centroid = 2197.0;
out1.perspective_sample = 2744.0;
FragmentInput _expr30 = out1;
const FragmentInput fragmentinput1 = _expr30;
const FragmentInput fragmentinput = _expr30;
const VertexOutput_main fragmentinput1 = { fragmentinput.flat, fragmentinput.linear1, fragmentinput.linear_centroid, fragmentinput.linear_sample, fragmentinput.perspective, fragmentinput.perspective_centroid, fragmentinput.perspective_sample, fragmentinput.position };
return fragmentinput1;
}
void main1(FragmentInput_main fragmentinput_main)
{
FragmentInput val = { fragmentinput_main.position, fragmentinput_main.flat, fragmentinput_main.linear2, fragmentinput_main.linear_centroid, fragmentinput_main.linear_sample, fragmentinput_main.perspective, fragmentinput_main.perspective_centroid, fragmentinput_main.perspective_sample };
return;
}

View File

@ -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;
@ -19,9 +19,12 @@ static float2 a_uv1 = (float2)0;
static gl_PerVertex perVertexStruct = { float4(0.0, 0.0, 0.0, 1.0), 1.0, { 0.0 }, { 0.0 } };
static float2 a_pos1 = (float2)0;
struct VertexInput_main {
float2 a_pos2 : LOC0;
float2 a_uv2 : LOC1;
struct VertexOutput_main {
float2 member : LOC0;
float4 gl_Position : SV_Position;
float gl_ClipDistance : SV_ClipDistance;
float gl_CullDistance : SV_CullDistance;
float gl_PointSize : PSIZE;
};
void main1()
@ -33,16 +36,17 @@ void main1()
return;
}
type10 main(VertexInput_main vertexinput_main)
VertexOutput_main main(float2 a_uv : LOC1, float2 a_pos : LOC0)
{
a_uv1 = vertexinput_main.a_uv2;
a_pos1 = vertexinput_main.a_pos2;
a_uv1 = a_uv;
a_pos1 = a_pos;
main1();
float2 _expr10 = v_uv;
float4 _expr11 = perVertexStruct.gl_Position;
float _expr12 = perVertexStruct.gl_PointSize;
float _expr13[1] = perVertexStruct.gl_ClipDistance;
float _expr14[1] = perVertexStruct.gl_CullDistance;
const type10 type10_ = { _expr10, _expr11, _expr13, _expr14, _expr12 };
return type10_;
const type10 type10_ = { _expr10, _expr11, _expr12, _expr13, _expr14 };
const VertexOutput_main type10_1 = { type10_.member, type10_.gl_Position, type10_.gl_ClipDistance, type10_.gl_CullDistance, type10_.gl_PointSize };
return type10_1;
}

View File

@ -8,24 +8,26 @@ struct VertexOutput {
Texture2D<float4> u_texture : register(t0);
SamplerState u_sampler : register(s1);
struct VertexInput_main {
float2 pos1 : LOC0;
float2 uv2 : LOC1;
struct VertexOutput_main {
float2 uv2 : LOC0;
float4 position : SV_Position;
};
struct FragmentInput_main {
float2 uv3 : LOC0;
};
VertexOutput main(VertexInput_main vertexinput_main)
VertexOutput_main main(float2 pos : LOC0, float2 uv : LOC1)
{
const VertexOutput vertexoutput1 = { vertexinput_main.uv2, float4((c_scale * vertexinput_main.pos1), 0.0, 1.0) };
const VertexOutput vertexoutput = { uv, float4((c_scale * pos), 0.0, 1.0) };
const VertexOutput_main vertexoutput1 = { vertexoutput.uv, vertexoutput.position };
return vertexoutput1;
}
float4 main1(FragmentInput_main fragmentinput_main) : SV_Target0
{
float4 color = u_texture.Sample(u_sampler, fragmentinput_main.uv3);
float2 uv1 = fragmentinput_main.uv3;
float4 color = u_texture.Sample(u_sampler, uv1);
if ((color.w == 0.0)) {
discard;
}

View File

@ -34,10 +34,12 @@ float fetch_shadow(uint light_id, float4 homogeneous_coords)
float4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0
{
float3 raw_normal = fragmentinput_fs_main.raw_normal1;
float4 position = fragmentinput_fs_main.position1;
float3 color = float3(0.05, 0.05, 0.05);
uint i = 0u;
float3 normal = normalize(fragmentinput_fs_main.raw_normal1);
float3 normal = normalize(raw_normal);
bool loop_init = true;
while(true) {
if (!loop_init) {
@ -53,8 +55,8 @@ float4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0
uint _expr19 = i;
Light light = {float4x4(asfloat(s_lights.Load4(_expr19*96+0+0+0)), asfloat(s_lights.Load4(_expr19*96+0+0+16)), asfloat(s_lights.Load4(_expr19*96+0+0+32)), asfloat(s_lights.Load4(_expr19*96+0+0+48))), asfloat(s_lights.Load4(_expr19*96+0+64)), asfloat(s_lights.Load4(_expr19*96+0+80))};
uint _expr22 = i;
const float _e25 = fetch_shadow(_expr22, mul(fragmentinput_fs_main.position1, light.proj));
float3 light_dir = normalize((light.pos.xyz - fragmentinput_fs_main.position1.xyz));
const float _e25 = fetch_shadow(_expr22, mul(position, light.proj));
float3 light_dir = normalize((light.pos.xyz - position.xyz));
float diffuse = max(0.0, dot(normal, light_dir));
float3 _expr34 = color;
color = (_expr34 + ((_e25 * diffuse) * light.color.xyz));

View File

@ -19,21 +19,23 @@ cbuffer r_data : register(b0) { Data r_data; }
TextureCube<float4> r_texture : register(t0);
SamplerState r_sampler : register(s0, space1);
struct VertexInput_vs_main {
uint vertex_index1 : SV_VertexID;
struct VertexOutput_vs_main {
float3 uv : LOC0;
float4 position : SV_Position;
};
struct FragmentInput_fs_main {
VertexOutput in2;
float3 uv : LOC0;
float4 position : SV_Position;
};
VertexOutput vs_main(VertexInput_vs_main vertexinput_vs_main)
VertexOutput_vs_main vs_main(uint vertex_index : SV_VertexID)
{
int tmp1_ = (int)0;
int tmp2_ = (int)0;
tmp1_ = (int((_NagaConstants.base_vertex + vertexinput_vs_main.vertex_index1)) / 2);
tmp2_ = (int((_NagaConstants.base_vertex + vertexinput_vs_main.vertex_index1)) & 1);
tmp1_ = (int((_NagaConstants.base_vertex + vertex_index)) / 2);
tmp2_ = (int((_NagaConstants.base_vertex + vertex_index)) & 1);
int _expr10 = tmp1_;
int _expr16 = tmp2_;
float4 pos = float4(((float(_expr10) * 4.0) - 1.0), ((float(_expr16) * 4.0) - 1.0), 0.0, 1.0);
@ -43,12 +45,14 @@ VertexOutput vs_main(VertexInput_vs_main vertexinput_vs_main)
float3x3 inv_model_view = transpose(float3x3(_expr27.xyz, _expr31.xyz, _expr35.xyz));
float4x4 _expr40 = r_data.proj_inv;
float4 unprojected = mul(pos, _expr40);
const VertexOutput vertexoutput1 = { pos, mul(unprojected.xyz, inv_model_view) };
const VertexOutput vertexoutput = { pos, mul(unprojected.xyz, inv_model_view) };
const VertexOutput_vs_main vertexoutput1 = { vertexoutput.uv, vertexoutput.position };
return vertexoutput1;
}
float4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0
{
float4 _expr5 = r_texture.Sample(r_sampler, fragmentinput_fs_main.in2.uv);
VertexOutput in1 = { fragmentinput_fs_main.position, fragmentinput_fs_main.uv };
float4 _expr5 = r_texture.Sample(r_sampler, in1.uv);
return _expr5;
}

View File

@ -5,8 +5,9 @@ struct FragmentInput_derivatives {
float4 derivatives(FragmentInput_derivatives fragmentinput_derivatives) : SV_Target0
{
float4 x = ddx(fragmentinput_derivatives.foo1);
float4 y = ddy(fragmentinput_derivatives.foo1);
float4 z = fwidth(fragmentinput_derivatives.foo1);
float4 foo = fragmentinput_derivatives.foo1;
float4 x = ddx(foo);
float4 y = ddy(foo);
float4 z = fwidth(foo);
return ((x + y) * z);
}