hlsl: special constants for base vertex/index

This commit is contained in:
Dzmitry Malyshau
2021-07-26 21:05:44 -04:00
committed by Dzmitry Malyshau
parent d3243bd774
commit a7ac13a61d
21 changed files with 124 additions and 31 deletions

View File

@@ -92,14 +92,18 @@ pub struct Options {
pub binding_map: BindingMap,
/// Don't panic on missing bindings, instead generate any HLSL.
pub fake_missing_bindings: bool,
/// 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>,
}
impl Default for Options {
fn default() -> Self {
Options {
shader_model: ShaderModel::V5_0,
shader_model: ShaderModel::V5_1,
binding_map: BindingMap::default(),
fake_missing_bindings: true,
special_constants_binding: None,
}
}
}

View File

@@ -11,6 +11,10 @@ use crate::{
use std::fmt;
const LOCATION_SEMANTIC: &str = "LOC";
const SPECIAL_CBUF_TYPE: &str = "NagaConstants";
const SPECIAL_CBUF_VAR: &str = "_NagaConstants";
const SPECIAL_BASE_VERTEX: &str = "base_vertex";
const SPECIAL_BASE_INSTANCE: &str = "base_instance";
/// Structure contains information required for generating
/// wrapped structure of all entry points arguments
@@ -65,6 +69,23 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
) -> Result<super::ReflectionInfo, Error> {
self.reset(module);
// Write special constants, if needed
if let Some(ref bt) = self.options.special_constants_binding {
writeln!(self.out, "struct {} {{", SPECIAL_CBUF_TYPE)?;
writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_BASE_VERTEX)?;
writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_BASE_INSTANCE)?;
writeln!(self.out, "}};")?;
write!(
self.out,
"ConstantBuffer<{}> {}: register(b{}",
SPECIAL_CBUF_TYPE, SPECIAL_CBUF_VAR, bt.register
)?;
if bt.space != 0 {
write!(self.out, ", space{}", bt.space)?;
}
writeln!(self.out, ");")?;
}
// Write all constants
// For example, input wgsl shader:
// ```wgsl
@@ -410,7 +431,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
// this was already resolved earlier when we started evaluating an entry point.
let bt = self.options.resolve_resource_binding(binding).unwrap();
write!(self.out, " : register({}{}", register_ty, bt.register)?;
if self.options.shader_model > super::ShaderModel::V5_0 {
if bt.space != 0 {
write!(self.out, ", space{}", bt.space)?;
}
write!(self.out, ")")?;
@@ -1105,8 +1126,30 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
) -> BackendResult {
use crate::Expression;
// Handle the special semantics for base vertex/instance
let ff_input = if self.options.special_constants_binding.is_some() {
func_ctx.is_fixed_function_input(expr, module)
} else {
None
};
let closing_bracket = match ff_input {
Some(crate::BuiltIn::VertexIndex) => {
write!(self.out, "({}.{} + ", SPECIAL_CBUF_VAR, SPECIAL_BASE_VERTEX)?;
")"
}
Some(crate::BuiltIn::InstanceIndex) => {
write!(
self.out,
"({}.{} + ",
SPECIAL_CBUF_VAR, SPECIAL_BASE_INSTANCE
)?;
")"
}
_ => "",
};
if let Some(name) = self.named_expressions.get(&expr) {
write!(self.out, "{}", name)?;
write!(self.out, "{}{}", name, closing_bracket)?;
return Ok(());
}
@@ -1627,6 +1670,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
_ => return Err(Error::Unimplemented(format!("write_expr {:?}", expression))),
}
if !closing_bracket.is_empty() {
write!(self.out, "{}", closing_bracket)?;
}
Ok(())
}

View File

@@ -66,6 +66,43 @@ impl<'a> FunctionCtx<'_> {
}
}
}
// Returns true if the given expression points to a fixed-function pipeline input.
fn is_fixed_function_input(
&self,
mut expression: crate::Handle<crate::Expression>,
module: &crate::Module,
) -> Option<crate::BuiltIn> {
let ep_function = match self.ty {
FunctionType::Function(_) => return None,
FunctionType::EntryPoint(ep_index) => &module.entry_points[ep_index as usize].function,
};
let mut built_in = None;
loop {
match self.expressions[expression] {
crate::Expression::FunctionArgument(arg_index) => {
return match ep_function.arguments[arg_index as usize].binding {
Some(crate::Binding::BuiltIn(bi)) => Some(bi),
_ => built_in,
};
}
crate::Expression::AccessIndex { base, index } => {
match *self.info[base].ty.inner_with(&module.types) {
crate::TypeInner::Struct { ref members, .. } => {
if let Some(crate::Binding::BuiltIn(bi)) =
members[index as usize].binding
{
built_in = Some(bi);
}
}
_ => return None,
}
expression = base;
}
_ => return None,
}
}
}
}
/// How should code generated by Naga do indexing bounds checks?

View File

@@ -53,5 +53,6 @@
(group: 0, binding: 2): (space: 1, register: 0),
},
fake_missing_bindings: false,
special_constants_binding: Some((space: 0, register: 1)),
),
)

View File

@@ -1,3 +1,3 @@
vertex=(foo:vs_5_0 )
vertex=(foo:vs_5_1 )
fragment=()
compute=()

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=()
compute=(main:cs_5_0 )
compute=(main:cs_5_1 )

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=()
compute=(main:cs_5_0 )
compute=(main:cs_5_1 )

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=()
compute=(main:cs_5_0 )
compute=(main:cs_5_1 )

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=()
compute=(main:cs_5_0 )
compute=(main:cs_5_1 )

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=()
compute=(main:cs_5_0 )
compute=(main:cs_5_1 )

View File

@@ -11,9 +11,9 @@ TextureCube<float4> image_cube : register(t3);
TextureCubeArray<float4> image_cube_array : register(t4);
Texture3D<float4> image_3d : register(t5);
Texture2DMS<float4> image_aa : register(t6);
SamplerState sampler_reg : register(s0);
SamplerComparisonState sampler_cmp : register(s1);
Texture2D<float> image_2d_depth : register(t2);
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;

View File

@@ -1,3 +1,3 @@
vertex=(queries:vs_5_0 )
fragment=(sample1:ps_5_0 sample_comparison:ps_5_0 )
compute=(main:cs_5_0 )
vertex=(queries:vs_5_1 )
fragment=(sample1:ps_5_1 sample_comparison:ps_5_1 )
compute=(main:cs_5_1 )

View File

@@ -1,3 +1,3 @@
vertex=(vertex:vs_5_0 )
fragment=(fragment:ps_5_0 )
compute=(compute:cs_5_0 )
vertex=(vertex:vs_5_1 )
fragment=(fragment:ps_5_1 )
compute=(compute:cs_5_1 )

View File

@@ -1,3 +1,3 @@
vertex=(main:vs_5_0 )
fragment=(main1:ps_5_0 )
vertex=(main:vs_5_1 )
fragment=(main1:ps_5_1 )
compute=()

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=()
compute=(main:cs_5_0 )
compute=(main:cs_5_1 )

View File

@@ -1,3 +1,3 @@
vertex=(main:vs_5_0 )
vertex=(main:vs_5_1 )
fragment=()
compute=()

View File

@@ -1,3 +1,3 @@
vertex=(main:vs_5_0 )
fragment=(main1:ps_5_0 fs_extra:ps_5_0 )
vertex=(main:vs_5_1 )
fragment=(main1:ps_5_1 fs_extra:ps_5_1 )
compute=()

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=(fs_main:ps_5_0 )
fragment=(fs_main:ps_5_1 )
compute=()

View File

@@ -1,3 +1,8 @@
struct NagaConstants {
int base_vertex;
int base_instance;
};
ConstantBuffer<NagaConstants> _NagaConstants: register(b1);
struct VertexOutput {
float4 position : SV_Position;
@@ -9,8 +14,8 @@ struct Data {
float4x4 view;
};
cbuffer r_data : register(b0, space0) { Data r_data; }
TextureCube<float4> r_texture : register(t0, space0);
cbuffer r_data : register(b0) { Data r_data; }
TextureCube<float4> r_texture : register(t0);
SamplerState r_sampler : register(s0, space1);
struct VertexInput_vs_main {
@@ -26,8 +31,8 @@ VertexOutput vs_main(VertexInput_vs_main vertexinput_vs_main)
int tmp1_ = (int)0;
int tmp2_ = (int)0;
tmp1_ = (int(vertexinput_vs_main.vertex_index1) / 2);
tmp2_ = (int(vertexinput_vs_main.vertex_index1) & 1);
tmp1_ = (int((_NagaConstants.base_vertex + vertexinput_vs_main.vertex_index1)) / 2);
tmp2_ = (int((_NagaConstants.base_vertex + vertexinput_vs_main.vertex_index1)) & 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);

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=(derivatives:ps_5_0 )
fragment=(derivatives:ps_5_1 )
compute=()

View File

@@ -1,3 +1,3 @@
vertex=()
fragment=(main:ps_5_0 )
fragment=(main:ps_5_1 )
compute=()