Browse Source

[spirv] Add support for SV_ViewID (#836)

Also error out on seeing unknown patch constant function parameter.
Lei Zhang 7 năm trước cách đây
mục cha
commit
d1f1665865

+ 11 - 1
docs/SPIR-V.rst

@@ -866,6 +866,16 @@ some system-value (SV) semantic strings will be translated into SPIR-V
 | SV_Coverage               +-------------+--------------------------+-----------------------+-----------------------+
 |                           | PSOut       | ``SampleMask``           | N/A                   | ``Shader``            |
 +---------------------------+-------------+--------------------------+-----------------------+-----------------------+
+|                           | VSIn        | ``ViewIndex``            | N/A                   | ``MultiView``         |
+|                           +-------------+--------------------------+-----------------------+-----------------------+
+|                           | HSIn        | ``ViewIndex``            | N/A                   | ``MultiView``         |
+|                           +-------------+--------------------------+-----------------------+-----------------------+
+| SV_ViewID                 | DSIn        | ``ViewIndex``            | N/A                   | ``MultiView``         |
+|                           +-------------+--------------------------+-----------------------+-----------------------+
+|                           | GSIn        | ``ViewIndex``            | N/A                   | ``MultiView``         |
+|                           +-------------+--------------------------+-----------------------+-----------------------+
+|                           | PSIn        | ``ViewIndex``            | N/A                   | ``MultiView``         |
++---------------------------+-------------+--------------------------+-----------------------+-----------------------+
 
 For entities (function parameters, function return values, struct fields) with
 the above SV semantic strings attached, SPIR-V variables of the
@@ -1449,7 +1459,7 @@ extended instruction mapping, so they are handled with additional steps:
   This is achieved by performing ``int4(input.zyxw * 255.002)`` using SPIR-V ``OpVectorShuffle``,
   ``OpVectorTimesScalar``, and ``OpConvertFToS``, respectively.
 - ``dst``: Calculates a distance vector. The resulting vector, ``dest``, has the following specifications:
-  ``dest.x = 1.0``, ``dest.y = src0.y * src1.y``, ``dest.z = src0.z``, and ``dest.w = src1.w``. 
+  ``dest.x = 1.0``, ``dest.y = src0.y * src1.y``, ``dest.z = src0.z``, and ``dest.w = src1.w``.
   Uses SPIR-V ``OpCompositeExtract`` and ``OpFMul``.
 
 Using SPIR-V opcode

+ 11 - 0
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -1564,6 +1564,17 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
     stageVar->setIsSpirvBuiltin();
     return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::SampleMask);
   }
+  // According to DXIL spec, the ViewID SV can only be used by VSIn, PCIn,
+  // HSIn, DSIn, GSIn, PSIn.
+  // According to Vulkan spec, the ViewIndex BuiltIn can only be used in
+  // VS/HS/DS/GS/PS input.
+  case hlsl::Semantic::Kind::ViewID: {
+    theBuilder.addExtension("SPV_KHR_multiview");
+    theBuilder.requireCapability(spv::Capability::MultiView);
+
+    stageVar->setIsSpirvBuiltin();
+    return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::ViewIndex);
+  }
   case hlsl::Semantic::Kind::InnerCoverage: {
     emitError("no equivalent for semantic SV_InnerCoverage in Vulkan", srcLoc);
     return 0;

+ 43 - 17
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -6097,6 +6097,7 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
   uint32_t numOutputControlPoints = 0;
   uint32_t outputControlPointIdVal = 0; // SV_OutputControlPointID value
   uint32_t primitiveIdVar = 0;          // SV_PrimitiveID variable
+  uint32_t viewIdVar = 0;               // SV_ViewID variable
   uint32_t hullMainInputPatchParam = 0; // Temporary parameter for InputPatch<>
 
   // The array size of per-vertex input/output variables
@@ -6225,12 +6226,15 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
 
       theBuilder.createStore(tempVar, loadedValue);
 
-      // Record the temporary variable holding SV_OutputControlPointID and
-      // SV_PrimitiveID. It may be used later in the patch constant function.
+      // Record the temporary variable holding SV_OutputControlPointID,
+      // SV_PrimitiveID, and SV_ViewID. It may be used later in the patch
+      // constant function.
       if (hasSemantic(param, hlsl::DXIL::SemanticKind::OutputControlPointID))
         outputControlPointIdVal = loadedValue;
-      if (hasSemantic(param, hlsl::DXIL::SemanticKind::PrimitiveID))
+      else if (hasSemantic(param, hlsl::DXIL::SemanticKind::PrimitiveID))
         primitiveIdVar = tempVar;
+      else if (hasSemantic(param, hlsl::DXIL::SemanticKind::ViewID))
+        viewIdVar = tempVar;
     }
   }
 
@@ -6250,9 +6254,10 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
     if (!declIdMapper.createStageOutputVar(decl, numOutputControlPoints,
                                            outputControlPointIdVal, retVal))
       return false;
-    if (!processHullEntryPointOutputAndPatchConstFunc(
+    if (!processHSEntryPointOutputAndPCF(
             decl, retType, retVal, numOutputControlPoints,
-            outputControlPointIdVal, primitiveIdVar, hullMainInputPatchParam))
+            outputControlPointIdVal, primitiveIdVar, viewIdVar,
+            hullMainInputPatchParam))
       return false;
   } else {
     if (!declIdMapper.createStageOutputVar(decl, retVal, /*forPCF*/ false))
@@ -6290,10 +6295,10 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
   return true;
 }
 
-bool SPIRVEmitter::processHullEntryPointOutputAndPatchConstFunc(
+bool SPIRVEmitter::processHSEntryPointOutputAndPCF(
     const FunctionDecl *hullMainFuncDecl, uint32_t retType, uint32_t retVal,
     uint32_t numOutputControlPoints, uint32_t outputControlPointId,
-    uint32_t primitiveId, uint32_t hullMainInputPatch) {
+    uint32_t primitiveId, uint32_t viewId, uint32_t hullMainInputPatch) {
   // This method may only be called for Hull shaders.
   assert(shaderModel.IsHS());
 
@@ -6356,32 +6361,53 @@ bool SPIRVEmitter::processHullEntryPointOutputAndPatchConstFunc(
   theBuilder.setMergeTarget(mergeBB);
 
   theBuilder.setInsertPoint(thenBB);
+
   // Call the PCF. Since the function is not explicitly called, we must first
   // register an ID for it.
   const uint32_t pcfId = declIdMapper.getOrRegisterFnResultId(patchConstFunc);
   const uint32_t pcfRetType =
       typeTranslator.translateType(patchConstFunc->getReturnType());
+
   std::vector<uint32_t> pcfParams;
+
+  // A lambda for creating a stage input variable and its associated temporary
+  // variable for function call. Also initializes the temporary variable using
+  // the contents loaded from the stage input variable. Returns the <result-id>
+  // of the temporary variable.
+  const auto createParmVarAndInitFromStageInputVar =
+      [this](const ParmVarDecl *param) {
+        const uint32_t typeId = typeTranslator.translateType(param->getType());
+        std::string tempVarName = "param.var." + param->getNameAsString();
+        const uint32_t tempVar = theBuilder.addFnVar(typeId, tempVarName);
+        uint32_t loadedValue = 0;
+        declIdMapper.createStageInputVar(param, &loadedValue, /*forPCF*/ true);
+        theBuilder.createStore(tempVar, loadedValue);
+        return tempVar;
+      };
+
   for (const auto *param : patchConstFunc->parameters()) {
     // Note: According to the HLSL reference, the PCF takes an InputPatch of
     // ControlPoints as well as the PatchID (PrimitiveID). This does not
     // necessarily mean that they are present. There is also no requirement
     // for the order of parameters passed to PCF.
-    if (hlsl::IsHLSLInputPatchType(param->getType()))
+    if (hlsl::IsHLSLInputPatchType(param->getType())) {
       pcfParams.push_back(hullMainInputPatch);
-    if (hlsl::IsHLSLOutputPatchType(param->getType()))
+    } else if (hlsl::IsHLSLOutputPatchType(param->getType())) {
       pcfParams.push_back(hullMainOutputPatch);
-    if (hasSemantic(param, hlsl::DXIL::SemanticKind::PrimitiveID)) {
+    } else if (hasSemantic(param, hlsl::DXIL::SemanticKind::PrimitiveID)) {
       if (!primitiveId) {
-        const uint32_t typeId = typeTranslator.translateType(param->getType());
-        std::string tempVarName = "param.var." + param->getNameAsString();
-        const uint32_t tempVar = theBuilder.addFnVar(typeId, tempVarName);
-        uint32_t loadedValue = 0;
-        declIdMapper.createStageInputVar(param, &loadedValue, /*forPCF*/ true);
-        theBuilder.createStore(tempVar, loadedValue);
-        primitiveId = tempVar;
+        primitiveId = createParmVarAndInitFromStageInputVar(param);
       }
       pcfParams.push_back(primitiveId);
+    } else if (hasSemantic(param, hlsl::DXIL::SemanticKind::ViewID)) {
+      if (!viewId) {
+        viewId = createParmVarAndInitFromStageInputVar(param);
+      }
+      pcfParams.push_back(viewId);
+    } else {
+      emitError("patch constant function parameter '%0' unknown",
+                param->getLocation())
+          << param->getName();
     }
   }
   const uint32_t pcfResultId =

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

@@ -467,10 +467,12 @@ private:
   ///
   /// The method panics if it is called for any shader kind other than Hull
   /// shaders.
-  bool processHullEntryPointOutputAndPatchConstFunc(
-      const FunctionDecl *hullMainFuncDecl, uint32_t retType, uint32_t retVal,
-      uint32_t numOutputControlPoints, uint32_t outputControlPointId,
-      uint32_t primitiveId, uint32_t hullMainInputPatch);
+  bool processHSEntryPointOutputAndPCF(const FunctionDecl *hullMainFuncDecl,
+                                       uint32_t retType, uint32_t retVal,
+                                       uint32_t numOutputControlPoints,
+                                       uint32_t outputControlPointId,
+                                       uint32_t primitiveId, uint32_t viewId,
+                                       uint32_t hullMainInputPatch);
 
 private:
   /// \brief Returns true iff *all* the case values in the given switch

+ 58 - 0
tools/clang/test/CodeGenSPIRV/hs.pcf.view-id.1.hlsl

@@ -0,0 +1,58 @@
+// Run: %dxc -T hs_6_1 -E main
+
+// Test: Both entry point and PCF Takes ViewID
+
+// CHECK:      OpCapability MultiView
+// CHECK:      OpExtension "SPV_KHR_multiview"
+
+// CHECK:      OpEntryPoint TessellationControl
+// CHECK-SAME: [[viewindex:%\d+]]
+
+// CHECK:      OpDecorate [[viewindex]] BuiltIn ViewIndex
+
+// CHECK:      [[pcfType:%\d+]] = OpTypeFunction %HsPcfOut %_ptr_Function_uint
+// CHECK:         [[viewindex]] = OpVariable %_ptr_Input_uint Input
+
+#define NumOutPoints 2
+
+struct HsCpIn {
+    int foo : FOO;
+};
+
+struct HsCpOut {
+    int bar : BAR;
+};
+
+struct HsPcfOut {
+  float tessOuter[4] : SV_TessFactor;
+  float tessInner[2] : SV_InsideTessFactor;
+};
+
+// Patch Constant Function
+HsPcfOut pcf(uint viewid : SV_ViewID) {
+  HsPcfOut output;
+  output = (HsPcfOut)0;
+  return output;
+}
+
+[domain("quad")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_ccw")]
+[outputcontrolpoints(NumOutPoints)]
+[patchconstantfunc("pcf")]
+HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch,
+             uint id : SV_OutputControlPointID,
+             uint viewid : SV_ViewID) {
+    HsCpOut output;
+    output = (HsCpOut)0;
+    return output;
+// CHECK:             %main = OpFunction %void None {{%\d+}}
+// CHECK: %param_var_viewid = OpVariable %_ptr_Function_uint Function
+
+// CHECK:      [[val:%\d+]] = OpLoad %uint [[viewindex]]
+// CHECK:                     OpStore %param_var_viewid [[val]]
+// CHECK:          {{%\d+}} = OpFunctionCall %HsPcfOut %pcf %param_var_viewid
+
+// CHECK:              %pcf = OpFunction %HsPcfOut None [[pcfType]]
+// CHECK:           %viewid = OpFunctionParameter %_ptr_Function_uint
+}

+ 57 - 0
tools/clang/test/CodeGenSPIRV/hs.pcf.view-id.2.hlsl

@@ -0,0 +1,57 @@
+// Run: %dxc -T hs_6_1 -E main
+
+// Test: Both entry point and PCF Takes ViewID
+
+// CHECK:      OpCapability MultiView
+// CHECK:      OpExtension "SPV_KHR_multiview"
+
+// CHECK:      OpEntryPoint TessellationControl
+// CHECK-SAME: [[viewindex:%\d+]]
+
+// CHECK:      OpDecorate [[viewindex]] BuiltIn ViewIndex
+
+// CHECK:      [[pcfType:%\d+]] = OpTypeFunction %HsPcfOut %_ptr_Function_uint
+// CHECK:         [[viewindex]] = OpVariable %_ptr_Input_uint Input
+
+#define NumOutPoints 2
+
+struct HsCpIn {
+    int foo : FOO;
+};
+
+struct HsCpOut {
+    int bar : BAR;
+};
+
+struct HsPcfOut {
+  float tessOuter[4] : SV_TessFactor;
+  float tessInner[2] : SV_InsideTessFactor;
+};
+
+// Patch Constant Function
+HsPcfOut pcf(uint viewid : SV_ViewID) {
+  HsPcfOut output;
+  output = (HsPcfOut)0;
+  return output;
+}
+
+[domain("quad")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_ccw")]
+[outputcontrolpoints(NumOutPoints)]
+[patchconstantfunc("pcf")]
+HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch,
+             uint id : SV_OutputControlPointID) {
+    HsCpOut output;
+    output = (HsCpOut)0;
+    return output;
+// CHECK:             %main = OpFunction %void None {{%\d+}}
+// CHECK: %param_var_viewid = OpVariable %_ptr_Function_uint Function
+
+// CHECK:      [[val:%\d+]] = OpLoad %uint [[viewindex]]
+// CHECK:                     OpStore %param_var_viewid [[val]]
+// CHECK:          {{%\d+}} = OpFunctionCall %HsPcfOut %pcf %param_var_viewid
+
+// CHECK:              %pcf = OpFunction %HsPcfOut None [[pcfType]]
+// CHECK:           %viewid = OpFunctionParameter %_ptr_Function_uint
+}

+ 37 - 0
tools/clang/test/CodeGenSPIRV/semantic.view-id.ds.hlsl

@@ -0,0 +1,37 @@
+// Run: %dxc -T ds_6_1 -E main
+
+// HS PCF output
+struct HsPcfOut {
+  float  outTessFactor[4]   : SV_TessFactor;
+  float  inTessFactor[2]    : SV_InsideTessFactor;
+};
+
+// Per-vertex input structs
+struct DsCpIn {
+    int foo : FOO;
+};
+
+// Per-vertex output structs
+struct DsCpOut {
+    int foo : FOO;
+};
+
+// CHECK:      OpCapability MultiView
+// CHECK:      OpExtension "SPV_KHR_multiview"
+
+// CHECK:      OpEntryPoint TessellationEvaluation
+// CHECK-SAME: [[viewindex:%\d+]]
+
+// CHECK:      OpDecorate [[viewindex]] BuiltIn ViewIndex
+
+// CHECK:      [[viewindex]] = OpVariable %_ptr_Input_uint Input
+
+
+[domain("quad")]
+DsCpOut main(OutputPatch<DsCpIn, 3> patch,
+             HsPcfOut pcfData,
+             uint viewid : SV_ViewID) {
+  DsCpOut dsOut;
+  dsOut = (DsCpOut)0;
+  return dsOut;
+}

+ 35 - 0
tools/clang/test/CodeGenSPIRV/semantic.view-id.gs.hlsl

@@ -0,0 +1,35 @@
+// Run: %dxc -T gs_6_1 -E main
+
+
+// GS per-vertex input
+struct GsVIn {
+    int foo : FOO;
+};
+
+// GS per-vertex output
+struct GsVOut {
+    int foo : FOO;
+};
+
+// CHECK:      OpCapability MultiView
+// CHECK:      OpExtension "SPV_KHR_multiview"
+
+// CHECK:      OpEntryPoint Geometry
+// CHECK-SAME: [[viewindex:%\d+]]
+
+// CHECK:      OpDecorate [[viewindex]] BuiltIn ViewIndex
+
+// CHECK:      [[viewindex]] = OpVariable %_ptr_Input_uint Input
+
+
+[maxvertexcount(2)]
+void main(in    line GsVIn              inData[2],
+          inout      LineStream<GsVOut> outData,
+                     uint               viewid  : SV_ViewID) {
+
+    GsVOut vertex;
+    vertex = (GsVOut)0;
+    outData.Append(vertex);
+
+    outData.RestartStrip();
+}

+ 46 - 0
tools/clang/test/CodeGenSPIRV/semantic.view-id.hs.hlsl

@@ -0,0 +1,46 @@
+// Run: %dxc -T hs_6_1 -E main
+
+// CHECK:      OpCapability MultiView
+// CHECK:      OpExtension "SPV_KHR_multiview"
+
+// CHECK:      OpEntryPoint TessellationControl
+// CHECK-SAME: [[viewindex:%\d+]]
+
+// CHECK:      OpDecorate [[viewindex]] BuiltIn ViewIndex
+
+// CHECK:      [[viewindex]] = OpVariable %_ptr_Input_uint Input
+
+#define NumOutPoints 2
+
+struct HsCpIn {
+    int foo : FOO;
+};
+
+struct HsCpOut {
+    int bar : BAR;
+};
+
+struct HsPcfOut {
+  float tessOuter[4] : SV_TessFactor;
+  float tessInner[2] : SV_InsideTessFactor;
+};
+
+// Patch Constant Function
+HsPcfOut pcf() {
+  HsPcfOut output;
+  output = (HsPcfOut)0;
+  return output;
+}
+
+[domain("quad")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_ccw")]
+[outputcontrolpoints(NumOutPoints)]
+[patchconstantfunc("pcf")]
+HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch,
+             uint id : SV_OutputControlPointID,
+             uint viewid : SV_ViewID) {
+    HsCpOut output;
+    output = (HsCpOut)0;
+    return output;
+}

+ 15 - 0
tools/clang/test/CodeGenSPIRV/semantic.view-id.ps.hlsl

@@ -0,0 +1,15 @@
+// Run: %dxc -T ps_6_1 -E main
+
+// CHECK:      OpCapability MultiView
+// CHECK:      OpExtension "SPV_KHR_multiview"
+
+// CHECK:      OpEntryPoint Fragment
+// CHECK-SAME: [[viewindex:%\d+]]
+
+// CHECK:      OpDecorate [[viewindex]] BuiltIn ViewIndex
+
+// CHECK:      [[viewindex]] = OpVariable %_ptr_Input_uint Input
+
+float4 main(uint viewid: SV_ViewID) : SV_Target {
+    return viewid;
+}

+ 16 - 0
tools/clang/test/CodeGenSPIRV/semantic.view-id.vs.hlsl

@@ -0,0 +1,16 @@
+// Run: %dxc -T vs_6_1 -E main
+
+// CHECK:      OpCapability MultiView
+// CHECK:      OpExtension "SPV_KHR_multiview"
+
+// CHECK:      OpEntryPoint Vertex
+// CHECK-SAME: [[viewindex:%\d+]]
+
+// CHECK:      OpDecorate [[viewindex]] BuiltIn ViewIndex
+
+// CHECK:      [[viewindex]] = OpVariable %_ptr_Input_int Input
+
+int main(int input: SV_ViewID) : A {
+    return input;
+}
+

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

@@ -496,6 +496,12 @@ TEST_F(FileTest, SemanticCoverageTypeMismatchPS) {
 TEST_F(FileTest, SemanticInnerCoveragePS) {
   runFileTest("semantic.inner-coverage.ps.hlsl", Expect::Failure);
 }
+TEST_F(FileTest, SemanticViewIDVS) { runFileTest("semantic.view-id.vs.hlsl"); }
+TEST_F(FileTest, SemanticViewIDHS) { runFileTest("semantic.view-id.hs.hlsl"); }
+TEST_F(FileTest, SemanticViewIDDS) { runFileTest("semantic.view-id.ds.hlsl"); }
+TEST_F(FileTest, SemanticViewIDGS) { runFileTest("semantic.view-id.gs.hlsl"); }
+TEST_F(FileTest, SemanticViewIDPS) { runFileTest("semantic.view-id.ps.hlsl"); }
+
 TEST_F(FileTest, SemanticArbitrary) { runFileTest("semantic.arbitrary.hlsl"); }
 TEST_F(FileTest, SemanticArbitraryDeclLocation) {
   runFileTest("semantic.arbitrary.location.decl.hlsl");
@@ -1018,6 +1024,12 @@ TEST_F(FileTest, HullShaderPCFTakesPrimitiveId) {
 TEST_F(FileTest, HullShaderPCFTakesPrimitiveIdButMainDoesnt) {
   runFileTest("hs.pcf.primitive-id.2.hlsl");
 }
+TEST_F(FileTest, HullShaderPCFTakesViewId) {
+  runFileTest("hs.pcf.view-id.1.hlsl");
+}
+TEST_F(FileTest, HullShaderPCFTakesViewIdButMainDoesnt) {
+  runFileTest("hs.pcf.view-id.2.hlsl");
+}
 // HS: for the structure of hull shaders
 TEST_F(FileTest, HullShaderStructure) { runFileTest("hs.structure.hlsl"); }