Bladeren bron

[spirv] Allow samplers and textures in initializer list (#815)

They are treated as primitive types and handled as a whole.
Lei Zhang 7 jaren geleden
bovenliggende
commit
cc61d9d294

+ 48 - 5
tools/clang/lib/SPIRV/InitListHandler.cpp

@@ -119,7 +119,9 @@ bool InitListHandler::tryToSplitStruct() {
 
   auto *init = const_cast<Expr *>(initializers.back());
   const QualType initType = init->getType();
-  if (!initType->isStructureType())
+  if (!initType->isStructureType() ||
+      // Sampler types will pass the above check but we cannot split it.
+      TypeTranslator::isSampler(initType))
     return false;
 
   // We are certain the current intializer will be replaced by now.
@@ -204,6 +206,11 @@ uint32_t InitListHandler::createInitForType(QualType type,
     return createInitForMatrixType(elemType, rowCount, colCount, srcLoc);
   }
 
+  // Samplers, (RW)Buffers, (RW)Textures
+  // It is important that this happens before checking of structure types.
+  if (TypeTranslator::isOpaqueType(type))
+    return createInitForSamplerImageType(type, srcLoc);
+
   if (type->isStructureType())
     return createInitForStructType(type);
 
@@ -224,7 +231,7 @@ uint32_t InitListHandler::createInitForBuiltinType(QualType type,
     return theEmitter.castToType(init.first, init.second, type, srcLoc);
   }
 
-  // Keep splitting structs or vectors
+  // Keep splitting structs or arrays
   while (tryToSplitStruct() || tryToSplitConstantArray())
     ;
 
@@ -248,7 +255,7 @@ uint32_t InitListHandler::createInitForVectorType(QualType elemType,
   // directly. For all other cases, we need to construct a new vector as the
   // initializer.
   if (scalars.empty()) {
-    // Keep splitting structs or vectors
+    // Keep splitting structs or arrays
     while (tryToSplitStruct() || tryToSplitConstantArray())
       ;
 
@@ -292,7 +299,7 @@ uint32_t InitListHandler::createInitForMatrixType(QualType elemType,
   // Same as the vector case, first try to see if we already have a matrix at
   // the beginning of the initializer queue.
   if (scalars.empty()) {
-    // Keep splitting structs or vectors
+    // Keep splitting structs or arrays
     while (tryToSplitStruct() || tryToSplitConstantArray())
       ;
 
@@ -332,7 +339,7 @@ uint32_t InitListHandler::createInitForMatrixType(QualType elemType,
 }
 
 uint32_t InitListHandler::createInitForStructType(QualType type) {
-  assert(type->isStructureType());
+  assert(type->isStructureType() && !TypeTranslator::isSampler(type));
 
   // Same as the vector case, first try to see if we already have a struct at
   // the beginning of the initializer queue.
@@ -407,5 +414,41 @@ InitListHandler::createInitForConstantArrayType(QualType type,
   return theBuilder.createCompositeConstruct(typeId, elements);
 }
 
+uint32_t InitListHandler::createInitForSamplerImageType(QualType type,
+                                                        SourceLocation srcLoc) {
+  assert(TypeTranslator::isOpaqueType(type));
+
+  // Samplers, (RW)Buffers, and (RW)Textures are translated into OpTypeSampler
+  // and OpTypeImage. They should be treated similar as builtin types.
+
+  if (!scalars.empty()) {
+    const auto init = scalars.front();
+    scalars.pop_front();
+    // Require exact type match between the initializer and the target component
+    if (init.second.getCanonicalType() != type.getCanonicalType()) {
+      emitError("cannot cast initializer type %0 into variable type %1", srcLoc)
+          << init.second << type;
+      return 0;
+    }
+    return init.first;
+  }
+
+  // Keep splitting structs or arrays
+  while (tryToSplitStruct() || tryToSplitConstantArray())
+    ;
+
+  const Expr *init = initializers.back();
+  initializers.pop_back();
+
+  if (init->getType().getCanonicalType() != type.getCanonicalType()) {
+    init->dump();
+    emitError("cannot cast initializer type %0 into variable type %1",
+              init->getLocStart())
+        << init->getType() << type;
+    return 0;
+  }
+  return theEmitter.loadIfGLValue(init);
+}
+
 } // end namespace spirv
 } // end namespace clang

+ 1 - 0
tools/clang/lib/SPIRV/InitListHandler.h

@@ -125,6 +125,7 @@ private:
                                    uint32_t colCount, SourceLocation);
   uint32_t createInitForStructType(QualType type);
   uint32_t createInitForConstantArrayType(QualType type, SourceLocation);
+  uint32_t createInitForSamplerImageType(QualType type, SourceLocation);
 
 private:
   SPIRVEmitter &theEmitter;

+ 7 - 0
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -620,6 +620,13 @@ TypeTranslator::getLayoutDecorations(const DeclContext *decl, LayoutRule rule) {
 }
 
 uint32_t TypeTranslator::translateResourceType(QualType type, LayoutRule rule) {
+  // Resource types are either represented like C struct or C++ class in the
+  // AST. Samplers are represented like C struct, so isStructureType() will
+  // return true for it; textures are represented like C++ class, so
+  // isClassType() will return true for it.
+
+  assert(type->isStructureOrClassType());
+
   const auto *recordType = type->getAs<RecordType>();
   assert(recordType);
   const llvm::StringRef name = recordType->getDecl()->getName();

+ 45 - 0
tools/clang/test/CodeGenSPIRV/var.init.opaque.hlsl

@@ -0,0 +1,45 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct Inner {
+    Texture3D    t;
+};
+
+struct Combined1 {
+    Inner        others;
+    SamplerState s1;
+    Texture2D    t1;
+    Texture2D    t2;
+    SamplerState s2;
+};
+
+struct Combined2 {
+    Texture3D    t;
+    SamplerState s;
+};
+
+Texture3D    gTex3D;
+SamplerState gSampler;
+Texture2D    gTex2D;
+
+float main() : A {
+
+// CHECK:      [[tex3d:%\d+]] = OpLoad %type_3d_image %gTex3D
+// CHECK-NEXT: [[sampl:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[comb2:%\d+]] = OpCompositeConstruct %Combined2 [[tex3d]] [[sampl]]
+// CHECK-NEXT:                  OpStore %comb2 [[comb2]]
+    Combined2 comb2 = {gTex3D, gSampler};
+
+// CHECK-NEXT:   [[ptr:%\d+]] = OpAccessChain %_ptr_Function_type_3d_image %comb2 %int_0
+// CHECK-NEXT: [[tex3d:%\d+]] = OpLoad %type_3d_image [[ptr]]
+// CHECK-NEXT: [[inner:%\d+]] = OpCompositeConstruct %Inner [[tex3d]]
+// CHECK-NEXT:   [[ptr:%\d+]] = OpAccessChain %_ptr_Function_type_sampler %comb2 %int_1
+// CHECK-NEXT: [[sampl1:%\d+]] = OpLoad %type_sampler [[ptr]]
+// CHECK-NEXT: [[tex2d1:%\d+]] = OpLoad %type_2d_image %gTex2D
+// CHECK-NEXT: [[tex2d2:%\d+]] = OpLoad %type_2d_image %gTex2D
+// CHECK-NEXT: [[sampl2:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[comb1:%\d+]] = OpCompositeConstruct %Combined1 [[inner]] [[sampl1]] [[tex2d1]] [[tex2d2]] [[sampl2]]
+// CHECK-NEXT:                  OpStore %comb1 [[comb1]]
+    Combined1 comb1 = {comb2, {gTex2D, gTex2D}, gSampler};
+
+    return 1.0;
+}

+ 1 - 0
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -97,6 +97,7 @@ TEST_F(FileTest, VarInitCbuffer) {
 TEST_F(FileTest, VarInitTbuffer) {
   runFileTest("var.init.tbuffer.hlsl", FileTest::Expect::Warning);
 }
+TEST_F(FileTest, VarInitOpaque) { runFileTest("var.init.opaque.hlsl"); }
 TEST_F(FileTest, StaticVar) { runFileTest("var.static.hlsl"); }
 
 // For prefix/postfix increment/decrement