Commit 067eb9b4 by steve-lunarg

WIP: HLSL: Support InputPatch variables in patch constant functions

Previously, patch constant functions only accepted OutputPatch. This adds InputPatch support, via a pseudo-builtin variable type, so that the patch can be tracked clear through from the qualifier.
parent b68b9a8b
// ***
// per-control-point invocation of PCF from entry point return value with
// both OutputPatch and InputPatch given to PCF.
// ***
struct hs_in_t
{
float3 val : TEXCOORD0;
};
struct hs_pcf_t
{
float tfactor[3] : SV_TessFactor; // must turn into a size 4 array in SPIR-V
float flInFactor : SV_InsideTessFactor; // must turn into a size 2 array in SPIR-V
};
struct hs_out_t
{
float3 val : TEXCOORD0;
};
[ domain ("tri") ]
[ partitioning ("fractional_odd") ]
[ outputtopology ("triangle_cw") ]
[ outputcontrolpoints (3) ]
[ patchconstantfunc ( "PCF" ) ]
hs_out_t main (InputPatch <hs_in_t, 3> i , uint cpid : SV_OutputControlPointID)
{
i[0].val;
hs_out_t o;
o.val = cpid;
return o;
}
hs_pcf_t PCF( const OutputPatch <hs_out_t, 3> pcf_out,
const InputPatch <hs_in_t, 3> pcf_in)
{
hs_pcf_t o;
o.tfactor[0] = pcf_out[0].val.x;
o.tfactor[1] = pcf_out[1].val.x;
o.tfactor[2] = pcf_out[2].val.x;
o.flInFactor = 4;
return o;
}
...@@ -220,6 +220,8 @@ enum TBuiltInVariable { ...@@ -220,6 +220,8 @@ enum TBuiltInVariable {
EbvFragDepthLesser, EbvFragDepthLesser,
EbvStencilRef, EbvStencilRef,
EbvGsOutputStream, EbvGsOutputStream,
EbvOutputPatch,
EbvInputPatch,
EbvLast EbvLast
}; };
......
...@@ -129,6 +129,7 @@ INSTANTIATE_TEST_CASE_P( ...@@ -129,6 +129,7 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.hull.2.tesc", "main"}, {"hlsl.hull.2.tesc", "main"},
{"hlsl.hull.void.tesc", "main"}, {"hlsl.hull.void.tesc", "main"},
{"hlsl.hull.ctrlpt-1.tesc", "main"}, {"hlsl.hull.ctrlpt-1.tesc", "main"},
{"hlsl.hull.ctrlpt-2.tesc", "main"},
{"hlsl.identifier.sample.frag", "main"}, {"hlsl.identifier.sample.frag", "main"},
{"hlsl.if.frag", "PixelShaderFunction"}, {"hlsl.if.frag", "PixelShaderFunction"},
{"hlsl.implicitBool.frag", "main"}, {"hlsl.implicitBool.frag", "main"},
......
...@@ -963,14 +963,14 @@ bool HlslGrammar::acceptOutputPrimitiveGeometry(TLayoutGeometry& geometry) ...@@ -963,14 +963,14 @@ bool HlslGrammar::acceptOutputPrimitiveGeometry(TLayoutGeometry& geometry)
// : INPUTPATCH // : INPUTPATCH
// | OUTPUTPATCH // | OUTPUTPATCH
// //
bool HlslGrammar::acceptTessellationDeclType() bool HlslGrammar::acceptTessellationDeclType(TBuiltInVariable& patchType)
{ {
// read geometry type // read geometry type
const EHlslTokenClass tessType = peek(); const EHlslTokenClass tessType = peek();
switch (tessType) { switch (tessType) {
case EHTokInputPatch: break; case EHTokInputPatch: patchType = EbvInputPatch; break;
case EHTokOutputPatch: break; case EHTokOutputPatch: patchType = EbvOutputPatch; break;
default: default:
return false; // not a tessellation decl return false; // not a tessellation decl
} }
...@@ -984,7 +984,9 @@ bool HlslGrammar::acceptTessellationDeclType() ...@@ -984,7 +984,9 @@ bool HlslGrammar::acceptTessellationDeclType()
// //
bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type) bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type)
{ {
if (! acceptTessellationDeclType()) TBuiltInVariable patchType;
if (! acceptTessellationDeclType(patchType))
return false; return false;
if (! acceptTokenClass(EHTokLeftAngle)) if (! acceptTokenClass(EHTokLeftAngle))
...@@ -1011,6 +1013,7 @@ bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type) ...@@ -1011,6 +1013,7 @@ bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type)
TArraySizes* arraySizes = new TArraySizes; TArraySizes* arraySizes = new TArraySizes;
arraySizes->addInnerSize(size->getAsConstantUnion()->getConstArray()[0].getIConst()); arraySizes->addInnerSize(size->getAsConstantUnion()->getConstArray()[0].getIConst());
type.newArraySizes(*arraySizes); type.newArraySizes(*arraySizes);
type.getQualifier().builtIn = patchType;
if (! acceptTokenClass(EHTokRightAngle)) { if (! acceptTokenClass(EHTokRightAngle)) {
expected("right angle bracket"); expected("right angle bracket");
......
...@@ -79,7 +79,7 @@ namespace glslang { ...@@ -79,7 +79,7 @@ namespace glslang {
bool acceptTemplateVecMatBasicType(TBasicType&); bool acceptTemplateVecMatBasicType(TBasicType&);
bool acceptVectorTemplateType(TType&); bool acceptVectorTemplateType(TType&);
bool acceptMatrixTemplateType(TType&); bool acceptMatrixTemplateType(TType&);
bool acceptTessellationDeclType(); bool acceptTessellationDeclType(TBuiltInVariable&);
bool acceptTessellationPatchTemplateType(TType&); bool acceptTessellationPatchTemplateType(TType&);
bool acceptStreamOutTemplateType(TType&, TLayoutGeometry&); bool acceptStreamOutTemplateType(TType&, TLayoutGeometry&);
bool acceptOutputPrimitiveGeometry(TLayoutGeometry&); bool acceptOutputPrimitiveGeometry(TLayoutGeometry&);
......
...@@ -67,7 +67,8 @@ HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& int ...@@ -67,7 +67,8 @@ HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& int
sourceEntryPointName(sourceEntryPointName), sourceEntryPointName(sourceEntryPointName),
entryPointFunction(nullptr), entryPointFunction(nullptr),
entryPointFunctionBody(nullptr), entryPointFunctionBody(nullptr),
gsStreamOutput(nullptr) gsStreamOutput(nullptr),
inputPatch(nullptr)
{ {
globalUniformDefaults.clear(); globalUniformDefaults.clear();
globalUniformDefaults.layoutMatrix = ElmRowMajor; globalUniformDefaults.layoutMatrix = ElmRowMajor;
...@@ -1377,6 +1378,7 @@ TIntermTyped* HlslParseContext::splitAccessStruct(const TSourceLoc& loc, TInterm ...@@ -1377,6 +1378,7 @@ TIntermTyped* HlslParseContext::splitAccessStruct(const TSourceLoc& loc, TInterm
void HlslParseContext::trackLinkage(TSymbol& symbol) void HlslParseContext::trackLinkage(TSymbol& symbol)
{ {
TBuiltInVariable biType = symbol.getType().getQualifier().builtIn; TBuiltInVariable biType = symbol.getType().getQualifier().builtIn;
if (biType != EbvNone) if (biType != EbvNone)
builtInLinkageSymbols[biType] = symbol.clone(); builtInLinkageSymbols[biType] = symbol.clone();
...@@ -1811,6 +1813,7 @@ TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunct ...@@ -1811,6 +1813,7 @@ TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunct
else if (variable.getType().containsBuiltInInterstageIO(language)) else if (variable.getType().containsBuiltInInterstageIO(language))
split(variable); split(variable);
} }
assignLocations(variable); assignLocations(variable);
}; };
if (entryPointOutput) if (entryPointOutput)
...@@ -1988,6 +1991,7 @@ void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& return ...@@ -1988,6 +1991,7 @@ void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& return
correctOutput(ioVariable->getWritableType().getQualifier()); correctOutput(ioVariable->getWritableType().getQualifier());
} }
ioVariable->getWritableType().getQualifier().storage = storage; ioVariable->getWritableType().getQualifier().storage = storage;
return ioVariable; return ioVariable;
}; };
...@@ -2022,6 +2026,9 @@ void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& return ...@@ -2022,6 +2026,9 @@ void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& return
if (paramType.getQualifier().isParamInput()) { if (paramType.getQualifier().isParamInput()) {
TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn); TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
inputs.push_back(argAsGlobal); inputs.push_back(argAsGlobal);
if (function[i].declaredBuiltIn == EbvInputPatch)
inputPatch = argAsGlobal;
} }
if (paramType.getQualifier().isParamOutput()) { if (paramType.getQualifier().isParamOutput()) {
TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut); TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut);
...@@ -7574,7 +7581,10 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -7574,7 +7581,10 @@ void HlslParseContext::addPatchConstantInvocation()
// Look for builtin variables in a function's parameter list. // Look for builtin variables in a function's parameter list.
const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) { const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) {
for (int p=0; p<function.getParamCount(); ++p) { for (int p=0; p<function.getParamCount(); ++p) {
const TStorageQualifier storage = function[p].type->getQualifier().storage; TStorageQualifier storage = function[p].type->getQualifier().storage;
if (storage == EvqConstReadOnly) // treated identically to input
storage = EvqIn;
if (function[p].declaredBuiltIn != EbvNone) if (function[p].declaredBuiltIn != EbvNone)
builtIns.insert(HlslParseContext::tInterstageIoData(function[p].declaredBuiltIn, storage)); builtIns.insert(HlslParseContext::tInterstageIoData(function[p].declaredBuiltIn, storage));
...@@ -7605,9 +7615,11 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -7605,9 +7615,11 @@ void HlslParseContext::addPatchConstantInvocation()
} }
}; };
const auto isPerCtrlPt = [this](const TType& type) { const auto isOutputPatch = [this](TFunction& patchConstantFunction, int param) {
// TODO: this is not sufficient to reject all such cases in malformed shaders. const TType& type = *patchConstantFunction[param].type;
return type.isArray() && !type.isRuntimeSizedArray(); const TBuiltInVariable biType = patchConstantFunction[param].declaredBuiltIn;
return type.isArray() && !type.isRuntimeSizedArray() && biType == EbvOutputPatch;
}; };
// We will perform these steps. Each is in a scoped block for separation: they could // We will perform these steps. Each is in a scoped block for separation: they could
...@@ -7636,7 +7648,7 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -7636,7 +7648,7 @@ void HlslParseContext::addPatchConstantInvocation()
TIntermSymbol* invocationIdSym = findLinkageSymbol(EbvInvocationId); TIntermSymbol* invocationIdSym = findLinkageSymbol(EbvInvocationId);
TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence(); TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence();
int perCtrlPtParam = -1; // -1 means there isn't one. int outPatchParam = -1; // -1 means there isn't one.
// ================ Step 1A: Union Interfaces ================ // ================ Step 1A: Union Interfaces ================
// Our patch constant function. // Our patch constant function.
...@@ -7662,25 +7674,37 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -7662,25 +7674,37 @@ void HlslParseContext::addPatchConstantInvocation()
// Now we'll add those to the entry and to the linkage. // Now we'll add those to the entry and to the linkage.
for (int p=0; p<pcfParamCount; ++p) { for (int p=0; p<pcfParamCount; ++p) {
const TBuiltInVariable biType = patchConstantFunction[p].declaredBuiltIn; const TBuiltInVariable biType = patchConstantFunction[p].declaredBuiltIn;
const TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage; TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage;
// Track whether there is any per control point input // Track whether there is an output patch param
if (isPerCtrlPt(*patchConstantFunction[p].type)) { if (isOutputPatch(patchConstantFunction, p)) {
if (perCtrlPtParam >= 0) { if (outPatchParam >= 0) {
// Presently we only support one per ctrl pt input. TODO: does HLSL even allow multiple? // Presently we only support one per ctrl pt input.
error(loc, "unimplemented: multiple per control point inputs to patch constant function", "", ""); error(loc, "unimplemented: multiple output patches in patch constant function", "", "");
return; return;
} }
perCtrlPtParam = p; outPatchParam = p;
} }
if (biType != EbvNone) { if (biType != EbvNone) {
TType* paramType = patchConstantFunction[p].type->clone(); TType* paramType = patchConstantFunction[p].type->clone();
// Use the original declaration type for the linkage
paramType->getQualifier().builtIn = biType;
if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1) if (storage == EvqConstReadOnly) // treated identically to input
addToLinkage(*paramType, patchConstantFunction[p].name, nullptr); storage = EvqIn;
// Presently, the only non-builtin we support is InputPatch, which is treated as
// a pseudo-builtin.
if (biType == EbvInputPatch) {
builtInLinkageSymbols[biType] = inputPatch;
} else if (biType == EbvOutputPatch) {
// Nothing...
} else {
// Use the original declaration type for the linkage
paramType->getQualifier().builtIn = biType;
if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1)
addToLinkage(*paramType, patchConstantFunction[p].name, nullptr);
}
} }
} }
...@@ -7703,18 +7727,12 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -7703,18 +7727,12 @@ void HlslParseContext::addPatchConstantInvocation()
// TODO: handle struct or array inputs // TODO: handle struct or array inputs
{ {
for (int p=0; p<pcfParamCount; ++p) { for (int p=0; p<pcfParamCount; ++p) {
if ((patchConstantFunction[p].type->isArray() && !isPerCtrlPt(*patchConstantFunction[p].type)) ||
(!patchConstantFunction[p].type->isArray() && patchConstantFunction[p].type->isStruct())) {
error(loc, "unimplemented array or variable in patch constant function signature", "", "");
return;
}
TIntermSymbol* inputArg = nullptr; TIntermSymbol* inputArg = nullptr;
if (p == perCtrlPtParam) { if (p == outPatchParam) {
if (perCtrlPtVar == nullptr) { if (perCtrlPtVar == nullptr) {
perCtrlPtVar = makeInternalVariable(*patchConstantFunction[perCtrlPtParam].name, perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name,
*patchConstantFunction[perCtrlPtParam].type); *patchConstantFunction[outPatchParam].type);
perCtrlPtVar->getWritableType().getQualifier().makeTemporary(); perCtrlPtVar->getWritableType().getQualifier().makeTemporary();
} }
...@@ -7724,7 +7742,7 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -7724,7 +7742,7 @@ void HlslParseContext::addPatchConstantInvocation()
const TBuiltInVariable biType = patchConstantFunction[p].declaredBuiltIn; const TBuiltInVariable biType = patchConstantFunction[p].declaredBuiltIn;
inputArg = findLinkageSymbol(biType); inputArg = findLinkageSymbol(biType);
if (inputArg == nullptr) { if (inputArg == nullptr) {
error(loc, "unable to find patch constant function builtin variable", "", ""); error(loc, "unable to find patch constant function builtin variable", "", "");
return; return;
...@@ -7769,9 +7787,9 @@ void HlslParseContext::addPatchConstantInvocation() ...@@ -7769,9 +7787,9 @@ void HlslParseContext::addPatchConstantInvocation()
// invocations of the entry point to build up an array, or (TODO:) use a yet // invocations of the entry point to build up an array, or (TODO:) use a yet
// unavailable extension to look across the SIMD lanes. This is the former // unavailable extension to look across the SIMD lanes. This is the former
// as a placeholder for the latter. // as a placeholder for the latter.
if (perCtrlPtParam >= 0) { if (outPatchParam >= 0) {
// We must introduce a local temp variable of the type wanted by the PCF input. // We must introduce a local temp variable of the type wanted by the PCF input.
const int arraySize = patchConstantFunction[perCtrlPtParam].type->getOuterArraySize(); const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize();
if (entryPointFunction->getType().getBasicType() == EbtVoid) { if (entryPointFunction->getType().getBasicType() == EbtVoid) {
error(loc, "entry point must return a value for use with patch constant function", "", ""); error(loc, "entry point must return a value for use with patch constant function", "", "");
......
...@@ -386,6 +386,7 @@ protected: ...@@ -386,6 +386,7 @@ protected:
}; };
TMap<tInterstageIoData, TVariable*> interstageBuiltInIo; // individual builtin interstage IO vars, indexed by builtin type. TMap<tInterstageIoData, TVariable*> interstageBuiltInIo; // individual builtin interstage IO vars, indexed by builtin type.
TVariable* inputPatch;
// We have to move array references to structs containing builtin interstage IO to the split variables. // We have to move array references to structs containing builtin interstage IO to the split variables.
// This is only handled for one level. This stores the index, because we'll need it in the future, since // This is only handled for one level. This stores the index, because we'll need it in the future, since
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment