[spirv] Support PS SV_Depth{Greater|Less}Equal and SV_IsFrontFace (#563)
This commit is contained in:
Родитель
8b2d5a0d0f
Коммит
a727fc8838
|
@ -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> ¶ms);
|
||||
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> ¶ms) {
|
||||
llvm::ArrayRef<uint32_t> params) {
|
||||
instBuilder.opExecutionMode(entryPointId, em);
|
||||
for (const auto ¶m : 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
|
||||
|
|
Загрузка…
Ссылка в новой задаче