writer/hlsl: Emit binding group as spaceN

When combined with the new transform::BindingRemapper, Dawn can now correctly emit resource bindings.

Change-Id: I3e7fd7b2fc5368c5da112ec8835f5c70d45ce6c8
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/46263
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index c5e4a4c..a2d7fe4 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -79,6 +79,22 @@
   }
 }
 
+// Helper for writing " : register(RX, spaceY)", where R is the register, X is
+// the binding point binding value, and Y is the binding point group value.
+struct RegisterAndSpace {
+  RegisterAndSpace(char r, ast::Variable::BindingPoint bp)
+      : reg(r), binding_point(bp) {}
+
+  char const reg;
+  ast::Variable::BindingPoint const binding_point;
+};
+
+std::ostream& operator<<(std::ostream& s, const RegisterAndSpace& rs) {
+  s << " : register(" << rs.reg << rs.binding_point.binding->value()
+    << ", space" << rs.binding_point.group->value() << ")";
+  return s;
+}
+
 }  // namespace
 
 GeneratorImpl::GeneratorImpl(const Program* program)
@@ -1579,29 +1595,18 @@
   bool emitted_uniform = false;
   for (auto data : func_sem->ReferencedUniformVariables()) {
     auto* var = data.first;
+    auto& binding_point = data.second;
     auto* decl = var->Declaration();
 
     if (!emitted_globals.emplace(decl->symbol()).second) {
       continue;  // Global already emitted
     }
 
-    // TODO(dsinclair): We're using the binding to make up the buffer number but
-    // we should instead be using a provided mapping that uses both buffer and
-    // set. https://bugs.chromium.org/p/tint/issues/detail?id=104
-    auto* binding = data.second.binding;
-    if (binding == nullptr) {
-      diagnostics_.add_error(
-          "unable to find binding information for uniform: " +
-          builder_.Symbols().NameFor(decl->symbol()));
-      return false;
-    }
-    // auto* set = data.second.set;
-
     auto* type = var->Type()->UnwrapIfNeeded();
     if (auto* strct = type->As<type::Struct>()) {
       out << "ConstantBuffer<" << builder_.Symbols().NameFor(strct->symbol())
           << "> " << builder_.Symbols().NameFor(decl->symbol())
-          << " : register(b" << binding->value() << ");" << std::endl;
+          << RegisterAndSpace('b', binding_point) << ";" << std::endl;
     } else {
       // TODO(dsinclair): There is outstanding spec work to require all uniform
       // buffers to be [[block]] decorated, which means structs. This is
@@ -1610,7 +1615,7 @@
       // Relevant: https://github.com/gpuweb/gpuweb/issues/1004
       //           https://github.com/gpuweb/gpuweb/issues/1008
       auto name = "cbuffer_" + builder_.Symbols().NameFor(decl->symbol());
-      out << "cbuffer " << name << " : register(b" << binding->value() << ") {"
+      out << "cbuffer " << name << RegisterAndSpace('b', binding_point) << " {"
           << std::endl;
 
       increment_indent();
@@ -1633,13 +1638,13 @@
   bool emitted_storagebuffer = false;
   for (auto data : func_sem->ReferencedStorageBufferVariables()) {
     auto* var = data.first;
+    auto& binding_point = data.second;
     auto* decl = var->Declaration();
 
     if (!emitted_globals.emplace(decl->symbol()).second) {
       continue;  // Global already emitted
     }
 
-    auto* binding = data.second.binding;
     auto* ac = var->Type()->As<type::AccessControl>();
     if (ac == nullptr) {
       diagnostics_.add_error("access control type required for storage buffer");
@@ -1650,8 +1655,8 @@
       out << "RW";
     }
     out << "ByteAddressBuffer " << builder_.Symbols().NameFor(decl->symbol())
-        << " : register(" << (ac->IsReadOnly() ? "t" : "u") << binding->value()
-        << ");" << std::endl;
+        << RegisterAndSpace(ac->IsReadOnly() ? 't' : 'u', binding_point) << ";"
+        << std::endl;
     emitted_storagebuffer = true;
   }
   if (emitted_storagebuffer) {
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index ba3e9c8..0153af2 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -260,7 +260,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
-  EXPECT_EQ(result(), R"(cbuffer cbuffer_coord : register(b0) {
+  EXPECT_EQ(result(), R"(cbuffer cbuffer_coord : register(b0, space1) {
   float4 coord;
 };
 
@@ -302,7 +302,7 @@
   float4 coord;
 };
 
-ConstantBuffer<Uniforms> uniforms : register(b0);
+ConstantBuffer<Uniforms> uniforms : register(b0, space1);
 
 void frag_main() {
   float v = uniforms.coord.x;
@@ -342,7 +342,8 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
-  EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
+  EXPECT_THAT(result(),
+              HasSubstr(R"(RWByteAddressBuffer coord : register(u0, space1);
 
 void frag_main() {
   float v = asfloat(coord.Load(4));
@@ -380,7 +381,8 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
-  EXPECT_THAT(result(), HasSubstr(R"(ByteAddressBuffer coord : register(t0);
+  EXPECT_THAT(result(),
+              HasSubstr(R"(ByteAddressBuffer coord : register(t0, space1);
 
 void frag_main() {
   float v = asfloat(coord.Load(4));
@@ -416,7 +418,8 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
-  EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
+  EXPECT_THAT(result(),
+              HasSubstr(R"(RWByteAddressBuffer coord : register(u0, space1);
 
 void frag_main() {
   coord.Store(4, asuint(2.0f));
@@ -452,7 +455,8 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
-  EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
+  EXPECT_THAT(result(),
+              HasSubstr(R"(RWByteAddressBuffer coord : register(u0, space1);
 
 void frag_main() {
   coord.Store(4, asuint(2.0f));
@@ -666,7 +670,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
-  EXPECT_EQ(result(), R"(cbuffer cbuffer_coord : register(b0) {
+  EXPECT_EQ(result(), R"(cbuffer cbuffer_coord : register(b0, space1) {
   float4 coord;
 };
 
@@ -714,7 +718,8 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
-  EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
+  EXPECT_THAT(result(),
+              HasSubstr(R"(RWByteAddressBuffer coord : register(u0, space1);
 
 float sub_func(float param) {
   return asfloat(coord.Load((4 * 0)));
@@ -915,7 +920,7 @@
   float d;
 };
 
-RWByteAddressBuffer data : register(u0);
+RWByteAddressBuffer data : register(u0, space0);
 
 [numthreads(1, 1, 1)]
 void a() {