Browse Source

[spirv] Execution modes for GS primitive types. (#734)

Ehsan 8 years ago
parent
commit
96c5c0ad17

+ 22 - 0
docs/SPIR-V.rst

@@ -1843,6 +1843,28 @@ and is translated to SPIR-V execution mode as follows:
 |``maxvertexcount``       | ``n``               | ``OutputVertices n``     |
 |``maxvertexcount``       | ``n``               | ``OutputVertices n``     |
 +-------------------------+---------------------+--------------------------+
 +-------------------------+---------------------+--------------------------+
 
 
+Translation for Primitive Types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Geometry shader vertex inputs may be qualified with primitive types. Only one primitive type
+is allowed to be used in a given geometry shader. The following table shows the SPIR-V execution
+mode that is used in order to represent the given primitive type.
+
+.. table:: Mapping from geometry shader primitive type to SPIR-V execution mode
+
++---------------------+-----------------------------+
+| HLSL Primitive Type | SPIR-V Execution Mode       |
++=====================+=============================+
+|``point``            | ``InputPoints``             |
++---------------------+-----------------------------+
+|``line``             | ``InputLines``              |
++---------------------+-----------------------------+
+|``triangle``         | ``Triangles``               |
++---------------------+-----------------------------+
+|``lineadj``          | ``InputLinesAdjacency``     |
++---------------------+-----------------------------+
+|``triangleadj``      | ``InputTrianglesAdjacency`` |
++---------------------+-----------------------------+
+
 TODO: Describe more details about how geometry shaders are translated. e.g. OutputStreams, etc.
 TODO: Describe more details about how geometry shaders are translated. e.g. OutputStreams, etc.
 
 
 Vulkan Command-line Options
 Vulkan Command-line Options

+ 16 - 1
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -54,6 +54,15 @@ ResourceVar::Category getResourceCategory(QualType type) {
   return ResourceVar::Category::Other;
   return ResourceVar::Category::Other;
 }
 }
 
 
+/// \brief Returns true if the given declaration has a primitive type qualifier.
+/// Returns false otherwise.
+bool hasGSPrimitiveTypeQualifier(const Decl *decl) {
+  return (decl->hasAttr<HLSLTriangleAttr>() ||
+          decl->hasAttr<HLSLTriangleAdjAttr>() ||
+          decl->hasAttr<HLSLPointAttr>() || decl->hasAttr<HLSLLineAdjAttr>() ||
+          decl->hasAttr<HLSLLineAttr>());
+}
+
 } // anonymous namespace
 } // anonymous namespace
 
 
 bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl,
 bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl,
@@ -684,8 +693,14 @@ bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl,
     // Found semantic attached directly to this Decl. This means we need to
     // Found semantic attached directly to this Decl. This means we need to
     // map this decl to a single stage variable.
     // map this decl to a single stage variable.
 
 
-    const hlsl::DxilParamInputQual qual =
+    hlsl::DxilParamInputQual qual =
         asInput ? hlsl::DxilParamInputQual::In : hlsl::DxilParamInputQual::Out;
         asInput ? hlsl::DxilParamInputQual::In : hlsl::DxilParamInputQual::Out;
+
+    // The inputs to the geometry shader that have a primitive type qualifier
+    // must use 'InputPrimitive'.
+    if (asInput && shaderModel.IsGS() && hasGSPrimitiveTypeQualifier(decl))
+      qual = hlsl::DxilParamInputQual::InputPrimitive;
+
     const hlsl::SigPoint *sigPoint =
     const hlsl::SigPoint *sigPoint =
         hlsl::SigPoint::GetSigPoint(hlsl::SigPointFromInputQual(
         hlsl::SigPoint::GetSigPoint(hlsl::SigPointFromInputQual(
             qual, shaderModel.GetKind(), isPatchConstant));
             qual, shaderModel.GetKind(), isPatchConstant));

+ 32 - 0
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -5142,6 +5142,38 @@ bool SPIRVEmitter::processGeometryShaderAttributes(const FunctionDecl *decl) {
                                 spv::ExecutionMode::OutputVertices,
                                 spv::ExecutionMode::OutputVertices,
                                 {static_cast<uint32_t>(vcAttr->getCount())});
                                 {static_cast<uint32_t>(vcAttr->getCount())});
   }
   }
+
+  // Only one primitive type is permitted for the geometry shader.
+  uint32_t primitiveTypes = 0;
+  for (const auto *param : decl->params()) {
+    if (param->hasAttr<HLSLTriangleAttr>()) {
+      ++primitiveTypes;
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::Triangles, {});
+    } else if (param->hasAttr<HLSLTriangleAdjAttr>()) {
+      ++primitiveTypes;
+      theBuilder.addExecutionMode(
+          entryFunctionId, spv::ExecutionMode::InputTrianglesAdjacency, {});
+    } else if (param->hasAttr<HLSLPointAttr>()) {
+      ++primitiveTypes;
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::InputPoints, {});
+    } else if (param->hasAttr<HLSLLineAdjAttr>()) {
+      ++primitiveTypes;
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::InputLinesAdjacency, {});
+    } else if (param->hasAttr<HLSLLineAttr>()) {
+      ++primitiveTypes;
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::InputLines, {});
+    }
+  }
+  if (primitiveTypes > 1) {
+    emitError(
+        "only one primitive type can be specified in the geometry shader");
+    return false;
+  }
+
   return true;
   return true;
 }
 }
 
 

+ 6 - 0
tools/clang/test/CodeGenSPIRV/primitive.error.gs.hlsl

@@ -0,0 +1,6 @@
+// Run: %dxc -T gs_6_0 -E main
+
+[maxvertexcount(3)]
+void main(triangle in uint i[3] : TriangleVertexID, line in uint j[2] : LineVertexID) {}
+
+// CHECK: error: only one primitive type can be specified in the geometry shader

+ 6 - 0
tools/clang/test/CodeGenSPIRV/primitive.line.gs.hlsl

@@ -0,0 +1,6 @@
+// Run: %dxc -T gs_6_0 -E main
+
+// CHECK: OpExecutionMode %main InputLines
+
+[maxvertexcount(3)]
+void main(line in uint id[2] : VertexID) {}

+ 6 - 0
tools/clang/test/CodeGenSPIRV/primitive.lineadj.gs.hlsl

@@ -0,0 +1,6 @@
+// Run: %dxc -T gs_6_0 -E main
+
+// CHECK: OpExecutionMode %main InputLinesAdjacency
+
+[maxvertexcount(3)]
+void main(lineadj in uint id[4] : VertexID) {}

+ 6 - 0
tools/clang/test/CodeGenSPIRV/primitive.point.gs.hlsl

@@ -0,0 +1,6 @@
+// Run: %dxc -T gs_6_0 -E main
+
+// CHECK: OpExecutionMode %main InputPoints
+
+[maxvertexcount(3)]
+void main(point in uint id[1] : VertexID) {}

+ 6 - 0
tools/clang/test/CodeGenSPIRV/primitive.triangle.gs.hlsl

@@ -0,0 +1,6 @@
+// Run: %dxc -T gs_6_0 -E main
+
+// CHECK: OpExecutionMode %main Triangles
+
+[maxvertexcount(3)]
+void main(triangle in uint i[3] : VertexID) {}

+ 6 - 0
tools/clang/test/CodeGenSPIRV/primitive.triangleadj.gs.hlsl

@@ -0,0 +1,6 @@
+// Run: %dxc -T gs_6_0 -E main
+
+// CHECK: OpExecutionMode %main InputTrianglesAdjacency
+
+[maxvertexcount(3)]
+void main(triangleadj in uint id[6] : VertexID) {}

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

@@ -683,6 +683,22 @@ TEST_F(FileTest, AttributeMaxVertexCount) {
   runFileTest("attribute.max-vertex-count.hlsl");
   runFileTest("attribute.max-vertex-count.hlsl");
 }
 }
 
 
+// For geometry shader primitive types
+TEST_F(FileTest, PrimitivePointGS) { runFileTest("primitive.point.gs.hlsl"); }
+TEST_F(FileTest, PrimitiveLineGS) { runFileTest("primitive.line.gs.hlsl"); }
+TEST_F(FileTest, PrimitiveTriangleGS) {
+  runFileTest("primitive.triangle.gs.hlsl");
+}
+TEST_F(FileTest, PrimitiveLineAdjGS) {
+  runFileTest("primitive.lineadj.gs.hlsl");
+}
+TEST_F(FileTest, PrimitiveTriangleAdjGS) {
+  runFileTest("primitive.triangleadj.gs.hlsl");
+}
+TEST_F(FileTest, PrimitiveErrorGS) {
+  runFileTest("primitive.error.gs.hlsl", /*expectSuccess*/ false);
+}
+
 // Vulkan/SPIR-V specific
 // Vulkan/SPIR-V specific
 TEST_F(FileTest, SpirvStorageClass) { runFileTest("spirv.storage-class.hlsl"); }
 TEST_F(FileTest, SpirvStorageClass) { runFileTest("spirv.storage-class.hlsl"); }