Merge pull request #1644 from jeffbolznv/buffer_reference
GL_EXT_buffer_reference
This commit is contained in:
commit
344a03c034
54 changed files with 3069 additions and 98 deletions
|
|
@ -66,6 +66,8 @@ enum TBasicType {
|
|||
EbtAccStructNV,
|
||||
#endif
|
||||
|
||||
EbtReference,
|
||||
|
||||
// HLSL types that live only temporarily.
|
||||
EbtString,
|
||||
|
||||
|
|
|
|||
|
|
@ -721,6 +721,7 @@ public:
|
|||
clearUniformLayout();
|
||||
|
||||
layoutPushConstant = false;
|
||||
layoutBufferReference = false;
|
||||
#ifdef NV_EXTENSIONS
|
||||
layoutPassthrough = false;
|
||||
layoutViewportRelative = false;
|
||||
|
|
@ -729,6 +730,8 @@ public:
|
|||
layoutShaderRecordNV = false;
|
||||
#endif
|
||||
|
||||
layoutBufferReferenceAlign = layoutBufferReferenceAlignEnd;
|
||||
|
||||
clearInterstageLayout();
|
||||
|
||||
layoutSpecConstantId = layoutSpecConstantIdEnd;
|
||||
|
|
@ -763,7 +766,8 @@ public:
|
|||
#ifdef NV_EXTENSIONS
|
||||
layoutShaderRecordNV ||
|
||||
#endif
|
||||
layoutPushConstant;
|
||||
layoutPushConstant ||
|
||||
layoutBufferReference;
|
||||
}
|
||||
bool hasLayout() const
|
||||
{
|
||||
|
|
@ -808,9 +812,14 @@ public:
|
|||
unsigned int layoutSpecConstantId : 11;
|
||||
static const unsigned int layoutSpecConstantIdEnd = 0x7FF;
|
||||
|
||||
// stored as log2 of the actual alignment value
|
||||
unsigned int layoutBufferReferenceAlign : 6;
|
||||
static const unsigned int layoutBufferReferenceAlignEnd = 0x3F;
|
||||
|
||||
TLayoutFormat layoutFormat : 8;
|
||||
|
||||
bool layoutPushConstant;
|
||||
bool layoutBufferReference;
|
||||
|
||||
#ifdef NV_EXTENSIONS
|
||||
bool layoutPassthrough;
|
||||
|
|
@ -918,6 +927,10 @@ public:
|
|||
// is just whether or not it was declared with an ID.
|
||||
return layoutSpecConstantId != layoutSpecConstantIdEnd;
|
||||
}
|
||||
bool hasBufferReferenceAlign() const
|
||||
{
|
||||
return layoutBufferReferenceAlign != layoutBufferReferenceAlignEnd;
|
||||
}
|
||||
bool isSpecConstant() const
|
||||
{
|
||||
// True if type is a specialization constant, whether or not it
|
||||
|
|
@ -1308,7 +1321,12 @@ public:
|
|||
sampler.clear();
|
||||
qualifier = p.qualifier;
|
||||
if (p.userDef) {
|
||||
structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues
|
||||
if (p.userDef->basicType == EbtReference) {
|
||||
basicType = EbtReference;
|
||||
referentType = p.userDef->referentType;
|
||||
} else {
|
||||
structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues
|
||||
}
|
||||
typeName = NewPoolTString(p.userDef->getTypeName().c_str());
|
||||
}
|
||||
}
|
||||
|
|
@ -1377,6 +1395,17 @@ public:
|
|||
sampler.clear();
|
||||
typeName = NewPoolTString(n.c_str());
|
||||
}
|
||||
// for block reference (first parameter must be EbtReference)
|
||||
explicit TType(TBasicType t, const TType &p, const TString& n) :
|
||||
basicType(t), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false),
|
||||
arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr)
|
||||
{
|
||||
assert(t == EbtReference);
|
||||
typeName = NewPoolTString(n.c_str());
|
||||
qualifier.clear();
|
||||
qualifier.storage = p.qualifier.storage;
|
||||
referentType = p.clone();
|
||||
}
|
||||
virtual ~TType() {}
|
||||
|
||||
// Not for use across pool pops; it will cause multiple instances of TType to point to the same information.
|
||||
|
|
@ -1392,9 +1421,13 @@ public:
|
|||
matrixRows = copyOf.matrixRows;
|
||||
vector1 = copyOf.vector1;
|
||||
arraySizes = copyOf.arraySizes; // copying the pointer only, not the contents
|
||||
structure = copyOf.structure;
|
||||
fieldName = copyOf.fieldName;
|
||||
typeName = copyOf.typeName;
|
||||
if (isStruct()) {
|
||||
structure = copyOf.structure;
|
||||
} else {
|
||||
referentType = copyOf.referentType;
|
||||
}
|
||||
}
|
||||
|
||||
// Make complete copy of the whole type graph rooted at 'copyOf'.
|
||||
|
|
@ -1457,6 +1490,7 @@ public:
|
|||
virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); }
|
||||
virtual const TArraySizes* getArraySizes() const { return arraySizes; }
|
||||
virtual TArraySizes* getArraySizes() { return arraySizes; }
|
||||
virtual TType* getReferentType() const { return referentType; }
|
||||
|
||||
virtual bool isScalar() const { return ! isVector() && ! isMatrix() && ! isStruct() && ! isArray(); }
|
||||
virtual bool isScalarOrVec1() const { return isScalar() || vector1; }
|
||||
|
|
@ -1468,7 +1502,7 @@ public:
|
|||
virtual bool isArrayVariablyIndexed() const { assert(isArray()); return arraySizes->isVariablyIndexed(); }
|
||||
virtual void setArrayVariablyIndexed() { assert(isArray()); arraySizes->setVariablyIndexed(); }
|
||||
virtual void updateImplicitArraySize(int size) { assert(isArray()); arraySizes->updateImplicitSize(size); }
|
||||
virtual bool isStruct() const { return structure != nullptr; }
|
||||
virtual bool isStruct() const { return basicType == EbtStruct || basicType == EbtBlock; }
|
||||
virtual bool isFloatingDomain() const { return basicType == EbtFloat || basicType == EbtDouble || basicType == EbtFloat16; }
|
||||
virtual bool isIntegerDomain() const
|
||||
{
|
||||
|
|
@ -1509,7 +1543,7 @@ public:
|
|||
|
||||
const auto hasa = [predicate](const TTypeLoc& tl) { return tl.type->contains(predicate); };
|
||||
|
||||
return structure && std::any_of(structure->begin(), structure->end(), hasa);
|
||||
return isStruct() && std::any_of(structure->begin(), structure->end(), hasa);
|
||||
}
|
||||
|
||||
// Recursively checks if the type contains the given basic type
|
||||
|
|
@ -1688,6 +1722,7 @@ public:
|
|||
#ifdef NV_EXTENSIONS
|
||||
case EbtAccStructNV: return "accelerationStructureNV";
|
||||
#endif
|
||||
case EbtReference: return "reference";
|
||||
default: return "unknown type";
|
||||
}
|
||||
}
|
||||
|
|
@ -1773,6 +1808,12 @@ public:
|
|||
}
|
||||
if (qualifier.layoutPushConstant)
|
||||
appendStr(" push_constant");
|
||||
if (qualifier.layoutBufferReference)
|
||||
appendStr(" buffer_reference");
|
||||
if (qualifier.hasBufferReferenceAlign()) {
|
||||
appendStr(" buffer_reference_align=");
|
||||
appendUint(1u << qualifier.layoutBufferReferenceAlign);
|
||||
}
|
||||
|
||||
#ifdef NV_EXTENSIONS
|
||||
if (qualifier.layoutPassthrough)
|
||||
|
|
@ -1892,7 +1933,7 @@ public:
|
|||
}
|
||||
|
||||
// Add struct/block members
|
||||
if (structure) {
|
||||
if (isStruct()) {
|
||||
appendStr("{");
|
||||
for (size_t i = 0; i < structure->size(); ++i) {
|
||||
if (! (*structure)[i].type->hiddenMember()) {
|
||||
|
|
@ -1920,9 +1961,9 @@ public:
|
|||
const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); }
|
||||
const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); }
|
||||
const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); }
|
||||
const TTypeList* getStruct() const { return structure; }
|
||||
void setStruct(TTypeList* s) { structure = s; }
|
||||
TTypeList* getWritableStruct() const { return structure; } // This should only be used when known to not be sharing with other threads
|
||||
const TTypeList* getStruct() const { assert(isStruct()); return structure; }
|
||||
void setStruct(TTypeList* s) { assert(isStruct()); structure = s; }
|
||||
TTypeList* getWritableStruct() const { assert(isStruct()); return structure; } // This should only be used when known to not be sharing with other threads
|
||||
|
||||
int computeNumComponents() const
|
||||
{
|
||||
|
|
@ -1961,11 +2002,12 @@ public:
|
|||
bool sameStructType(const TType& right) const
|
||||
{
|
||||
// Most commonly, they are both nullptr, or the same pointer to the same actual structure
|
||||
if (structure == right.structure)
|
||||
if ((!isStruct() && !right.isStruct()) ||
|
||||
isStruct() && right.isStruct() && structure == right.structure)
|
||||
return true;
|
||||
|
||||
// Both being nullptr was caught above, now they both have to be structures of the same number of elements
|
||||
if (structure == nullptr || right.structure == nullptr ||
|
||||
if (!isStruct() || !right.isStruct() ||
|
||||
structure->size() != right.structure->size())
|
||||
return false;
|
||||
|
||||
|
|
@ -1985,6 +2027,23 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool sameReferenceType(const TType& right) const
|
||||
{
|
||||
if ((basicType == EbtReference) != (right.basicType == EbtReference))
|
||||
return false;
|
||||
|
||||
if ((basicType != EbtReference) && (right.basicType != EbtReference))
|
||||
return true;
|
||||
|
||||
assert(referentType != nullptr);
|
||||
assert(right.referentType != nullptr);
|
||||
|
||||
if (referentType == right.referentType)
|
||||
return true;
|
||||
|
||||
return *referentType == *right.referentType;
|
||||
}
|
||||
|
||||
// See if two types match, in all aspects except arrayness
|
||||
bool sameElementType(const TType& right) const
|
||||
{
|
||||
|
|
@ -2013,7 +2072,8 @@ public:
|
|||
matrixCols == right.matrixCols &&
|
||||
matrixRows == right.matrixRows &&
|
||||
vector1 == right.vector1 &&
|
||||
sameStructType(right);
|
||||
sameStructType(right) &&
|
||||
sameReferenceType(right);
|
||||
}
|
||||
|
||||
// See if two types match in all ways (just the actual type, not qualification)
|
||||
|
|
@ -2044,7 +2104,7 @@ protected:
|
|||
*arraySizes = *copyOf.arraySizes;
|
||||
}
|
||||
|
||||
if (copyOf.structure) {
|
||||
if (copyOf.isStruct() && copyOf.structure) {
|
||||
auto prevCopy = copiedMap.find(copyOf.structure);
|
||||
if (prevCopy != copiedMap.end())
|
||||
structure = prevCopy->second;
|
||||
|
|
@ -2082,7 +2142,12 @@ protected:
|
|||
TQualifier qualifier;
|
||||
|
||||
TArraySizes* arraySizes; // nullptr unless an array; can be shared across types
|
||||
TTypeList* structure; // nullptr unless this is a struct; can be shared across types
|
||||
// A type can't be both a structure (EbtStruct/EbtBlock) and a reference (EbtReference), so
|
||||
// conserve space by making these a union
|
||||
union {
|
||||
TTypeList* structure; // invalid unless this is a struct; can be shared across types
|
||||
TType *referentType; // invalid unless this is an EbtReference
|
||||
};
|
||||
TString *fieldName; // for structure field names
|
||||
TString *typeName; // for structure type name
|
||||
TSampler sampler;
|
||||
|
|
|
|||
|
|
@ -269,6 +269,10 @@ enum TOperator {
|
|||
EOpConvDoubleToFloat16,
|
||||
EOpConvDoubleToFloat,
|
||||
|
||||
// uint64_t <-> pointer
|
||||
EOpConvUint64ToPtr,
|
||||
EOpConvPtrToUint64,
|
||||
|
||||
//
|
||||
// binary operations
|
||||
//
|
||||
|
|
@ -732,6 +736,7 @@ enum TOperator {
|
|||
EOpConstructStruct,
|
||||
EOpConstructTextureSampler,
|
||||
EOpConstructNonuniform, // expected to be transformed away, not present in final AST
|
||||
EOpConstructReference,
|
||||
EOpConstructGuardEnd,
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -984,6 +984,14 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt
|
|||
case EOpSequence:
|
||||
case EOpConstructStruct:
|
||||
|
||||
if (type.getBasicType() == EbtReference || node->getType().getBasicType() == EbtReference) {
|
||||
// types must match to assign a reference
|
||||
if (type == node->getType())
|
||||
return node;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type.getBasicType() == node->getType().getBasicType())
|
||||
return node;
|
||||
|
||||
|
|
@ -2131,6 +2139,9 @@ TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const
|
|||
}
|
||||
}
|
||||
break;
|
||||
case EbtReference:
|
||||
op = EOpConstructReference;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,6 +354,11 @@ TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symb
|
|||
if (variable->getType().getQualifier().isIo())
|
||||
intermediate.addIoAccessed(*string);
|
||||
|
||||
if (variable->getType().getBasicType() == EbtReference &&
|
||||
variable->getType().getQualifier().isMemory()) {
|
||||
intermediate.setUseVulkanMemoryModel();
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
|
@ -811,8 +816,12 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm
|
|||
if (base->getType().getQualifier().isSpecConstant())
|
||||
result->getWritableType().getQualifier().makeSpecConstant();
|
||||
}
|
||||
} else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
|
||||
const TTypeList* fields = base->getType().getStruct();
|
||||
} else if (base->getBasicType() == EbtStruct ||
|
||||
base->getBasicType() == EbtBlock ||
|
||||
base->getBasicType() == EbtReference) {
|
||||
const TTypeList* fields = base->getBasicType() == EbtReference ?
|
||||
base->getType().getReferentType()->getStruct() :
|
||||
base->getType().getStruct();
|
||||
bool fieldFound = false;
|
||||
int member;
|
||||
for (member = 0; member < (int)fields->size(); ++member) {
|
||||
|
|
@ -2386,6 +2395,10 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TInt
|
|||
}
|
||||
}
|
||||
|
||||
if (binaryNode && binaryNode->getOp() == EOpIndexDirectStruct &&
|
||||
binaryNode->getLeft()->getBasicType() == EbtReference)
|
||||
return false;
|
||||
|
||||
// Let the base class check errors
|
||||
if (TParseContextBase::lValueErrorCheck(loc, op, node))
|
||||
return true;
|
||||
|
|
@ -3096,13 +3109,17 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali
|
|||
if (! symbolTable.atGlobalLevel())
|
||||
return;
|
||||
|
||||
if (qualifier.isMemoryQualifierImageAndSSBOOnly() && ! publicType.isImage() && publicType.qualifier.storage != EvqBuffer) {
|
||||
error(loc, "memory qualifiers cannot be used on this type", "", "");
|
||||
} else if (qualifier.isMemory() && (publicType.basicType != EbtSampler) && !publicType.qualifier.isUniformOrBuffer()) {
|
||||
error(loc, "memory qualifiers cannot be used on this type", "", "");
|
||||
if (!(publicType.userDef && publicType.userDef->getBasicType() == EbtReference)) {
|
||||
if (qualifier.isMemoryQualifierImageAndSSBOOnly() && ! publicType.isImage() && publicType.qualifier.storage != EvqBuffer) {
|
||||
error(loc, "memory qualifiers cannot be used on this type", "", "");
|
||||
} else if (qualifier.isMemory() && (publicType.basicType != EbtSampler) && !publicType.qualifier.isUniformOrBuffer()) {
|
||||
error(loc, "memory qualifiers cannot be used on this type", "", "");
|
||||
}
|
||||
}
|
||||
|
||||
if (qualifier.storage == EvqBuffer && publicType.basicType != EbtBlock)
|
||||
if (qualifier.storage == EvqBuffer &&
|
||||
publicType.basicType != EbtBlock &&
|
||||
!qualifier.layoutBufferReference)
|
||||
error(loc, "buffers can be declared only as blocks", "buffer", "");
|
||||
|
||||
if (qualifier.storage != EvqVaryingIn && qualifier.storage != EvqVaryingOut)
|
||||
|
|
@ -3760,6 +3777,21 @@ void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermType
|
|||
if (isRuntimeLength(base))
|
||||
return;
|
||||
|
||||
// Check for last member of a bufferreference type, which is runtime sizeable
|
||||
// but doesn't support runtime length
|
||||
if (base.getType().getQualifier().storage == EvqBuffer) {
|
||||
const TIntermBinary* binary = base.getAsBinaryNode();
|
||||
if (binary != nullptr &&
|
||||
binary->getOp() == EOpIndexDirectStruct &&
|
||||
binary->getLeft()->getBasicType() == EbtReference) {
|
||||
|
||||
const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
|
||||
const int memberCount = (int)binary->getLeft()->getType().getReferentType()->getStruct()->size();
|
||||
if (index == memberCount - 1)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check for additional things allowed by GL_EXT_nonuniform_qualifier
|
||||
if (base.getBasicType() == EbtSampler ||
|
||||
(base.getBasicType() == EbtBlock && base.getType().getQualifier().isUniformOrBuffer()))
|
||||
|
|
@ -3777,6 +3809,10 @@ bool TParseContext::isRuntimeLength(const TIntermTyped& base) const
|
|||
if (binary != nullptr && binary->getOp() == EOpIndexDirectStruct) {
|
||||
// is it the last member?
|
||||
const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
|
||||
|
||||
if (binary->getLeft()->getBasicType() == EbtReference)
|
||||
return false;
|
||||
|
||||
const int memberCount = (int)binary->getLeft()->getType().getStruct()->size();
|
||||
if (index == memberCount - 1)
|
||||
return true;
|
||||
|
|
@ -4655,6 +4691,14 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
|
|||
publicType.qualifier.layoutPushConstant = true;
|
||||
return;
|
||||
}
|
||||
if (id == "buffer_reference") {
|
||||
requireVulkan(loc, "buffer_reference");
|
||||
requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference");
|
||||
publicType.qualifier.layoutBufferReference = true;
|
||||
intermediate.setUseStorageBuffer();
|
||||
intermediate.setUsePhysicalStorageBuffer();
|
||||
return;
|
||||
}
|
||||
if (language == EShLangGeometry || language == EShLangTessEvaluation
|
||||
#ifdef NV_EXTENSIONS
|
||||
|| language == EShLangMeshNV
|
||||
|
|
@ -5013,6 +5057,15 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
|
|||
}
|
||||
#endif
|
||||
|
||||
if (id == "buffer_reference_align") {
|
||||
requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference_align");
|
||||
if (! IsPow2(value))
|
||||
error(loc, "must be a power of 2", "buffer_reference_align", "");
|
||||
else
|
||||
publicType.qualifier.layoutBufferReferenceAlign = std::log2(value);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (language) {
|
||||
case EShLangVertex:
|
||||
break;
|
||||
|
|
@ -5177,6 +5230,9 @@ void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifie
|
|||
if (src.hasAlign())
|
||||
dst.layoutAlign = src.layoutAlign;
|
||||
|
||||
if (src.hasBufferReferenceAlign())
|
||||
dst.layoutBufferReferenceAlign = src.layoutBufferReferenceAlign;
|
||||
|
||||
if (! inheritOnly) {
|
||||
if (src.hasLocation())
|
||||
dst.layoutLocation = src.layoutLocation;
|
||||
|
|
@ -5205,6 +5261,9 @@ void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifie
|
|||
if (src.layoutPushConstant)
|
||||
dst.layoutPushConstant = true;
|
||||
|
||||
if (src.layoutBufferReference)
|
||||
dst.layoutBufferReference = true;
|
||||
|
||||
#ifdef NV_EXTENSIONS
|
||||
if (src.layoutPassthrough)
|
||||
dst.layoutPassthrough = true;
|
||||
|
|
@ -5452,7 +5511,8 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
|
|||
#ifdef NV_EXTENSIONS
|
||||
!qualifier.layoutShaderRecordNV &&
|
||||
#endif
|
||||
!qualifier.layoutAttachment)
|
||||
!qualifier.layoutAttachment &&
|
||||
!qualifier.layoutBufferReference)
|
||||
error(loc, "uniform/buffer blocks require layout(binding=X)", "binding", "");
|
||||
else if (spvVersion.vulkan > 0 && type.getBasicType() == EbtSampler)
|
||||
error(loc, "sampler/texture/image requires layout(binding=X)", "binding", "");
|
||||
|
|
@ -5504,6 +5564,9 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
|
|||
if (qualifier.layoutPushConstant && type.getBasicType() != EbtBlock)
|
||||
error(loc, "can only be used with a block", "push_constant", "");
|
||||
|
||||
if (qualifier.layoutBufferReference && type.getBasicType() != EbtBlock)
|
||||
error(loc, "can only be used with a block", "buffer_reference", "");
|
||||
|
||||
#ifdef NV_EXTENSIONS
|
||||
if (qualifier.layoutShaderRecordNV && type.getBasicType() != EbtBlock)
|
||||
error(loc, "can only be used with a block", "shaderRecordNV", "");
|
||||
|
|
@ -5644,6 +5707,10 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier
|
|||
if (qualifier.hasSet())
|
||||
error(loc, "cannot be used with push_constant", "set", "");
|
||||
}
|
||||
if (qualifier.layoutBufferReference) {
|
||||
if (qualifier.storage != EvqBuffer)
|
||||
error(loc, "can only be used with buffer", "buffer_reference", "");
|
||||
}
|
||||
#ifdef NV_EXTENSIONS
|
||||
if (qualifier.layoutShaderRecordNV) {
|
||||
if (qualifier.storage != EvqBuffer)
|
||||
|
|
@ -6051,7 +6118,7 @@ void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType
|
|||
return;
|
||||
}
|
||||
|
||||
if (publicType.qualifier.hasLayout())
|
||||
if (publicType.qualifier.hasLayout() && !publicType.qualifier.layoutBufferReference)
|
||||
warn(loc, "useless application of layout qualifier", "layout", "");
|
||||
}
|
||||
|
||||
|
|
@ -6659,10 +6726,15 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T
|
|||
basicOp = EOpConstructInt64;
|
||||
break;
|
||||
|
||||
case EOpConstructUint64:
|
||||
if (type.isScalar() && node->getType().getBasicType() == EbtReference) {
|
||||
TIntermUnary* newNode = intermediate.addUnaryNode(EOpConvPtrToUint64, node, node->getLoc(), type);
|
||||
return newNode;
|
||||
}
|
||||
// fall through
|
||||
case EOpConstructU64Vec2:
|
||||
case EOpConstructU64Vec3:
|
||||
case EOpConstructU64Vec4:
|
||||
case EOpConstructUint64:
|
||||
basicOp = EOpConstructUint64;
|
||||
break;
|
||||
|
||||
|
|
@ -6678,6 +6750,19 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T
|
|||
return node;
|
||||
break;
|
||||
|
||||
case EOpConstructReference:
|
||||
// construct reference from reference
|
||||
if (node->getType().getBasicType() == EbtReference) {
|
||||
newNode = intermediate.addUnaryNode(EOpConstructReference, node, node->getLoc(), type);
|
||||
return newNode;
|
||||
// construct reference from uint64
|
||||
} else if (node->getType().isScalar() && node->getType().getBasicType() == EbtUint64) {
|
||||
TIntermUnary* newNode = intermediate.addUnaryNode(EOpConvUint64ToPtr, node, node->getLoc(), type);
|
||||
return newNode;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
default:
|
||||
error(loc, "unsupported construction", "", "");
|
||||
|
||||
|
|
@ -6922,31 +7007,57 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
|
|||
else
|
||||
ioArrayCheck(loc, blockType, instanceName ? *instanceName : *blockName);
|
||||
|
||||
//
|
||||
// Don't make a user-defined type out of block name; that will cause an error
|
||||
// if the same block name gets reused in a different interface.
|
||||
//
|
||||
// "Block names have no other use within a shader
|
||||
// beyond interface matching; it is a compile-time error to use a block name at global scope for anything
|
||||
// other than as a block name (e.g., use of a block name for a global variable name or function name is
|
||||
// currently reserved)."
|
||||
//
|
||||
// Use the symbol table to prevent normal reuse of the block's name, as a variable entry,
|
||||
// whose type is EbtBlock, but without all the structure; that will come from the type
|
||||
// the instances point to.
|
||||
//
|
||||
TType blockNameType(EbtBlock, blockType.getQualifier().storage);
|
||||
TVariable* blockNameVar = new TVariable(blockName, blockNameType);
|
||||
if (! symbolTable.insert(*blockNameVar)) {
|
||||
TSymbol* existingName = symbolTable.find(*blockName);
|
||||
if (existingName->getType().getBasicType() == EbtBlock) {
|
||||
if (existingName->getType().getQualifier().storage == blockType.getQualifier().storage) {
|
||||
error(loc, "Cannot reuse block name within the same interface:", blockName->c_str(), blockType.getStorageQualifierString());
|
||||
if (currentBlockQualifier.layoutBufferReference) {
|
||||
|
||||
if (currentBlockQualifier.storage != EvqBuffer)
|
||||
error(loc, "can only be used with buffer", "buffer_reference", "");
|
||||
|
||||
// Create the block reference type. If it was forward-declared, detect that
|
||||
// as a referent struct type with no members. Replace the referent type with
|
||||
// blockType.
|
||||
TType blockNameType(EbtReference, blockType, *blockName);
|
||||
TVariable* blockNameVar = new TVariable(blockName, blockNameType, true);
|
||||
if (! symbolTable.insert(*blockNameVar)) {
|
||||
TSymbol* existingName = symbolTable.find(*blockName);
|
||||
if (existingName->getType().getBasicType() == EbtReference &&
|
||||
existingName->getType().getReferentType()->getStruct() &&
|
||||
existingName->getType().getReferentType()->getStruct()->size() == 0 &&
|
||||
existingName->getType().getQualifier().storage == blockType.getQualifier().storage) {
|
||||
existingName->getType().getReferentType()->deepCopy(blockType);
|
||||
} else {
|
||||
error(loc, "block name cannot be redefined", blockName->c_str(), "");
|
||||
}
|
||||
}
|
||||
if (!instanceName) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Don't make a user-defined type out of block name; that will cause an error
|
||||
// if the same block name gets reused in a different interface.
|
||||
//
|
||||
// "Block names have no other use within a shader
|
||||
// beyond interface matching; it is a compile-time error to use a block name at global scope for anything
|
||||
// other than as a block name (e.g., use of a block name for a global variable name or function name is
|
||||
// currently reserved)."
|
||||
//
|
||||
// Use the symbol table to prevent normal reuse of the block's name, as a variable entry,
|
||||
// whose type is EbtBlock, but without all the structure; that will come from the type
|
||||
// the instances point to.
|
||||
//
|
||||
TType blockNameType(EbtBlock, blockType.getQualifier().storage);
|
||||
TVariable* blockNameVar = new TVariable(blockName, blockNameType);
|
||||
if (! symbolTable.insert(*blockNameVar)) {
|
||||
TSymbol* existingName = symbolTable.find(*blockName);
|
||||
if (existingName->getType().getBasicType() == EbtBlock) {
|
||||
if (existingName->getType().getQualifier().storage == blockType.getQualifier().storage) {
|
||||
error(loc, "Cannot reuse block name within the same interface:", blockName->c_str(), blockType.getStorageQualifierString());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
error(loc, "block name cannot redefine a non-block name", blockName->c_str(), "");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
error(loc, "block name cannot redefine a non-block name", blockName->c_str(), "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7246,6 +7357,22 @@ void TParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typ
|
|||
void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
|
||||
{
|
||||
TSymbol* symbol = symbolTable.find(identifier);
|
||||
|
||||
// A forward declaration of a block reference looks to the grammar like adding
|
||||
// a qualifier to an existing symbol. Detect this and create the block reference
|
||||
// type with an empty type list, which will be filled in later in
|
||||
// TParseContext::declareBlock.
|
||||
if (!symbol && qualifier.layoutBufferReference) {
|
||||
TTypeList typeList;
|
||||
TType blockType(&typeList, identifier, qualifier);;
|
||||
TType blockNameType(EbtReference, blockType, identifier);
|
||||
TVariable* blockNameVar = new TVariable(&identifier, blockNameType, true);
|
||||
if (! symbolTable.insert(*blockNameVar)) {
|
||||
error(loc, "block name cannot redefine a non-block name", blockName->c_str(), "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (! symbol) {
|
||||
error(loc, "identifier not previously declared", identifier.c_str(), "");
|
||||
return;
|
||||
|
|
@ -7580,6 +7707,8 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con
|
|||
error(loc, "cannot declare a default, use a full declaration", "xfb_offset", "");
|
||||
if (qualifier.layoutPushConstant)
|
||||
error(loc, "cannot declare a default, can only be used on a block", "push_constant", "");
|
||||
if (qualifier.layoutBufferReference)
|
||||
error(loc, "cannot declare a default, can only be used on a block", "buffer_reference", "");
|
||||
if (qualifier.hasSpecConstantId())
|
||||
error(loc, "cannot declare a default, can only be used on a scalar", "constant_id", "");
|
||||
#ifdef NV_EXTENSIONS
|
||||
|
|
|
|||
|
|
@ -776,7 +776,7 @@ int TScanContext::tokenize(TPpContext* pp, TParserToken& token)
|
|||
loc = ppToken.loc;
|
||||
parserToken->sType.lex.loc = loc;
|
||||
switch (token) {
|
||||
case ';': afterType = false; return SEMICOLON;
|
||||
case ';': afterType = false; afterBuffer = false; return SEMICOLON;
|
||||
case ',': afterType = false; return COMMA;
|
||||
case ':': return COLON;
|
||||
case '=': afterType = false; return EQUAL;
|
||||
|
|
@ -798,7 +798,7 @@ int TScanContext::tokenize(TPpContext* pp, TParserToken& token)
|
|||
case '?': return QUESTION;
|
||||
case '[': return LEFT_BRACKET;
|
||||
case ']': return RIGHT_BRACKET;
|
||||
case '{': afterStruct = false; return LEFT_BRACE;
|
||||
case '{': afterStruct = false; afterBuffer = false; return LEFT_BRACE;
|
||||
case '}': return RIGHT_BRACE;
|
||||
case '\\':
|
||||
parseContext.error(loc, "illegal use of escape character", "\\", "");
|
||||
|
|
@ -945,6 +945,7 @@ int TScanContext::tokenizeIdentifier()
|
|||
return keyword;
|
||||
|
||||
case BUFFER:
|
||||
afterBuffer = true;
|
||||
if ((parseContext.profile == EEsProfile && parseContext.version < 310) ||
|
||||
(parseContext.profile != EEsProfile && parseContext.version < 430))
|
||||
return identifierOrType();
|
||||
|
|
@ -1617,7 +1618,9 @@ int TScanContext::identifierOrType()
|
|||
parserToken->sType.lex.symbol = parseContext.symbolTable.find(*parserToken->sType.lex.string);
|
||||
if ((afterType == false && afterStruct == false) && parserToken->sType.lex.symbol != nullptr) {
|
||||
if (const TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) {
|
||||
if (variable->isUserType()) {
|
||||
if (variable->isUserType() &&
|
||||
// treat redeclaration of forward-declared buffer/uniform reference as an identifier
|
||||
!(variable->getType().getBasicType() == EbtReference && afterBuffer)) {
|
||||
afterType = true;
|
||||
|
||||
return TYPE_NAME;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public:
|
|||
explicit TScanContext(TParseContextBase& pc) :
|
||||
parseContext(pc),
|
||||
afterType(false), afterStruct(false),
|
||||
field(false) { }
|
||||
field(false), afterBuffer(false) { }
|
||||
virtual ~TScanContext() { }
|
||||
|
||||
static void fillInKeywordMap();
|
||||
|
|
@ -81,6 +81,7 @@ protected:
|
|||
bool afterType; // true if we've recognized a type, so can only be looking for an identifier
|
||||
bool afterStruct; // true if we've recognized the STRUCT keyword, so can only be looking for an identifier
|
||||
bool field; // true if we're on a field, right after a '.'
|
||||
bool afterBuffer; // true if we've recognized the BUFFER keyword
|
||||
TSourceLoc loc;
|
||||
TParserToken* parserToken;
|
||||
TPpToken* ppToken;
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ void TParseVersions::initializeExtensionBehavior()
|
|||
extensionBehavior[E_GL_EXT_samplerless_texture_functions] = EBhDisable;
|
||||
extensionBehavior[E_GL_EXT_scalar_block_layout] = EBhDisable;
|
||||
extensionBehavior[E_GL_EXT_fragment_invocation_density] = EBhDisable;
|
||||
extensionBehavior[E_GL_EXT_buffer_reference] = EBhDisable;
|
||||
|
||||
extensionBehavior[E_GL_EXT_shader_16bit_storage] = EBhDisable;
|
||||
extensionBehavior[E_GL_EXT_shader_8bit_storage] = EBhDisable;
|
||||
|
|
@ -383,6 +384,7 @@ void TParseVersions::getPreamble(std::string& preamble)
|
|||
"#define GL_EXT_samplerless_texture_functions 1\n"
|
||||
"#define GL_EXT_scalar_block_layout 1\n"
|
||||
"#define GL_EXT_fragment_invocation_density 1\n"
|
||||
"#define GL_EXT_buffer_reference 1\n"
|
||||
|
||||
// GL_KHR_shader_subgroup
|
||||
"#define GL_KHR_shader_subgroup_basic 1\n"
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ const char* const E_GL_EXT_nonuniform_qualifier = "GL_EXT_nonuniform
|
|||
const char* const E_GL_EXT_samplerless_texture_functions = "GL_EXT_samplerless_texture_functions";
|
||||
const char* const E_GL_EXT_scalar_block_layout = "GL_EXT_scalar_block_layout";
|
||||
const char* const E_GL_EXT_fragment_invocation_density = "GL_EXT_fragment_invocation_density";
|
||||
const char* const E_GL_EXT_buffer_reference = "GL_EXT_buffer_reference";
|
||||
|
||||
// Arrays of extensions for the above viewportEXTs duplications
|
||||
|
||||
|
|
|
|||
|
|
@ -172,8 +172,12 @@ bool TOutputTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node)
|
|||
case EOpIndexDirect: out.debug << "direct index"; break;
|
||||
case EOpIndexIndirect: out.debug << "indirect index"; break;
|
||||
case EOpIndexDirectStruct:
|
||||
out.debug << (*node->getLeft()->getType().getStruct())[node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()].type->getFieldName();
|
||||
out.debug << ": direct index for structure"; break;
|
||||
{
|
||||
bool reference = node->getLeft()->getType().getBasicType() == EbtReference;
|
||||
const TTypeList *members = reference ? node->getLeft()->getType().getReferentType()->getStruct() : node->getLeft()->getType().getStruct();
|
||||
out.debug << (*members)[node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()].type->getFieldName();
|
||||
out.debug << ": direct index for structure"; break;
|
||||
}
|
||||
case EOpVectorSwizzle: out.debug << "vector swizzle"; break;
|
||||
case EOpMatrixSwizzle: out.debug << "matrix swizzle"; break;
|
||||
|
||||
|
|
@ -419,6 +423,8 @@ bool TOutputTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node)
|
|||
case EOpConvDoubleToUint: out.debug << "Convert double to uint"; break;
|
||||
case EOpConvDoubleToUint64: out.debug << "Convert double to uint64"; break;
|
||||
|
||||
case EOpConvUint64ToPtr: out.debug << "Convert uint64_t to pointer"; break;
|
||||
case EOpConvPtrToUint64: out.debug << "Convert pointer to uint64_t"; break;
|
||||
|
||||
case EOpRadians: out.debug << "radians"; break;
|
||||
case EOpDegrees: out.debug << "degrees"; break;
|
||||
|
|
@ -674,6 +680,8 @@ bool TOutputTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node)
|
|||
case EOpSubpassLoad: out.debug << "subpassLoad"; break;
|
||||
case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break;
|
||||
|
||||
case EOpConstructReference: out.debug << "Construct reference type"; break;
|
||||
|
||||
default: out.debug.message(EPrefixError, "Bad unary op");
|
||||
}
|
||||
|
||||
|
|
@ -808,6 +816,7 @@ bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node
|
|||
case EOpConstructF16Mat4x4: out.debug << "Construct f16mat4"; break;
|
||||
case EOpConstructStruct: out.debug << "Construct structure"; break;
|
||||
case EOpConstructTextureSampler: out.debug << "Construct combined texture-sampler"; break;
|
||||
case EOpConstructReference: out.debug << "Construct reference"; break;
|
||||
|
||||
case EOpLessThan: out.debug << "Compare Less Than"; break;
|
||||
case EOpGreaterThan: out.debug << "Compare Greater Than"; break;
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit)
|
|||
|
||||
MERGE_TRUE(needToLegalize);
|
||||
MERGE_TRUE(binaryDoubleOutput);
|
||||
MERGE_TRUE(usePhysicalStorageBuffer);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -1355,6 +1356,7 @@ int TIntermediate::getBaseAlignmentScalar(const TType& type, int& size)
|
|||
case EbtUint8: size = 1; return 1;
|
||||
case EbtInt16:
|
||||
case EbtUint16: size = 2; return 2;
|
||||
case EbtReference: size = 8; return 8;
|
||||
default: size = 4; return 4;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ public:
|
|||
textureSamplerTransformMode(EShTexSampTransKeep),
|
||||
needToLegalize(false),
|
||||
binaryDoubleOutput(false),
|
||||
usePhysicalStorageBuffer(false),
|
||||
uniformLocationBase(0)
|
||||
{
|
||||
localSize[0] = 1;
|
||||
|
|
@ -390,6 +391,11 @@ public:
|
|||
processes.addProcess("use-vulkan-memory-model");
|
||||
}
|
||||
bool usingVulkanMemoryModel() const { return useVulkanMemoryModel; }
|
||||
void setUsePhysicalStorageBuffer()
|
||||
{
|
||||
usePhysicalStorageBuffer = true;
|
||||
}
|
||||
bool usingPhysicalStorageBuffer() const { return usePhysicalStorageBuffer; }
|
||||
|
||||
template<class T> T addCounterBufferName(const T& name) const { return name + implicitCounterName; }
|
||||
bool hasCounterBufferName(const TString& name) const {
|
||||
|
|
@ -825,6 +831,7 @@ protected:
|
|||
|
||||
bool needToLegalize;
|
||||
bool binaryDoubleOutput;
|
||||
bool usePhysicalStorageBuffer;
|
||||
|
||||
std::unordered_map<std::string, int> uniformLocationOverrides;
|
||||
int uniformLocationBase;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue