[spirv] Add mechanism for Vulkan-specific builtins (#805)
[[vk::builtin("...")]] is introduced to support Vulkan-specific builtins. There are two supported in this commit: * gl_PointSize * gl_HelperInvocation Validating the usages of these two builtins is left for anther commit.
This commit is contained in:
Родитель
8903528e88
Коммит
53f3f69b36
|
@ -160,8 +160,8 @@ non-intrusive ways in HLSL, which means we will prefer native language
|
|||
constructs when possible. If that is inadequate, we then consider attaching
|
||||
`Vulkan specific attributes`_ to them, or introducing new syntax.
|
||||
|
||||
Descriptor sets
|
||||
~~~~~~~~~~~~~~~
|
||||
Descriptors
|
||||
~~~~~~~~~~~
|
||||
|
||||
To specify which Vulkan descriptor a particular resource binds to, use the
|
||||
``[[vk::binding(X[, Y])]]`` attribute.
|
||||
|
@ -176,6 +176,19 @@ annotated with the ``[[vk::push_constant]]`` attribute.
|
|||
Please note as per the requirements of Vulkan, "there must be no more than one
|
||||
push constant block statically used per shader entry point."
|
||||
|
||||
Builtin variables
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some of the Vulkan builtin variables have no equivalents in native HLSL
|
||||
language. To support them, ``[[vk::builtin("<builtin>")]]`` is introduced.
|
||||
Right now only two ``<builtin>`` are supported:
|
||||
|
||||
* ``PointSize``: The GLSL equivalent is ``gl_PointSize``.
|
||||
* ``HelperInvocation``: The GLSL equivalent is ``gl_HelperInvocation``.
|
||||
|
||||
Please see Vulkan spec. `14.6. Built-In Variables <https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#interfaces-builtin-variables>`_
|
||||
for detailed explanation of these builtins.
|
||||
|
||||
Vulkan specific attributes
|
||||
--------------------------
|
||||
|
||||
|
@ -196,6 +209,9 @@ The namespace ``vk`` will be used for all Vulkan attributes:
|
|||
- ``push_constant``: For marking a variable as the push constant block. Allowed
|
||||
on global variables of struct type. At most one variable can be marked as
|
||||
``push_constant`` in a shader.
|
||||
- ``builtin("X")``: For specifying an entity should be translated into a certain
|
||||
Vulkan builtin variable. Allowed on function parameters, function returns,
|
||||
and struct fields.
|
||||
|
||||
Only ``vk::`` attributes in the above list are supported. Other attributes will
|
||||
result in warnings and be ignored by the compiler. All C++11 attributes will
|
||||
|
|
|
@ -863,6 +863,14 @@ def HLSLExperimental : InheritableAttr {
|
|||
|
||||
// SPIRV Change Starts
|
||||
|
||||
def VKBuiltIn : InheritableAttr {
|
||||
let Spellings = [CXX11<"vk", "builtin">];
|
||||
let Subjects = SubjectList<[Function, ParmVar, Field], ErrorDiag>;
|
||||
let Args = [StringArgument<"BuiltIn">];
|
||||
let LangOpts = [SPIRV];
|
||||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def VKLocation : InheritableAttr {
|
||||
let Spellings = [CXX11<"vk", "location">];
|
||||
let Subjects = SubjectList<[Function, ParmVar, Field], ErrorDiag>;
|
||||
|
|
|
@ -739,11 +739,10 @@ bool DeclResultIdMapper::decorateResourceBindings() {
|
|||
// - m3, mX * c2
|
||||
|
||||
BindingSet bindingSet;
|
||||
bool noError = true;
|
||||
|
||||
// Decorates the given varId of the given category with set number
|
||||
// setNo, binding number bindingNo. Emits warning if overlap.
|
||||
const auto tryToDecorate = [this, &bindingSet, &noError](
|
||||
const auto tryToDecorate = [this, &bindingSet](
|
||||
const uint32_t varId, const uint32_t setNo,
|
||||
const uint32_t bindingNo,
|
||||
const ResourceVar::Category cat,
|
||||
|
@ -754,7 +753,6 @@ bool DeclResultIdMapper::decorateResourceBindings() {
|
|||
loc)
|
||||
<< bindingNo << setNo;
|
||||
emitNote("binding number previously assigned here", prevUseLoc);
|
||||
// noError = false;
|
||||
}
|
||||
theBuilder.decorateDSetBinding(varId, setNo, bindingNo);
|
||||
};
|
||||
|
@ -838,7 +836,7 @@ bool DeclResultIdMapper::decorateResourceBindings() {
|
|||
}
|
||||
}
|
||||
|
||||
return noError;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeclResultIdMapper::createStageVars(
|
||||
|
@ -907,6 +905,16 @@ bool DeclResultIdMapper::createStageVars(
|
|||
return false;
|
||||
}
|
||||
|
||||
const auto *builtinAttr = decl->getAttr<VKBuiltInAttr>();
|
||||
|
||||
// For VS/HS/DS, the PointSize builtin is handled in gl_PerVertex.
|
||||
// For GSVIn also in gl_PerVertex; for GSOut, it's a stand-alone
|
||||
// variable handled below.
|
||||
if (builtinAttr && builtinAttr->getBuiltIn() == "PointSize" &&
|
||||
glPerVertex.tryToAccessPointSize(sigPoint->GetKind(), invocationId,
|
||||
value, noWriteBack))
|
||||
return true;
|
||||
|
||||
// Special handling of certain mappings between HLSL semantics and
|
||||
// SPIR-V builtins:
|
||||
// * SV_Position/SV_CullDistance/SV_ClipDistance should be grouped into the
|
||||
|
@ -957,7 +965,8 @@ bool DeclResultIdMapper::createStageVars(
|
|||
theBuilder.getConstantUint32(arraySize));
|
||||
|
||||
StageVar stageVar(sigPoint, semanticToUse->str, semanticToUse->semantic,
|
||||
semanticToUse->name, semanticToUse->index, typeId);
|
||||
semanticToUse->name, semanticToUse->index, builtinAttr,
|
||||
typeId);
|
||||
const auto name = namePrefix.str() + "." + stageVar.getSemanticStr();
|
||||
const uint32_t varId =
|
||||
createSpirvStageVar(&stageVar, decl, name, semanticToUse->loc);
|
||||
|
@ -1297,6 +1306,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
|
|||
const llvm::StringRef name,
|
||||
SourceLocation srcLoc) {
|
||||
using spv::BuiltIn;
|
||||
|
||||
const auto sigPoint = stageVar->getSigPoint();
|
||||
const auto semanticKind = stageVar->getSemantic()->GetKind();
|
||||
const auto sigPointKind = sigPoint->GetKind();
|
||||
|
@ -1307,6 +1317,19 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
|
|||
return 0;
|
||||
stageVar->setStorageClass(sc);
|
||||
|
||||
// [[vk::builtin(...)]] takes precedence.
|
||||
if (const auto *builtinAttr = stageVar->getBuiltInAttr()) {
|
||||
const auto spvBuiltIn =
|
||||
llvm::StringSwitch<BuiltIn>(builtinAttr->getBuiltIn())
|
||||
.Case("PointSize", BuiltIn::PointSize)
|
||||
.Case("HelperInvocation", BuiltIn::HelperInvocation)
|
||||
.Default(BuiltIn::Max);
|
||||
|
||||
assert(spvBuiltIn != BuiltIn::Max); // The frontend should guarantee this.
|
||||
|
||||
return theBuilder.addStageBuiltinVar(type, sc, spvBuiltIn);
|
||||
}
|
||||
|
||||
// The following translation assumes that semantic validity in the current
|
||||
// shader model is already checked, so it only covers valid SigPoints for
|
||||
// each semantic.
|
||||
|
|
|
@ -37,11 +37,14 @@ class StageVar {
|
|||
public:
|
||||
inline StageVar(const hlsl::SigPoint *sig, llvm::StringRef semaStr,
|
||||
const hlsl::Semantic *sema, llvm::StringRef semaName,
|
||||
uint32_t semaIndex, uint32_t type)
|
||||
uint32_t semaIndex, const VKBuiltInAttr *builtin,
|
||||
uint32_t type)
|
||||
: sigPoint(sig), semanticStr(semaStr), semantic(sema),
|
||||
semanticName(semaName), semanticIndex(semaIndex), typeId(type),
|
||||
valueId(0), isBuiltin(false), storageClass(spv::StorageClass::Max),
|
||||
location(nullptr) {}
|
||||
semanticName(semaName), semanticIndex(semaIndex), builtinAttr(builtin),
|
||||
typeId(type), valueId(0), isBuiltin(false),
|
||||
storageClass(spv::StorageClass::Max), location(nullptr) {
|
||||
isBuiltin = builtinAttr != nullptr;
|
||||
}
|
||||
|
||||
const hlsl::SigPoint *getSigPoint() const { return sigPoint; }
|
||||
const hlsl::Semantic *getSemantic() const { return semantic; }
|
||||
|
@ -51,6 +54,8 @@ public:
|
|||
uint32_t getSpirvId() const { return valueId; }
|
||||
void setSpirvId(uint32_t id) { valueId = id; }
|
||||
|
||||
const VKBuiltInAttr *getBuiltInAttr() const { return builtinAttr; }
|
||||
|
||||
std::string getSemanticStr() const;
|
||||
uint32_t getSemanticIndex() const { return semanticIndex; }
|
||||
|
||||
|
@ -75,6 +80,8 @@ private:
|
|||
llvm::StringRef semanticName;
|
||||
/// HLSL semantic index.
|
||||
uint32_t semanticIndex;
|
||||
/// SPIR-V BuiltIn attribute.
|
||||
const VKBuiltInAttr *builtinAttr;
|
||||
/// SPIR-V <type-id>.
|
||||
uint32_t typeId;
|
||||
/// SPIR-V <result-id>.
|
||||
|
|
|
@ -397,7 +397,7 @@ bool GlPerVertex::tryToAccess(hlsl::SigPoint::Kind sigPointKind,
|
|||
if (semanticKind == hlsl::Semantic::Kind::Position)
|
||||
return false; // Fall back to the normal path
|
||||
|
||||
// Fall through
|
||||
// Fall through
|
||||
|
||||
case hlsl::SigPoint::Kind::HSCPIn:
|
||||
case hlsl::SigPoint::Kind::DSCPIn:
|
||||
|
@ -409,7 +409,7 @@ bool GlPerVertex::tryToAccess(hlsl::SigPoint::Kind sigPointKind,
|
|||
if (semanticKind == hlsl::Semantic::Kind::Position)
|
||||
return false; // Fall back to the normal path
|
||||
|
||||
// Fall through
|
||||
// Fall through
|
||||
|
||||
case hlsl::SigPoint::Kind::VSOut:
|
||||
case hlsl::SigPoint::Kind::HSCPOut:
|
||||
|
@ -423,19 +423,41 @@ bool GlPerVertex::tryToAccess(hlsl::SigPoint::Kind sigPointKind,
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t GlPerVertex::readPosition() const {
|
||||
assert(inIsGrouped); // We do not handle stand-alone Position builtin here.
|
||||
bool GlPerVertex::tryToAccessPointSize(hlsl::SigPoint::Kind sigPointKind,
|
||||
llvm::Optional<uint32_t> invocation,
|
||||
uint32_t *value, bool noWriteBack) {
|
||||
switch (sigPointKind) {
|
||||
case hlsl::SigPoint::Kind::HSCPIn:
|
||||
case hlsl::SigPoint::Kind::DSCPIn:
|
||||
case hlsl::SigPoint::Kind::GSVIn:
|
||||
*value = readPositionOrPointSize(/*isPosition=*/false);
|
||||
return true;
|
||||
case hlsl::SigPoint::Kind::VSOut:
|
||||
case hlsl::SigPoint::Kind::HSCPOut:
|
||||
case hlsl::SigPoint::Kind::DSOut:
|
||||
writePositionOrPointSize(/*isPosition=*/false, invocation, *value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Fall back to normal path: GSOut
|
||||
}
|
||||
|
||||
uint32_t GlPerVertex::readPositionOrPointSize(bool isPosition) const {
|
||||
// We do not handle stand-alone Position/PointSize builtin here.
|
||||
assert(inIsGrouped);
|
||||
|
||||
// The PointSize builtin is always of float type.
|
||||
// The Position builtin is always of float4 type.
|
||||
const uint32_t f32Type = theBuilder.getFloat32Type();
|
||||
const uint32_t fieldType =
|
||||
theBuilder.getVecType(theBuilder.getFloat32Type(), 4);
|
||||
isPosition ? theBuilder.getVecType(f32Type, 4) : f32Type;
|
||||
const uint32_t ptrType =
|
||||
theBuilder.getPointerType(fieldType, spv::StorageClass::Input);
|
||||
const uint32_t fieldIndex = theBuilder.getConstantUint32(0);
|
||||
const uint32_t fieldIndex = theBuilder.getConstantUint32(isPosition ? 0 : 1);
|
||||
|
||||
if (inArraySize == 0) {
|
||||
// The input builtin block is a single block. Only need one index to
|
||||
// locate the Position builtin.
|
||||
// locate the Position/PointSize builtin.
|
||||
const uint32_t ptr =
|
||||
theBuilder.createAccessChain(ptrType, inBlockVar, {fieldIndex});
|
||||
return theBuilder.createLoad(fieldType, ptr);
|
||||
|
@ -448,13 +470,13 @@ uint32_t GlPerVertex::readPosition() const {
|
|||
for (uint32_t i = 0; i < inArraySize; ++i) {
|
||||
const uint32_t arrayIndex = theBuilder.getConstantUint32(i);
|
||||
// Get pointer into the array of structs. We need two indices to locate
|
||||
// the Position builtin now: the first one is the array index, and the
|
||||
// second one is the struct index.
|
||||
// the Position/PointSize builtin now: the first one is the array index,
|
||||
// and the second one is the struct index.
|
||||
const uint32_t ptr = theBuilder.createAccessChain(ptrType, inBlockVar,
|
||||
{arrayIndex, fieldIndex});
|
||||
elements.push_back(theBuilder.createLoad(fieldType, ptr));
|
||||
}
|
||||
// Construct a new array of float4 for the Position builtins
|
||||
// Construct a new array of float4/float for the Position/PointSize builtins
|
||||
const uint32_t arrayType = theBuilder.getArrayType(
|
||||
fieldType, theBuilder.getConstantUint32(inArraySize));
|
||||
return theBuilder.createCompositeConstruct(arrayType, elements);
|
||||
|
@ -575,7 +597,7 @@ bool GlPerVertex::readField(hlsl::Semantic::Kind semanticKind,
|
|||
uint32_t semanticIndex, uint32_t *value) {
|
||||
switch (semanticKind) {
|
||||
case hlsl::Semantic::Kind::Position:
|
||||
*value = readPosition();
|
||||
*value = readPositionOrPointSize(/*isPosition=*/true);
|
||||
return true;
|
||||
case hlsl::Semantic::Kind::ClipDistance: {
|
||||
const auto offsetIter = inClipOffset.find(semanticIndex);
|
||||
|
@ -601,20 +623,24 @@ bool GlPerVertex::readField(hlsl::Semantic::Kind semanticKind,
|
|||
return false;
|
||||
}
|
||||
|
||||
void GlPerVertex::writePosition(llvm::Optional<uint32_t> invocationId,
|
||||
uint32_t value) const {
|
||||
assert(outIsGrouped); // We do not handle stand-alone Position builtin here.
|
||||
void GlPerVertex::writePositionOrPointSize(
|
||||
bool isPosition, llvm::Optional<uint32_t> invocationId,
|
||||
uint32_t value) const {
|
||||
// We do not handle stand-alone Position/PointSize builtin here.
|
||||
assert(outIsGrouped);
|
||||
|
||||
// The Position builtin is always of float4 type.
|
||||
// The PointSize builtin is always of float type.
|
||||
const uint32_t f32Type = theBuilder.getFloat32Type();
|
||||
const uint32_t fieldType =
|
||||
theBuilder.getVecType(theBuilder.getFloat32Type(), 4);
|
||||
isPosition ? theBuilder.getVecType(f32Type, 4) : f32Type;
|
||||
const uint32_t ptrType =
|
||||
theBuilder.getPointerType(fieldType, spv::StorageClass::Output);
|
||||
const uint32_t fieldIndex = theBuilder.getConstantUint32(0);
|
||||
const uint32_t fieldIndex = theBuilder.getConstantUint32(isPosition ? 0 : 1);
|
||||
|
||||
if (outArraySize == 0) {
|
||||
// The input builtin block is a single block. Only need one index to
|
||||
// locate the Position builtin.
|
||||
// locate the Position/PointSize builtin.
|
||||
const uint32_t ptr =
|
||||
theBuilder.createAccessChain(ptrType, outBlockVar, {fieldIndex});
|
||||
theBuilder.createStore(ptr, value);
|
||||
|
@ -631,8 +657,8 @@ void GlPerVertex::writePosition(llvm::Optional<uint32_t> invocationId,
|
|||
|
||||
const uint32_t arrayIndex = invocationId.getValue();
|
||||
// Get pointer into the array of structs. We need two indices to locate
|
||||
// the Position builtin now: the first one is the array index, and the
|
||||
// second one is the struct index.
|
||||
// the Position/PointSize builtin now: the first one is the array index,
|
||||
// and the second one is the struct index.
|
||||
const uint32_t ptr = theBuilder.createAccessChain(ptrType, outBlockVar,
|
||||
{arrayIndex, fieldIndex});
|
||||
theBuilder.createStore(ptr, value);
|
||||
|
@ -771,7 +797,7 @@ bool GlPerVertex::writeField(hlsl::Semantic::Kind semanticKind,
|
|||
// out the value to the correct array element.
|
||||
switch (semanticKind) {
|
||||
case hlsl::Semantic::Kind::Position:
|
||||
writePosition(invocationId, *value);
|
||||
writePositionOrPointSize(/*isPosition=*/true, invocationId, *value);
|
||||
return true;
|
||||
case hlsl::Semantic::Kind::ClipDistance: {
|
||||
const auto offsetIter = outClipOffset.find(semanticIndex);
|
||||
|
|
|
@ -25,8 +25,9 @@ namespace spirv {
|
|||
|
||||
/// The class for representing special gl_PerVertex builtin interface block.
|
||||
/// The Position, PointSize, ClipDistance, and CullDistance builtin should
|
||||
/// be handled by this class, except for Position builtin used in GS output
|
||||
/// and PS input.
|
||||
/// be handled by this class, except for
|
||||
/// * Position builtin used in GS output and PS input,
|
||||
/// * PointSize builtin used in GS output.
|
||||
///
|
||||
/// Although the Vulkan spec does not require this directly, it seems the only
|
||||
/// way to avoid violating the spec is to group the Position, ClipDistance, and
|
||||
|
@ -101,10 +102,14 @@ public:
|
|||
uint32_t semanticIndex, llvm::Optional<uint32_t> invocation,
|
||||
uint32_t *value, bool noWriteBack);
|
||||
|
||||
/// Similar to tryToAccess, but only used for the PointSize builtin.
|
||||
bool tryToAccessPointSize(hlsl::SigPoint::Kind sigPoint,
|
||||
llvm::Optional<uint32_t> invocation,
|
||||
uint32_t *value, bool noWriteBack);
|
||||
|
||||
private:
|
||||
template <unsigned N>
|
||||
DiagnosticBuilder emitError(const char (&message)[N],
|
||||
SourceLocation loc) {
|
||||
DiagnosticBuilder emitError(const char (&message)[N], SourceLocation loc) {
|
||||
const auto diagId = astContext.getDiagnostics().getCustomDiagID(
|
||||
clang::DiagnosticsEngine::Error, message);
|
||||
return astContext.getDiagnostics().Report(loc, diagId);
|
||||
|
@ -121,8 +126,8 @@ private:
|
|||
/// Creates a stand-alone CullDistance builtin variable.
|
||||
uint32_t createCullDistanceVar(bool asInput, uint32_t arraySize);
|
||||
|
||||
/// Emits SPIR-V instructions for reading the Position builtin.
|
||||
uint32_t readPosition() const;
|
||||
/// Emits SPIR-V instructions for reading the Position/PointSize builtin.
|
||||
uint32_t readPositionOrPointSize(bool isPosition) const;
|
||||
/// Emits SPIR-V instructions for reading the data starting from offset in
|
||||
/// the ClipDistance/CullDistance builtin. The data read will be transformed
|
||||
/// into the given type asType.
|
||||
|
@ -132,9 +137,10 @@ private:
|
|||
bool readField(hlsl::Semantic::Kind semanticKind, uint32_t semanticIndex,
|
||||
uint32_t *value);
|
||||
|
||||
/// Emits SPIR-V instructions for writing the Position builtin.
|
||||
void writePosition(llvm::Optional<uint32_t> invocationId,
|
||||
uint32_t value) const;
|
||||
/// Emits SPIR-V instructions for writing the Position/PointSize builtin.
|
||||
void writePositionOrPointSize(bool isPosition,
|
||||
llvm::Optional<uint32_t> invocationId,
|
||||
uint32_t value) const;
|
||||
/// Emits SPIR-V instructions for writing data into the ClipDistance/
|
||||
/// CullDistance builtin starting from offset. The value to be written is
|
||||
/// fromValue, whose type is fromType. Necessary transformations will be
|
||||
|
|
|
@ -10351,6 +10351,11 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
|
|||
Handled = true;
|
||||
switch (A.getKind())
|
||||
{
|
||||
case AttributeList::AT_VKBuiltIn:
|
||||
declAttr = ::new (S.Context) VKBuiltInAttr(A.getRange(), S.Context,
|
||||
ValidateAttributeStringArg(S, A, "PointSize,HelperInvocation"),
|
||||
A.getAttributeSpellingListIndex());
|
||||
break;
|
||||
case AttributeList::AT_VKLocation:
|
||||
declAttr = ::new (S.Context) VKLocationAttr(A.getRange(), S.Context,
|
||||
ValidateAttributeIntArg(S, A), A.getAttributeSpellingListIndex());
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// Run: %dxc -T ps_6_0 -E main
|
||||
|
||||
// CHECK: OpEntryPoint Fragment
|
||||
// CHECK-SAME: %gl_HelperInvocation
|
||||
|
||||
// CHECK: OpDecorate %gl_HelperInvocation BuiltIn HelperInvocation
|
||||
|
||||
// CHECK: %gl_HelperInvocation = OpVariable %_ptr_Input_bool Input
|
||||
|
||||
float4 main([[vk::builtin("HelperInvocation")]] bool isHI : HI) : SV_Target {
|
||||
// CHECK: [[val:%\d+]] = OpLoad %bool %gl_HelperInvocation
|
||||
// CHECK-NEXT: OpStore %param_var_isHI [[val]]
|
||||
float ret = 1.0;
|
||||
|
||||
if (isHI) ret = 2.0;
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -31,6 +31,8 @@ struct PerVertexIn {
|
|||
float4 cull3 : SV_CullDistance3; // Builtin CullDistance
|
||||
InnerPerVertexIn s;
|
||||
float2 bar : BAR; // Input variable
|
||||
[[vk::builtin("PointSize")]]
|
||||
float ptSize : PSIZE; // Builtin PointSize
|
||||
};
|
||||
|
||||
// Per-vertex output structs
|
||||
|
@ -45,6 +47,8 @@ struct InnerPerVertexOut {
|
|||
Inner2PerVertexOut s;
|
||||
float2 cull4 : SV_CullDistance4; // Builtin CullDistance
|
||||
float4 bar : BAR; // Output variable
|
||||
[[vk::builtin("PointSize")]]
|
||||
float ptSize : PSIZE; // Builtin PointSize
|
||||
};
|
||||
|
||||
struct DsOut {
|
||||
|
@ -280,22 +284,34 @@ DsOut main( const OutputPatch<PerVertexIn, 3> patch,
|
|||
|
||||
// CHECK-NEXT: [[inBarArr:%\d+]] = OpLoad %_arr_v2float_uint_3 %in_var_BAR
|
||||
|
||||
// Compose an array of input PointSize for later use
|
||||
// CHECK-NEXT: [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_1
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpLoad %float [[ptr0]]
|
||||
// CHECK-NEXT: [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_1
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpLoad %float [[ptr1]]
|
||||
// CHECK-NEXT: [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_1
|
||||
// CHECK-NEXT: [[val2:%\d+]] = OpLoad %float [[ptr2]]
|
||||
// CHECK-NEXT: [[inPtSizeArr:%\d+]] = OpCompositeConstruct %_arr_float_uint_3 [[val0]] [[val1]] [[val2]]
|
||||
|
||||
// Decompose temporary arrays created before to compose PerVertexIn
|
||||
|
||||
// CHECK-NEXT: [[field0:%\d+]] = OpCompositeExtract %v4float [[inCull3Arr]] 0
|
||||
// CHECK-NEXT: [[field1:%\d+]] = OpCompositeExtract %InnerPerVertexIn [[inInPVArr]] 0
|
||||
// CHECK-NEXT: [[field2:%\d+]] = OpCompositeExtract %v2float [[inBarArr]] 0
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]]
|
||||
// CHECK-NEXT: [[field3:%\d+]] = OpCompositeExtract %float [[inPtSizeArr]] 0
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]] [[field3]]
|
||||
|
||||
// CHECK-NEXT: [[field0:%\d+]] = OpCompositeExtract %v4float [[inCull3Arr]] 1
|
||||
// CHECK-NEXT: [[field1:%\d+]] = OpCompositeExtract %InnerPerVertexIn [[inInPVArr]] 1
|
||||
// CHECK-NEXT: [[field2:%\d+]] = OpCompositeExtract %v2float [[inBarArr]] 1
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]]
|
||||
// CHECK-NEXT: [[field3:%\d+]] = OpCompositeExtract %float [[inPtSizeArr]] 1
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]] [[field3]]
|
||||
|
||||
// CHECK-NEXT: [[field0:%\d+]] = OpCompositeExtract %v4float [[inCull3Arr]] 2
|
||||
// CHECK-NEXT: [[field1:%\d+]] = OpCompositeExtract %InnerPerVertexIn [[inInPVArr]] 2
|
||||
// CHECK-NEXT: [[field2:%\d+]] = OpCompositeExtract %v2float [[inBarArr]] 2
|
||||
// CHECK-NEXT: [[val2:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]]
|
||||
// CHECK-NEXT: [[field3:%\d+]] = OpCompositeExtract %float [[inPtSizeArr]] 2
|
||||
// CHECK-NEXT: [[val2:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]] [[field3]]
|
||||
|
||||
// The final value for the patch parameter!
|
||||
// CHECK-NEXT: [[patch:%\d+]] = OpCompositeConstruct %_arr_PerVertexIn_uint_3 [[val0]] [[val1]] [[val2]]
|
||||
|
@ -355,6 +371,11 @@ DsOut main( const OutputPatch<PerVertexIn, 3> patch,
|
|||
// CHECK-NEXT: [[bar:%\d+]] = OpCompositeExtract %v4float [[outInPV]] 2
|
||||
// CHECK-NEXT: OpStore %out_var_BAR [[bar]]
|
||||
|
||||
// Decompose InnerPerVertexOut and write out DsOut.s.ptSize (PointSize)
|
||||
// CHECK-NEXT: [[ptSize:%\d+]] = OpCompositeExtract %float [[outInPV]] 3
|
||||
// CHECK-NEXT: [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_1
|
||||
// CHECK-NEXT: OpStore [[ptr]] [[ptSize]]
|
||||
|
||||
// Write out clip5 (SV_ClipDistance5) at offset 1
|
||||
// CHECK-NEXT: [[clip5:%\d+]] = OpLoad %v3float %param_var_clip5
|
||||
// CHECK-NEXT: [[ptr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_1
|
||||
|
|
|
@ -9,12 +9,18 @@ struct GsPerVertexIn {
|
|||
float3 clip2 : SV_ClipDistance2; // Builtin ClipDistance
|
||||
float2 clip0 : SV_ClipDistance0; // Builtin ClipDistance
|
||||
float3 foo : FOO; // Input variable
|
||||
|
||||
[[vk::builtin("PointSize")]]
|
||||
float ptSize : PSIZE; // Builtin PointSize
|
||||
};
|
||||
|
||||
struct GsInnerOut {
|
||||
float4 pos : SV_Position; // Builtion Position
|
||||
float2 foo : FOO; // Output variable
|
||||
float2 cull3 : SV_CullDistance3; // Builtin CullDistance
|
||||
|
||||
[[vk::builtin("PointSize")]]
|
||||
float ptSize : PSIZE; // Builtin PointSize
|
||||
};
|
||||
|
||||
struct GsPerVertexOut {
|
||||
|
@ -29,7 +35,7 @@ struct GsPerVertexOut {
|
|||
// Input variable: FOO, BAR
|
||||
// Output variable: FOO, BAR
|
||||
|
||||
// CHECK: OpEntryPoint Geometry %main "main" %gl_PerVertexIn %gl_ClipDistance %gl_CullDistance %in_var_BAR %in_var_FOO %gl_Position %out_var_FOO %out_var_BAR
|
||||
// CHECK: OpEntryPoint Geometry %main "main" %gl_PerVertexIn %gl_ClipDistance %gl_CullDistance %in_var_BAR %in_var_FOO %gl_Position %out_var_FOO %gl_PointSize %out_var_BAR
|
||||
|
||||
// CHECK: OpMemberDecorate %type_gl_PerVertex 0 BuiltIn Position
|
||||
// CHECK: OpMemberDecorate %type_gl_PerVertex 1 BuiltIn PointSize
|
||||
|
@ -40,6 +46,7 @@ struct GsPerVertexOut {
|
|||
// CHECK: OpDecorate %gl_ClipDistance BuiltIn ClipDistance
|
||||
// CHECK: OpDecorate %gl_CullDistance BuiltIn CullDistance
|
||||
// CHECK: OpDecorate %gl_Position BuiltIn Position
|
||||
// CHECK: OpDecorate %gl_PointSize BuiltIn PointSize
|
||||
|
||||
// CHECK: OpDecorate %in_var_BAR Location 0
|
||||
// CHECK: OpDecorate %in_var_FOO Location 1
|
||||
|
@ -61,6 +68,7 @@ struct GsPerVertexOut {
|
|||
// CHECK: %in_var_FOO = OpVariable %_ptr_Input__arr_v3float_uint_2 Input
|
||||
// CHECK: %gl_Position = OpVariable %_ptr_Output_v4float Output
|
||||
// CHECK: %out_var_FOO = OpVariable %_ptr_Output_v2float Output
|
||||
// CHECK: %gl_PointSize = OpVariable %_ptr_Output_float Output
|
||||
// CHECK: %out_var_BAR = OpVariable %_ptr_Output_v4float Output
|
||||
|
||||
[maxvertexcount(2)]
|
||||
|
@ -128,16 +136,25 @@ void main(in line float2 bar [2] : BAR,
|
|||
|
||||
// CHECK-NEXT: [[inFooArr:%\d+]] = OpLoad %_arr_v3float_uint_2 %in_var_FOO
|
||||
|
||||
// Compose an array for GsPerVertexIn::ptSize
|
||||
// CHECK-NEXT: [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_1
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpLoad %float [[ptr0]]
|
||||
// CHECK-NEXT: [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_1
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpLoad %float [[ptr1]]
|
||||
// CHECK-NEXT: [[inPtSzArr:%\d+]] = OpCompositeConstruct %_arr_float_uint_2 [[val0]] [[val1]]
|
||||
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 0
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpCompositeExtract %v3float [[inClip2Arr]] 0
|
||||
// CHECK-NEXT: [[val2:%\d+]] = OpCompositeExtract %v2float [[inClip0Arr]] 0
|
||||
// CHECK-NEXT: [[val3:%\d+]] = OpCompositeExtract %v3float [[inFooArr]] 0
|
||||
// CHECK-NEXT: [[inData0:%\d+]] = OpCompositeConstruct %GsPerVertexIn [[val0]] [[val1]] [[val2]] [[val3]]
|
||||
// CHECK-NEXT: [[val4:%\d+]] = OpCompositeExtract %float [[inPtSzArr]] 0
|
||||
// CHECK-NEXT: [[inData0:%\d+]] = OpCompositeConstruct %GsPerVertexIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]]
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 1
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpCompositeExtract %v3float [[inClip2Arr]] 1
|
||||
// CHECK-NEXT: [[val2:%\d+]] = OpCompositeExtract %v2float [[inClip0Arr]] 1
|
||||
// CHECK-NEXT: [[val3:%\d+]] = OpCompositeExtract %v3float [[inFooArr]] 1
|
||||
// CHECK-NEXT: [[inData1:%\d+]] = OpCompositeConstruct %GsPerVertexIn [[val0]] [[val1]] [[val2]] [[val3]]
|
||||
// CHECK-NEXT: [[val4:%\d+]] = OpCompositeExtract %float [[inPtSzArr]] 1
|
||||
// CHECK-NEXT: [[inData1:%\d+]] = OpCompositeConstruct %GsPerVertexIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]]
|
||||
|
||||
// CHECK-NEXT: [[inData:%\d+]] = OpCompositeConstruct %_arr_GsPerVertexIn_uint_2 [[inData0]] [[inData1]]
|
||||
// CHECK-NEXT: OpStore %param_var_inData [[inData]]
|
||||
|
|
|
@ -16,12 +16,18 @@ struct HsCpIn
|
|||
float3 cull3 : SV_CullDistance3; // Builtin CullDistance
|
||||
|
||||
float3 baz : BAZ; // Input variable
|
||||
|
||||
[[vk::builtin("PointSize")]]
|
||||
float ptSize : PSIZE; // Builtin PointSize
|
||||
};
|
||||
|
||||
struct CpInner2 {
|
||||
float1 clip8 : SV_ClipDistance8; // Builtin ClipDistance
|
||||
float2 cull6 : SV_CullDistance6; // Builtin CullDistance
|
||||
float3 foo : FOO; // Output variable
|
||||
|
||||
[[vk::builtin("PointSize")]]
|
||||
float ptSize : PSIZE; // Builtin PointSize
|
||||
};
|
||||
|
||||
struct CpInner1 {
|
||||
|
@ -203,6 +209,13 @@ HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch, uint cpId : SV_OutputContro
|
|||
|
||||
// CHECK-NEXT: [[inBazArr:%\d+]] = OpLoad %_arr_v3float_uint_2 %in_var_BAZ
|
||||
|
||||
// Read gl_PerVertex[].gl_PointSize[] to compose a new array for HsCpIn::ptSize
|
||||
// CHECK-NEXT: [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_1
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpLoad %float [[ptr0]]
|
||||
// CHECK-NEXT: [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_1
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpLoad %float [[ptr1]]
|
||||
// CHECK-NEXT: [[inPtSzArr:%\d+]] = OpCompositeConstruct %_arr_float_uint_2 [[val0]] [[val1]]
|
||||
|
||||
// Compose a temporary HsCpIn value out of the temporary arrays constructed before
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 0
|
||||
// CHECK-NEXT: [[val1:%\d+]] = OpCompositeExtract %v2float [[inClip0Arr]] 0
|
||||
|
@ -210,7 +223,8 @@ HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch, uint cpId : SV_OutputContro
|
|||
// CHECK-NEXT: [[val3:%\d+]] = OpCompositeExtract %float [[inClip2Arr]] 0
|
||||
// CHECK-NEXT: [[val4:%\d+]] = OpCompositeExtract %v3float [[inCull3Arr]] 0
|
||||
// CHECK-NEXT: [[val5:%\d+]] = OpCompositeExtract %v3float [[inBazArr]] 0
|
||||
// CHECK-NEXT: [[hscpin0:%\d+]] = OpCompositeConstruct %HsCpIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]] [[val5]]
|
||||
// CHECK-NEXT: [[val6:%\d+]] = OpCompositeExtract %float [[inPtSzArr]] 0
|
||||
// CHECK-NEXT: [[hscpin0:%\d+]] = OpCompositeConstruct %HsCpIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]] [[val5]] [[val6]]
|
||||
|
||||
// Compose a temporary HsCpIn value out of the temporary arrays constructed before
|
||||
// CHECK-NEXT: [[val0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 1
|
||||
|
@ -219,7 +233,8 @@ HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch, uint cpId : SV_OutputContro
|
|||
// CHECK-NEXT: [[val3:%\d+]] = OpCompositeExtract %float [[inClip2Arr]] 1
|
||||
// CHECK-NEXT: [[val4:%\d+]] = OpCompositeExtract %v3float [[inCull3Arr]] 1
|
||||
// CHECK-NEXT: [[val5:%\d+]] = OpCompositeExtract %v3float [[inBazArr]] 1
|
||||
// CHECK-NEXT: [[hscpin1:%\d+]] = OpCompositeConstruct %HsCpIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]] [[val5]]
|
||||
// CHECK-NEXT: [[val6:%\d+]] = OpCompositeExtract %float [[inPtSzArr]] 1
|
||||
// CHECK-NEXT: [[hscpin1:%\d+]] = OpCompositeConstruct %HsCpIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]] [[val5]] [[val6]]
|
||||
|
||||
// Populate the temporary variables for function call
|
||||
|
||||
|
@ -273,6 +288,11 @@ HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch, uint cpId : SV_OutputContro
|
|||
// CHECK-NEXT: [[ptr:%\d+]] = OpAccessChain %_ptr_Output_v3float %out_var_FOO [[invoId]]
|
||||
// CHECK-NEXT: OpStore [[ptr]] [[foo]]
|
||||
|
||||
// Write out HsCpOut::CpInner1::CpInner2::PointSize to gl_PerVertex[].gl_PointSize
|
||||
// CHECK-NEXT: [[ptSize:%\d+]] = OpCompositeExtract %float [[outInner2]] 3
|
||||
// CHECK-NEXT: [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_1
|
||||
// CHECK-NEXT: OpStore [[ptr]] [[ptSize]]
|
||||
|
||||
// Write out HsCpOut::CpInner1::clip6 to gl_PerVertex[].gl_ClipDistance
|
||||
// CHECK-NEXT: [[clip6:%\d+]] = OpCompositeExtract %float [[outInner1]] 2
|
||||
// CHECK-NEXT: [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_2 %uint_0
|
||||
|
|
|
@ -45,16 +45,17 @@ struct VSOut {
|
|||
InnerStruct s;
|
||||
};
|
||||
|
||||
void main(out VSOut vsOut,
|
||||
out float3 clipdis0 : SV_ClipDistance0, // -> BuiltIn ClipDistance in gl_PerVertex
|
||||
inout float4 coord : TEXCOORD, // -> Input & output variable
|
||||
out float culldis5 : SV_CullDistance5, // -> BuiltIn CullDistance in gl_PerVertex
|
||||
out float culldis3 : SV_CullDistance3, // -> BuiltIn CullDistance in gl_PerVertex
|
||||
out float culldis6 : SV_CullDistance6, // -> BuiltIn CullDistance in gl_PerVertex
|
||||
in float4 inPos : SV_Position, // -> Input variable
|
||||
in float2 inClip : SV_ClipDistance, // -> Input variable
|
||||
in float3 inCull : SV_CullDistance0 // -> Input variable
|
||||
) {
|
||||
[[vk::builtin("PointSize")]]
|
||||
float main(out VSOut vsOut,
|
||||
out float3 clipdis0 : SV_ClipDistance0, // -> BuiltIn ClipDistance in gl_PerVertex
|
||||
inout float4 coord : TEXCOORD, // -> Input & output variable
|
||||
out float culldis5 : SV_CullDistance5, // -> BuiltIn CullDistance in gl_PerVertex
|
||||
out float culldis3 : SV_CullDistance3, // -> BuiltIn CullDistance in gl_PerVertex
|
||||
out float culldis6 : SV_CullDistance6, // -> BuiltIn CullDistance in gl_PerVertex
|
||||
in float4 inPos : SV_Position, // -> Input variable
|
||||
in float2 inClip : SV_ClipDistance, // -> Input variable
|
||||
in float3 inCull : SV_CullDistance0 // -> Input variable
|
||||
) : PSize { // -> Builtin PointSize
|
||||
vsOut = (VSOut)0;
|
||||
clipdis0 = 1.;
|
||||
coord = 2.;
|
||||
|
@ -63,6 +64,8 @@ void main(out VSOut vsOut,
|
|||
culldis6 = 5.;
|
||||
inPos = 6.;
|
||||
|
||||
return 7.;
|
||||
|
||||
// Layout of ClipDistance array:
|
||||
// clipdis0: 3 floats, offset 0
|
||||
// clipdis1: 2 floats, offset 3
|
||||
|
@ -81,7 +84,10 @@ void main(out VSOut vsOut,
|
|||
// CHECK-NEXT: [[inCull:%\d+]] = OpLoad %v3float %in_var_SV_CullDistance0
|
||||
// CHECK-NEXT: OpStore %param_var_inCull [[inCull]]
|
||||
|
||||
// CHECK-NEXT: OpFunctionCall %void %src_main
|
||||
// CHECK-NEXT: [[ptSize:%\d+]] = OpFunctionCall %float %src_main
|
||||
|
||||
// CHECK-NEXT: [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_1
|
||||
// CHECK-NEXT: OpStore [[ptr]] [[ptSize]]
|
||||
|
||||
// Write out COLOR
|
||||
// CHECK-NEXT: [[vsOut:%\d+]] = OpLoad %VSOut %param_var_vsOut
|
||||
|
|
|
@ -936,6 +936,10 @@ TEST_F(FileTest, SpirvEntryFunctionInOut) {
|
|||
runFileTest("spirv.entry-function.inout.hlsl");
|
||||
}
|
||||
|
||||
TEST_F(FileTest, SpirvBuiltInHelperInvocation) {
|
||||
runFileTest("spirv.builtin.helper-invocation.hlsl");
|
||||
}
|
||||
|
||||
// For shader stage input/output interface
|
||||
// For semantic SV_Position, SV_ClipDistance, SV_CullDistance
|
||||
TEST_F(FileTest, SpirvStageIOInterfaceVS) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче