Skip to content

Commit 97af068

Browse files
authored
[SPIR-V] Add -fvk-bind-*-heap flag to DXC (microsoft#6919)
Those flags allow the user to hardcode the binding ID for the heaps. This allow the user to either force heap aliasing, or simply have more control over the heaps. Fixes microsoft#6913 --------- Signed-off-by: Nathan Gauër <[email protected]>
1 parent 9bfbee6 commit 97af068

18 files changed

+277
-19
lines changed

include/dxc/Support/HLSLOptions.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,12 @@ def fspv_preserve_interface : Flag<["-"], "fspv-preserve-interface">, Group<spir
416416
HelpText<"Preserves all interface variables in the entry point, even when those variables are unused">;
417417
def fspv_max_id : MultiArg<["-"], "fspv-max-id", 1>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
418418
HelpText<"Set the maximum value for an id in the SPIR-V binary. Default is 0x3FFFFF, which is the largest value all drivers must support.">;
419+
def fvk_bind_resource_heap : MultiArg<["-"], "fvk-bind-resource-heap", 2>, MetaVarName<"<binding> <set>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
420+
HelpText<"Specify Vulkan binding number and set number for the resource heap.">;
421+
def fvk_bind_sampler_heap : MultiArg<["-"], "fvk-bind-sampler-heap", 2>, MetaVarName<"<binding> <set>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
422+
HelpText<"Specify Vulkan binding number and set number for the sampler heap.">;
423+
def fvk_bind_counter_heap : MultiArg<["-"], "fvk-bind-counter-heap", 2>, MetaVarName<"<binding> <set>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
424+
HelpText<"Specify Vulkan binding number and set number for the counter heap.">;
419425
// SPIRV Change Ends
420426

421427
//////////////////////////////////////////////////////////////////////////////

include/dxc/Support/SPIRVOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "llvm/ADT/StringRef.h"
2121
#include "llvm/Option/ArgList.h"
2222

23+
#include <optional>
24+
2325
namespace clang {
2426
namespace spirv {
2527

@@ -97,6 +99,15 @@ struct SpirvCodeGenOptions {
9799
std::string entrypointName;
98100
std::string floatDenormalMode; // OPT_denorm
99101

102+
// User-defined bindings/set numbers for resource/sampler/counter heaps.
103+
struct BindingInfo {
104+
size_t binding;
105+
size_t set;
106+
};
107+
std::optional<BindingInfo> resourceHeapBinding;
108+
std::optional<BindingInfo> samplerHeapBinding;
109+
std::optional<BindingInfo> counterHeapBinding;
110+
100111
bool signaturePacking; ///< Whether signature packing is enabled or not
101112

102113
bool printAll; // Dump SPIR-V module before each pass and after the last one.

lib/DxcSupport/HLSLOptions.cpp

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@
2525
#include "llvm/Support/Path.h"
2626
#include "llvm/Support/raw_ostream.h"
2727

28+
#include <optional>
29+
2830
using namespace llvm::opt;
2931
using namespace dxc;
3032
using namespace hlsl;
3133
using namespace hlsl::options;
34+
using namespace clang::spirv;
3235

3336
#define PREFIX(NAME, VALUE) static const char *const NAME[] = VALUE;
3437
#include "dxc/Support/HLSLOptions.inc"
@@ -320,6 +323,46 @@ static bool handleVkShiftArgs(const InputArgList &args, OptSpecifier id,
320323
return true;
321324
}
322325

326+
// Parses the given flag |id| in |args|. If present and valid, sets |info| to
327+
// the correct value. Returns true if parsing succeeded. Returns false if
328+
// parsing failed, and outputs in |errors| a message using |name| as pretty name
329+
// for the flag.
330+
static bool
331+
handleFixedBinding(const InputArgList &args, OptSpecifier id,
332+
std::optional<SpirvCodeGenOptions::BindingInfo> *info,
333+
llvm::StringRef name, llvm::raw_ostream &errors) {
334+
const auto values = args.getAllArgValues(id);
335+
if (values.size() == 0) {
336+
*info = std::nullopt;
337+
return true;
338+
}
339+
340+
if (!args.hasArg(OPT_spirv)) {
341+
errors << name << " requires -spirv";
342+
return false;
343+
}
344+
345+
assert(values.size() == 2);
346+
347+
size_t output[2] = {0, 0};
348+
for (unsigned i = 0; i < 2; ++i) {
349+
int number = 0;
350+
if (llvm::StringRef(values[i]).getAsInteger(10, number)) {
351+
errors << "invalid " << name << " argument: '" << values[i] << "'";
352+
return false;
353+
}
354+
if (number < 0) {
355+
errors << "expected positive integer for " << name
356+
<< ", got: " << values[i];
357+
return false;
358+
}
359+
output[i] = number;
360+
}
361+
362+
*info = {output[0], output[1]};
363+
return true;
364+
}
365+
323366
// Check if any options that are unsupported with SPIR-V are used.
324367
static bool hasUnsupportedSpirvOption(const InputArgList &args,
325368
llvm::raw_ostream &errors) {
@@ -1109,6 +1152,18 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
11091152
return 1;
11101153
}
11111154

1155+
if (!handleFixedBinding(Args, OPT_fvk_bind_resource_heap,
1156+
&opts.SpirvOptions.resourceHeapBinding,
1157+
"-fvk-bind-resource-heap", errors) ||
1158+
!handleFixedBinding(Args, OPT_fvk_bind_sampler_heap,
1159+
&opts.SpirvOptions.samplerHeapBinding,
1160+
"-fvk-bind-sampler-heap", errors) ||
1161+
!handleFixedBinding(Args, OPT_fvk_bind_counter_heap,
1162+
&opts.SpirvOptions.counterHeapBinding,
1163+
"-fvk-bind-counter-heap", errors)) {
1164+
return 1;
1165+
}
1166+
11121167
for (const Arg *A : Args.filtered(OPT_fspv_extension_EQ)) {
11131168
opts.SpirvOptions.allowedExtensions.push_back(A->getValue());
11141169
}
@@ -1245,7 +1300,10 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
12451300
!Args.getLastArgValue(OPT_fvk_b_shift).empty() ||
12461301
!Args.getLastArgValue(OPT_fvk_t_shift).empty() ||
12471302
!Args.getLastArgValue(OPT_fvk_s_shift).empty() ||
1248-
!Args.getLastArgValue(OPT_fvk_u_shift).empty()) {
1303+
!Args.getLastArgValue(OPT_fvk_u_shift).empty() ||
1304+
!Args.getLastArgValue(OPT_fvk_bind_resource_heap).empty() ||
1305+
!Args.getLastArgValue(OPT_fvk_bind_sampler_heap).empty() ||
1306+
!Args.getLastArgValue(OPT_fvk_bind_counter_heap).empty()) {
12491307
errors << "SPIR-V CodeGen not available. "
12501308
"Please recompile with -DENABLE_SPIRV_CODEGEN=ON.";
12511309
return 1;

tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "dxc/DXIL/DxilConstants.h"
1717
#include "dxc/DXIL/DxilTypeSystem.h"
18+
#include "dxc/Support/SPIRVOptions.h"
1819
#include "clang/AST/Expr.h"
1920
#include "clang/AST/HlslTypes.h"
2021
#include "clang/SPIRV/AstTypeProbe.h"
@@ -2506,6 +2507,17 @@ bool DeclResultIdMapper::decorateResourceBindings() {
25062507

25072508
BindingSet bindingSet;
25082509

2510+
// If some bindings are reserved for heaps, mark those are used.
2511+
if (spirvOptions.resourceHeapBinding)
2512+
bindingSet.useBinding(spirvOptions.resourceHeapBinding->binding,
2513+
spirvOptions.resourceHeapBinding->set);
2514+
if (spirvOptions.samplerHeapBinding)
2515+
bindingSet.useBinding(spirvOptions.samplerHeapBinding->binding,
2516+
spirvOptions.samplerHeapBinding->set);
2517+
if (spirvOptions.counterHeapBinding)
2518+
bindingSet.useBinding(spirvOptions.counterHeapBinding->binding,
2519+
spirvOptions.counterHeapBinding->set);
2520+
25092521
// Decorates the given varId of the given category with set number
25102522
// setNo, binding number bindingNo. Ignores overlaps.
25112523
const auto tryToDecorate = [this, &bindingSet](const ResourceVar &var,
@@ -2698,6 +2710,15 @@ bool DeclResultIdMapper::decorateResourceBindings() {
26982710
return true;
26992711
}
27002712

2713+
SpirvCodeGenOptions::BindingInfo DeclResultIdMapper::getBindingInfo(
2714+
BindingSet &bindingSet,
2715+
const std::optional<SpirvCodeGenOptions::BindingInfo> &userProvidedInfo) {
2716+
if (userProvidedInfo.has_value()) {
2717+
return *userProvidedInfo;
2718+
}
2719+
return {bindingSet.useNextBinding(0), /* set= */ 0};
2720+
}
2721+
27012722
void DeclResultIdMapper::decorateResourceHeapsBindings(BindingSet &bindingSet) {
27022723
bool hasResource = false;
27032724
bool hasSamplers = false;
@@ -2725,12 +2746,21 @@ void DeclResultIdMapper::decorateResourceHeapsBindings(BindingSet &bindingSet) {
27252746
// Allocate bindings only for used resources. The order of this allocation is
27262747
// important:
27272748
// - First resource heaps, then sampler heaps, and finally counter heaps.
2728-
const uint32_t resourceBinding =
2729-
hasResource ? bindingSet.useNextBinding(0) : 0;
2730-
const uint32_t samplersBinding =
2731-
hasSamplers ? bindingSet.useNextBinding(0) : 0;
2732-
const uint32_t countersBinding =
2733-
hasCounters ? bindingSet.useNextBinding(0) : 0;
2749+
SpirvCodeGenOptions::BindingInfo resourceBinding = {/* binding= */ 0,
2750+
/* set= */ 0};
2751+
SpirvCodeGenOptions::BindingInfo samplersBinding = {/* binding= */ 0,
2752+
/* set= */ 0};
2753+
SpirvCodeGenOptions::BindingInfo countersBinding = {/* binding= */ 0,
2754+
/* set= */ 0};
2755+
if (hasResource)
2756+
resourceBinding =
2757+
getBindingInfo(bindingSet, spirvOptions.resourceHeapBinding);
2758+
if (hasSamplers)
2759+
samplersBinding =
2760+
getBindingInfo(bindingSet, spirvOptions.samplerHeapBinding);
2761+
if (hasCounters)
2762+
countersBinding =
2763+
getBindingInfo(bindingSet, spirvOptions.counterHeapBinding);
27342764

27352765
for (const auto &var : resourceVars) {
27362766
if (!var.getDeclaration())
@@ -2739,13 +2769,14 @@ void DeclResultIdMapper::decorateResourceHeapsBindings(BindingSet &bindingSet) {
27392769
if (!decl)
27402770
continue;
27412771

2742-
if (isResourceDescriptorHeap(decl->getType()))
2743-
spvBuilder.decorateDSetBinding(var.getSpirvInstr(), /* set= */ 0,
2744-
var.isCounter() ? countersBinding
2745-
: resourceBinding);
2746-
else if (isSamplerDescriptorHeap(decl->getType()))
2747-
spvBuilder.decorateDSetBinding(var.getSpirvInstr(), /* set= */ 0,
2748-
samplersBinding);
2772+
const bool isResourceHeap = isResourceDescriptorHeap(decl->getType());
2773+
const bool isSamplerHeap = isSamplerDescriptorHeap(decl->getType());
2774+
if (!isSamplerHeap && !isResourceHeap)
2775+
continue;
2776+
const SpirvCodeGenOptions::BindingInfo &info =
2777+
isSamplerHeap ? samplersBinding
2778+
: (var.isCounter() ? countersBinding : resourceBinding);
2779+
spvBuilder.decorateDSetBinding(var.getSpirvInstr(), info.set, info.binding);
27492780
}
27502781
}
27512782

tools/clang/lib/SPIRV/DeclResultIdMapper.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,13 @@ class DeclResultIdMapper {
643643
llvm::DenseSet<StageVariableLocationInfo, StageVariableLocationInfo>
644644
*stageVariableLocationInfo);
645645

646-
/// \bried Decorates used Resource/Sampler descriptor heaps with the correct
646+
/// \brief Get a valid BindingInfo. If no user provided binding info is given,
647+
/// allocates a new binding and returns it.
648+
static SpirvCodeGenOptions::BindingInfo getBindingInfo(
649+
BindingSet &bindingSet,
650+
const std::optional<SpirvCodeGenOptions::BindingInfo> &userProvidedInfo);
651+
652+
/// \brief Decorates used Resource/Sampler descriptor heaps with the correct
647653
/// binding/set decorations.
648654
void decorateResourceHeapsBindings(BindingSet &bindingSet);
649655

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %dxc -T ps_6_6 -spirv %s -fvk-bind-sampler-heap 4 7 -fvk-bind-resource-heap 4 7 | FileCheck %s
2+
3+
// CHECK-DAG: OpDecorate %ResourceDescriptorHeap DescriptorSet 7
4+
// CHECK-DAG: OpDecorate %ResourceDescriptorHeap Binding 4
5+
// CHECK-DAG: OpDecorate %SamplerDescriptorHeap DescriptorSet 7
6+
// CHECK-DAG: OpDecorate %SamplerDescriptorHeap Binding 4
7+
8+
float4 main() : SV_Target {
9+
SamplerState Sampler = SamplerDescriptorHeap[2];
10+
Texture2D Texture = ResourceDescriptorHeap[3];
11+
return Texture.Sample(Sampler, float2(0, 0));
12+
}
13+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-sampler-heap a 2 | FileCheck %s --check-prefix=CHECK-SAMPLER-A
2+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-sampler-heap 1 b | FileCheck %s --check-prefix=CHECK-SAMPLER-B
3+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-resource-heap a 2 | FileCheck %s --check-prefix=CHECK-RESOURCE-A
4+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-resource-heap 1 b | FileCheck %s --check-prefix=CHECK-RESOURCE-B
5+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-counter-heap a 2 | FileCheck %s --check-prefix=CHECK-COUNTER-A
6+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-counter-heap 1 b | FileCheck %s --check-prefix=CHECK-COUNTER-B
7+
8+
// CHECK-SAMPLER-A: invalid -fvk-bind-sampler-heap argument: 'a'
9+
// CHECK-RESOURCE-A: invalid -fvk-bind-resource-heap argument: 'a'
10+
// CHECK-COUNTER-A: invalid -fvk-bind-counter-heap argument: 'a'
11+
// CHECK-SAMPLER-B: invalid -fvk-bind-sampler-heap argument: 'b'
12+
// CHECK-RESOURCE-B: invalid -fvk-bind-resource-heap argument: 'b'
13+
// CHECK-COUNTER-B: invalid -fvk-bind-counter-heap argument: 'b'
14+
[numthreads(1, 1, 1)]
15+
void main() {
16+
SamplerState Sampler = SamplerDescriptorHeap[0];
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-sampler-heap 1 | FileCheck %s --check-prefix=CHECK-SAMPLER
2+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-sampler-heap | FileCheck %s --check-prefix=CHECK-SAMPLER
3+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-resource-heap 1 | FileCheck %s --check-prefix=CHECK-RESOURCE
4+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-resource-heap | FileCheck %s --check-prefix=CHECK-RESOURCE
5+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-counter-heap 1 | FileCheck %s --check-prefix=CHECK-COUNTER
6+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-counter-heap | FileCheck %s --check-prefix=CHECK-COUNTER
7+
8+
// CHECK-SAMPLER: Argument to '-fvk-bind-sampler-heap' is missing.
9+
// CHECK-RESOURCE: Argument to '-fvk-bind-resource-heap' is missing.
10+
// CHECK-COUNTER: Argument to '-fvk-bind-counter-heap' is missing.
11+
[numthreads(1, 1, 1)]
12+
void main() {
13+
SamplerState Sampler = SamplerDescriptorHeap[0];
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-sampler-heap -1 2 | FileCheck %s --check-prefix=CHECK-SAMPLER
2+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-sampler-heap 1 -1 | FileCheck %s --check-prefix=CHECK-SAMPLER
3+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-resource-heap -1 2 | FileCheck %s --check-prefix=CHECK-RESOURCE
4+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-resource-heap 1 -1 | FileCheck %s --check-prefix=CHECK-RESOURCE
5+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-counter-heap -1 2 | FileCheck %s --check-prefix=CHECK-COUNTER
6+
// RUN: not %dxc -T cs_6_6 -spirv %s 2>&1 -fvk-bind-counter-heap 1 -1 | FileCheck %s --check-prefix=CHECK-COUNTER
7+
8+
// CHECK-SAMPLER: expected positive integer for -fvk-bind-sampler-heap, got: -1
9+
// CHECK-RESOURCE: expected positive integer for -fvk-bind-resource-heap, got: -1
10+
// CHECK-COUNTER: expected positive integer for -fvk-bind-counter-heap, got: -1
11+
[numthreads(1, 1, 1)]
12+
void main() {
13+
SamplerState Sampler = SamplerDescriptorHeap[0];
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %dxc -T ps_6_6 -E main %s -spirv -fvk-bind-resource-heap 6 7 | FileCheck %s
2+
3+
// CHECK-DAG: OpDecorate %a DescriptorSet 0
4+
// CHECK-DAG: OpDecorate %a Binding 0
5+
cbuffer a {
6+
float4 value;
7+
}
8+
9+
float4 main() : SV_Target {
10+
// CHECK-DAG: OpDecorate %ResourceDescriptorHeap DescriptorSet 7
11+
// CHECK-DAG: OpDecorate %ResourceDescriptorHeap Binding 6
12+
RWStructuredBuffer<float4> buffer = ResourceDescriptorHeap[1];
13+
buffer[0] = value;
14+
return value;
15+
}
16+

0 commit comments

Comments
 (0)