Skip to content

Commit 4353db3

Browse files
authored
Make the location map run per entry point (microsoft#6688)
The code that adds the input and output decoration in the entry points inputs and outputs assumes that there is a single entry point in the module. When using the `lib` profile that is not true. This commit modifies the code so that it groups the stage variables by entry point, and runs the current code on each group separably. I hesitate to make this change because it will change the locations for code that currently works, and will force users to update their applications accordingly. Or they could modify their shaders to use explicit locations attributes. Neither is great. However, the advantage is that this allows the implicit locations to match what would happen if the shader were compiled individually. It also makes the locations more predictable because change in another shader would change all shader after it. This is a better design, and worth the breakage. Fixes microsoft#6678 Fixes microsoft#5213
1 parent 80f6e46 commit 4353db3

File tree

4 files changed

+130
-8
lines changed

4 files changed

+130
-8
lines changed

tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2143,10 +2143,8 @@ bool DeclResultIdMapper::assignLocations(
21432143
return true;
21442144
}
21452145

2146-
bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
2147-
if (!checkSemanticDuplication(forInput))
2148-
return false;
2149-
2146+
bool DeclResultIdMapper::finalizeStageIOLocationsForASingleEntryPoint(
2147+
bool forInput, ArrayRef<StageVar> functionStageVars) {
21502148
// Returns false if the given StageVar is an input/output variable without
21512149
// explicit location assignment. Otherwise, returns true.
21522150
const auto locAssigned = [forInput, this](const StageVar &v) {
@@ -2166,11 +2164,12 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
21662164

21672165
// If we have explicit location specified for all input/output variables,
21682166
// use them instead assign by ourselves.
2169-
if (std::all_of(stageVars.begin(), stageVars.end(), locAssigned)) {
2167+
if (std::all_of(functionStageVars.begin(), functionStageVars.end(),
2168+
locAssigned)) {
21702169
LocationSet locSet;
21712170
bool noError = true;
21722171

2173-
for (const auto &var : stageVars) {
2172+
for (const auto &var : functionStageVars) {
21742173
// Skip builtins & those stage variables we are not handling for this call
21752174
if (var.isSpirvBuitin() || var.hasLocOrBuiltinDecorateAttr() ||
21762175
forInput != isInputStorageClass(var)) {
@@ -2189,7 +2188,7 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
21892188
emitError("stage %select{output|input}0 location #%1 already "
21902189
"consumed by semantic '%2'",
21912190
attrLoc)
2192-
<< forInput << l << stageVars[idx].getSemanticStr();
2191+
<< forInput << l << functionStageVars[idx].getSemanticStr();
21932192
noError = false;
21942193
}
21952194

@@ -2213,7 +2212,7 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
22132212
std::vector<const StageVar *> vars;
22142213
LocationSet locSet;
22152214

2216-
for (const auto &var : stageVars) {
2215+
for (const auto &var : functionStageVars) {
22172216
if (var.isSpirvBuitin() || var.hasLocOrBuiltinDecorateAttr() ||
22182217
forInput != isInputStorageClass(var)) {
22192218
continue;
@@ -2286,6 +2285,29 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
22862285
return assignLocations(vars, nextLocs, &stageVariableLocationInfo);
22872286
}
22882287

2288+
llvm::DenseMap<const SpirvFunction *, SmallVector<StageVar, 8>>
2289+
DeclResultIdMapper::getStageVarsPerFunction() {
2290+
llvm::DenseMap<const SpirvFunction *, SmallVector<StageVar, 8>> result;
2291+
for (const auto &var : stageVars) {
2292+
result[var.getEntryPoint()].push_back(var);
2293+
}
2294+
return result;
2295+
}
2296+
2297+
bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
2298+
if (!checkSemanticDuplication(forInput))
2299+
return false;
2300+
2301+
auto stageVarPerFunction = getStageVarsPerFunction();
2302+
for (const auto &functionStageVars : stageVarPerFunction) {
2303+
if (!finalizeStageIOLocationsForASingleEntryPoint(
2304+
forInput, functionStageVars.getSecond())) {
2305+
return false;
2306+
}
2307+
}
2308+
return true;
2309+
}
2310+
22892311
namespace {
22902312
/// A class for maintaining the binding number shift requested for descriptor
22912313
/// sets.

tools/clang/lib/SPIRV/DeclResultIdMapper.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,22 @@ class DeclResultIdMapper {
627627
llvm::DenseSet<StageVariableLocationInfo, StageVariableLocationInfo>
628628
*stageVariableLocationInfo);
629629

630+
/// \brief Returns a map that divides all of the shader stage variables into
631+
/// separate vectors for each entry point.
632+
llvm::DenseMap<const SpirvFunction *, SmallVector<StageVar, 8>>
633+
getStageVarsPerFunction();
634+
635+
/// \brief Decorates all stage variables in `functionStageVars` with proper
636+
/// location and returns true on success.
637+
///
638+
/// It is assumed that all variables in `functionStageVars` belong to the same
639+
/// entry point.
640+
///
641+
/// This method will write the location assignment into the module under
642+
/// construction.
643+
bool finalizeStageIOLocationsForASingleEntryPoint(
644+
bool forInput, ArrayRef<StageVar> functionStageVars);
645+
630646
/// \brief Decorates all stage input (if forInput is true) or output (if
631647
/// forInput is false) variables with proper location and returns true on
632648
/// success.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %dxc -T lib_6_6 %s -spirv | FileCheck %s
2+
3+
4+
// The inputs to the vertex shader should be at location 0 and 1 to match
5+
// the location attributes.
6+
// CHECK-DAG: OpDecorate %in_var_Position Location 0
7+
// CHECK-DAG: OpDecorate %in_var_Color Location 1
8+
struct Vertex {
9+
[[vk::location(0)]] float3 position : Position;
10+
[[vk::location(1)]] float4 color : Color;
11+
};
12+
13+
// The "color" output to the vertex shader should match the "color" input to the
14+
// pixel shader. The should both be at location 1 to match the attribute.
15+
// CHECK-DAG: OpDecorate %out_var_Color Location 1
16+
// CHECK-DAG: OpDecorate %in_var_Color_0 Location 1
17+
struct Fragment {
18+
[[vk::location(0)]] float4 sv_position : SV_Position;
19+
[[vk::location(1)]] float4 color : Color;
20+
};
21+
22+
[shader("vertex")]
23+
Fragment vsmain(in Vertex vertex) {
24+
Fragment o;
25+
26+
o.sv_position = float4(vertex.position, 1);
27+
o.color = vertex.color;
28+
29+
return o;
30+
}
31+
32+
// This location should set the location of the pixel shader output to location 0.
33+
// CHECK-DAG: OpDecorate %out_var_SV_Target0 Location 0
34+
[[vk::location(0)]]
35+
[shader("pixel")]
36+
float4 psmain(in Fragment fragment) : SV_Target0 {
37+
return fragment.color;
38+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %dxc -T lib_6_6 %s -spirv | FileCheck %s
2+
3+
// The inputs for the vertex shader are 0 and 1. vId is a builtin and does not
4+
// get a location.
5+
// CHECK-DAG: OpDecorate %in_var_COLOR0 Location 0
6+
// CHECK-DAG: OpDecorate %in_var_COLOR1 Location 1
7+
struct VIN {
8+
uint vId : SV_VertexID;
9+
float2 col0 : COLOR0;
10+
float2 col1 : COLOR1;
11+
};
12+
13+
// The output for the vertex shader and the input for the pixel shader should
14+
// both be 0. Position is a builtin, and each shader should restart their
15+
// numbering at 0.
16+
// CHECK-DAG: OpDecorate %in_var_COLOR2 Location 0
17+
// CHECK-DAG: OpDecorate %out_var_COLOR2 Location 0
18+
struct V2P
19+
{
20+
float4 Pos : SV_Position;
21+
float2 Uv : COLOR2;
22+
};
23+
24+
#define PI (3.14159f)
25+
26+
[shader("vertex")]
27+
V2P VSMain(VIN vIn)
28+
{
29+
float2 uv = vIn.col0 + vIn.col1;
30+
V2P vsOut;
31+
vsOut.Uv = float2(uv.x, 1.0 - uv.y);
32+
vsOut.Pos = float4((2.0 * uv) - 1.0, 0.0, 1.0);
33+
return vsOut;
34+
}
35+
36+
[[vk::binding(0)]] Texture2D<float3> source_tex;
37+
[[vk::binding(1)]] SamplerState samp;
38+
39+
// The location for the sole output from the pixel shader should be 0. The
40+
// numbering should restart at 0 for each shader.
41+
// CHECK-DAG: OpDecorate %out_var_SV_Target0 Location 0
42+
[shader("pixel")]
43+
float4 PSMain(V2P psIn) : SV_Target0
44+
{
45+
return float4(source_tex.Sample(samp, psIn.Uv), 1.0);
46+
}

0 commit comments

Comments
 (0)