|
|
@@ -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", "", "");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Handle [patchconstantfunction("...")]
|
|
|
- if (attributes.contains(EatPatchConstantFunc)) {
|
|
|
- TString pcfName;
|
|
|
- if (! attributes.getString(EatPatchConstantFunc, pcfName, 0, false)) {
|
|
|
- error(loc, "invalid patch constant function", "", "");
|
|
|
- } else {
|
|
|
- patchConstantFunctionName = pcfName;
|
|
|
+ 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 [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;
|
|
|
+ if (! it->getInt(maxVertexCount)) {
|
|
|
+ error(loc, "invalid maxvertexcount", "", "");
|
|
|
} else {
|
|
|
- error(loc, "unsupported domain type", domainStr.c_str(), "");
|
|
|
+ if (! intermediate.setVertices(maxVertexCount))
|
|
|
+ error(loc, "cannot change previously set maxvertexcount attribute", "", "");
|
|
|
}
|
|
|
-
|
|
|
- if (language == EShLangTessEvaluation) {
|
|
|
- if (! intermediate.setInputPrimitive(domain))
|
|
|
- error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case EatPatchConstantFunc:
|
|
|
+ {
|
|
|
+ TString pcfName;
|
|
|
+ if (! it->getString(pcfName, 0, false)) {
|
|
|
+ error(loc, "invalid patch constant function", "", "");
|
|
|
} 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 (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 (vertexOrder != EvoNone) {
|
|
|
- if (! intermediate.setVertexOrder(vertexOrder)) {
|
|
|
- error(loc, "cannot change previously set outputtopology",
|
|
|
- TQualifier::getVertexOrderString(vertexOrder), "");
|
|
|
+ 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 (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 (! intermediate.setVertexSpacing(partitioning))
|
|
|
- error(loc, "cannot change previously set partitioning",
|
|
|
- TQualifier::getVertexSpacingString(partitioning), "");
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//
|
|
|
-// Loop hints
|
|
|
+// Switch attributes
|
|
|
//
|
|
|
-TLoopControl HlslParseContext::handleLoopControl(const TAttributeMap& attributes) const
|
|
|
+void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
|
|
|
+ const TAttributes& attributes)
|
|
|
{
|
|
|
- if (attributes.contains(EatUnroll))
|
|
|
- return ELoopControlUnroll;
|
|
|
- else if (attributes.contains(EatLoop))
|
|
|
- return ELoopControlDontUnroll;
|
|
|
- else
|
|
|
- return ELoopControlNone;
|
|
|
+ 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 attributes
|
|
|
+//
|
|
|
+void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
|
|
|
+ const TAttributes& attributes)
|
|
|
+{
|
|
|
+ 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;
|
|
|
}
|