Browse Source

[spirv] Support PS SV_Depth{Greater|Less}Equal and SV_IsFrontFace (#563)

Lei Zhang 8 years ago
parent
commit
a727fc8838

+ 17 - 13
docs/SPIR-V.rst

@@ -319,19 +319,23 @@ Firstly, under certain `SigPoints <https://github.com/Microsoft/DirectXShaderCom
 some system-value (SV) semantic strings will be translated into SPIR-V
 ``BuiltIn`` decorations:
 
-+---------------+----------+--------------------+
-| HLSL Semantic | SigPoint | SPIR-V ``BuiltIn`` |
-+===============+==========+====================+
-|               | VSOut    | ``Position``       |
-+ SV_Position   +----------+--------------------+
-|               | PSIn     | ``FragCoord``      |
-+---------------+----------+--------------------+
-| SV_VertexID   | VSIn     | ``VertexIndex``    |
-+---------------+----------+--------------------+
-| SV_InstanceID | VSIn     | ``InstanceIndex``  |
-+---------------+----------+--------------------+
-| SV_Depth      | PSOut    | ``FragDepth``      |
-+---------------+----------+--------------------+
++----------------------+----------+--------------------+-----------------------+
+| HLSL Semantic        | SigPoint | SPIR-V ``BuiltIn`` | SPIR-V Execution Mode |
++======================+==========+====================+=======================+
+|                      | VSOut    | ``Position``       | N/A                   |
+| SV_Position          +----------+--------------------+-----------------------+
+|                      | PSIn     | ``FragCoord``      | N/A                   |
++----------------------+----------+--------------------+-----------------------+
+| SV_VertexID          | VSIn     | ``VertexIndex``    | N/A                   |
++----------------------+----------+--------------------+-----------------------+
+| SV_InstanceID        | VSIn     | ``InstanceIndex``  | N/A                   |
++----------------------+----------+--------------------+-----------------------+
+| SV_Depth             | PSOut    | ``FragDepth``      | N/A                   |
++----------------------+----------+--------------------+-----------------------+
+| SV_DepthGreaterEqual | PSOut    | ``FragDepth``      | ``DepthGreater``      |
++----------------------+----------+--------------------+-----------------------+
+| SV_DepthLessEqual    | PSOut    | ``FragDepth``      | ``DepthLess``         |
++----------------------+----------+--------------------+-----------------------+
 
 [TODO] add other SV semantic strings in the above
 

+ 2 - 1
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -11,6 +11,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "clang/SPIRV/InstBuilder.h"
 #include "clang/SPIRV/SPIRVContext.h"
@@ -204,7 +205,7 @@ public:
 
   /// \brief Adds an execution mode to the module under construction.
   void addExecutionMode(uint32_t entryPointId, spv::ExecutionMode em,
-                        const std::vector<uint32_t> &params);
+                        llvm::ArrayRef<uint32_t> params);
 
   /// \brief If not added already, adds an OpExtInstImport (import of extended
   /// instruction set) of the GLSL instruction set. Returns the <result-id> for

+ 31 - 3
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -207,6 +207,9 @@ bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl,
                            ? namePrefix + "." + semantic->GetName()
                            : namePrefix + "." + semanticStr;
     const uint32_t varId = createSpirvStageVar(&stageVar, name);
+    if (varId == 0)
+      return false;
+
     stageVar.setSpirvId(varId);
 
     stageVars.push_back(stageVar);
@@ -273,7 +276,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
   switch (semanticKind) {
   // According to DXIL spec, the Position SV can be used by all SigPoints
   // other than PCIn, HSIn, GSIn, PSOut, CSIn.
-  // According to Vulkan spec, the Position decoration can only be used
+  // According to Vulkan spec, the Position BuiltIn can only be used
   // by PSOut, HS/DS/GS In/Out.
   case hlsl::Semantic::Kind::Position: {
     switch (sigPointKind) {
@@ -297,7 +300,8 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
     return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::VertexIndex);
   // According to DXIL spec, the InstanceID SV can  be used by VSIn, VSOut,
   // HSCPIn, HSCPOut, DSCPIn, DSOut, GSVIn, GSOut, PSIn.
-  // According to Vulkan spec, the InstanceIndex can only be used by VSIn.
+  // According to Vulkan spec, the InstanceIndex BuitIn can only be used by
+  // VSIn.
   case hlsl::Semantic::Kind::InstanceID: {
     switch (sigPointKind) {
     case hlsl::SigPoint::Kind::VSIn:
@@ -313,10 +317,34 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
       break;
     }
   }
-  // According to DXIL spec, the Depth SV can only be used by PSOut.
+  // According to DXIL spec, the Depth{|GreaterEqual|LessEqual} SV can only be
+  // used by PSOut.
   case hlsl::Semantic::Kind::Depth:
+  case hlsl::Semantic::Kind::DepthGreaterEqual:
+  case hlsl::Semantic::Kind::DepthLessEqual: {
     stageVar->setIsSpirvBuiltin();
+    if (semanticKind == hlsl::Semantic::Kind::DepthGreaterEqual)
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::DepthGreater, {});
+    else if (semanticKind == hlsl::Semantic::Kind::DepthLessEqual)
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::DepthLess, {});
     return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::FragDepth);
+  }
+  // According to DXIL spec, the IsFrontFace SV can only be used by GSOut and
+  // PSIn.
+  // According to Vulkan spec, the FrontFacing BuitIn can only be used in PSIn.
+  case hlsl::Semantic::Kind::IsFrontFace: {
+    switch (sigPointKind) {
+    case hlsl::SigPoint::Kind::PSIn:
+      stageVar->setIsSpirvBuiltin();
+      return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::FrontFacing);
+    default:
+      emitError("semantic IsFrontFace for SigPoint %0 unimplemented yet")
+          << stageVar->getSigPoint()->GetName();
+      break;
+    }
+  }
   // According to DXIL spec, the Target SV can only be used by PSOut.
   // There is no corresponding builtin decoration in SPIR-V. So generate normal
   // Vulkan stage input/output variables.

+ 5 - 1
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -112,6 +112,8 @@ public:
   /// instruction. The given decl will be treated as normal decl.
   void registerDeclResultId(const NamedDecl *symbol, uint32_t resultId);
 
+  void setEntryFunctionId(uint32_t id) { entryFunctionId = id; }
+
 public:
   /// The struct containing SPIR-V information of a AST Decl.
   struct DeclSpirvInfo {
@@ -184,6 +186,8 @@ private:
   TypeTranslator typeTranslator;
   DiagnosticsEngine &diags;
 
+  uint32_t entryFunctionId;
+
   /// Mapping of all Clang AST decls to their <result-id>s.
   llvm::DenseMap<const NamedDecl *, DeclSpirvInfo> astDecls;
   /// Vector of all defined stage variables.
@@ -194,7 +198,7 @@ DeclResultIdMapper::DeclResultIdMapper(const hlsl::ShaderModel &model,
                                        ModuleBuilder &builder,
                                        DiagnosticsEngine &diag)
     : shaderModel(model), theBuilder(builder), typeTranslator(builder, diag),
-      diags(diag) {}
+      diags(diag), entryFunctionId(0) {}
 
 } // end namespace spirv
 } // end namespace clang

+ 1 - 1
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -299,7 +299,7 @@ uint32_t ModuleBuilder::createExtInst(uint32_t resultType, uint32_t setId,
 
 void ModuleBuilder::addExecutionMode(uint32_t entryPointId,
                                      spv::ExecutionMode em,
-                                     const std::vector<uint32_t> &params) {
+                                     llvm::ArrayRef<uint32_t> params) {
   instBuilder.opExecutionMode(entryPointId, em);
   for (const auto &param : params) {
     instBuilder.literalInteger(param);

+ 10 - 19
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -365,8 +365,7 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
     funcName = "src." + funcName;
 
     // Create wrapper for the entry function
-    entryFunctionId = emitEntryFunctionWrapper(decl, funcId);
-    if (entryFunctionId == 0)
+    if (!emitEntryFunctionWrapper(decl, funcId))
       return;
   } else {
     // Non-entry functions are added to the work queue following function
@@ -3107,31 +3106,23 @@ void SPIRVEmitter::AddRequiredCapabilitiesForShaderModel() {
 
 void SPIRVEmitter::AddExecutionModeForEntryPoint(uint32_t entryPointId) {
   if (shaderModel.IsPS()) {
-    // TODO: Implement the logic to determine the proper Execution Mode for
-    // fragment shaders. Currently using OriginUpperLeft as default.
     theBuilder.addExecutionMode(entryPointId,
                                 spv::ExecutionMode::OriginUpperLeft, {});
-    emitWarning("Execution mode for fragment shaders is "
-                "currently set to OriginUpperLeft by default.");
-  } else {
-    emitWarning(
-        "Execution mode is currently only defined for fragment shaders.");
-    // TODO: Implement logic for adding proper execution mode for other
-    // shader stages. Silently skipping for now.
   }
 }
 
-uint32_t SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
-                                                uint32_t entryFuncId) {
+bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
+                                            const uint32_t entryFuncId) {
   // Construct the wrapper function signature.
   const uint32_t voidType = theBuilder.getVoidType();
   const uint32_t funcType = theBuilder.getFunctionType(voidType, {});
 
   // The wrapper entry function surely does not have pre-assigned <result-id>
   // for it like other functions that got added to the work queue following
-  // function calls.
-  const uint32_t funcId =
+  // function calls. And the wrapper is the entry function.
+  entryFunctionId =
       theBuilder.beginFunction(funcType, voidType, decl->getName());
+  declIdMapper.setEntryFunctionId(entryFunctionId);
 
   // The entry basic block.
   const uint32_t entryLabel = theBuilder.createBasicBlock();
@@ -3156,7 +3147,7 @@ uint32_t SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
     if (!param->getAttr<HLSLOutAttr>()) {
       uint32_t loadedValue = 0;
       if (!declIdMapper.createStageInputVar(param, &loadedValue))
-        return 0;
+        return false;
 
       theBuilder.createStore(tempVar, loadedValue);
     }
@@ -3169,7 +3160,7 @@ uint32_t SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
 
   // Create and write stage output variables for return value
   if (!declIdMapper.createStageOutputVar(decl, retVal))
-    return 0;
+    return false;
 
   // Create and write stage output variables for parameters marked as out/inout
   for (uint32_t i = 0; i < decl->getNumParams(); ++i) {
@@ -3180,14 +3171,14 @@ uint32_t SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
       const uint32_t loadedParam = theBuilder.createLoad(typeId, params[i]);
 
       if (!declIdMapper.createStageOutputVar(param, loadedParam))
-        return 0;
+        return false;
     }
   }
 
   theBuilder.createReturn();
   theBuilder.endFunction();
 
-  return funcId;
+  return true;
 }
 
 bool SPIRVEmitter::allSwitchCasesAreIntegerLiterals(const Stmt *root) {

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

@@ -313,7 +313,8 @@ private:
   /// shader model.
   void AddExecutionModeForEntryPoint(uint32_t entryPointId);
 
-  /// \brief Emits a wrapper function for the entry function.
+  /// \brief Emits a wrapper function for the entry function and returns true
+  /// on success.
   ///
   /// The wrapper function loads the values of all stage input variables and
   /// creates composites as expected by the source code entry function. It then
@@ -323,8 +324,8 @@ private:
   ///
   /// The wrapper function is also responsible for initializing global static
   /// variables for some cases.
-  uint32_t emitEntryFunctionWrapper(const FunctionDecl *entryFunction,
-                                    uint32_t entryFuncId);
+  bool emitEntryFunctionWrapper(const FunctionDecl *entryFunction,
+                                uint32_t entryFuncId);
 
 private:
   /// \brief Returns true iff *all* the case values in the given switch

+ 13 - 0
tools/clang/test/CodeGenSPIRV/semantic.depth-greater-equal.ps.hlsl

@@ -0,0 +1,13 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpEntryPoint Fragment %main "main" %in_var_A %gl_FragDepth
+
+// CHECK: OpExecutionMode %main DepthGreater
+
+// CHECK: OpDecorate %gl_FragDepth BuiltIn FragDepth
+
+// CHECK: %gl_FragDepth = OpVariable %_ptr_Output_float Output
+
+float main(float input: A) : SV_DepthGreaterEqual {
+    return input;
+}

+ 14 - 0
tools/clang/test/CodeGenSPIRV/semantic.depth-less-equal.ps.hlsl

@@ -0,0 +1,14 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpEntryPoint Fragment %main "main" %in_var_A %gl_FragDepth
+
+// CHECK: OpExecutionMode %main DepthLess
+
+// CHECK: OpDecorate %gl_FragDepth BuiltIn FragDepth
+
+// CHECK: %gl_FragDepth = OpVariable %_ptr_Output_float Output
+
+float main(float input: A) : SV_DepthLessEqual {
+    return input;
+}
+

+ 11 - 0
tools/clang/test/CodeGenSPIRV/semantic.is-front-face.ps.hlsl

@@ -0,0 +1,11 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpEntryPoint Fragment %main "main" %gl_FrontFacing %out_var_SV_Target
+
+// CHECK: OpDecorate %gl_FrontFacing BuiltIn FrontFacing
+
+// CHECK: %gl_FrontFacing = OpVariable %_ptr_Input_bool Input
+
+float4 main(bool ff: SV_IsFrontFace) : SV_Target {
+    return ff;
+}

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

@@ -269,6 +269,15 @@ TEST_F(FileTest, SemanticInstanceIDPS) {
 }
 TEST_F(FileTest, SemanticTargetPS) { runFileTest("semantic.target.ps.hlsl"); }
 TEST_F(FileTest, SemanticDepthPS) { runFileTest("semantic.depth.ps.hlsl"); }
+TEST_F(FileTest, SemanticDepthGreaterEqualPS) {
+  runFileTest("semantic.depth-greater-equal.ps.hlsl");
+}
+TEST_F(FileTest, SemanticDepthLessEqualPS) {
+  runFileTest("semantic.depth-less-equal.ps.hlsl");
+}
+TEST_F(FileTest, SemanticIsFrontFacePS) {
+  runFileTest("semantic.is-front-face.ps.hlsl");
+}
 TEST_F(FileTest, SemanticArbitrary) { runFileTest("semantic.arbitrary.hlsl"); }
 
 // For intrinsic functions