Jelajahi Sumber

[spirv] Add support for -fvk-bind-register (#1480)

format: -fvk-bind-register <type-number> <space> <binding> <set>

Also created a short alias for it: -vkbr.

This option gives the ultimate manual control of descriptor
assignment. It requires:

* All resources are annotated with :register() in the source code
* -fvk-bind-register is specified for every resource

It overrules all other mechanisms.
It cannot be used together with -fvk-{u|b|s|t}-shift.
Lei Zhang 7 tahun lalu
induk
melakukan
05cda8da2a

+ 35 - 2
docs/SPIR-V.rst

@@ -144,8 +144,35 @@ constructs when possible. If that is inadequate, we then consider attaching
 Descriptors
 ~~~~~~~~~~~
 
-To specify which Vulkan descriptor a particular resource binds to, use the
-``[[vk::binding(X[, Y])]]`` attribute.
+The compiler provides multiple mechanisms to specify which Vulkan descriptor
+a particular resource binds to.
+
+In the source code, you can use the ``[[vk::binding(X[, Y])]]`` and
+``[[vk::counter_binding(X)]]`` attribute. The native ``:register()`` attribute
+is also respected.
+
+On the command-line, you can use the ``-fvk-{b|s|t|u}-shift`` or
+``-fvk-bind-register`` option.
+
+If you can modify the source code, the ``[[vk::binding(X[, Y])]]`` and
+``[[vk::counter_binding(X)]]`` attribute gives you find-grained control over
+descriptor assignment.
+
+If you cannot modify the source code, you can use command-line options to change
+how ``:register()`` attribute is handled by the compiler. ``-fvk-bind-register``
+lets you to specify the descriptor for the source at a certain register.
+``-fvk-{b|s|t|u}-shift`` lets you to apply shifts to all register numbers
+of a certain register type. They cannot be used together, though.
+
+Without attribute and command-line option, ``:register(xX, spaceY)`` will be
+mapped to binding ``X`` in descriptor set ``Y``. Note that register type ``x``
+is ignored, so this may cause overlap.
+
+The more specific a mechanism is, the higher precedence it has, and command-line
+option has higher precedence over source code attribute.
+
+For more details, see `HLSL register and Vulkan binding`_, `Vulkan specific
+attributes`_, and `Vulkan-specific options`_.
 
 Subpass inputs
 ~~~~~~~~~~~~~~
@@ -2829,6 +2856,12 @@ codegen for Vulkan:
 - ``-fvk-t-shift N M``, similar to ``-fvk-b-shift``, but for t-type registers.
 - ``-fvk-s-shift N M``, similar to ``-fvk-b-shift``, but for s-type registers.
 - ``-fvk-u-shift N M``, similar to ``-fvk-b-shift``, but for u-type registers.
+- ``-fvk-bind-register xX Y N M`` (short alias: ``-vkbr``): Binds the resouce
+  at ``register(xX, spaceY)`` to descriptor set ``M`` and binding ``N``. This
+  option cannot be used together with other binding assignment options.
+  It requires all source code resources have ``:register()`` attribute and
+  all registers have corresponding Vulkan descriptors specified using this
+  option.
 - ``-fvk-use-gl-layout``: Uses strict OpenGL ``std140``/``std430``
   layout rules for resources.
 - ``-fvk-use-dx-layout``: Uses DirectX layout rules for resources.

+ 1 - 0
include/dxc/Support/HLSLOptions.h

@@ -172,6 +172,7 @@ public:
   llvm::SmallVector<int32_t, 4> VkTShift;  // OPT_fvk_t_shift
   llvm::SmallVector<int32_t, 4> VkSShift;  // OPT_fvk_s_shift
   llvm::SmallVector<int32_t, 4> VkUShift;  // OPT_fvk_u_shift
+  std::vector<std::string> VkBindRegister; // OPT_fvk_bind_register
   llvm::SmallVector<llvm::StringRef, 4> SpvExtensions; // OPT_fspv_extension
   llvm::StringRef SpvTargetEnv;                        // OPT_fspv_target_env
   llvm::SmallVector<llvm::StringRef, 4> SpvOconfig;    // OPT_Oconfig

+ 3 - 0
include/dxc/Support/HLSLOptions.td

@@ -246,6 +246,9 @@ def fvk_s_shift : MultiArg<["-"], "fvk-s-shift", 2>, MetaVarName<"<shift> <space
   HelpText<"Specify Vulkan binding number shift for s-type register">;
 def fvk_u_shift : MultiArg<["-"], "fvk-u-shift", 2>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
   HelpText<"Specify Vulkan binding number shift for u-type register">;
+def fvk_bind_register : MultiArg<["-"], "fvk-bind-register", 4>, MetaVarName<"<type-number> <space> <binding> <set>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Specify Vulkan descriptor set and binding for a specific register">;
+def vkbr : MultiArg<["-"], "vkbr", 4>, Flags<[CoreOption, DriverOption]>, Alias<fvk_bind_register>;
 def fvk_invert_y: Flag<["-"], "fvk-invert-y">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
   HelpText<"Negate SV_Position.y before writing to stage output in VS/DS/GS to accommodate Vulkan's coordinate system">;
 def fvk_use_dx_position_w: Flag<["-"], "fvk-use-dx-position-w">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,

+ 62 - 46
lib/DxcSupport/HLSLOptions.cpp

@@ -206,6 +206,59 @@ static bool GetTargetVersionFromString(llvm::StringRef ref, unsigned *major, uns
   }
 }
 
+// SPIRV Change Starts
+#ifdef ENABLE_SPIRV_CODEGEN
+/// Checks and collects the arguments for -fvk-{b|s|t|u}-shift into *shifts.
+static bool handleVkShiftArgs(const InputArgList &args, OptSpecifier id,
+                              const char *name,
+                              llvm::SmallVectorImpl<int32_t> *shifts,
+                              llvm::raw_ostream &errors) {
+  const auto values = args.getAllArgValues(id);
+
+  if (values.empty())
+    return true;
+
+  if (!args.hasArg(OPT_spirv)) {
+    errors << "-fvk-" << name << "-shift requires -spirv";
+    return false;
+  }
+
+  if (!args.getLastArgValue(OPT_fvk_bind_register).empty()) {
+    errors << "-fvk-" << name
+           << "-shift cannot be used together with -fvk-bind-register";
+    return false;
+  }
+
+  shifts->clear();
+  bool setForAll = false;
+
+  for (const auto &val : values) {
+    int32_t number = 0;
+    if (val == "all") {
+      number = -1;
+      setForAll = true;
+    } else {
+      if (llvm::StringRef(val).getAsInteger(10, number)) {
+        errors << "invalid -fvk-" << name << "-shift argument: " << val;
+        return false;
+      }
+      if (number < 0) {
+        errors << "negative -fvk-" << name << "-shift argument: " << val;
+        return false;
+      }
+    }
+    shifts->push_back(number);
+  }
+  if (setForAll && shifts->size() > 2) {
+    errors << "setting all sets via -fvk-" << name
+           << "-shift argument should be used alone";
+    return false;
+  }
+  return true;
+};
+#endif
+// SPIRV Change Ends
+
 namespace hlsl {
 namespace options {
 
@@ -511,51 +564,14 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   opts.SpvEnableReflect = Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false);
   opts.VkNoWarnIgnoredFeatures = Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false);
 
-  // Collects the arguments for -fvk-{b|s|t|u}-shift.
-  const auto handleVkShiftArgs =
-      [genSpirv, &Args, &errors](OptSpecifier id, const char *name,
-                                 llvm::SmallVectorImpl<int32_t> *shifts) {
-        const auto values = Args.getAllArgValues(id);
-
-        if (!genSpirv && !values.empty()) {
-          errors << "-fvk-" << name << "-shift requires -spirv";
-          return false;
-        }
-
-        shifts->clear();
-        bool setForAll = false;
-
-        for (const auto &val : values) {
-          int32_t number = 0;
-          if (val == "all") {
-            number = -1;
-            setForAll = true;
-          } else {
-            if (llvm::StringRef(val).getAsInteger(10, number)) {
-              errors << "invalid -fvk-" << name << "-shift argument: " << val;
-              return false;
-            }
-            if (number < 0) {
-              errors << "negative -fvk-" << name << "-shift argument: " << val;
-              return false;
-            }
-          }
-          shifts->push_back(number);
-        }
-        if (setForAll && shifts->size() > 2) {
-          errors << "setting all sets via -fvk-" << name
-                 << "-shift argument should be used alone";
-          return false;
-        }
-        return true;
-      };
-
-  if (!handleVkShiftArgs(OPT_fvk_b_shift, "b", &opts.VkBShift) ||
-      !handleVkShiftArgs(OPT_fvk_t_shift, "t", &opts.VkTShift) ||
-      !handleVkShiftArgs(OPT_fvk_s_shift, "s", &opts.VkSShift) ||
-      !handleVkShiftArgs(OPT_fvk_u_shift, "u", &opts.VkUShift))
+  if (!handleVkShiftArgs(Args, OPT_fvk_b_shift, "b", &opts.VkBShift, errors) ||
+      !handleVkShiftArgs(Args, OPT_fvk_t_shift, "t", &opts.VkTShift, errors) ||
+      !handleVkShiftArgs(Args, OPT_fvk_s_shift, "s", &opts.VkSShift, errors) ||
+      !handleVkShiftArgs(Args, OPT_fvk_u_shift, "u", &opts.VkUShift, errors))
     return 1;
 
+  opts.VkBindRegister = Args.getAllArgValues(OPT_fvk_bind_register);
+
   opts.VkStageIoOrder = Args.getLastArgValue(OPT_fvk_stage_io_order_EQ, "decl");
   if (opts.VkStageIoOrder != "alpha" && opts.VkStageIoOrder != "decl") {
     errors << "unknown Vulkan stage I/O location assignment order: "
@@ -598,16 +614,16 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
       !Args.getLastArgValue(OPT_fspv_extension_EQ).empty() ||
       !Args.getLastArgValue(OPT_fspv_target_env_EQ).empty() ||
       !Args.getLastArgValue(OPT_Oconfig).empty() ||
+      !Args.getLastArgValue(OPT_fvk_bind_register).empty() ||
       !Args.getLastArgValue(OPT_fvk_b_shift).empty() ||
       !Args.getLastArgValue(OPT_fvk_t_shift).empty() ||
       !Args.getLastArgValue(OPT_fvk_s_shift).empty() ||
-      !Args.getLastArgValue(OPT_fvk_u_shift).empty()
-      ) {
+      !Args.getLastArgValue(OPT_fvk_u_shift).empty()) {
     errors << "SPIR-V CodeGen not available. "
               "Please recompile with -DENABLE_SPIRV_CODEGEN=ON.";
     return 1;
   }
-#endif
+#endif // ENABLE_SPIRV_CODEGEN
   // SPIRV Change Ends
 
   opts.Args = std::move(Args);

+ 4 - 0
tools/clang/include/clang/SPIRV/EmitSPIRVOptions.h

@@ -9,6 +9,9 @@
 #ifndef LLVM_CLANG_SPIRV_EMITSPIRVOPTIONS_H
 #define LLVM_CLANG_SPIRV_EMITSPIRVOPTIONS_H
 
+#include <string>
+#include <vector>
+
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 
@@ -45,6 +48,7 @@ struct EmitSPIRVOptions {
   llvm::SmallVector<int32_t, 4> tShift;
   llvm::SmallVector<int32_t, 4> sShift;
   llvm::SmallVector<int32_t, 4> uShift;
+  std::vector<std::string> bindRegister;
   llvm::SmallVector<llvm::StringRef, 4> allowedExtensions;
   llvm::StringRef targetEnv;
   spirv::LayoutRule cBufferLayoutRule;

+ 103 - 9
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -20,6 +20,7 @@
 #include "clang/AST/HlslTypes.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringSet.h"
 
 namespace clang {
@@ -436,7 +437,8 @@ SpirvEvalInfo DeclResultIdMapper::createExternVar(const VarDecl *var) {
   const auto *bindingAttr = var->getAttr<VKBindingAttr>();
   const auto *counterBindingAttr = var->getAttr<VKCounterBindingAttr>();
 
-  resourceVars.emplace_back(id, regAttr, bindingAttr, counterBindingAttr);
+  resourceVars.emplace_back(id, var->getLocation(), regAttr, bindingAttr,
+                            counterBindingAttr);
 
   if (const auto *inputAttachment = var->getAttr<VKInputAttachmentIndexAttr>())
     theBuilder.decorateInputAttachmentIndex(id, inputAttachment->getIndex());
@@ -594,9 +596,9 @@ uint32_t DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
                                              : spirvOptions.tBufferLayoutRule);
     astDecls[varDecl].indexInCTBuffer = index++;
   }
-  resourceVars.emplace_back(bufferVar, getResourceBinding(decl),
-                            decl->getAttr<VKBindingAttr>(),
-                            decl->getAttr<VKCounterBindingAttr>());
+  resourceVars.emplace_back(
+      bufferVar, decl->getLocation(), getResourceBinding(decl),
+      decl->getAttr<VKBindingAttr>(), decl->getAttr<VKCounterBindingAttr>());
 
   return bufferVar;
 }
@@ -642,9 +644,9 @@ uint32_t DeclResultIdMapper::createCTBuffer(const VarDecl *decl) {
           .setStorageClass(spv::StorageClass::Uniform)
           .setLayoutRule(context->isCBuffer() ? spirvOptions.cBufferLayoutRule
                                               : spirvOptions.tBufferLayoutRule);
-  resourceVars.emplace_back(bufferVar, getResourceBinding(context),
-                            decl->getAttr<VKBindingAttr>(),
-                            decl->getAttr<VKCounterBindingAttr>());
+  resourceVars.emplace_back(
+      bufferVar, decl->getLocation(), getResourceBinding(context),
+      decl->getAttr<VKBindingAttr>(), decl->getAttr<VKCounterBindingAttr>());
 
   return bufferVar;
 }
@@ -679,7 +681,8 @@ void DeclResultIdMapper::createGlobalsCBuffer(const VarDecl *var) {
       context, /*arraySize*/ 0, ContextUsageKind::Globals, "type.$Globals",
       "$Globals");
 
-  resourceVars.emplace_back(globals, nullptr, nullptr, nullptr);
+  resourceVars.emplace_back(globals, SourceLocation(), nullptr, nullptr,
+                            nullptr);
 
   uint32_t index = 0;
   for (const auto *decl : typeTranslator.collectDeclsInDeclContext(context))
@@ -793,7 +796,8 @@ void DeclResultIdMapper::createCounterVar(
   if (!isAlias) {
     // Non-alias counter variables should be put in to resourceVars so that
     // descriptors can be allocated for them.
-    resourceVars.emplace_back(counterId, getResourceBinding(decl),
+    resourceVars.emplace_back(counterId, decl->getLocation(),
+                              getResourceBinding(decl),
                               decl->getAttr<VKBindingAttr>(),
                               decl->getAttr<VKCounterBindingAttr>(), true);
     assert(declId);
@@ -1096,6 +1100,59 @@ private:
   uint32_t masterShift; /// Shift amount applies to all sets.
   llvm::DenseMap<int32_t, int32_t> perSetShift;
 };
+
+/// A class for maintaining the mapping from source code register attributes to
+/// descriptor set and number settings.
+class RegisterBindingMapper {
+public:
+  /// Takes in the relation between register attributes and descriptor settings.
+  /// Each relation is represented by four strings:
+  ///   <register-type-number> <space> <descriptor-binding> <set>
+  bool takeInRelation(const std::vector<std::string> &relation,
+                      std::string *error) {
+    assert(relation.size() % 4 == 0);
+    mapping.clear();
+
+    for (uint32_t i = 0; i < relation.size(); i += 4) {
+      int32_t spaceNo = -1, setNo = -1, bindNo = -1;
+      if (StringRef(relation[i + 1]).getAsInteger(10, spaceNo) || spaceNo < 0) {
+        *error = "space number: " + relation[i + 1];
+        return false;
+      }
+      if (StringRef(relation[i + 2]).getAsInteger(10, bindNo) || bindNo < 0) {
+        *error = "binding number: " + relation[i + 2];
+        return false;
+      }
+      if (StringRef(relation[i + 3]).getAsInteger(10, setNo) || setNo < 0) {
+        *error = "set number: " + relation[i + 3];
+        return false;
+      }
+      mapping[relation[i + 1] + relation[i]] = std::make_pair(setNo, bindNo);
+    }
+    return true;
+  }
+
+  /// Returns true and set the correct set and binding number if we can find a
+  /// descriptor setting for the given register. False otherwise.
+  bool getSetBinding(const hlsl::RegisterAssignment *regAttr, int *setNo,
+                     int *bindNo) const {
+    std::ostringstream iss;
+    iss << regAttr->RegisterSpace << regAttr->RegisterType
+        << regAttr->RegisterNumber;
+
+    auto found = mapping.find(iss.str());
+    if (found != mapping.end()) {
+      *setNo = found->second.first;
+      *bindNo = found->second.second;
+      return true;
+    }
+
+    return false;
+  }
+
+private:
+  llvm::StringMap<std::pair<int, int>> mapping;
+};
 } // namespace
 
 bool DeclResultIdMapper::decorateResourceBindings() {
@@ -1117,6 +1174,43 @@ bool DeclResultIdMapper::decorateResourceBindings() {
   // - m2
   // - m3, mX * c2
 
+  // Special handling of -fvk-bind-register, which requires
+  // * All resources are annoated with :register() in the source code
+  // * -fvk-bind-register is specified for every resource
+  if (!spirvOptions.bindRegister.empty()) {
+    RegisterBindingMapper bindingMapper;
+    std::string error;
+
+    if (!bindingMapper.takeInRelation(spirvOptions.bindRegister, &error)) {
+      emitError("invalid -fvk-bind-register %0", {}) << error;
+      return false;
+    }
+
+    for (const auto &var : resourceVars)
+      if (const auto *regAttr = var.getRegister()) {
+        if (var.isCounter()) {
+          emitError("-fvk-bind-register for RW/Append/Consume StructuredBuffer "
+                    "umimplemented",
+                    var.getSourceLocation());
+        } else {
+          int setNo = 0, bindNo = 0;
+          if (!bindingMapper.getSetBinding(regAttr, &setNo, &bindNo)) {
+            emitError("missing -fvk-bind-register for resource",
+                      var.getSourceLocation());
+            return false;
+          }
+          theBuilder.decorateDSetBinding(var.getSpirvId(), setNo, bindNo);
+        }
+      } else {
+        emitError(
+            "-fvk-bind-register requires register annotations on all resources",
+            var.getSourceLocation());
+        return false;
+      }
+
+    return true;
+  }
+
   BindingSet bindingSet;
 
   // Decorates the given varId of the given category with set number

+ 6 - 4
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -112,13 +112,14 @@ private:
 
 class ResourceVar {
 public:
-  ResourceVar(uint32_t id, const hlsl::RegisterAssignment *r,
-              const VKBindingAttr *b, const VKCounterBindingAttr *cb,
-              bool counter = false)
-      : varId(id), reg(r), binding(b), counterBinding(cb),
+  ResourceVar(uint32_t id, SourceLocation loc,
+              const hlsl::RegisterAssignment *r, const VKBindingAttr *b,
+              const VKCounterBindingAttr *cb, bool counter = false)
+      : varId(id), srcLoc(loc), reg(r), binding(b), counterBinding(cb),
         isCounterVar(counter) {}
 
   uint32_t getSpirvId() const { return varId; }
+  SourceLocation getSourceLocation() const { return srcLoc; }
   const hlsl::RegisterAssignment *getRegister() const { return reg; }
   const VKBindingAttr *getBinding() const { return binding; }
   bool isCounter() const { return isCounterVar; }
@@ -128,6 +129,7 @@ public:
 
 private:
   uint32_t varId;                             ///< <result-id>
+  SourceLocation srcLoc;                      ///< Source location
   const hlsl::RegisterAssignment *reg;        ///< HLSL register assignment
   const VKBindingAttr *binding;               ///< Vulkan binding assignment
   const VKCounterBindingAttr *counterBinding; ///< Vulkan counter binding

+ 10 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.counter.hlsl

@@ -0,0 +1,10 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-bind-register u10 2 10 1
+
+struct S { float4 val; };
+RWStructuredBuffer<S>  MyBuffer : register(u10, space2);
+
+float4 main() : SV_Target {
+  return MyBuffer[0].val;
+}
+
+// CHECK: :4:24: error: -fvk-bind-register for RW/Append/Consume StructuredBuffer umimplemented

+ 13 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.hlsl

@@ -0,0 +1,13 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-bind-register t5 0 1 2 -vkbr s3 1 3 4
+
+// CHECK: OpDecorate %MyTexture DescriptorSet 2
+// CHECK: OpDecorate %MyTexture Binding 1
+Texture2D MyTexture    : register(t5);
+// CHECK: OpDecorate %MySampler DescriptorSet 4
+// CHECK: OpDecorate %MySampler Binding 3
+SamplerState MySampler : register(s3, space1);
+
+float4 main() : SV_Target {
+  return MyTexture.Sample(MySampler, float2(0.1, 0.2));
+}
+

+ 10 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.invalid-bind.hlsl

@@ -0,0 +1,10 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-bind-register s10 0 -10 0
+
+Texture2D MyTexture;
+SamplerState MySampler;
+
+float4 main() : SV_Target {
+  return MyTexture.Sample(MySampler, float2(0.1, 0.2));
+}
+
+// CHECK: error: invalid -fvk-bind-register binding number: -10

+ 10 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.invalid-set.hlsl

@@ -0,0 +1,10 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-bind-register s10 0 10 ff
+
+Texture2D MyTexture;
+SamplerState MySampler;
+
+float4 main() : SV_Target {
+  return MyTexture.Sample(MySampler, float2(0.1, 0.2));
+}
+
+// CHECK: error: invalid -fvk-bind-register set number: ff

+ 10 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.invalid-space.hlsl

@@ -0,0 +1,10 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-bind-register s10 5t 10 1
+
+Texture2D MyTexture;
+SamplerState MySampler;
+
+float4 main() : SV_Target {
+  return MyTexture.Sample(MySampler, float2(0.1, 0.2));
+}
+
+// CHECK: error: invalid -fvk-bind-register space number: 5t

+ 10 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.missing-attr.hlsl

@@ -0,0 +1,10 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-bind-register s10 0 10 0
+
+Texture2D MyTexture;
+SamplerState MySampler;
+
+float4 main() : SV_Target {
+  return MyTexture.Sample(MySampler, float2(0.1, 0.2));
+}
+
+// CHECK: :3:11: error: -fvk-bind-register requires register annotations on all resources

+ 10 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.missing-cl.hlsl

@@ -0,0 +1,10 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-bind-register t5 1 10 1
+
+Texture2D MyTexture    : register(t5, space1);
+SamplerState MySampler : register(s0);
+
+float4 main() : SV_Target {
+  return MyTexture.Sample(MySampler, float2(0.1, 0.2));
+}
+
+// CHECK: :4:14: error: missing -fvk-bind-register for resource

+ 0 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.all-sets.hlsl → tools/clang/test/CodeGenSPIRV/vk.binding.cl.shift.all-sets.hlsl


+ 0 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.hlsl → tools/clang/test/CodeGenSPIRV/vk.binding.cl.shift.hlsl


+ 1 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -525,6 +525,7 @@ public:
           spirvOpts.tShift = opts.VkTShift;
           spirvOpts.sShift = opts.VkSShift;
           spirvOpts.uShift = opts.VkUShift;
+          spirvOpts.bindRegister = opts.VkBindRegister;
           spirvOpts.allowedExtensions = opts.SpvExtensions;
           spirvOpts.targetEnv = opts.SpvTargetEnv;
           spirvOpts.enable16BitTypes = opts.Enable16BitTypes;

+ 23 - 2
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -1409,12 +1409,33 @@ TEST_F(FileTest, VulkanRegisterBinding) {
 TEST_F(FileTest, VulkanRegisterBindingShift) {
   // Resource binding from :register() with shift specified via
   // command line option
-  runFileTest("vk.binding.cl.hlsl");
+  runFileTest("vk.binding.cl.shift.hlsl");
 }
 TEST_F(FileTest, VulkanRegisterBindingShiftAllSets) {
   // Resource binding from :register() with shift specified for all sets via
   // command line option
-  runFileTest("vk.binding.cl.all-sets.hlsl");
+  runFileTest("vk.binding.cl.shift.all-sets.hlsl");
+}
+TEST_F(FileTest, VulkanRegisterBinding1to1Mapping) {
+  runFileTest("vk.binding.cl.register.hlsl");
+}
+TEST_F(FileTest, VulkanRegisterBinding1to1MappingInvalidSpaceNo) {
+  runFileTest("vk.binding.cl.register.invalid-space.hlsl", Expect::Failure);
+}
+TEST_F(FileTest, VulkanRegisterBinding1to1MappingInvalidSetNo) {
+  runFileTest("vk.binding.cl.register.invalid-set.hlsl", Expect::Failure);
+}
+TEST_F(FileTest, VulkanRegisterBinding1to1MappingInvalidBindNo) {
+  runFileTest("vk.binding.cl.register.invalid-bind.hlsl", Expect::Failure);
+}
+TEST_F(FileTest, VulkanRegisterBinding1to1MappingMissingAttr) {
+  runFileTest("vk.binding.cl.register.missing-attr.hlsl", Expect::Failure);
+}
+TEST_F(FileTest, VulkanRegisterBinding1to1MappingMissingCLOption) {
+  runFileTest("vk.binding.cl.register.missing-cl.hlsl", Expect::Failure);
+}
+TEST_F(FileTest, VulkanRegisterBinding1to1MappingAssociatedCounter) {
+  runFileTest("vk.binding.cl.register.counter.hlsl", Expect::Failure);
 }
 TEST_F(FileTest, VulkanStructuredBufferCounter) {
   // [[vk::counter_binding()]] for RWStructuredBuffer, AppendStructuredBuffer,