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

@@ -34,157 +34,73 @@
//
#include "hlslAttributes.h"
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include "hlslParseHelper.h"
namespace glslang {
// Map the given string to an attribute enum from TAttributeType,
// or EatNone if invalid.
TAttributeType TAttributeMap::attributeFromName(const TString& nameSpace, const TString& name)
TAttributeType HlslParseContext::attributeFromName(const TString& nameSpace, const TString& name) const
{
// These are case insensitive.
TString lowername(name);
std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
TString lowernameSpace(nameSpace);
std::transform(lowernameSpace.begin(), lowernameSpace.end(), lowernameSpace.begin(), ::tolower);
// handle names within a namespace
if (lowernameSpace == "vk") {
if (lowername == "input_attachment_index")
if (nameSpace == "vk") {
if (name == "input_attachment_index")
return EatInputAttachment;
else if (lowername == "location")
else if (name == "location")
return EatLocation;
else if (lowername == "binding")
else if (name == "binding")
return EatBinding;
else if (lowername == "global_cbuffer_binding")
else if (name == "global_cbuffer_binding")
return EatGlobalBinding;
else if (lowername == "builtin")
else if (name == "builtin")
return EatBuiltIn;
else if (lowername == "constant_id")
else if (name == "constant_id")
return EatConstantId;
else if (lowername == "push_constant")
else if (name == "push_constant")
return EatPushConstant;
} else if (lowernameSpace.size() > 0)
} else if (nameSpace.size() > 0)
return EatNone;
// handle names with no namespace
if (lowername == "allow_uav_condition")
if (name == "allow_uav_condition")
return EatAllow_uav_condition;
else if (lowername == "branch")
else if (name == "branch")
return EatBranch;
else if (lowername == "call")
else if (name == "call")
return EatCall;
else if (lowername == "domain")
else if (name == "domain")
return EatDomain;
else if (lowername == "earlydepthstencil")
else if (name == "earlydepthstencil")
return EatEarlyDepthStencil;
else if (lowername == "fastopt")
else if (name == "fastopt")
return EatFastOpt;
else if (lowername == "flatten")
else if (name == "flatten")
return EatFlatten;
else if (lowername == "forcecase")
else if (name == "forcecase")
return EatForceCase;
else if (lowername == "instance")
else if (name == "instance")
return EatInstance;
else if (lowername == "maxtessfactor")
else if (name == "maxtessfactor")
return EatMaxTessFactor;
else if (lowername == "maxvertexcount")
else if (name == "maxvertexcount")
return EatMaxVertexCount;
else if (lowername == "numthreads")
else if (name == "numthreads")
return EatNumThreads;
else if (lowername == "outputcontrolpoints")
else if (name == "outputcontrolpoints")
return EatOutputControlPoints;
else if (lowername == "outputtopology")
else if (name == "outputtopology")
return EatOutputTopology;
else if (lowername == "partitioning")
else if (name == "partitioning")
return EatPartitioning;
else if (lowername == "patchconstantfunc")
else if (name == "patchconstantfunc")
return EatPatchConstantFunc;
else if (lowername == "unroll")
else if (name == "unroll")
return EatUnroll;
else if (lowername == "loop")
else if (name == "loop")
return EatLoop;
else
return EatNone;
}
// Look up entry, inserting if it's not there, and if name is a valid attribute name
// as known by attributeFromName.
TAttributeType TAttributeMap::setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value)
{
if (name == nullptr)
return EatNone;
const TAttributeType attr = attributeFromName(nameSpace, *name);
if (attr != EatNone)
attributes[attr] = value;
return attr;
}
// Look up entry (const version), and return aggregate node. This cannot change the map.
const TIntermAggregate* TAttributeMap::operator[](TAttributeType attr) const
{
const auto entry = attributes.find(attr);
return (entry == attributes.end()) ? nullptr : entry->second;
}
// True if entry exists in map (even if value is nullptr)
bool TAttributeMap::contains(TAttributeType attr) const
{
return attributes.find(attr) != attributes.end();
}
// extract integers out of attribute arguments stored in attribute aggregate
bool TAttributeMap::getInt(TAttributeType attr, int& value, int argNum) const
{
const TConstUnion* intConst = getConstUnion(attr, EbtInt, argNum);
if (intConst == nullptr)
return false;
value = intConst->getIConst();
return true;
};
// extract strings out of attribute arguments stored in attribute aggregate.
// convert to lower case if converToLower is true (for case-insensitive compare convenience)
bool TAttributeMap::getString(TAttributeType attr, TString& value, int argNum, bool convertToLower) const
{
const TConstUnion* stringConst = getConstUnion(attr, EbtString, argNum);
if (stringConst == nullptr)
return false;
value = *stringConst->getSConst();
// Convenience.
if (convertToLower)
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
return true;
};
// Helper to get attribute const union. Returns nullptr on failure.
const TConstUnion* TAttributeMap::getConstUnion(TAttributeType attr, TBasicType basicType, int argNum) const
{
const TIntermAggregate* attrAgg = (*this)[attr];
if (attrAgg == nullptr)
return nullptr;
if (argNum >= int(attrAgg->getSequence().size()))
return nullptr;
const TConstUnion* constVal = &attrAgg->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
if (constVal == nullptr || constVal->getType() != basicType)
return nullptr;
return constVal;
}
} // end namespace glslang

View File

@@ -38,93 +38,22 @@
#include <unordered_map>
#include <functional>
#include "../glslang/MachineIndependent/attribute.h"
#include "../glslang/MachineIndependent/SymbolTable.h"
#include "hlslScanContext.h"
#include "../glslang/Include/Common.h"
namespace glslang {
enum TAttributeType {
EatNone,
EatAllow_uav_condition,
EatBranch,
EatCall,
EatDomain,
EatEarlyDepthStencil,
EatFastOpt,
EatFlatten,
EatForceCase,
EatInstance,
EatMaxTessFactor,
EatNumThreads,
EatMaxVertexCount,
EatOutputControlPoints,
EatOutputTopology,
EatPartitioning,
EatPatchConstantFunc,
EatPatchSize,
EatUnroll,
EatLoop,
EatBinding,
EatGlobalBinding,
EatLocation,
EatInputAttachment,
EatBuiltIn,
EatPushConstant,
EatConstantId
};
}
namespace std {
// Allow use of TAttributeType enum in hash_map without calling code having to cast.
template <> struct hash<glslang::TAttributeType> {
std::size_t operator()(glslang::TAttributeType attr) const {
return std::hash<int>()(int(attr));
}
};
} // end namespace std
namespace glslang {
class TIntermAggregate;
class TAttributeMap {
public:
int size() const { return (int)attributes.size(); }
// Search for and potentially add the attribute into the map. Return the
// attribute type enum for it, if found, else EatNone.
TAttributeType setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value);
// Const lookup: search for (but do not modify) the attribute in the map.
const TIntermAggregate* operator[](TAttributeType) const;
// True if entry exists in map (even if value is nullptr)
bool contains(TAttributeType) const;
// Obtain attribute as integer
bool getInt(TAttributeType attr, int& value, int argNum = 0) const;
// Obtain attribute as string, with optional to-lower transform
bool getString(TAttributeType attr, TString& value, int argNum = 0, bool convertToLower = true) const;
protected:
// Helper to get attribute const union
const TConstUnion* getConstUnion(TAttributeType attr, TBasicType, int argNum) const;
// Find an attribute enum given its name.
static TAttributeType attributeFromName(const TString& nameSpace, const TString& name);
std::unordered_map<TAttributeType, TIntermAggregate*> attributes;
};
class TFunctionDeclarator {
public:
TFunctionDeclarator() : function(nullptr), body(nullptr) { }
TSourceLoc loc;
TFunction* function;
TAttributeMap attributes;
TAttributes attributes;
TVector<HlslToken>* body;
};
} // end namespace glslang
#endif // HLSLATTRIBUTES_H_

View File

@@ -396,6 +396,9 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
if (peekTokenClass(EHTokLeftParen)) {
// looks like function parameters
// merge in the attributes into the return type
parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType, true);
// Potentially rename shader entry point function. No-op most of the time.
parseContext.renameShaderFunction(fullName);
@@ -423,7 +426,13 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, true);
}
} else {
// A variable declaration. Fix the storage qualifier if it's a global.
// A variable declaration.
// merge in the attributes, the first time around, into the shared type
if (! declarator_list)
parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType);
// Fix the storage qualifier if it's a global.
if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel())
declaredType.getQualifier().storage = EvqUniform;
@@ -536,13 +545,16 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node)
{
node = nullptr;
TAttributeMap attributes;
TAttributes attributes;
// fully_specified_type
TType type;
if (! acceptFullySpecifiedType(type, attributes))
return false;
if (attributes.size() > 0)
parseContext.warn(token.loc, "attributes don't apply to control declaration", "", "");
// filter out type casts
if (peekTokenClass(EHTokLeftParen)) {
recedeToken();
@@ -578,12 +590,12 @@ bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node)
// : type_specifier
// | type_qualifier type_specifier
//
bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributeMap& attributes)
bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributes& attributes)
{
TIntermNode* nodeList = nullptr;
return acceptFullySpecifiedType(type, nodeList, attributes);
}
bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributeMap& attributes, bool forbidDeclarators)
bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributes& attributes, bool forbidDeclarators)
{
// type_qualifier
TQualifier qualifier;
@@ -608,7 +620,7 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList,
parseContext.mergeQualifiers(type.getQualifier(), qualifier);
// merge in the attributes
parseContext.transferTypeAttributes(attributes, type);
parseContext.transferTypeAttributes(token.loc, attributes, type);
// further, it can create an anonymous instance of the block
// (cbuffer and tbuffer don't consume the next identifier, and
@@ -633,9 +645,6 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList,
qualifier.builtIn = type.getQualifier().builtIn;
type.getQualifier() = qualifier;
// merge in the attributes
parseContext.transferTypeAttributes(attributes, type);
}
return true;
@@ -2335,7 +2344,7 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*
// struct_declaration
// attributes
TAttributeMap attributes;
TAttributes attributes;
acceptAttributes(attributes);
bool declarator_list = false;
@@ -2346,6 +2355,9 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*
expected("member type");
return false;
}
// merge in the attributes
parseContext.transferTypeAttributes(token.loc, attributes, memberType);
// struct_declarator COMMA struct_declarator ...
bool functionDefinitionAccepted = false;
@@ -2542,7 +2554,7 @@ bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTy
bool HlslGrammar::acceptParameterDeclaration(TFunction& function)
{
// attributes
TAttributeMap attributes;
TAttributes attributes;
acceptAttributes(attributes);
// fully_specified_type
@@ -2550,6 +2562,9 @@ bool HlslGrammar::acceptParameterDeclaration(TFunction& function)
if (! acceptFullySpecifiedType(*type, attributes))
return false;
// merge in the attributes
parseContext.transferTypeAttributes(token.loc, attributes, *type);
// identifier
HlslToken idToken;
acceptIdentifier(idToken);
@@ -3386,7 +3401,7 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement)
statement = nullptr;
// attributes
TAttributeMap attributes;
TAttributes attributes;
acceptAttributes(attributes);
// attributed_statement
@@ -3458,7 +3473,7 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement)
// | PATCHCONSTANTFUNC
// | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN
//
void HlslGrammar::acceptAttributes(TAttributeMap& attributes)
void HlslGrammar::acceptAttributes(TAttributes& attributes)
{
// For now, accept the [ XXX(X) ] syntax, but drop all but
// numthreads, which is used to set the CS local size.
@@ -3529,9 +3544,16 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes)
return;
}
// Add any values we found into the attribute map. This accepts
// (and ignores) values not mapping to a known TAttributeType;
attributes.setAttribute(nameSpace, attributeToken.string, expressions);
// Add any values we found into the attribute map.
if (attributeToken.string != nullptr) {
TAttributeType attributeType = parseContext.attributeFromName(nameSpace, *attributeToken.string);
if (attributeType == EatNone)
parseContext.warn(attributeToken.loc, "unrecognized attribute", attributeToken.string->c_str(), "");
else {
TAttributeArgs attributeArgs = { attributeType, expressions };
attributes.push_back(attributeArgs);
}
}
} while (true);
}
@@ -3539,12 +3561,10 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes)
// : IF LEFT_PAREN expression RIGHT_PAREN statement
// : IF LEFT_PAREN expression RIGHT_PAREN statement ELSE statement
//
bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributeMap& attributes)
bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributes& attributes)
{
TSourceLoc loc = token.loc;
const TSelectionControl control = parseContext.handleSelectionControl(attributes);
// IF
if (! acceptTokenClass(EHTokIf))
return false;
@@ -3582,7 +3602,9 @@ bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttri
}
// Put the pieces together
statement = intermediate.addSelection(condition, thenElse, loc, control);
statement = intermediate.addSelection(condition, thenElse, loc);
parseContext.handleSelectionAttributes(loc, statement->getAsSelectionNode(), attributes);
parseContext.popScope();
--parseContext.controlFlowNestingLevel;
@@ -3592,13 +3614,11 @@ bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttri
// switch_statement
// : SWITCH LEFT_PAREN expression RIGHT_PAREN compound_statement
//
bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributeMap& attributes)
bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributes& attributes)
{
// SWITCH
TSourceLoc loc = token.loc;
const TSelectionControl control = parseContext.handleSelectionControl(attributes);
if (! acceptTokenClass(EHTokSwitch))
return false;
@@ -3618,7 +3638,8 @@ bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttribut
--parseContext.controlFlowNestingLevel;
if (statementOkay)
statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr, control);
statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr,
attributes);
parseContext.popSwitchSequence();
parseContext.popScope();
@@ -3632,7 +3653,7 @@ bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttribut
// | FOR LEFT_PAREN for_init_statement for_rest_statement RIGHT_PAREN statement
//
// Non-speculative, only call if it needs to be found; WHILE or DO or FOR already seen.
bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributeMap& attributes)
bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributes& attributes)
{
TSourceLoc loc = token.loc;
TIntermTyped* condition = nullptr;
@@ -3642,9 +3663,8 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
// WHILE or DO or FOR
advanceToken();
const TLoopControl control = parseContext.handleLoopControl(attributes);
TIntermLoop* loopNode = nullptr;
switch (loop) {
case EHTokWhile:
// so that something declared in the condition is scoped to the lifetime
@@ -3670,9 +3690,9 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
parseContext.popScope();
--parseContext.controlFlowNestingLevel;
statement = intermediate.addLoop(statement, condition, nullptr, true, loc, control);
return true;
loopNode = intermediate.addLoop(statement, condition, nullptr, true, loc);
statement = loopNode;
break;
case EHTokDo:
parseContext.nestLooping(); // this only needs to work right if no errors
@@ -3703,9 +3723,9 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
parseContext.unnestLooping();
--parseContext.controlFlowNestingLevel;
statement = intermediate.addLoop(statement, condition, 0, false, loc, control);
return true;
loopNode = intermediate.addLoop(statement, condition, 0, false, loc);
statement = loopNode;
break;
case EHTokFor:
{
@@ -3747,18 +3767,21 @@ bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttri
return false;
}
statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, control);
statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, loopNode);
parseContext.popScope();
parseContext.unnestLooping();
--parseContext.controlFlowNestingLevel;
return true;
break;
}
default:
return false;
}
parseContext.handleLoopAttributes(loc, loopNode, attributes);
return true;
}
// jump_statement

View File

@@ -43,7 +43,6 @@
namespace glslang {
class TAttributeMap;
class TFunctionDeclarator;
// Should just be the grammar aspect of HLSL.
@@ -71,8 +70,8 @@ namespace glslang {
bool acceptControlDeclaration(TIntermNode*& node);
bool acceptSamplerDeclarationDX9(TType&);
bool acceptSamplerState();
bool acceptFullySpecifiedType(TType&, const TAttributeMap&);
bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributeMap&, bool forbidDeclarators = false);
bool acceptFullySpecifiedType(TType&, const TAttributes&);
bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributes&, bool forbidDeclarators = false);
bool acceptQualifier(TQualifier&);
bool acceptLayoutQualifierList(TQualifier&);
bool acceptType(TType&);
@@ -117,10 +116,10 @@ namespace glslang {
bool acceptScopedCompoundStatement(TIntermNode*&);
bool acceptStatement(TIntermNode*&);
bool acceptNestedStatement(TIntermNode*&);
void acceptAttributes(TAttributeMap&);
bool acceptSelectionStatement(TIntermNode*&, const TAttributeMap&);
bool acceptSwitchStatement(TIntermNode*&, const TAttributeMap&);
bool acceptIterationStatement(TIntermNode*&, const TAttributeMap&);
void acceptAttributes(TAttributes&);
bool acceptSelectionStatement(TIntermNode*&, const TAttributes&);
bool acceptSwitchStatement(TIntermNode*&, const TAttributes&);
bool acceptIterationStatement(TIntermNode*&, const TAttributes&);
bool acceptJumpStatement(TIntermNode*&);
bool acceptCaseLabel(TIntermNode*&);
bool acceptDefaultLabel(TIntermNode*&);

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;
}

View File

@@ -38,12 +38,12 @@
#include "../glslang/MachineIndependent/parseVersions.h"
#include "../glslang/MachineIndependent/ParseHelper.h"
#include "../glslang/MachineIndependent/attribute.h"
#include <array>
namespace glslang {
class TAttributeMap; // forward declare
class TFunctionDeclarator;
class HlslParseContext : public TParseContextBase {
@@ -80,10 +80,10 @@ public:
bool isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field);
void assignToInterface(TVariable& variable);
void handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributeMap&, TIntermNode*& entryPointTree);
TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributeMap&);
void handleEntryPointAttributes(const TSourceLoc&, const TAttributeMap&);
void transferTypeAttributes(const TAttributeMap&, TType&);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributes&, TIntermNode*& entryPointTree);
TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributes&);
void handleEntryPointAttributes(const TSourceLoc&, const TAttributes&);
void transferTypeAttributes(const TSourceLoc&, const TAttributes&, TType&, bool allowEntry = false);
void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node);
void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector<TVariable*>& inputs, TVector<TVariable*>& outputs);
void remapNonEntryPointIO(TFunction& function);
@@ -163,7 +163,7 @@ public:
void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&);
void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&);
void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, TSelectionControl control);
TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, const TAttributes&);
void updateImplicitArraySize(const TSourceLoc&, TIntermNode*, int index);
@@ -203,10 +203,11 @@ public:
bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);
// Determine selection control from attributes
TSelectionControl handleSelectionControl(const TAttributeMap& attributes) const;
void handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection*, const TAttributes& attributes);
void handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch*, const TAttributes& attributes);
// Determine loop control from attributes
TLoopControl handleLoopControl(const TAttributeMap& attributes) const;
void handleLoopAttributes(const TSourceLoc& loc, TIntermLoop*, const TAttributes& attributes);
// Share struct buffer deep types
void shareStructBufferType(TType&);
@@ -217,6 +218,8 @@ public:
// Obtain the sampler return type of the given sampler in retType.
void getTextureReturnType(const TSampler& sampler, TType& retType) const;
TAttributeType attributeFromName(const TString& nameSpace, const TString& name) const;
protected:
struct TFlattenData {
TFlattenData() : nextBinding(TQualifier::layoutBindingEnd),