diff --git a/Makefile b/Makefile index a491722af2..de27e30493 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ validate-wgsl: $(SNAPSHOTS_BASE_OUT)/wgsl/*.wgsl cargo run $${file}; \ done -validate-hlsl-dxc: SHELL:=/bin/bash # required because config files uses arrays +validate-hlsl-dxc: SHELL:=/usr/bin/env bash # required because config files uses arrays validate-hlsl-dxc: $(SNAPSHOTS_BASE_OUT)/hlsl/*.hlsl @set -e && for file in $^ ; do \ DXC_PARAMS="-Wno-parentheses-equality -Zi -Qembed_debug -Od"; \ @@ -94,7 +94,7 @@ validate-hlsl-dxc: $(SNAPSHOTS_BASE_OUT)/hlsl/*.hlsl echo "======================"; \ done -validate-hlsl-fxc: SHELL:=/bin/bash # required because config files uses arrays +validate-hlsl-fxc: SHELL:=/usr/bin/env bash # required because config files uses arrays validate-hlsl-fxc: $(SNAPSHOTS_BASE_OUT)/hlsl/*.hlsl @set -e && for file in $^ ; do \ FXC_PARAMS="-Zi -Od"; \ diff --git a/src/back/hlsl/mod.rs b/src/back/hlsl/mod.rs index 76abb29783..333ea2cf1a 100644 --- a/src/back/hlsl/mod.rs +++ b/src/back/hlsl/mod.rs @@ -189,6 +189,8 @@ pub struct Options { /// Add special constants to `SV_VertexIndex` and `SV_InstanceIndex`, /// to make them work like in Vulkan/Metal, with help of the host. pub special_constants_binding: Option, + /// Bind target of the push constant buffer + pub push_constants_target: Option, } impl Default for Options { @@ -198,6 +200,7 @@ impl Default for Options { binding_map: BindingMap::default(), fake_missing_bindings: true, special_constants_binding: None, + push_constants_target: None, } } } diff --git a/src/back/hlsl/writer.rs b/src/back/hlsl/writer.rs index ae761ead4f..42e3060798 100644 --- a/src/back/hlsl/writer.rs +++ b/src/back/hlsl/writer.rs @@ -623,12 +623,45 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { self.write_type(module, global.ty)?; register } - crate::AddressSpace::PushConstant => unimplemented!("Push constants"), + crate::AddressSpace::PushConstant => { + // The type of the push constants will be wrapped in `ConstantBuffer` + write!(self.out, "ConstantBuffer<")?; + "b" + } }; + // If the global is a push constant write the type now because it will be a + // generic argument to `ConstantBuffer` + if global.space == crate::AddressSpace::PushConstant { + self.write_global_type(module, global.ty)?; + + // need to write the array size if the type was emitted with `write_type` + if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner { + self.write_array_size(module, base, size)?; + } + + // Close the angled brackets for the generic argument + write!(self.out, ">")?; + } + let name = &self.names[&NameKey::GlobalVariable(handle)]; write!(self.out, " {}", name)?; + // Push constants need to be assigned a binding explicitly by the consumer + // since naga has no way to know the binding from the shader alone + if global.space == crate::AddressSpace::PushConstant { + let target = self + .options + .push_constants_target + .as_ref() + .expect("No bind target was defined for the push constants block"); + write!(self.out, ": register(b{}", target.register)?; + if target.space != 0 { + write!(self.out, ", space{}", target.space)?; + } + write!(self.out, ")")?; + } + if let Some(ref binding) = global.binding { // this was already resolved earlier when we started evaluating an entry point. let bt = self.options.resolve_resource_binding(binding).unwrap(); @@ -665,34 +698,13 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { if global.space == crate::AddressSpace::Uniform { write!(self.out, " {{ ")?; - let matrix_data = get_inner_matrix_data(module, global.ty); - - // We treat matrices of the form `matCx2` as a sequence of C `vec2`s. - // See the module-level block comment in mod.rs for details. - if let Some(MatrixType { - columns, - rows: crate::VectorSize::Bi, - width: 4, - }) = matrix_data - { - write!( - self.out, - "__mat{}x2 {}", - columns as u8, - &self.names[&NameKey::GlobalVariable(handle)] - )?; - } else { - // Even though Naga IR matrices are column-major, we must describe - // matrices passed from the CPU as being in row-major order. - // See the module-level block comment in mod.rs for details. - if matrix_data.is_some() { - write!(self.out, "row_major ")?; - } + self.write_global_type(module, global.ty)?; - self.write_type(module, global.ty)?; - let sub_name = &self.names[&NameKey::GlobalVariable(handle)]; - write!(self.out, " {}", sub_name)?; - } + write!( + self.out, + " {}", + &self.names[&NameKey::GlobalVariable(handle)] + )?; // need to write the array size if the type was emitted with `write_type` if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner { @@ -829,27 +841,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { TypeInner::Array { base, size, .. } => { // HLSL arrays are written as `type name[size]` - let matrix_data = get_inner_matrix_data(module, member.ty); - - // We treat matrices of the form `matCx2` as a sequence of C `vec2`s. - // See the module-level block comment in mod.rs for details. - if let Some(MatrixType { - columns, - rows: crate::VectorSize::Bi, - width: 4, - }) = matrix_data - { - write!(self.out, "__mat{}x2", columns as u8)?; - } else { - // Even though Naga IR matrices are column-major, we must describe - // matrices passed from the CPU as being in row-major order. - // See the module-level block comment in mod.rs for details. - if matrix_data.is_some() { - write!(self.out, "row_major ")?; - } - - self.write_type(module, base)?; - } + self.write_global_type(module, member.ty)?; // Write `name` write!( @@ -923,6 +915,40 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { Ok(()) } + /// Helper method used to write global/structs non image/sampler types + /// + /// # Notes + /// Adds no trailing or leading whitespace + pub(super) fn write_global_type( + &mut self, + module: &Module, + ty: Handle, + ) -> BackendResult { + let matrix_data = get_inner_matrix_data(module, ty); + + // We treat matrices of the form `matCx2` as a sequence of C `vec2`s. + // See the module-level block comment in mod.rs for details. + if let Some(MatrixType { + columns, + rows: crate::VectorSize::Bi, + width: 4, + }) = matrix_data + { + write!(self.out, "__mat{}x2", columns as u8)?; + } else { + // Even though Naga IR matrices are column-major, we must describe + // matrices passed from the CPU as being in row-major order. + // See the module-level block comment in mod.rs for details. + if matrix_data.is_some() { + write!(self.out, "row_major ")?; + } + + self.write_type(module, ty)?; + } + + Ok(()) + } + /// Helper method used to write non image/sampler types /// /// # Notes diff --git a/tests/in/push-constants.param.ron b/tests/in/push-constants.param.ron index 3972328c9c..a5cfe142c4 100644 --- a/tests/in/push-constants.param.ron +++ b/tests/in/push-constants.param.ron @@ -8,4 +8,11 @@ writer_flags: (bits: 0), binding_map: {}, ), + hlsl: ( + shader_model: V5_1, + binding_map: {}, + fake_missing_bindings: true, + special_constants_binding: Some((space: 1, register: 0)), + push_constants_target: Some((space: 0, register: 0)), + ), ) diff --git a/tests/in/push-constants.wgsl b/tests/in/push-constants.wgsl index cc8c4ab021..b3dc515230 100644 --- a/tests/in/push-constants.wgsl +++ b/tests/in/push-constants.wgsl @@ -7,6 +7,14 @@ struct FragmentIn { @location(0) color: vec4 } +@vertex +fn vert_main( + @location(0) pos : vec2, + @builtin(vertex_index) vi: u32, +) -> @builtin(position) vec4 { + return vec4(f32(vi) * pc.multiplier * pos, 0.0, 1.0); +} + @fragment fn main(in: FragmentIn) -> @location(0) vec4 { return in.color * pc.multiplier; diff --git a/tests/out/glsl/push-constants.vert_main.Vertex.glsl b/tests/out/glsl/push-constants.vert_main.Vertex.glsl new file mode 100644 index 0000000000..27cd7037ab --- /dev/null +++ b/tests/out/glsl/push-constants.vert_main.Vertex.glsl @@ -0,0 +1,23 @@ +#version 320 es + +precision highp float; +precision highp int; + +struct PushConstants { + float multiplier; +}; +struct FragmentIn { + vec4 color; +}; +uniform PushConstants pc; + +layout(location = 0) in vec2 _p2vs_location0; + +void main() { + vec2 pos = _p2vs_location0; + uint vi = uint(gl_VertexID); + float _e5 = pc.multiplier; + gl_Position = vec4(((float(vi) * _e5) * pos), 0.0, 1.0); + return; +} + diff --git a/tests/out/hlsl/push-constants.hlsl b/tests/out/hlsl/push-constants.hlsl new file mode 100644 index 0000000000..22b4b6acdf --- /dev/null +++ b/tests/out/hlsl/push-constants.hlsl @@ -0,0 +1,33 @@ +struct NagaConstants { + int base_vertex; + int base_instance; + uint other; +}; +ConstantBuffer _NagaConstants: register(b0, space1); + +struct PushConstants { + float multiplier; +}; + +struct FragmentIn { + float4 color : LOC0; +}; + +ConstantBuffer pc: register(b0); + +struct FragmentInput_main { + float4 color : LOC0; +}; + +float4 vert_main(float2 pos : LOC0, uint vi : SV_VertexID) : SV_Position +{ + float _expr5 = pc.multiplier; + return float4(((float((_NagaConstants.base_vertex + vi)) * _expr5) * pos), 0.0, 1.0); +} + +float4 main(FragmentInput_main fragmentinput_main) : SV_Target0 +{ + FragmentIn in_ = { fragmentinput_main.color }; + float _expr4 = pc.multiplier; + return (in_.color * _expr4); +} diff --git a/tests/out/hlsl/push-constants.hlsl.config b/tests/out/hlsl/push-constants.hlsl.config new file mode 100644 index 0000000000..f214655f7c --- /dev/null +++ b/tests/out/hlsl/push-constants.hlsl.config @@ -0,0 +1,3 @@ +vertex=(vert_main:vs_5_1 ) +fragment=(main:ps_5_1 ) +compute=() diff --git a/tests/snapshots.rs b/tests/snapshots.rs index d7837e34f0..fa32580e3b 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -471,7 +471,7 @@ fn convert_wgsl() { Targets::SPIRV | Targets::METAL | Targets::HLSL | Targets::WGSL | Targets::GLSL, ), ("extra", Targets::SPIRV | Targets::METAL | Targets::WGSL), - ("push-constants", Targets::GLSL), + ("push-constants", Targets::GLSL | Targets::HLSL), ( "operators", Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,