HLSL: Refactor attribute implementation.

- make it sharable with GLSL
- correct the case insensitivity
- remove the map; queries are not needed, all entries need processing
- make it easier to build bottom up (will help GLSL parsing)
- support semantic checking and reporting
- allow front-end dependent semantics and attribute name mapping
This commit is contained in:
John Kessenich
2018-01-30 11:01:39 -07:00
parent e349af7e20
commit e18fd20d5c
17 changed files with 638 additions and 500 deletions

View File

@@ -1620,7 +1620,7 @@ void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc,
// Returns an aggregate of parameter-symbol nodes.
//
TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
const TAttributeMap& attributes,
const TAttributes& attributes,
TIntermNode*& entryPointTree)
{
currentCaller = function.getMangledName();
@@ -1717,189 +1717,217 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
}
// Handle all [attrib] attribute for the shader entry point
void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributeMap& attributes)
void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes)
{
// Handle entry-point function attributes
const TIntermAggregate* numThreads = attributes[EatNumThreads];
if (numThreads != nullptr) {
const TIntermSequence& sequence = numThreads->getSequence();
for (int lid = 0; lid < int(sequence.size()); ++lid)
intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
}
// MaxVertexCount
if (attributes.contains(EatMaxVertexCount)) {
int maxVertexCount;
if (! attributes.getInt(EatMaxVertexCount, maxVertexCount)) {
error(loc, "invalid maxvertexcount", "", "");
} else {
if (! intermediate.setVertices(maxVertexCount))
error(loc, "cannot change previously set maxvertexcount attribute", "", "");
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatNumThreads:
{
const TIntermSequence& sequence = it->args->getSequence();
for (int lid = 0; lid < int(sequence.size()); ++lid)
intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
break;
}
}
case EatMaxVertexCount:
{
int maxVertexCount;
// Handle [patchconstantfunction("...")]
if (attributes.contains(EatPatchConstantFunc)) {
TString pcfName;
if (! attributes.getString(EatPatchConstantFunc, pcfName, 0, false)) {
error(loc, "invalid patch constant function", "", "");
} else {
patchConstantFunctionName = pcfName;
if (! it->getInt(maxVertexCount)) {
error(loc, "invalid maxvertexcount", "", "");
} else {
if (! intermediate.setVertices(maxVertexCount))
error(loc, "cannot change previously set maxvertexcount attribute", "", "");
}
break;
}
}
// Handle [domain("...")]
if (attributes.contains(EatDomain)) {
TString domainStr;
if (! attributes.getString(EatDomain, domainStr)) {
error(loc, "invalid domain", "", "");
} else {
TLayoutGeometry domain = ElgNone;
if (domainStr == "tri") {
domain = ElgTriangles;
} else if (domainStr == "quad") {
domain = ElgQuads;
} else if (domainStr == "isoline") {
domain = ElgIsolines;
case EatPatchConstantFunc:
{
TString pcfName;
if (! it->getString(pcfName, 0, false)) {
error(loc, "invalid patch constant function", "", "");
} else {
error(loc, "unsupported domain type", domainStr.c_str(), "");
}
if (language == EShLangTessEvaluation) {
if (! intermediate.setInputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
} else {
if (! intermediate.setOutputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
patchConstantFunctionName = pcfName;
}
break;
}
}
// Handle [outputtopology("...")]
if (attributes.contains(EatOutputTopology)) {
TString topologyStr;
if (! attributes.getString(EatOutputTopology, topologyStr)) {
error(loc, "invalid outputtopology", "", "");
} else {
TVertexOrder vertexOrder = EvoNone;
TLayoutGeometry primitive = ElgNone;
if (topologyStr == "point") {
intermediate.setPointMode();
} else if (topologyStr == "line") {
primitive = ElgIsolines;
} else if (topologyStr == "triangle_cw") {
vertexOrder = EvoCw;
primitive = ElgTriangles;
} else if (topologyStr == "triangle_ccw") {
vertexOrder = EvoCcw;
primitive = ElgTriangles;
case EatDomain:
{
// Handle [domain("...")]
TString domainStr;
if (! it->getString(domainStr)) {
error(loc, "invalid domain", "", "");
} else {
error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
}
TLayoutGeometry domain = ElgNone;
if (vertexOrder != EvoNone) {
if (! intermediate.setVertexOrder(vertexOrder)) {
error(loc, "cannot change previously set outputtopology",
TQualifier::getVertexOrderString(vertexOrder), "");
if (domainStr == "tri") {
domain = ElgTriangles;
} else if (domainStr == "quad") {
domain = ElgQuads;
} else if (domainStr == "isoline") {
domain = ElgIsolines;
} else {
error(loc, "unsupported domain type", domainStr.c_str(), "");
}
if (language == EShLangTessEvaluation) {
if (! intermediate.setInputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
} else {
if (! intermediate.setOutputPrimitive(domain))
error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
}
}
if (primitive != ElgNone)
intermediate.setOutputPrimitive(primitive);
break;
}
}
// Handle [partitioning("...")]
if (attributes.contains(EatPartitioning)) {
TString partitionStr;
if (! attributes.getString(EatPartitioning, partitionStr)) {
error(loc, "invalid partitioning", "", "");
} else {
TVertexSpacing partitioning = EvsNone;
if (partitionStr == "integer") {
partitioning = EvsEqual;
} else if (partitionStr == "fractional_even") {
partitioning = EvsFractionalEven;
} else if (partitionStr == "fractional_odd") {
partitioning = EvsFractionalOdd;
//} else if (partition == "pow2") { // TODO: currently nothing to map this to.
case EatOutputTopology:
{
// Handle [outputtopology("...")]
TString topologyStr;
if (! it->getString(topologyStr)) {
error(loc, "invalid outputtopology", "", "");
} else {
error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
}
TVertexOrder vertexOrder = EvoNone;
TLayoutGeometry primitive = ElgNone;
if (! intermediate.setVertexSpacing(partitioning))
error(loc, "cannot change previously set partitioning",
TQualifier::getVertexSpacingString(partitioning), "");
if (topologyStr == "point") {
intermediate.setPointMode();
} else if (topologyStr == "line") {
primitive = ElgIsolines;
} else if (topologyStr == "triangle_cw") {
vertexOrder = EvoCw;
primitive = ElgTriangles;
} else if (topologyStr == "triangle_ccw") {
vertexOrder = EvoCcw;
primitive = ElgTriangles;
} else {
error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
}
if (vertexOrder != EvoNone) {
if (! intermediate.setVertexOrder(vertexOrder)) {
error(loc, "cannot change previously set outputtopology",
TQualifier::getVertexOrderString(vertexOrder), "");
}
}
if (primitive != ElgNone)
intermediate.setOutputPrimitive(primitive);
}
break;
}
}
case EatPartitioning:
{
// Handle [partitioning("...")]
TString partitionStr;
if (! it->getString(partitionStr)) {
error(loc, "invalid partitioning", "", "");
} else {
TVertexSpacing partitioning = EvsNone;
if (partitionStr == "integer") {
partitioning = EvsEqual;
} else if (partitionStr == "fractional_even") {
partitioning = EvsFractionalEven;
} else if (partitionStr == "fractional_odd") {
partitioning = EvsFractionalOdd;
//} else if (partition == "pow2") { // TODO: currently nothing to map this to.
} else {
error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
}
// Handle [outputcontrolpoints("...")]
if (attributes.contains(EatOutputControlPoints)) {
int ctrlPoints;
if (! attributes.getInt(EatOutputControlPoints, ctrlPoints)) {
error(loc, "invalid outputcontrolpoints", "", "");
} else {
if (! intermediate.setVertices(ctrlPoints)) {
error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
if (! intermediate.setVertexSpacing(partitioning))
error(loc, "cannot change previously set partitioning",
TQualifier::getVertexSpacingString(partitioning), "");
}
break;
}
case EatOutputControlPoints:
{
// Handle [outputcontrolpoints("...")]
int ctrlPoints;
if (! it->getInt(ctrlPoints)) {
error(loc, "invalid outputcontrolpoints", "", "");
} else {
if (! intermediate.setVertices(ctrlPoints)) {
error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
}
}
break;
}
case EatBuiltIn:
case EatLocation:
// tolerate these because of dual use of entrypoint and type attributes
break;
default:
warn(loc, "attribute does not apply to entry point", "", "");
break;
}
}
}
// Update the given type with any type-like attribute information in the
// attributes.
void HlslParseContext::transferTypeAttributes(const TAttributeMap& attributes, TType& type)
void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type,
bool allowEntry)
{
if (attributes.size() == 0)
return;
// location
int value;
if (attributes.getInt(EatLocation, value))
type.getQualifier().layoutLocation = value;
// binding
if (attributes.getInt(EatBinding, value)) {
type.getQualifier().layoutBinding = value;
type.getQualifier().layoutSet = 0;
}
// set
if (attributes.getInt(EatBinding, value, 1))
type.getQualifier().layoutSet = value;
// global cbuffer binding
if (attributes.getInt(EatGlobalBinding, value))
globalUniformBinding = value;
// global cbuffer binding
if (attributes.getInt(EatGlobalBinding, value, 1))
globalUniformSet = value;
// input attachment
if (attributes.getInt(EatInputAttachment, value))
type.getQualifier().layoutAttachment = value;
// PointSize built-in
TString builtInString;
if (attributes.getString(EatBuiltIn, builtInString, 0, false)) {
if (builtInString == "PointSize")
type.getQualifier().builtIn = EbvPointSize;
}
// push_constant
if (attributes.contains(EatPushConstant))
type.getQualifier().layoutPushConstant = true;
// specialization constant
if (attributes.getInt(EatConstantId, value)) {
TSourceLoc loc;
loc.init();
setSpecConstantId(loc, type.getQualifier(), value);
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatLocation:
// location
if (it->getInt(value))
type.getQualifier().layoutLocation = value;
break;
case EatBinding:
// binding
if (it->getInt(value)) {
type.getQualifier().layoutBinding = value;
type.getQualifier().layoutSet = 0;
}
// set
if (it->getInt(value, 1))
type.getQualifier().layoutSet = value;
break;
case EatGlobalBinding:
// global cbuffer binding
if (it->getInt(value))
globalUniformBinding = value;
// global cbuffer binding
if (it->getInt(value, 1))
globalUniformSet = value;
break;
case EatInputAttachment:
// input attachment
if (it->getInt(value))
type.getQualifier().layoutAttachment = value;
break;
case EatBuiltIn:
// PointSize built-in
if (it->getString(builtInString, 0, false)) {
if (builtInString == "PointSize")
type.getQualifier().builtIn = EbvPointSize;
}
break;
case EatPushConstant:
// push_constant
type.getQualifier().layoutPushConstant = true;
break;
case EatConstantId:
// specialization constant
if (it->getInt(value)) {
TSourceLoc loc;
loc.init();
setSpecConstantId(loc, type.getQualifier(), value);
}
break;
default:
if (! allowEntry)
warn(loc, "attribute does not apply to a type", "", "");
break;
}
}
}
@@ -1936,7 +1964,7 @@ void HlslParseContext::transferTypeAttributes(const TAttributeMap& attributes, T
// a subtree that creates the entry point.
//
TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction,
const TAttributeMap& attributes)
const TAttributes& attributes)
{
// Return true if this is a tessellation patch constant function input to a domain shader.
const auto isDsPcfInput = [this](const TType& type) {
@@ -8792,29 +8820,75 @@ bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayout
}
//
// Selection hints
// Selection attributes
//
TSelectionControl HlslParseContext::handleSelectionControl(const TAttributeMap& attributes) const
void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection,
const TAttributes& attributes)
{
if (attributes.contains(EatFlatten))
return ESelectionControlFlatten;
else if (attributes.contains(EatBranch))
return ESelectionControlDontFlatten;
else
return ESelectionControlNone;
if (selection == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatFlatten:
selection->setFlatten();
break;
case EatBranch:
selection->setDontFlatten();
break;
default:
warn(loc, "attribute does not apply to a selection", "", "");
break;
}
}
}
//
// Switch attributes
//
void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
const TAttributes& attributes)
{
if (selection == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatFlatten:
selection->setFlatten();
break;
case EatBranch:
selection->setDontFlatten();
break;
default:
warn(loc, "attribute does not apply to a switch", "", "");
break;
}
}
}
//
// Loop hints
//
TLoopControl HlslParseContext::handleLoopControl(const TAttributeMap& attributes) const
void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
const TAttributes& attributes)
{
if (attributes.contains(EatUnroll))
return ELoopControlUnroll;
else if (attributes.contains(EatLoop))
return ELoopControlDontUnroll;
else
return ELoopControlNone;
if (loop == nullptr)
return;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
switch (it->name) {
case EatUnroll:
loop->setUnroll();
break;
case EatLoop:
loop->setDontUnroll();
break;
default:
warn(loc, "attribute does not apply to a loop", "", "");
break;
}
}
}
//
@@ -8959,7 +9033,7 @@ void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIn
// into a switch node.
//
TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression,
TIntermAggregate* lastStatements, TSelectionControl control)
TIntermAggregate* lastStatements, const TAttributes& attributes)
{
wrapupSwitchSubsequence(lastStatements, nullptr);
@@ -8986,7 +9060,7 @@ TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* ex
TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
switchNode->setLoc(loc);
switchNode->setSelectionControl(control);
handleSwitchAttributes(loc, switchNode, attributes);
return switchNode;
}