diff --git a/src/back/hlsl/writer.rs b/src/back/hlsl/writer.rs index 019260339e..9dc899277f 100644 --- a/src/back/hlsl/writer.rs +++ b/src/back/hlsl/writer.rs @@ -5,7 +5,7 @@ use crate::{ back::{hlsl::keywords::RESERVED, vector_size_str}, proc::{EntryPointIndex, NameKey, Namer, TypeResolution}, valid::{FunctionInfo, ModuleInfo}, - Arena, BuiltIn, Bytes, Constant, ConstantInner, Expression, FastHashMap, Function, + Arena, ArraySize, BuiltIn, Bytes, Constant, ConstantInner, Expression, FastHashMap, Function, GlobalVariable, Handle, ImageDimension, LocalVariable, Module, ScalarKind, ScalarValue, ShaderStage, Statement, StructMember, Type, TypeInner, }; @@ -14,6 +14,7 @@ use std::fmt::Write; const INDENT: &str = " "; const COMPONENTS: &[char] = &['x', 'y', 'z', 'w']; const LOCATION_SEMANTIC: &str = "LOC"; +const BAKE_PREFIX: &str = "_e"; /// Shorthand result used internally by the backend type BackendResult = Result<(), Error>; @@ -119,16 +120,6 @@ impl<'a, W: Write> Writer<'a, W> { } } - // Write all globals - for (ty, _) in module.global_variables.iter() { - self.write_global(module, ty)?; - } - - if !module.global_variables.is_empty() { - // Add extra newline for readability - writeln!(self.out)?; - } - // Write all structs for (handle, ty) in module.types.iter() { if let TypeInner::Struct { @@ -142,6 +133,16 @@ impl<'a, W: Write> Writer<'a, W> { } } + // Write all globals + for (ty, _) in module.global_variables.iter() { + self.write_global(module, ty)?; + } + + if !module.global_variables.is_empty() { + // Add extra newline for readability + writeln!(self.out)?; + } + // Write all entry points wrapped structs for (index, ep) in module.entry_points.iter().enumerate() { self.write_ep_input_struct(module, &ep.function, ep.stage, index)?; @@ -276,27 +277,33 @@ impl<'a, W: Write> Writer<'a, W> { let global = &module.global_variables[handle]; let inner = &module.types[global.ty].inner; - let register_ty = match *inner { - TypeInner::Image { .. } => "t", - TypeInner::Sampler { .. } => "s", + let (storage_class, register_ty) = match *inner { + TypeInner::Image { .. } => ("", "t"), + TypeInner::Sampler { .. } => ("", "s"), + TypeInner::Struct { .. } | TypeInner::Vector { .. } => ("static ", ""), // TODO: other register ty https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-register _ => return Err(Error::Unimplemented(format!("register_ty {:?}", inner))), }; - let register = if let Some(ref binding) = global.binding { - format!("register({}{})", register_ty, binding.binding) - } else { - String::from("") - }; - - let name = self.names[&NameKey::GlobalVariable(handle)].clone(); + write!(self.out, "{}", storage_class)?; self.write_type(module, global.ty)?; - write!(self.out, " {}", name)?; + if let TypeInner::Array { size, .. } = module.types[global.ty].inner { + self.write_array_size(module, size)?; + } + write!( + self.out, + " {}", + &self.names[&NameKey::GlobalVariable(handle)] + )?; - if register.is_empty() { - writeln!(self.out, ";")?; + if let Some(ref binding) = global.binding { + writeln!(self.out, " : register({}{});", register_ty, binding.binding)?; } else { - writeln!(self.out, " : {};", register)?; + if let Some(init) = global.init { + write!(self.out, " = ")?; + self.write_constant(module, init)?; + } + writeln!(self.out, ";")?; } Ok(()) @@ -353,6 +360,28 @@ impl<'a, W: Write> Writer<'a, W> { Ok(()) } + // copy-paste from glsl-out + fn write_array_size(&mut self, module: &Module, size: ArraySize) -> BackendResult { + write!(self.out, "[")?; + + // Write the array size + // Writes nothing if `ArraySize::Dynamic` + // Panics if `ArraySize::Constant` has a constant that isn't an uint + match size { + ArraySize::Constant(const_handle) => match module.constants[const_handle].inner { + ConstantInner::Scalar { + width: _, + value: ScalarValue::Uint(size), + } => write!(self.out, "{}", size)?, + _ => unreachable!(), + }, + ArraySize::Dynamic => (), + } + + write!(self.out, "]")?; + Ok(()) + } + /// Helper method used to write structs /// /// # Notes @@ -371,10 +400,42 @@ impl<'a, W: Write> Writer<'a, W> { for (index, member) in members.iter().enumerate() { // The indentation is only for readability write!(self.out, "{}", INDENT)?; - // Write struct member type and name - self.write_type(module, member.ty)?; - let member_name = &self.names[&NameKey::StructMember(handle, index as u32)]; - write!(self.out, " {}", member_name)?; + + match module.types[member.ty].inner { + TypeInner::Array { + base, + size, + stride: _, + } => { + // HLSL arrays are written as `type name[size]` + let ty_name = match module.types[base].inner { + // Write scalar type by backend so as not to depend on the front-end implementation + // Name returned from frontend can be generated (type1, float1, etc.) + TypeInner::Scalar { kind, width } => scalar_kind_str(kind, width)?, + _ => &self.names[&NameKey::Type(base)], + }; + + // Write `type` and `name` + write!(self.out, "{}", ty_name)?; + write!( + self.out, + " {}", + &self.names[&NameKey::StructMember(handle, index as u32)] + )?; + // Write [size] + self.write_array_size(module, size)?; + } + _ => { + // Write the member type and name + self.write_type(module, member.ty)?; + write!( + self.out, + " {}", + &self.names[&NameKey::StructMember(handle, index as u32)] + )?; + } + } + if let Some(ref binding) = member.binding { self.write_binding(binding)?; }; @@ -394,6 +455,8 @@ impl<'a, W: Write> Writer<'a, W> { let inner = &module.types[ty].inner; match *inner { TypeInner::Struct { .. } => write!(self.out, "{}", self.names[&NameKey::Type(ty)])?, + // hlsl array has the size separated from the base type + TypeInner::Array { base, .. } => self.write_type(module, base)?, ref other => self.write_value_type(module, other)?, } @@ -404,7 +467,7 @@ impl<'a, W: Write> Writer<'a, W> { /// /// # Notes /// Adds no trailing or leading whitespace - fn write_value_type(&mut self, _module: &Module, inner: &TypeInner) -> BackendResult { + fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult { match *inner { TypeInner::Scalar { kind, width } => { write!(self.out, "{}", scalar_kind_str(kind, width)?)?; @@ -422,7 +485,7 @@ impl<'a, W: Write> Writer<'a, W> { rows, width, } => { - //TODO: int matrix ? + // The IR supports only float matrix // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-matrix write!( self.out, @@ -455,6 +518,12 @@ impl<'a, W: Write> Writer<'a, W> { TypeInner::Sampler { comparison: false } => { write!(self.out, "SamplerState")?; } + // HLSL arrays are written as `type name[size]` + // Current code is written arrays only as `[size]` + // Base `type` and `name` should be written outside + TypeInner::Array { size, .. } => { + self.write_array_size(module, size)?; + } _ => { return Err(Error::Unimplemented(format!( "write_value_type {:?}", @@ -701,6 +770,38 @@ impl<'a, W: Write> Writer<'a, W> { writeln!(self.out, ";")? } } + Statement::Store { pointer, value } => { + write!(self.out, "{}", INDENT.repeat(indent))?; + self.write_expr(module, pointer, func_ctx)?; + write!(self.out, " = ")?; + self.write_expr(module, value, func_ctx)?; + writeln!(self.out, ";")? + } + Statement::Call { + function, + ref arguments, + result, + } => { + write!(self.out, "{}", INDENT.repeat(indent))?; + if let Some(expr) = result { + let name = format!("{}{}", BAKE_PREFIX, expr.index()); + write!(self.out, "const {} = ", name)?; + self.write_expr(module, expr, func_ctx)?; + self.named_expressions.insert(expr, name); + writeln!(self.out, ";")? + } + let func_name = &self.names[&NameKey::Function(function)]; + write!(self.out, "{}(", func_name)?; + for (index, argument) in arguments.iter().enumerate() { + self.write_expr(module, *argument, func_ctx)?; + // Only write a comma if isn't the last element + if index != arguments.len().saturating_sub(1) { + // The leading space is for readability only + write!(self.out, ", ")?; + } + } + writeln!(self.out, ");")? + } _ => return Err(Error::Unimplemented(format!("write_stmt {:?}", stmt))), } @@ -838,6 +939,7 @@ impl<'a, W: Write> Writer<'a, W> { let name = &self.names[&NameKey::GlobalVariable(handle)]; write!(self.out, "{}", name)?; } + Expression::Load { pointer } => self.write_expr(module, pointer, func_ctx)?, _ => return Err(Error::Unimplemented(format!("write_expr {:?}", expression))), } @@ -861,11 +963,25 @@ impl<'a, W: Write> Writer<'a, W> { self.write_scalar_value(*value)?; } } - _ => { - return Err(Error::Unimplemented(format!( - "write_constant {:?}", - constant - ))) + crate::ConstantInner::Composite { ty, ref components } => { + let (open_b, close_b) = match module.types[ty].inner { + TypeInner::Struct { .. } => ("{ ", " }"), + _ => { + // We should write type only for non struct constants + self.write_type(module, ty)?; + ("(", ")") + } + }; + write!(self.out, "{}", open_b)?; + for (index, constant) in components.iter().enumerate() { + self.write_constant(module, *constant)?; + // Only write a comma if isn't the last element + if index != components.len().saturating_sub(1) { + // The leading space is for readability only + write!(self.out, ", ")?; + } + } + write!(self.out, "{}", close_b)?; } } @@ -914,14 +1030,12 @@ impl<'a, W: Write> Writer<'a, W> { let base_ty_res = &ctx.info[handle].ty; let resolved = base_ty_res.inner_with(&module.types); - // If rhs is a array type, we should write temp variable as a dynamic array - let array_str = if let TypeInner::Array { .. } = *resolved { - "[]" - } else { - "" - }; - - write!(self.out, " {}{} = ", name, array_str)?; + write!(self.out, " {}", name)?; + // If rhs is a array type, we should write array size + if let TypeInner::Array { size, .. } = *resolved { + self.write_array_size(module, size)?; + } + write!(self.out, " = ")?; self.write_expr(module, handle, ctx)?; writeln!(self.out, ";")?; self.named_expressions.insert(handle, name); diff --git a/tests/out/quad-vert.hlsl b/tests/out/quad-vert.hlsl new file mode 100644 index 0000000000..4c28ecfb60 --- /dev/null +++ b/tests/out/quad-vert.hlsl @@ -0,0 +1,47 @@ +struct gl_PerVertex { + float4 gl_Position : SV_Position; + float gl_PointSize : PSIZE; + float gl_ClipDistance[1] : SV_ClipDistance; + float gl_CullDistance[1] : SV_CullDistance; +}; + +struct type10 { + float2 member : LOC0; + float4 gl_Position : SV_Position; + float gl_PointSize : PSIZE; + float gl_ClipDistance[1] : SV_ClipDistance; + float gl_CullDistance[1] : SV_CullDistance; +}; + +static float2 v_uv; +static float2 a_uv1; +static gl_PerVertex perVertexStruct = { float4(0.0, 0.0, 0.0, 1.0), 1.0, float(0.0), float(0.0) }; +static float2 a_pos1; + +struct VertexInput { + float2 a_uv2 : LOC1; + float2 a_pos2 : LOC0; +}; + +void main1() +{ + float2 _expr12 = a_uv1; + v_uv = _expr12; + float2 _expr13 = a_pos1; + perVertexStruct.gl_Position = float4(_expr13.x, _expr13.y, 0.0, 1.0); + return; +} + +type10 vert_main(VertexInput vertexinput) +{ + a_uv1 = vertexinput.a_uv2; + a_pos1 = vertexinput.a_pos2; + 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, _expr12, _expr13, _expr14 }; + return type10_; +} diff --git a/tests/out/quad-vert.hlsl.config b/tests/out/quad-vert.hlsl.config new file mode 100644 index 0000000000..c016979838 --- /dev/null +++ b/tests/out/quad-vert.hlsl.config @@ -0,0 +1,2 @@ +vertex=vs_5_0 +vertex_name=vert_main diff --git a/tests/out/quad.hlsl b/tests/out/quad.hlsl index 81f661fc73..bb524e8358 100644 --- a/tests/out/quad.hlsl +++ b/tests/out/quad.hlsl @@ -1,13 +1,13 @@ static const float c_scale = 1.2; -Texture2D u_texture : register(t0); -SamplerState u_sampler : register(s1); - struct VertexOutput { float2 uv : LOC0; float4 position : SV_Position; }; +Texture2D u_texture : register(t0); +SamplerState u_sampler : register(s1); + struct VertexInput { float2 pos1 : LOC0; float2 uv2 : LOC1; diff --git a/tests/snapshots.rs b/tests/snapshots.rs index 9505f2f873..c6ebaab81d 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -358,7 +358,7 @@ fn convert_spv_quad_vert() { convert_spv( "quad-vert", false, - Targets::METAL | Targets::GLSL | Targets::WGSL, + Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL, ); }