Skip to content

Commit

Permalink
hlsl-out: Add support for push constants (gfx-rs#2005)
Browse files Browse the repository at this point in the history
Push constants need to be configured by the consumer which must pass the
bind target of the constant buffer used for the push constants.
  • Loading branch information
JCapucho authored Aug 29, 2022
1 parent e7ddd35 commit 9df243c
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 52 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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"; \
Expand All @@ -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"; \
Expand Down
3 changes: 3 additions & 0 deletions src/back/hlsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BindTarget>,
/// Bind target of the push constant buffer
pub push_constants_target: Option<BindTarget>,
}

impl Default for Options {
Expand All @@ -198,6 +200,7 @@ impl Default for Options {
binding_map: BindingMap::default(),
fake_missing_bindings: true,
special_constants_binding: None,
push_constants_target: None,
}
}
}
Expand Down
124 changes: 75 additions & 49 deletions src/back/hlsl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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<crate::Type>,
) -> 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
Expand Down
7 changes: 7 additions & 0 deletions tests/in/push-constants.param.ron
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
),
)
8 changes: 8 additions & 0 deletions tests/in/push-constants.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ struct FragmentIn {
@location(0) color: vec4<f32>
}

@vertex
fn vert_main(
@location(0) pos : vec2<f32>,
@builtin(vertex_index) vi: u32,
) -> @builtin(position) vec4<f32> {
return vec4<f32>(f32(vi) * pc.multiplier * pos, 0.0, 1.0);
}

@fragment
fn main(in: FragmentIn) -> @location(0) vec4<f32> {
return in.color * pc.multiplier;
Expand Down
23 changes: 23 additions & 0 deletions tests/out/glsl/push-constants.vert_main.Vertex.glsl
Original file line number Diff line number Diff line change
@@ -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;
}

33 changes: 33 additions & 0 deletions tests/out/hlsl/push-constants.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
struct NagaConstants {
int base_vertex;
int base_instance;
uint other;
};
ConstantBuffer<NagaConstants> _NagaConstants: register(b0, space1);

struct PushConstants {
float multiplier;
};

struct FragmentIn {
float4 color : LOC0;
};

ConstantBuffer<PushConstants> 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);
}
3 changes: 3 additions & 0 deletions tests/out/hlsl/push-constants.hlsl.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vertex=(vert_main:vs_5_1 )
fragment=(main:ps_5_1 )
compute=()
2 changes: 1 addition & 1 deletion tests/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 9df243c

Please sign in to comment.