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

This commit is contained in:
Lei Zhang 2017-08-14 16:43:06 -04:00 коммит произвёл David Peixotto
Родитель 8b2d5a0d0f
Коммит a727fc8838
11 изменённых файлов: 117 добавлений и 41 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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.

Просмотреть файл

@ -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

Просмотреть файл

@ -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);

Просмотреть файл

@ -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) {

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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