Revert "Revert "GL_ext_vulkan_glsl_relaxed extension support, and cross stage aware IO mapper""
This commit is contained in:
@@ -501,6 +501,7 @@ public:
|
||||
noContraction = false;
|
||||
nullInit = false;
|
||||
#endif
|
||||
defaultBlock = false;
|
||||
}
|
||||
|
||||
// drop qualifiers that don't belong in a temporary variable
|
||||
@@ -514,6 +515,7 @@ public:
|
||||
specConstant = false;
|
||||
nonUniform = false;
|
||||
nullInit = false;
|
||||
defaultBlock = false;
|
||||
clearLayout();
|
||||
}
|
||||
|
||||
@@ -572,6 +574,7 @@ public:
|
||||
bool specConstant : 1;
|
||||
bool nonUniform : 1;
|
||||
bool explicitOffset : 1;
|
||||
bool defaultBlock : 1; // default blocks with matching names have structures merged when linking
|
||||
|
||||
#ifdef GLSLANG_WEB
|
||||
bool isWriteOnly() const { return false; }
|
||||
@@ -756,6 +759,46 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
TBlockStorageClass getBlockStorage() const {
|
||||
if (storage == EvqUniform && !isPushConstant()) {
|
||||
return EbsUniform;
|
||||
}
|
||||
else if (storage == EvqUniform) {
|
||||
return EbsPushConstant;
|
||||
}
|
||||
else if (storage == EvqBuffer) {
|
||||
return EbsStorageBuffer;
|
||||
}
|
||||
return EbsNone;
|
||||
}
|
||||
|
||||
void setBlockStorage(TBlockStorageClass newBacking) {
|
||||
#ifndef GLSLANG_WEB
|
||||
layoutPushConstant = (newBacking == EbsPushConstant);
|
||||
#endif
|
||||
switch (newBacking) {
|
||||
case EbsUniform :
|
||||
if (layoutPacking == ElpStd430) {
|
||||
// std430 would not be valid
|
||||
layoutPacking = ElpStd140;
|
||||
}
|
||||
storage = EvqUniform;
|
||||
break;
|
||||
case EbsStorageBuffer :
|
||||
storage = EvqBuffer;
|
||||
break;
|
||||
#ifndef GLSLANG_WEB
|
||||
case EbsPushConstant :
|
||||
storage = EvqUniform;
|
||||
layoutSet = TQualifier::layoutSetEnd;
|
||||
layoutBinding = TQualifier::layoutBindingEnd;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef GLSLANG_WEB
|
||||
bool isPerView() const { return false; }
|
||||
bool isTaskMemory() const { return false; }
|
||||
@@ -852,6 +895,7 @@ public:
|
||||
return hasNonXfbLayout() ||
|
||||
hasXfb();
|
||||
}
|
||||
|
||||
TLayoutMatrix layoutMatrix : 3;
|
||||
TLayoutPacking layoutPacking : 4;
|
||||
int layoutOffset;
|
||||
|
||||
@@ -593,6 +593,7 @@ enum TOperator {
|
||||
EOpTime,
|
||||
|
||||
EOpAtomicAdd,
|
||||
EOpAtomicSubtract,
|
||||
EOpAtomicMin,
|
||||
EOpAtomicMax,
|
||||
EOpAtomicAnd,
|
||||
@@ -1135,6 +1136,8 @@ public:
|
||||
virtual TBasicType getBasicType() const { return type.getBasicType(); }
|
||||
virtual TQualifier& getQualifier() { return type.getQualifier(); }
|
||||
virtual const TQualifier& getQualifier() const { return type.getQualifier(); }
|
||||
virtual TArraySizes* getArraySizes() { return type.getArraySizes(); }
|
||||
virtual const TArraySizes* getArraySizes() const { return type.getArraySizes(); }
|
||||
virtual void propagatePrecision(TPrecisionQualifier);
|
||||
virtual int getVectorSize() const { return type.getVectorSize(); }
|
||||
virtual int getMatrixCols() const { return type.getMatrixCols(); }
|
||||
|
||||
@@ -1627,6 +1627,36 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
||||
"uint atomicCounterExchange(atomic_uint, uint);"
|
||||
"uint atomicCounterCompSwap(atomic_uint, uint, uint);"
|
||||
|
||||
"\n");
|
||||
}
|
||||
}
|
||||
else if (spvVersion.vulkanRelaxed) {
|
||||
//
|
||||
// Atomic counter functions act as aliases to normal atomic functions.
|
||||
// replace definitions to take 'volatile coherent uint' instead of 'atomic_uint'
|
||||
// and map to equivalent non-counter atomic op
|
||||
//
|
||||
if ((profile != EEsProfile && version >= 300) ||
|
||||
(profile == EEsProfile && version >= 310)) {
|
||||
commonBuiltins.append(
|
||||
"uint atomicCounterIncrement(volatile coherent uint);"
|
||||
"uint atomicCounterDecrement(volatile coherent uint);"
|
||||
"uint atomicCounter(volatile coherent uint);"
|
||||
|
||||
"\n");
|
||||
}
|
||||
if (profile != EEsProfile && version >= 460) {
|
||||
commonBuiltins.append(
|
||||
"uint atomicCounterAdd(volatile coherent uint, uint);"
|
||||
"uint atomicCounterSubtract(volatile coherent uint, uint);"
|
||||
"uint atomicCounterMin(volatile coherent uint, uint);"
|
||||
"uint atomicCounterMax(volatile coherent uint, uint);"
|
||||
"uint atomicCounterAnd(volatile coherent uint, uint);"
|
||||
"uint atomicCounterOr(volatile coherent uint, uint);"
|
||||
"uint atomicCounterXor(volatile coherent uint, uint);"
|
||||
"uint atomicCounterExchange(volatile coherent uint, uint);"
|
||||
"uint atomicCounterCompSwap(volatile coherent uint, uint, uint);"
|
||||
|
||||
"\n");
|
||||
}
|
||||
}
|
||||
@@ -4124,7 +4154,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
||||
}
|
||||
#ifndef GLSLANG_WEB
|
||||
if ((profile != EEsProfile && version >= 420) || esBarrier) {
|
||||
if (spvVersion.vulkan == 0) {
|
||||
if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed) {
|
||||
commonBuiltins.append("void memoryBarrierAtomicCounter();");
|
||||
}
|
||||
commonBuiltins.append("void memoryBarrierImage();");
|
||||
@@ -4848,6 +4878,13 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
||||
"in int gl_VertexIndex;"
|
||||
"in int gl_InstanceIndex;"
|
||||
);
|
||||
|
||||
if (spvVersion.vulkan > 0 && version >= 140 && spvVersion.vulkanRelaxed)
|
||||
stageBuiltins[EShLangVertex].append(
|
||||
"in int gl_VertexID;" // declare with 'in' qualifier
|
||||
"in int gl_InstanceID;"
|
||||
);
|
||||
|
||||
if (version >= 440) {
|
||||
stageBuiltins[EShLangVertex].append(
|
||||
"in int gl_BaseVertexARB;"
|
||||
@@ -4885,7 +4922,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV
|
||||
"mediump float gl_PointSize;" // needs qualifier fixed later
|
||||
);
|
||||
} else {
|
||||
if (spvVersion.vulkan == 0)
|
||||
if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed)
|
||||
stageBuiltins[EShLangVertex].append(
|
||||
"in highp int gl_VertexID;" // needs qualifier fixed later
|
||||
"in highp int gl_InstanceID;" // needs qualifier fixed later
|
||||
@@ -7436,6 +7473,12 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
|
||||
SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable);
|
||||
}
|
||||
|
||||
if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) {
|
||||
// treat these built-ins as aliases of VertexIndex and InstanceIndex
|
||||
BuiltInVariable("gl_VertexID", EbvVertexIndex, symbolTable);
|
||||
BuiltInVariable("gl_InstanceID", EbvInstanceIndex, symbolTable);
|
||||
}
|
||||
|
||||
if (profile != EEsProfile) {
|
||||
if (version >= 440) {
|
||||
symbolTable.setVariableExtensions("gl_BaseVertexARB", 1, &E_GL_ARB_shader_draw_parameters);
|
||||
@@ -8912,6 +8955,14 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
|
||||
symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierAtomicCounter);
|
||||
symbolTable.relateToOperator("memoryBarrierImage", EOpMemoryBarrierImage);
|
||||
|
||||
if (spvVersion.vulkanRelaxed) {
|
||||
//
|
||||
// functions signature have been replaced to take uint operations on buffer variables
|
||||
// remap atomic counter functions to atomic operations
|
||||
//
|
||||
symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierBuffer);
|
||||
}
|
||||
|
||||
symbolTable.relateToOperator("atomicLoad", EOpAtomicLoad);
|
||||
symbolTable.relateToOperator("atomicStore", EOpAtomicStore);
|
||||
|
||||
@@ -8919,6 +8970,20 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
|
||||
symbolTable.relateToOperator("atomicCounterDecrement", EOpAtomicCounterDecrement);
|
||||
symbolTable.relateToOperator("atomicCounter", EOpAtomicCounter);
|
||||
|
||||
if (spvVersion.vulkanRelaxed) {
|
||||
//
|
||||
// functions signature have been replaced to take uint operations
|
||||
// remap atomic counter functions to atomic operations
|
||||
//
|
||||
// these atomic counter functions do not match signatures of glsl
|
||||
// atomic functions, so they will be remapped to semantically
|
||||
// equivalent functions in the parser
|
||||
//
|
||||
symbolTable.relateToOperator("atomicCounterIncrement", EOpNull);
|
||||
symbolTable.relateToOperator("atomicCounterDecrement", EOpNull);
|
||||
symbolTable.relateToOperator("atomicCounter", EOpNull);
|
||||
}
|
||||
|
||||
symbolTable.relateToOperator("clockARB", EOpReadClockSubgroupKHR);
|
||||
symbolTable.relateToOperator("clock2x32ARB", EOpReadClockSubgroupKHR);
|
||||
|
||||
@@ -8937,6 +9002,23 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion
|
||||
symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCounterCompSwap);
|
||||
}
|
||||
|
||||
if (spvVersion.vulkanRelaxed) {
|
||||
//
|
||||
// functions signature have been replaced to take 'uint' instead of 'atomic_uint'
|
||||
// remap atomic counter functions to non-counter atomic ops so
|
||||
// functions act as aliases to non-counter atomic ops
|
||||
//
|
||||
symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicAdd);
|
||||
symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicSubtract);
|
||||
symbolTable.relateToOperator("atomicCounterMin", EOpAtomicMin);
|
||||
symbolTable.relateToOperator("atomicCounterMax", EOpAtomicMax);
|
||||
symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicAnd);
|
||||
symbolTable.relateToOperator("atomicCounterOr", EOpAtomicOr);
|
||||
symbolTable.relateToOperator("atomicCounterXor", EOpAtomicXor);
|
||||
symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicExchange);
|
||||
symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCompSwap);
|
||||
}
|
||||
|
||||
symbolTable.relateToOperator("fma", EOpFma);
|
||||
symbolTable.relateToOperator("frexp", EOpFrexp);
|
||||
symbolTable.relateToOperator("ldexp", EOpLdexp);
|
||||
|
||||
@@ -601,7 +601,6 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin
|
||||
selector.push_back(0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HLSL
|
||||
//
|
||||
// Make the passed-in variable information become a member of the
|
||||
// global uniform block. If this doesn't exist yet, make it.
|
||||
@@ -646,7 +645,67 @@ void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& mem
|
||||
|
||||
++firstNewMember;
|
||||
}
|
||||
#endif
|
||||
|
||||
void TParseContextBase::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) {
|
||||
// Make the atomic counter block, if not yet made.
|
||||
const auto &at = atomicCounterBuffers.find(binding);
|
||||
if (at == atomicCounterBuffers.end()) {
|
||||
atomicCounterBuffers.insert({binding, (TVariable*)nullptr });
|
||||
atomicCounterBlockFirstNewMember.insert({binding, 0});
|
||||
}
|
||||
|
||||
TVariable*& atomicCounterBuffer = atomicCounterBuffers[binding];
|
||||
int& bufferNewMember = atomicCounterBlockFirstNewMember[binding];
|
||||
|
||||
if (atomicCounterBuffer == nullptr) {
|
||||
TQualifier blockQualifier;
|
||||
blockQualifier.clear();
|
||||
blockQualifier.storage = EvqBuffer;
|
||||
|
||||
char charBuffer[512];
|
||||
if (binding != TQualifier::layoutBindingEnd) {
|
||||
snprintf(charBuffer, 512, "%s_%d", getAtomicCounterBlockName(), binding);
|
||||
} else {
|
||||
snprintf(charBuffer, 512, "%s_0", getAtomicCounterBlockName());
|
||||
}
|
||||
|
||||
TType blockType(new TTypeList, *NewPoolTString(charBuffer), blockQualifier);
|
||||
setUniformBlockDefaults(blockType);
|
||||
blockType.getQualifier().layoutPacking = ElpStd430;
|
||||
atomicCounterBuffer = new TVariable(NewPoolTString(""), blockType, true);
|
||||
// If we arn't auto mapping bindings then set the block to use the same
|
||||
// binding as what the atomic was set to use
|
||||
if (!intermediate.getAutoMapBindings()) {
|
||||
atomicCounterBuffer->getWritableType().getQualifier().layoutBinding = binding;
|
||||
}
|
||||
bufferNewMember = 0;
|
||||
|
||||
atomicCounterBuffer->getWritableType().getQualifier().layoutSet = atomicCounterBlockSet;
|
||||
}
|
||||
|
||||
// Add the requested member as a member to the global block.
|
||||
TType* type = new TType;
|
||||
type->shallowCopy(memberType);
|
||||
type->setFieldName(memberName);
|
||||
if (typeList)
|
||||
type->setStruct(typeList);
|
||||
TTypeLoc typeLoc = {type, loc};
|
||||
atomicCounterBuffer->getType().getWritableStruct()->push_back(typeLoc);
|
||||
|
||||
// Insert into the symbol table.
|
||||
if (bufferNewMember == 0) {
|
||||
// This is the first request; we need a normal symbol table insert
|
||||
if (symbolTable.insert(*atomicCounterBuffer))
|
||||
trackLinkage(*atomicCounterBuffer);
|
||||
else
|
||||
error(loc, "failed to insert the global constant buffer", "buffer", "");
|
||||
} else {
|
||||
// This is a follow-on request; we need to amend the first insert
|
||||
symbolTable.amend(*atomicCounterBuffer, bufferNewMember);
|
||||
}
|
||||
|
||||
++bufferNewMember;
|
||||
}
|
||||
|
||||
void TParseContextBase::finish()
|
||||
{
|
||||
|
||||
@@ -225,6 +225,108 @@ void TParseContext::parserError(const char* s)
|
||||
error(getCurrentLoc(), "compilation terminated", "", "");
|
||||
}
|
||||
|
||||
void TParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList)
|
||||
{
|
||||
bool createBlock = globalUniformBlock == nullptr;
|
||||
|
||||
if (createBlock) {
|
||||
globalUniformBinding = intermediate.getGlobalUniformBinding();
|
||||
globalUniformSet = intermediate.getGlobalUniformSet();
|
||||
}
|
||||
|
||||
// use base class function to create/expand block
|
||||
TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, typeList);
|
||||
|
||||
if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) {
|
||||
// check for a block storage override
|
||||
TBlockStorageClass storageOverride = intermediate.getBlockStorageOverride(getGlobalUniformBlockName());
|
||||
TQualifier& qualifier = globalUniformBlock->getWritableType().getQualifier();
|
||||
qualifier.defaultBlock = true;
|
||||
|
||||
if (storageOverride != EbsNone) {
|
||||
if (createBlock) {
|
||||
// Remap block storage
|
||||
qualifier.setBlockStorage(storageOverride);
|
||||
|
||||
// check that the change didn't create errors
|
||||
blockQualifierCheck(loc, qualifier, false);
|
||||
}
|
||||
|
||||
// remap meber storage as well
|
||||
memberType.getQualifier().setBlockStorage(storageOverride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TParseContext::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList)
|
||||
{
|
||||
bool createBlock = atomicCounterBuffers.find(binding) == atomicCounterBuffers.end();
|
||||
|
||||
if (createBlock) {
|
||||
atomicCounterBlockSet = intermediate.getAtomicCounterBlockSet();
|
||||
}
|
||||
|
||||
// use base class function to create/expand block
|
||||
TParseContextBase::growAtomicCounterBlock(binding, loc, memberType, memberName, typeList);
|
||||
TQualifier& qualifier = atomicCounterBuffers[binding]->getWritableType().getQualifier();
|
||||
qualifier.defaultBlock = true;
|
||||
|
||||
if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) {
|
||||
// check for a Block storage override
|
||||
TBlockStorageClass storageOverride = intermediate.getBlockStorageOverride(getAtomicCounterBlockName());
|
||||
|
||||
if (storageOverride != EbsNone) {
|
||||
if (createBlock) {
|
||||
// Remap block storage
|
||||
|
||||
qualifier.setBlockStorage(storageOverride);
|
||||
|
||||
// check that the change didn't create errors
|
||||
blockQualifierCheck(loc, qualifier, false);
|
||||
}
|
||||
|
||||
// remap meber storage as well
|
||||
memberType.getQualifier().setBlockStorage(storageOverride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* TParseContext::getGlobalUniformBlockName() const
|
||||
{
|
||||
const char* name = intermediate.getGlobalUniformBlockName();
|
||||
if (std::string(name) == "")
|
||||
return "gl_DefaultUniformBlock";
|
||||
else
|
||||
return name;
|
||||
}
|
||||
void TParseContext::finalizeGlobalUniformBlockLayout(TVariable&)
|
||||
{
|
||||
}
|
||||
void TParseContext::setUniformBlockDefaults(TType& block) const
|
||||
{
|
||||
block.getQualifier().layoutPacking = ElpStd140;
|
||||
block.getQualifier().layoutMatrix = ElmColumnMajor;
|
||||
}
|
||||
|
||||
|
||||
const char* TParseContext::getAtomicCounterBlockName() const
|
||||
{
|
||||
const char* name = intermediate.getAtomicCounterBlockName();
|
||||
if (std::string(name) == "")
|
||||
return "gl_AtomicCounterBlock";
|
||||
else
|
||||
return name;
|
||||
}
|
||||
void TParseContext::finalizeAtomicCounterBlockLayout(TVariable&)
|
||||
{
|
||||
}
|
||||
|
||||
void TParseContext::setAtomicCounterBlockDefaults(TType& block) const
|
||||
{
|
||||
block.getQualifier().layoutPacking = ElpStd430;
|
||||
block.getQualifier().layoutMatrix = ElmRowMajor;
|
||||
}
|
||||
|
||||
void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
|
||||
{
|
||||
#ifndef GLSLANG_WEB
|
||||
@@ -1135,6 +1237,14 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction
|
||||
{
|
||||
TIntermTyped* result = nullptr;
|
||||
|
||||
if (spvVersion.vulkan != 0 && spvVersion.vulkanRelaxed) {
|
||||
// allow calls that are invalid in Vulkan Semantics to be invisibily
|
||||
// remapped to equivalent valid functions
|
||||
result = vkRelaxedRemapFunctionCall(loc, function, arguments);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (function->getBuiltInOp() == EOpArrayLength)
|
||||
result = handleLengthMethod(loc, function, arguments);
|
||||
else if (function->getBuiltInOp() != EOpNull) {
|
||||
@@ -1727,6 +1837,7 @@ void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction&
|
||||
// Grab the semantics and storage class semantics from the operands, based on opcode
|
||||
switch (callNode.getOp()) {
|
||||
case EOpAtomicAdd:
|
||||
case EOpAtomicSubtract:
|
||||
case EOpAtomicMin:
|
||||
case EOpAtomicMax:
|
||||
case EOpAtomicAnd:
|
||||
@@ -2176,6 +2287,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan
|
||||
}
|
||||
|
||||
case EOpAtomicAdd:
|
||||
case EOpAtomicSubtract:
|
||||
case EOpAtomicMin:
|
||||
case EOpAtomicMax:
|
||||
case EOpAtomicAnd:
|
||||
@@ -3388,7 +3500,7 @@ void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& t
|
||||
|
||||
if (type.containsNonOpaque()) {
|
||||
// Vulkan doesn't allow transparent uniforms outside of blocks
|
||||
if (spvVersion.vulkan > 0)
|
||||
if (spvVersion.vulkan > 0 && !spvVersion.vulkanRelaxed)
|
||||
vulkanRemoved(loc, "non-opaque uniforms outside a block");
|
||||
// OpenGL wants locations on these (unless they are getting automapped)
|
||||
if (spvVersion.openGl > 0 && !type.getQualifier().hasLocation() && !intermediate.getAutoMapLocations())
|
||||
@@ -5019,14 +5131,22 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
|
||||
return;
|
||||
}
|
||||
if (id == TQualifier::getLayoutPackingString(ElpPacked)) {
|
||||
if (spvVersion.spv != 0)
|
||||
spvRemoved(loc, "packed");
|
||||
if (spvVersion.spv != 0) {
|
||||
if (spvVersion.vulkanRelaxed)
|
||||
return; // silently ignore qualifier
|
||||
else
|
||||
spvRemoved(loc, "packed");
|
||||
}
|
||||
publicType.qualifier.layoutPacking = ElpPacked;
|
||||
return;
|
||||
}
|
||||
if (id == TQualifier::getLayoutPackingString(ElpShared)) {
|
||||
if (spvVersion.spv != 0)
|
||||
spvRemoved(loc, "shared");
|
||||
if (spvVersion.spv != 0) {
|
||||
if (spvVersion.vulkanRelaxed)
|
||||
return; // silently ignore qualifier
|
||||
else
|
||||
spvRemoved(loc, "shared");
|
||||
}
|
||||
publicType.qualifier.layoutPacking = ElpShared;
|
||||
return;
|
||||
}
|
||||
@@ -5928,7 +6048,7 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
|
||||
error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : "");
|
||||
#endif
|
||||
}
|
||||
if (type.isAtomic()) {
|
||||
if (type.isAtomic() && !spvVersion.vulkanRelaxed) {
|
||||
if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) {
|
||||
error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", "");
|
||||
return;
|
||||
@@ -6598,6 +6718,68 @@ const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc,
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
//
|
||||
// Adjust function calls that aren't declared in Vulkan to a
|
||||
// calls with equivalent effects
|
||||
//
|
||||
TIntermTyped* TParseContext::vkRelaxedRemapFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments)
|
||||
{
|
||||
TIntermTyped* result = nullptr;
|
||||
|
||||
#ifndef GLSLANG_WEB
|
||||
if (function->getBuiltInOp() != EOpNull) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (function->getName() == "atomicCounterIncrement") {
|
||||
// change atomicCounterIncrement into an atomicAdd of 1
|
||||
TString name("atomicAdd");
|
||||
TType uintType(EbtUint);
|
||||
|
||||
TFunction realFunc(&name, function->getType());
|
||||
|
||||
for (int i = 0; i < function->getParamCount(); ++i) {
|
||||
realFunc.addParameter((*function)[i]);
|
||||
}
|
||||
|
||||
TParameter tmpP = { 0, &uintType };
|
||||
realFunc.addParameter(tmpP);
|
||||
arguments = intermediate.growAggregate(arguments, intermediate.addConstantUnion(1, loc, true));
|
||||
|
||||
result = handleFunctionCall(loc, &realFunc, arguments);
|
||||
} else if (function->getName() == "atomicCounterDecrement") {
|
||||
// change atomicCounterDecrement into an atomicAdd with -1
|
||||
// and subtract 1 from result, to return post-decrement value
|
||||
TString name("atomicAdd");
|
||||
TType uintType(EbtUint);
|
||||
|
||||
TFunction realFunc(&name, function->getType());
|
||||
|
||||
for (int i = 0; i < function->getParamCount(); ++i) {
|
||||
realFunc.addParameter((*function)[i]);
|
||||
}
|
||||
|
||||
TParameter tmpP = { 0, &uintType };
|
||||
realFunc.addParameter(tmpP);
|
||||
arguments = intermediate.growAggregate(arguments, intermediate.addConstantUnion(-1, loc, true));
|
||||
|
||||
result = handleFunctionCall(loc, &realFunc, arguments);
|
||||
|
||||
// post decrement, so that it matches AtomicCounterDecrement semantics
|
||||
if (result) {
|
||||
result = handleBinaryMath(loc, "-", EOpSub, result, intermediate.addConstantUnion(1, loc, true));
|
||||
}
|
||||
} else if (function->getName() == "atomicCounter") {
|
||||
// change atomicCounter into a direct read of the variable
|
||||
if (arguments->getAsTyped()) {
|
||||
result = arguments->getAsTyped();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// When a declaration includes a type, but not a variable name, it can be used
|
||||
// to establish defaults.
|
||||
void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType& publicType)
|
||||
@@ -6622,6 +6804,91 @@ void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TParseContext::vkRelaxedRemapUniformVariable(const TSourceLoc& loc, TString& identifier, const TPublicType&,
|
||||
TArraySizes*, TIntermTyped* initializer, TType& type)
|
||||
{
|
||||
if (parsingBuiltins || symbolTable.atBuiltInLevel() || !symbolTable.atGlobalLevel() ||
|
||||
type.getQualifier().storage != EvqUniform ||
|
||||
!(type.containsNonOpaque()
|
||||
#ifndef GLSLANG_WEB
|
||||
|| type.getBasicType() == EbtAtomicUint
|
||||
#endif
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type.getQualifier().hasLocation()) {
|
||||
warn(loc, "ignoring layout qualifier for uniform", identifier.c_str(), "location");
|
||||
type.getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
|
||||
}
|
||||
|
||||
if (initializer) {
|
||||
warn(loc, "Ignoring initializer for uniform", identifier.c_str(), "");
|
||||
initializer = nullptr;
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
// do array size checks here
|
||||
arraySizesCheck(loc, type.getQualifier(), type.getArraySizes(), initializer, false);
|
||||
|
||||
if (arrayQualifierError(loc, type.getQualifier()) || arrayError(loc, type)) {
|
||||
error(loc, "array param error", identifier.c_str(), "");
|
||||
}
|
||||
}
|
||||
|
||||
// do some checking on the type as it was declared
|
||||
layoutTypeCheck(loc, type);
|
||||
|
||||
int bufferBinding = TQualifier::layoutBindingEnd;
|
||||
TVariable* updatedBlock = nullptr;
|
||||
|
||||
#ifndef GLSLANG_WEB
|
||||
// Convert atomic_uint into members of a buffer block
|
||||
if (type.isAtomic()) {
|
||||
type.setBasicType(EbtUint);
|
||||
type.getQualifier().storage = EvqBuffer;
|
||||
|
||||
type.getQualifier().volatil = true;
|
||||
type.getQualifier().coherent = true;
|
||||
|
||||
// xxTODO: use logic from fixOffset() to apply explicit member offset
|
||||
bufferBinding = type.getQualifier().layoutBinding;
|
||||
type.getQualifier().layoutBinding = TQualifier::layoutBindingEnd;
|
||||
type.getQualifier().explicitOffset = false;
|
||||
growAtomicCounterBlock(bufferBinding, loc, type, identifier, nullptr);
|
||||
updatedBlock = atomicCounterBuffers[bufferBinding];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!updatedBlock) {
|
||||
growGlobalUniformBlock(loc, type, identifier, nullptr);
|
||||
updatedBlock = globalUniformBlock;
|
||||
}
|
||||
|
||||
//
|
||||
// don't assign explicit member offsets here
|
||||
// if any are assigned, need to be updated here and in the merge/link step
|
||||
// fixBlockUniformOffsets(updatedBlock->getWritableType().getQualifier(), *updatedBlock->getWritableType().getWritableStruct());
|
||||
|
||||
// checks on update buffer object
|
||||
layoutObjectCheck(loc, *updatedBlock);
|
||||
|
||||
TSymbol* symbol = symbolTable.find(identifier);
|
||||
|
||||
if (!symbol) {
|
||||
if (updatedBlock == globalUniformBlock)
|
||||
error(loc, "error adding uniform to default uniform block", identifier.c_str(), "");
|
||||
else
|
||||
error(loc, "error adding atomic counter to atomic counter block", identifier.c_str(), "");
|
||||
return false;
|
||||
}
|
||||
|
||||
// merge qualifiers
|
||||
mergeObjectLayoutQualifiers(updatedBlock->getWritableType().getQualifier(), type.getQualifier(), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Do everything necessary to handle a variable (non-block) declaration.
|
||||
// Either redeclaring a variable, or making a new one, updating the symbol
|
||||
@@ -6733,6 +7000,14 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden
|
||||
if (symbol == nullptr)
|
||||
reservedErrorCheck(loc, identifier);
|
||||
|
||||
if (symbol == nullptr && spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) {
|
||||
bool remapped = vkRelaxedRemapUniformVariable(loc, identifier, publicType, arraySizes, initializer, type);
|
||||
|
||||
if (remapped) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inheritGlobalDefaults(type.getQualifier());
|
||||
|
||||
// Declare the variable
|
||||
@@ -7625,6 +7900,8 @@ void TParseContext::inheritMemoryQualifiers(const TQualifier& from, TQualifier&
|
||||
void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName,
|
||||
TArraySizes* arraySizes)
|
||||
{
|
||||
if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed)
|
||||
blockStorageRemap(loc, blockName, currentBlockQualifier);
|
||||
blockStageIoCheck(loc, currentBlockQualifier);
|
||||
blockQualifierCheck(loc, currentBlockQualifier, instanceName != nullptr);
|
||||
if (arraySizes != nullptr) {
|
||||
@@ -7914,6 +8191,17 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
|
||||
trackLinkage(variable);
|
||||
}
|
||||
|
||||
//
|
||||
// allow storage type of block to be remapped at compile time
|
||||
//
|
||||
void TParseContext::blockStorageRemap(const TSourceLoc&, const TString* instanceName, TQualifier& qualifier)
|
||||
{
|
||||
TBlockStorageClass type = intermediate.getBlockStorageOverride(instanceName->c_str());
|
||||
if (type != EbsNone) {
|
||||
qualifier.setBlockStorage(type);
|
||||
}
|
||||
}
|
||||
|
||||
// Do all block-declaration checking regarding the combination of in/out/uniform/buffer
|
||||
// with a particular stage.
|
||||
void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& qualifier)
|
||||
|
||||
@@ -92,7 +92,8 @@ public:
|
||||
limits(resources.limits),
|
||||
globalUniformBlock(nullptr),
|
||||
globalUniformBinding(TQualifier::layoutBindingEnd),
|
||||
globalUniformSet(TQualifier::layoutSetEnd)
|
||||
globalUniformSet(TQualifier::layoutSetEnd),
|
||||
atomicCounterBlockSet(TQualifier::layoutSetEnd)
|
||||
{
|
||||
if (entryPoint != nullptr)
|
||||
sourceEntryPointName = *entryPoint;
|
||||
@@ -154,10 +155,11 @@ public:
|
||||
extensionCallback(line, extension, behavior);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_HLSL
|
||||
// Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
|
||||
virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr);
|
||||
#endif
|
||||
|
||||
// Manage global buffer (used for backing atomic counters in GLSL when using relaxed Vulkan semantics)
|
||||
virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr);
|
||||
|
||||
// Potentially rename shader entry point function
|
||||
void renameShaderFunction(TString*& name) const
|
||||
@@ -230,7 +232,24 @@ protected:
|
||||
// override this to set the language-specific name
|
||||
virtual const char* getGlobalUniformBlockName() const { return ""; }
|
||||
virtual void setUniformBlockDefaults(TType&) const { }
|
||||
virtual void finalizeGlobalUniformBlockLayout(TVariable&) { }
|
||||
virtual void finalizeGlobalUniformBlockLayout(TVariable&) {}
|
||||
|
||||
// Manage the atomic counter block (used for atomic_uints with Vulkan-Relaxed)
|
||||
TMap<int, TVariable*> atomicCounterBuffers;
|
||||
unsigned int atomicCounterBlockSet;
|
||||
TMap<int, int> atomicCounterBlockFirstNewMember;
|
||||
// override this to set the language-specific name
|
||||
virtual const char* getAtomicCounterBlockName() const { return ""; }
|
||||
virtual void setAtomicCounterBlockDefaults(TType&) const {}
|
||||
virtual void finalizeAtomicCounterBlockLayout(TVariable&) {}
|
||||
bool isAtomicCounterBlock(const TSymbol& symbol) {
|
||||
const TVariable* var = symbol.getAsVariable();
|
||||
if (!var)
|
||||
return false;
|
||||
const auto& at = atomicCounterBuffers.find(var->getType().getQualifier().layoutBinding);
|
||||
return (at != atomicCounterBuffers.end() && (*at).second->getType() == var->getType());
|
||||
}
|
||||
|
||||
virtual void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken,
|
||||
const char* szExtraInfoFormat, TPrefixType prefix,
|
||||
va_list args);
|
||||
@@ -293,6 +312,9 @@ public:
|
||||
bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override;
|
||||
void parserError(const char* s); // for bison's yyerror
|
||||
|
||||
virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override;
|
||||
virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override;
|
||||
|
||||
void reservedErrorCheck(const TSourceLoc&, const TString&);
|
||||
void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) override;
|
||||
bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) override;
|
||||
@@ -340,6 +362,10 @@ public:
|
||||
void checkPrecisionQualifier(const TSourceLoc&, TPrecisionQualifier);
|
||||
void memorySemanticsCheck(const TSourceLoc&, const TFunction&, const TIntermOperator& callNode);
|
||||
|
||||
TIntermTyped* vkRelaxedRemapFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*);
|
||||
// returns true if the variable was remapped to something else
|
||||
bool vkRelaxedRemapUniformVariable(const TSourceLoc&, TString&, const TPublicType&, TArraySizes*, TIntermTyped*, TType&);
|
||||
|
||||
void assignError(const TSourceLoc&, const char* op, TString left, TString right);
|
||||
void unaryOpError(const TSourceLoc&, const char* op, TString operand);
|
||||
void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right);
|
||||
@@ -417,6 +443,7 @@ public:
|
||||
TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
|
||||
void inheritMemoryQualifiers(const TQualifier& from, TQualifier& to);
|
||||
void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
|
||||
void blockStorageRemap(const TSourceLoc&, const TString*, TQualifier&);
|
||||
void blockStageIoCheck(const TSourceLoc&, const TQualifier&);
|
||||
void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName);
|
||||
void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
|
||||
@@ -461,6 +488,14 @@ protected:
|
||||
void finish() override;
|
||||
#endif
|
||||
|
||||
virtual const char* getGlobalUniformBlockName() const override;
|
||||
virtual void finalizeGlobalUniformBlockLayout(TVariable&) override;
|
||||
virtual void setUniformBlockDefaults(TType& block) const override;
|
||||
|
||||
virtual const char* getAtomicCounterBlockName() const override;
|
||||
virtual void finalizeAtomicCounterBlockLayout(TVariable&) override;
|
||||
virtual void setAtomicCounterBlockDefaults(TType& block) const override;
|
||||
|
||||
public:
|
||||
//
|
||||
// Generally, bison productions, the scanner, and the PP need read/write access to these; just give them direct access
|
||||
|
||||
@@ -159,7 +159,7 @@ int MapVersionToIndex(int version)
|
||||
return index;
|
||||
}
|
||||
|
||||
const int SpvVersionCount = 3; // index range in MapSpvVersionToIndex
|
||||
const int SpvVersionCount = 4; // index range in MapSpvVersionToIndex
|
||||
|
||||
int MapSpvVersionToIndex(const SpvVersion& spvVersion)
|
||||
{
|
||||
@@ -167,8 +167,12 @@ int MapSpvVersionToIndex(const SpvVersion& spvVersion)
|
||||
|
||||
if (spvVersion.openGl > 0)
|
||||
index = 1;
|
||||
else if (spvVersion.vulkan > 0)
|
||||
index = 2;
|
||||
else if (spvVersion.vulkan > 0) {
|
||||
if (!spvVersion.vulkanRelaxed)
|
||||
index = 2;
|
||||
else
|
||||
index = 3;
|
||||
}
|
||||
|
||||
assert(index < SpvVersionCount);
|
||||
|
||||
@@ -723,6 +727,7 @@ void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages
|
||||
break;
|
||||
case EShClientVulkan:
|
||||
spvVersion.vulkanGlsl = environment->input.dialectVersion;
|
||||
spvVersion.vulkanRelaxed = environment->input.VulkanRulesRelaxed;
|
||||
break;
|
||||
case EShClientOpenGL:
|
||||
spvVersion.openGl = environment->input.dialectVersion;
|
||||
@@ -1868,6 +1873,15 @@ void TShader::setResourceSetBinding(const std::vector<std::string>& base) { in
|
||||
void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); }
|
||||
#endif
|
||||
|
||||
void TShader::addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) { intermediate->addBlockStorageOverride(nameStr, backing); }
|
||||
|
||||
void TShader::setGlobalUniformBlockName(const char* name) { intermediate->setGlobalUniformBlockName(name); }
|
||||
void TShader::setGlobalUniformSet(unsigned int set) { intermediate->setGlobalUniformSet(set); }
|
||||
void TShader::setGlobalUniformBinding(unsigned int binding) { intermediate->setGlobalUniformBinding(binding); }
|
||||
|
||||
void TShader::setAtomicCounterBlockName(const char* name) { intermediate->setAtomicCounterBlockName(name); }
|
||||
void TShader::setAtomicCounterBlockSet(unsigned int set) { intermediate->setAtomicCounterBlockSet(set); }
|
||||
|
||||
#ifdef ENABLE_HLSL
|
||||
// See comment above TDefaultHlslIoMapper in iomapper.cpp:
|
||||
void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); }
|
||||
@@ -1983,7 +1997,10 @@ bool TProgram::link(EShMessages messages)
|
||||
error = true;
|
||||
}
|
||||
|
||||
// TODO: Link: cross-stage error checking
|
||||
if (!error) {
|
||||
if (! crossStageCheck(messages))
|
||||
error = true;
|
||||
}
|
||||
|
||||
return ! error;
|
||||
}
|
||||
@@ -2060,6 +2077,64 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
|
||||
return intermediate[stage]->getNumErrors() == 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Check that there are no errors in linker objects accross stages
|
||||
//
|
||||
// Return true if no errors.
|
||||
//
|
||||
bool TProgram::crossStageCheck(EShMessages) {
|
||||
|
||||
// make temporary intermediates to hold the linkage symbols for each linking interface
|
||||
// while we do the checks
|
||||
// Independent interfaces are:
|
||||
// all uniform variables and blocks
|
||||
// all buffer blocks
|
||||
// all in/out on a stage boundary
|
||||
|
||||
TVector<TIntermediate*> activeStages;
|
||||
for (int s = 0; s < EShLangCount; ++s) {
|
||||
if (intermediate[s])
|
||||
activeStages.push_back(intermediate[s]);
|
||||
}
|
||||
|
||||
// no extra linking if there is only one stage
|
||||
if (! (activeStages.size() > 1))
|
||||
return true;
|
||||
|
||||
// setup temporary tree to hold unfirom objects from different stages
|
||||
TIntermediate* firstIntermediate = activeStages.front();
|
||||
TIntermediate uniforms(EShLangCount,
|
||||
firstIntermediate->getVersion(),
|
||||
firstIntermediate->getProfile());
|
||||
uniforms.setSpv(firstIntermediate->getSpv());
|
||||
|
||||
TIntermAggregate uniformObjects(EOpLinkerObjects);
|
||||
TIntermAggregate root(EOpSequence);
|
||||
root.getSequence().push_back(&uniformObjects);
|
||||
uniforms.setTreeRoot(&root);
|
||||
|
||||
bool error = false;
|
||||
|
||||
// merge uniforms from all stages into a single intermediate
|
||||
for (unsigned int i = 0; i < activeStages.size(); ++i) {
|
||||
uniforms.mergeUniformObjects(*infoSink, *activeStages[i]);
|
||||
}
|
||||
error |= uniforms.getNumErrors() != 0;
|
||||
|
||||
// copy final definition of global block back into each stage
|
||||
for (unsigned int i = 0; i < activeStages.size(); ++i) {
|
||||
activeStages[i]->mergeGlobalUniformBlocks(*infoSink, uniforms);
|
||||
}
|
||||
|
||||
// compare cross stage symbols for each stage boundary
|
||||
for (unsigned int i = 1; i < activeStages.size(); ++i) {
|
||||
activeStages[i - 1]->checkStageIO(*infoSink, *activeStages[i]);
|
||||
error |= (activeStages[i - 1]->getNumErrors() != 0);
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
const char* TProgram::getInfoLog()
|
||||
{
|
||||
return infoSink->info.c_str();
|
||||
|
||||
@@ -1273,7 +1273,7 @@ void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op)
|
||||
// Call for any operation removed because Vulkan SPIR-V is being generated.
|
||||
void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op)
|
||||
{
|
||||
if (spvVersion.vulkan > 0)
|
||||
if (spvVersion.vulkan > 0 && !spvVersion.vulkanRelaxed)
|
||||
error(loc, "not allowed when using GLSL for Vulkan", op, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -87,11 +87,12 @@ inline const char* ProfileName(EProfile profile)
|
||||
// The union of all requested rule sets will be applied.
|
||||
//
|
||||
struct SpvVersion {
|
||||
SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {}
|
||||
SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0), vulkanRelaxed(false) {}
|
||||
unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header
|
||||
int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX"
|
||||
int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use
|
||||
int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX"
|
||||
bool vulkanRelaxed; // relax changes to GLSL for Vulkan, allowing some GL-specific to be compiled to Vulkan SPIR-V target
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
@@ -886,6 +886,7 @@ bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node
|
||||
case EOpTime: out.debug << "time"; break;
|
||||
|
||||
case EOpAtomicAdd: out.debug << "AtomicAdd"; break;
|
||||
case EOpAtomicSubtract: out.debug << "AtomicSubtract"; break;
|
||||
case EOpAtomicMin: out.debug << "AtomicMin"; break;
|
||||
case EOpAtomicMax: out.debug << "AtomicMax"; break;
|
||||
case EOpAtomicAnd: out.debug << "AtomicAnd"; break;
|
||||
|
||||
@@ -210,8 +210,8 @@ struct TResolverUniformAdaptor {
|
||||
ent.newIndex = -1;
|
||||
const bool isValid = resolver.validateBinding(stage, ent);
|
||||
if (isValid) {
|
||||
resolver.resolveBinding(ent.stage, ent);
|
||||
resolver.resolveSet(ent.stage, ent);
|
||||
resolver.resolveBinding(ent.stage, ent);
|
||||
resolver.resolveUniformLocation(ent.stage, ent);
|
||||
|
||||
if (ent.newBinding != -1) {
|
||||
@@ -317,15 +317,13 @@ private:
|
||||
};
|
||||
|
||||
// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
|
||||
// xxTODO: maybe this logic should be moved into the resolver's "validateInOut" and "validateUniform"
|
||||
|
||||
struct TSymbolValidater
|
||||
{
|
||||
TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
|
||||
TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version)
|
||||
: preStage(EShLangCount)
|
||||
, currentStage(EShLangCount)
|
||||
, nextStage(EShLangCount)
|
||||
, resolver(r)
|
||||
: resolver(r)
|
||||
, infoSink(i)
|
||||
, hadError(hadError)
|
||||
, profile(profile)
|
||||
@@ -438,17 +436,23 @@ struct TSymbolValidater
|
||||
TIntermSymbol* base = ent1.symbol;
|
||||
const TType& type = ent1.symbol->getType();
|
||||
const TString& name = entKey.first;
|
||||
EShLanguage stage = ent1.stage;
|
||||
TString mangleName1, mangleName2;
|
||||
if (currentStage != stage) {
|
||||
preStage = currentStage;
|
||||
currentStage = stage;
|
||||
nextStage = EShLangCount;
|
||||
for (int i = currentStage + 1; i < EShLangCount; i++) {
|
||||
if (inVarMaps[i] != nullptr) {
|
||||
nextStage = static_cast<EShLanguage>(i);
|
||||
break;
|
||||
}
|
||||
EShLanguage stage = ent1.stage;
|
||||
EShLanguage preStage, currentStage, nextStage;
|
||||
|
||||
preStage = EShLangCount;
|
||||
for (int i = stage - 1; i >= 0; i--) {
|
||||
if (inVarMaps[i] != nullptr) {
|
||||
preStage = static_cast<EShLanguage>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentStage = stage;
|
||||
nextStage = EShLangCount;
|
||||
for (int i = stage + 1; i < EShLangCount; i++) {
|
||||
if (inVarMaps[i] != nullptr) {
|
||||
nextStage = static_cast<EShLanguage>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +463,9 @@ struct TSymbolValidater
|
||||
type.appendMangledName(mangleName1);
|
||||
}
|
||||
|
||||
|
||||
// basic checking that symbols match
|
||||
// more extensive checking in the link stage
|
||||
if (base->getQualifier().storage == EvqVaryingIn) {
|
||||
// validate stage in;
|
||||
if (preStage == EShLangCount)
|
||||
@@ -484,8 +491,7 @@ struct TSymbolValidater
|
||||
if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
|
||||
TType subType(ent2->second.symbol->getType(), 0);
|
||||
subType.appendMangledName(mangleName2);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ent2->second.symbol->getType().appendMangledName(mangleName2);
|
||||
}
|
||||
|
||||
@@ -536,8 +542,7 @@ struct TSymbolValidater
|
||||
if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
|
||||
TType subType(ent2->second.symbol->getType(), 0);
|
||||
subType.appendMangledName(mangleName2);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ent2->second.symbol->getType().appendMangledName(mangleName2);
|
||||
}
|
||||
if (mangleName1 == mangleName2)
|
||||
@@ -550,7 +555,7 @@ struct TSymbolValidater
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) {
|
||||
} else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) {
|
||||
// validate uniform type;
|
||||
for (int i = 0; i < EShLangCount; i++) {
|
||||
if (i != currentStage && outVarMaps[i] != nullptr) {
|
||||
@@ -558,6 +563,7 @@ struct TSymbolValidater
|
||||
if (ent2 != uniformVarMap[i]->end()) {
|
||||
ent2->second.symbol->getType().appendMangledName(mangleName2);
|
||||
if (mangleName1 != mangleName2) {
|
||||
ent2->second.symbol->getType().sameElementType(type);
|
||||
TString err = "Invalid Uniform variable type : " + entKey.first;
|
||||
infoSink.info.message(EPrefixInternalError, err.c_str());
|
||||
hadError = true;
|
||||
@@ -608,8 +614,7 @@ struct TSymbolValidater
|
||||
}
|
||||
|
||||
TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
|
||||
// Use for mark pre stage, to get more interface symbol information.
|
||||
EShLanguage preStage, currentStage, nextStage;
|
||||
|
||||
// Use for mark current shader stage for resolver
|
||||
TIoMapResolver& resolver;
|
||||
TInfoSink& infoSink;
|
||||
@@ -749,14 +754,18 @@ TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate
|
||||
, nextOutputLocation(0)
|
||||
{
|
||||
memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
|
||||
memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount));
|
||||
stageIntermediates[intermediate.getStage()] = &intermediate;
|
||||
}
|
||||
|
||||
int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const {
|
||||
return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
|
||||
int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const {
|
||||
return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set))
|
||||
: selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
|
||||
}
|
||||
|
||||
const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
|
||||
return intermediate.getResourceSetBinding();
|
||||
const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const {
|
||||
return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding()
|
||||
: intermediate.getResourceSetBinding();
|
||||
}
|
||||
|
||||
bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
|
||||
@@ -797,14 +806,14 @@ int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
|
||||
return reserveSlot(set, base, size);
|
||||
}
|
||||
|
||||
int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) {
|
||||
int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) {
|
||||
const TType& type = ent.symbol->getType();
|
||||
if (type.getQualifier().hasSet()) {
|
||||
return ent.newSet = type.getQualifier().layoutSet;
|
||||
}
|
||||
// If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
|
||||
if (getResourceSetBinding().size() == 1) {
|
||||
return ent.newSet = atoi(getResourceSetBinding()[0].c_str());
|
||||
if (getResourceSetBinding(stage).size() == 1) {
|
||||
return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str());
|
||||
}
|
||||
return ent.newSet = 0;
|
||||
}
|
||||
@@ -925,7 +934,7 @@ int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInf
|
||||
preStage = currentStage;
|
||||
currentStage = stage;
|
||||
}
|
||||
// kick out of not doing this
|
||||
// kick out if not doing this
|
||||
if (! doAutoLocationMapping()) {
|
||||
return ent.newLocation = -1;
|
||||
}
|
||||
@@ -1073,7 +1082,7 @@ int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEn
|
||||
return ent.newLocation = location;
|
||||
}
|
||||
|
||||
int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
|
||||
int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) {
|
||||
const TType& type = ent.symbol->getType();
|
||||
const TString& name = ent.symbol->getAccessName();
|
||||
// On OpenGL arrays of opaque types take a separate binding for each element
|
||||
@@ -1086,30 +1095,32 @@ int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo&
|
||||
// There is no 'set' qualifier in OpenGL shading language, each resource has its own
|
||||
// binding name space, so remap the 'set' to resource type which make each resource
|
||||
// binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
|
||||
int set = resource;
|
||||
int set = intermediate.getSpv().openGl != 0 ? resource : ent.newSet;
|
||||
int resourceKey = set;
|
||||
if (resource < EResCount) {
|
||||
if (type.getQualifier().hasBinding()) {
|
||||
ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
|
||||
return ent.newBinding;
|
||||
} else if (ent.live && doAutoBindingMapping()) {
|
||||
int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
|
||||
return ent.newBinding = newBinding;
|
||||
|
||||
} else {
|
||||
// The resource in current stage is not declared with binding, but it is possible declared
|
||||
// with explicit binding in other stages, find the resourceSlotMap firstly to check whether
|
||||
// the resource has binding, don't need to allocate if it already has a binding
|
||||
bool hasBinding = false;
|
||||
if (! resourceSlotMap[resource].empty()) {
|
||||
TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name);
|
||||
if (iter != resourceSlotMap[resource].end()) {
|
||||
ent.newBinding = -1; // leave as -1 if it isn't set below
|
||||
|
||||
if (! resourceSlotMap[resourceKey].empty()) {
|
||||
TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name);
|
||||
if (iter != resourceSlotMap[resourceKey].end()) {
|
||||
hasBinding = true;
|
||||
ent.newBinding = iter->second;
|
||||
}
|
||||
}
|
||||
if (! hasBinding) {
|
||||
TVarSlotMap varSlotMap;
|
||||
if (!hasBinding && (ent.live && doAutoBindingMapping())) {
|
||||
// find free slot, the caller did make sure it passes all vars with binding
|
||||
// first and now all are passed that do not have a binding and needs one
|
||||
int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings);
|
||||
varSlotMap[name] = binding;
|
||||
resourceSlotMap[resource] = varSlotMap;
|
||||
int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings);
|
||||
resourceSlotMap[resourceKey][name] = binding;
|
||||
ent.newBinding = binding;
|
||||
}
|
||||
return ent.newBinding;
|
||||
@@ -1211,16 +1222,20 @@ void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink&
|
||||
void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
|
||||
const TType& type = ent.symbol->getType();
|
||||
const TString& name = ent.symbol->getAccessName();
|
||||
int resource = getResourceType(type);
|
||||
TResourceType resource = getResourceType(type);
|
||||
int set = intermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent);
|
||||
int resourceKey = set;
|
||||
|
||||
if (type.getQualifier().hasBinding()) {
|
||||
TVarSlotMap& varSlotMap = resourceSlotMap[resource];
|
||||
TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey];
|
||||
TVarSlotMap::iterator iter = varSlotMap.find(name);
|
||||
int binding = type.getQualifier().layoutBinding;
|
||||
int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set);
|
||||
|
||||
if (iter == varSlotMap.end()) {
|
||||
// Reserve the slots for the ubo, ssbo and opaques who has explicit binding
|
||||
int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1;
|
||||
int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
|
||||
varSlotMap[name] = binding;
|
||||
reserveSlot(resource, binding, numBindings);
|
||||
reserveSlot(resourceKey, binding, numBindings);
|
||||
} else {
|
||||
// Allocate binding by name for OpenGL driver, so the resource in different
|
||||
// stages should be declared with the same binding
|
||||
@@ -1269,7 +1284,7 @@ struct TDefaultIoResolver : public TDefaultIoResolverBase {
|
||||
return EResCount;
|
||||
}
|
||||
|
||||
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
|
||||
int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
|
||||
const TType& type = ent.symbol->getType();
|
||||
const int set = getLayoutSet(type);
|
||||
// On OpenGL arrays of opaque types take a seperate binding for each element
|
||||
@@ -1278,11 +1293,11 @@ struct TDefaultIoResolver : public TDefaultIoResolverBase {
|
||||
if (resource < EResCount) {
|
||||
if (type.getQualifier().hasBinding()) {
|
||||
return ent.newBinding = reserveSlot(
|
||||
set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
|
||||
set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
|
||||
} else if (ent.live && doAutoBindingMapping()) {
|
||||
// find free slot, the caller did make sure it passes all vars with binding
|
||||
// first and now all are passed that do not have a binding and needs one
|
||||
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings);
|
||||
return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings);
|
||||
}
|
||||
}
|
||||
return ent.newBinding = -1;
|
||||
@@ -1354,17 +1369,17 @@ struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
|
||||
return EResCount;
|
||||
}
|
||||
|
||||
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
|
||||
int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
|
||||
const TType& type = ent.symbol->getType();
|
||||
const int set = getLayoutSet(type);
|
||||
TResourceType resource = getResourceType(type);
|
||||
if (resource < EResCount) {
|
||||
if (type.getQualifier().hasBinding()) {
|
||||
return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding);
|
||||
return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding);
|
||||
} else if (ent.live && doAutoBindingMapping()) {
|
||||
// find free slot, the caller did make sure it passes all vars with binding
|
||||
// first and now all are passed that do not have a binding and needs one
|
||||
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set));
|
||||
return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set));
|
||||
}
|
||||
}
|
||||
return ent.newBinding = -1;
|
||||
@@ -1403,10 +1418,10 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSi
|
||||
else
|
||||
resolver = &defaultResolver;
|
||||
}
|
||||
resolver->addStage(stage);
|
||||
#else
|
||||
resolver = &defaultResolver;
|
||||
#endif
|
||||
resolver->addStage(stage, intermediate);
|
||||
|
||||
TVarLiveMap inVarMap, outVarMap, uniformVarMap;
|
||||
TVarLiveVector inVector, outVector, uniformVector;
|
||||
@@ -1502,10 +1517,21 @@ bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TIn
|
||||
}
|
||||
// if no resolver is provided, use the default resolver with the given shifts and auto map settings
|
||||
TDefaultGlslIoResolver defaultResolver(intermediate);
|
||||
#ifdef ENABLE_HLSL
|
||||
TDefaultHlslIoResolver defaultHlslResolver(intermediate);
|
||||
if (resolver == nullptr) {
|
||||
// TODO: use a passed in IO mapper for this
|
||||
if (intermediate.usingHlslIoMapping())
|
||||
resolver = &defaultHlslResolver;
|
||||
else
|
||||
resolver = &defaultResolver;
|
||||
}
|
||||
#else
|
||||
if (resolver == nullptr) {
|
||||
resolver = &defaultResolver;
|
||||
}
|
||||
resolver->addStage(stage);
|
||||
#endif
|
||||
resolver->addStage(stage, intermediate);
|
||||
inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
|
||||
TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
|
||||
*uniformVarMap[stage]);
|
||||
@@ -1547,15 +1573,51 @@ bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
|
||||
TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
|
||||
TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
|
||||
outVarMaps, uniformVarMap, hadError, profile, version);
|
||||
|
||||
TVarLiveVector inVectors[EShLangCount];
|
||||
TVarLiveVector outVectors[EShLangCount];
|
||||
TVarLiveVector uniformVector;
|
||||
|
||||
resolver->beginResolve(EShLangCount);
|
||||
for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
|
||||
if (inVarMaps[stage] != nullptr) {
|
||||
inOutResolve.setStage(EShLanguage(stage));
|
||||
for (auto& var : *(inVarMaps[stage])) { symbolValidater(var); }
|
||||
for (auto& var : *(inVarMaps[stage])) { inOutResolve(var); }
|
||||
for (auto& var : *(outVarMaps[stage])) { symbolValidater(var); }
|
||||
for (auto& var : *(outVarMaps[stage])) { inOutResolve(var); }
|
||||
|
||||
// copy vars into a sorted list
|
||||
std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(),
|
||||
[&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); });
|
||||
std::sort(inVectors[stage].begin(), inVectors[stage].end(),
|
||||
[](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
|
||||
return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
|
||||
});
|
||||
|
||||
std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(),
|
||||
[&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); });
|
||||
std::sort(outVectors[stage].begin(), outVectors[stage].end(),
|
||||
[](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
|
||||
return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
|
||||
});
|
||||
|
||||
for (auto& var : inVectors[stage]) { symbolValidater(var); }
|
||||
for (auto& var : inVectors[stage]) { inOutResolve(var); }
|
||||
for (auto& var : outVectors[stage]) { symbolValidater(var); }
|
||||
for (auto& var : outVectors[stage]) { inOutResolve(var); }
|
||||
|
||||
// copy results back into maps
|
||||
std::for_each(inVectors[stage].begin(), inVectors[stage].end(),
|
||||
[this, stage](TVarLivePair p) {
|
||||
auto at = inVarMaps[stage]->find(p.first);
|
||||
if (at != inVarMaps[stage]->end())
|
||||
at->second = p.second;
|
||||
});
|
||||
|
||||
std::for_each(outVectors[stage].begin(), outVectors[stage].end(),
|
||||
[this, stage](TVarLivePair p) {
|
||||
auto at = outVarMaps[stage]->find(p.first);
|
||||
if (at != outVarMaps[stage]->end())
|
||||
at->second = p.second;
|
||||
});
|
||||
|
||||
}
|
||||
if (uniformVarMap[stage] != nullptr) {
|
||||
uniformResolve.setStage(EShLanguage(stage));
|
||||
@@ -1563,7 +1625,7 @@ bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
|
||||
}
|
||||
}
|
||||
std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
|
||||
return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
|
||||
return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second);
|
||||
});
|
||||
for (auto& var : uniformVector) { symbolValidater(var); }
|
||||
for (auto& var : uniformVector) { uniformResolve(var); }
|
||||
|
||||
@@ -87,6 +87,35 @@ struct TVarEntryInfo {
|
||||
return lPoints > rPoints;
|
||||
}
|
||||
};
|
||||
|
||||
struct TOrderByPriorityAndLive {
|
||||
// ordering:
|
||||
// 1) do live variables first
|
||||
// 2) has both binding and set
|
||||
// 3) has binding but no set
|
||||
// 4) has no binding but set
|
||||
// 5) has no binding and no set
|
||||
inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) {
|
||||
|
||||
const TQualifier& lq = l.symbol->getQualifier();
|
||||
const TQualifier& rq = r.symbol->getQualifier();
|
||||
|
||||
// simple rules:
|
||||
// has binding gives 2 points
|
||||
// has set gives 1 point
|
||||
// who has the most points is more important.
|
||||
int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
|
||||
int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
|
||||
|
||||
if (l.live != r.live)
|
||||
return l.live > r.live;
|
||||
|
||||
if (lPoints != rPoints)
|
||||
return lPoints > rPoints;
|
||||
|
||||
return l.id < r.id;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Base class for shared TIoMapResolver services, used by several derivations.
|
||||
@@ -107,8 +136,8 @@ public:
|
||||
void endCollect(EShLanguage) override {}
|
||||
void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {}
|
||||
void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {}
|
||||
int getBaseBinding(TResourceType res, unsigned int set) const;
|
||||
const std::vector<std::string>& getResourceSetBinding() const;
|
||||
int getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const;
|
||||
const std::vector<std::string>& getResourceSetBinding(EShLanguage stage) const;
|
||||
virtual TResourceType getResourceType(const glslang::TType& type) = 0;
|
||||
bool doAutoBindingMapping() const;
|
||||
bool doAutoLocationMapping() const;
|
||||
@@ -122,9 +151,11 @@ public:
|
||||
int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override;
|
||||
int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
|
||||
int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
|
||||
void addStage(EShLanguage stage) override {
|
||||
if (stage < EShLangCount)
|
||||
void addStage(EShLanguage stage, TIntermediate& stageIntermediate) override {
|
||||
if (stage < EShLangCount) {
|
||||
stageMask[stage] = true;
|
||||
stageIntermediates[stage] = &stageIntermediate;
|
||||
}
|
||||
}
|
||||
uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage);
|
||||
|
||||
@@ -139,6 +170,8 @@ protected:
|
||||
int nextInputLocation;
|
||||
int nextOutputLocation;
|
||||
bool stageMask[EShLangCount + 1];
|
||||
const TIntermediate* stageIntermediates[EShLangCount];
|
||||
|
||||
// Return descriptor set specific base if there is one, and the generic base otherwise.
|
||||
int selectBaseBinding(int base, int descriptorSetBase) const {
|
||||
return descriptorSetBase != -1 ? descriptorSetBase : base;
|
||||
|
||||
@@ -90,6 +90,55 @@ void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit)
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// check that link objects between stages
|
||||
//
|
||||
void TIntermediate::mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit) {
|
||||
if (unit.treeRoot == nullptr || treeRoot == nullptr)
|
||||
return;
|
||||
|
||||
// Get the linker-object lists
|
||||
TIntermSequence& linkerObjects = findLinkerObjects()->getSequence();
|
||||
TIntermSequence unitLinkerObjects = unit.findLinkerObjects()->getSequence();
|
||||
|
||||
// filter unitLinkerObjects to only contain uniforms
|
||||
auto end = std::remove_if(unitLinkerObjects.begin(), unitLinkerObjects.end(),
|
||||
[](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqUniform &&
|
||||
node->getAsSymbolNode()->getQualifier().storage != EvqBuffer; });
|
||||
unitLinkerObjects.resize(end - unitLinkerObjects.begin());
|
||||
|
||||
// merge uniforms and do error checking
|
||||
mergeGlobalUniformBlocks(infoSink, unit);
|
||||
mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage());
|
||||
}
|
||||
|
||||
//
|
||||
// do error checking on the shader boundary in / out vars
|
||||
//
|
||||
void TIntermediate::checkStageIO(TInfoSink& infoSink, TIntermediate& unit) {
|
||||
if (unit.treeRoot == nullptr || treeRoot == nullptr)
|
||||
return;
|
||||
|
||||
// Get copies of the linker-object lists
|
||||
TIntermSequence linkerObjects = findLinkerObjects()->getSequence();
|
||||
TIntermSequence unitLinkerObjects = unit.findLinkerObjects()->getSequence();
|
||||
|
||||
// filter linkerObjects to only contain out variables
|
||||
auto end = std::remove_if(linkerObjects.begin(), linkerObjects.end(),
|
||||
[](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqVaryingOut; });
|
||||
linkerObjects.resize(end - linkerObjects.begin());
|
||||
|
||||
// filter unitLinkerObjects to only contain in variables
|
||||
auto unitEnd = std::remove_if(unitLinkerObjects.begin(), unitLinkerObjects.end(),
|
||||
[](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqVaryingIn; });
|
||||
unitLinkerObjects.resize(unitEnd - unitLinkerObjects.begin());
|
||||
|
||||
// do matching and error checking
|
||||
mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage());
|
||||
|
||||
// TODO: final check; make sure that any statically used `in` have matching `out` written to
|
||||
}
|
||||
|
||||
void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit)
|
||||
{
|
||||
if (unit.getNumEntryPoints() > 0) {
|
||||
@@ -137,6 +186,7 @@ void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit)
|
||||
MERGE_MAX(spvVersion.vulkanGlsl);
|
||||
MERGE_MAX(spvVersion.vulkan);
|
||||
MERGE_MAX(spvVersion.openGl);
|
||||
MERGE_TRUE(spvVersion.vulkanRelaxed);
|
||||
|
||||
numErrors += unit.getNumErrors();
|
||||
// Only one push_constant is allowed, mergeLinkerObjects() will ensure the push_constant
|
||||
@@ -312,7 +362,8 @@ void TIntermediate::mergeTrees(TInfoSink& infoSink, TIntermediate& unit)
|
||||
remapIds(idMaps, idShift + 1, unit);
|
||||
|
||||
mergeBodies(infoSink, globals, unitGlobals);
|
||||
mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects);
|
||||
mergeGlobalUniformBlocks(infoSink, unit);
|
||||
mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage());
|
||||
ioAccessed.insert(unit.ioAccessed.begin(), unit.ioAccessed.end());
|
||||
}
|
||||
|
||||
@@ -456,11 +507,193 @@ void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, c
|
||||
globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1);
|
||||
}
|
||||
|
||||
static inline bool isSameInterface(TIntermSymbol* symbol, EShLanguage stage, TIntermSymbol* unitSymbol, EShLanguage unitStage) {
|
||||
return // 1) same stage and same shader interface
|
||||
(stage == unitStage && symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) ||
|
||||
// 2) accross stages and both are uniform or buffer
|
||||
(symbol->getQualifier().storage == EvqUniform && unitSymbol->getQualifier().storage == EvqUniform) ||
|
||||
(symbol->getQualifier().storage == EvqBuffer && unitSymbol->getQualifier().storage == EvqBuffer) ||
|
||||
// 3) in/out matched across stage boundary
|
||||
(stage < unitStage && symbol->getQualifier().storage == EvqVaryingOut && unitSymbol->getQualifier().storage == EvqVaryingIn) ||
|
||||
(unitStage < stage && symbol->getQualifier().storage == EvqVaryingIn && unitSymbol->getQualifier().storage == EvqVaryingOut);
|
||||
}
|
||||
|
||||
//
|
||||
// Global Unfiform block stores any default uniforms (i.e. uniforms without a block)
|
||||
// If two linked stages declare the same member, they are meant to be the same uniform
|
||||
// and need to be in the same block
|
||||
// merge the members of different stages to allow them to be linked properly
|
||||
// as a single block
|
||||
//
|
||||
void TIntermediate::mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit)
|
||||
{
|
||||
TIntermSequence& linkerObjects = findLinkerObjects()->getSequence();
|
||||
TIntermSequence& unitLinkerObjects = unit.findLinkerObjects()->getSequence();
|
||||
|
||||
// build lists of default blocks from the intermediates
|
||||
TIntermSequence defaultBlocks;
|
||||
TIntermSequence unitDefaultBlocks;
|
||||
|
||||
auto filter = [](TIntermSequence& list, TIntermNode* node) {
|
||||
if (node->getAsSymbolNode()->getQualifier().defaultBlock) {
|
||||
list.push_back(node);
|
||||
}
|
||||
};
|
||||
|
||||
std::for_each(linkerObjects.begin(), linkerObjects.end(),
|
||||
[&defaultBlocks, &filter](TIntermNode* node) {
|
||||
filter(defaultBlocks, node);
|
||||
});
|
||||
std::for_each(unitLinkerObjects.begin(), unitLinkerObjects.end(),
|
||||
[&unitDefaultBlocks, &filter](TIntermNode* node) {
|
||||
filter(unitDefaultBlocks, node);
|
||||
});
|
||||
|
||||
auto itUnitBlock = unitDefaultBlocks.begin();
|
||||
for (; itUnitBlock != unitDefaultBlocks.end(); itUnitBlock++) {
|
||||
|
||||
bool add = true;
|
||||
auto itBlock = defaultBlocks.begin();
|
||||
|
||||
for (; itBlock != defaultBlocks.end(); itBlock++) {
|
||||
TIntermSymbol* block = (*itBlock)->getAsSymbolNode();
|
||||
TIntermSymbol* unitBlock = (*itUnitBlock)->getAsSymbolNode();
|
||||
|
||||
assert(block && unitBlock);
|
||||
|
||||
// if the two default blocks match, then merge their definitions
|
||||
if (block->getType().getTypeName() == unitBlock->getType().getTypeName() &&
|
||||
block->getQualifier().storage == unitBlock->getQualifier().storage) {
|
||||
add = false;
|
||||
mergeBlockDefinitions(infoSink, block, unitBlock, &unit);
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
// push back on original list; won't change the size of the list we're iterating over
|
||||
linkerObjects.push_back(*itUnitBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TIntermediate::mergeBlockDefinitions(TInfoSink& infoSink, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unit) {
|
||||
if (block->getType() == unitBlock->getType()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (block->getType().getTypeName() != unitBlock->getType().getTypeName() ||
|
||||
block->getType().getBasicType() != unitBlock->getType().getBasicType() ||
|
||||
block->getQualifier().storage != unitBlock->getQualifier().storage ||
|
||||
block->getQualifier().layoutSet != unitBlock->getQualifier().layoutSet) {
|
||||
// different block names likely means different blocks
|
||||
return;
|
||||
}
|
||||
|
||||
// merge the struct
|
||||
// order of declarations doesn't matter and they matched based on member name
|
||||
TTypeList* memberList = block->getType().getWritableStruct();
|
||||
TTypeList* unitMemberList = unitBlock->getType().getWritableStruct();
|
||||
|
||||
// keep track of which members have changed position
|
||||
// so we don't have to search the array again
|
||||
std::map<unsigned int, unsigned int> memberIndexUpdates;
|
||||
|
||||
size_t memberListStartSize = memberList->size();
|
||||
for (unsigned int i = 0; i < unitMemberList->size(); ++i) {
|
||||
bool merge = true;
|
||||
for (unsigned int j = 0; j < memberListStartSize; ++j) {
|
||||
if ((*memberList)[j].type->getFieldName() == (*unitMemberList)[i].type->getFieldName()) {
|
||||
merge = false;
|
||||
const TType* memberType = (*memberList)[j].type;
|
||||
const TType* unitMemberType = (*unitMemberList)[i].type;
|
||||
|
||||
// compare types
|
||||
// don't need as many checks as when merging symbols, since
|
||||
// initializers and most qualifiers are stripped when the member is moved into the block
|
||||
if ((*memberType) != (*unitMemberType)) {
|
||||
error(infoSink, "Types must match:");
|
||||
infoSink.info << " " << memberType->getFieldName() << ": ";
|
||||
infoSink.info << "\"" << memberType->getCompleteString() << "\" versus ";
|
||||
infoSink.info << "\"" << unitMemberType->getCompleteString() << "\"\n";
|
||||
}
|
||||
|
||||
memberIndexUpdates[i] = j;
|
||||
}
|
||||
}
|
||||
if (merge) {
|
||||
memberList->push_back((*unitMemberList)[i]);
|
||||
memberIndexUpdates[i] = (unsigned int)memberList->size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
TType unitType;
|
||||
unitType.shallowCopy(unitBlock->getType());
|
||||
|
||||
// update symbol node in unit tree,
|
||||
// and other nodes that may reference it
|
||||
class TMergeBlockTraverser : public TIntermTraverser {
|
||||
public:
|
||||
TMergeBlockTraverser(const glslang::TType &type, const glslang::TType& unitType,
|
||||
glslang::TIntermediate& unit,
|
||||
const std::map<unsigned int, unsigned int>& memberIdxUpdates) :
|
||||
newType(type), unitType(unitType), unit(unit), memberIndexUpdates(memberIdxUpdates)
|
||||
{ }
|
||||
virtual ~TMergeBlockTraverser() { }
|
||||
|
||||
const glslang::TType& newType; // type with modifications
|
||||
const glslang::TType& unitType; // copy of original type
|
||||
glslang::TIntermediate& unit; // intermediate that is being updated
|
||||
const std::map<unsigned int, unsigned int>& memberIndexUpdates;
|
||||
|
||||
virtual void visitSymbol(TIntermSymbol* symbol)
|
||||
{
|
||||
glslang::TType& symType = symbol->getWritableType();
|
||||
|
||||
if (symType == unitType) {
|
||||
// each symbol node has a local copy of the unitType
|
||||
// if merging involves changing properties that aren't shared objects
|
||||
// they should be updated in all instances
|
||||
|
||||
// e.g. the struct list is a ptr to an object, so it can be updated
|
||||
// once, outside the traverser
|
||||
//*symType.getWritableStruct() = *newType.getStruct();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual bool visitBinary(TVisit, glslang::TIntermBinary* node)
|
||||
{
|
||||
if (node->getOp() == EOpIndexDirectStruct && node->getLeft()->getType() == unitType) {
|
||||
// this is a dereference to a member of the block since the
|
||||
// member list changed, need to update this to point to the
|
||||
// right index
|
||||
assert(node->getRight()->getAsConstantUnion());
|
||||
|
||||
glslang::TIntermConstantUnion* constNode = node->getRight()->getAsConstantUnion();
|
||||
unsigned int memberIdx = constNode->getConstArray()[0].getUConst();
|
||||
unsigned int newIdx = memberIndexUpdates.at(memberIdx);
|
||||
TIntermTyped* newConstNode = unit.addConstantUnion(newIdx, node->getRight()->getLoc());
|
||||
|
||||
node->setRight(newConstNode);
|
||||
delete constNode;
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} finalLinkTraverser(block->getType(), unitType, *unit, memberIndexUpdates);
|
||||
|
||||
// update the tree to use the new type
|
||||
unit->getTreeRoot()->traverse(&finalLinkTraverser);
|
||||
|
||||
// update the member list
|
||||
(*unitMemberList) = (*memberList);
|
||||
}
|
||||
|
||||
//
|
||||
// Merge the linker objects from unitLinkerObjects into linkerObjects.
|
||||
// Duplication is expected and filtered out, but contradictions are an error.
|
||||
//
|
||||
void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects)
|
||||
void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage unitStage)
|
||||
{
|
||||
// Error check and merge the linker objects (duplicates should not be created)
|
||||
std::size_t initialNumLinkerObjects = linkerObjects.size();
|
||||
@@ -475,7 +708,7 @@ void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& lin
|
||||
// If they are both blocks in the same shader interface,
|
||||
// match by the block-name, not the identifier name.
|
||||
if (symbol->getType().getBasicType() == EbtBlock && unitSymbol->getType().getBasicType() == EbtBlock) {
|
||||
if (symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) {
|
||||
if (isSameInterface(symbol, getStage(), unitSymbol, unitStage)) {
|
||||
isSameSymbol = symbol->getType().getTypeName() == unitSymbol->getType().getTypeName();
|
||||
}
|
||||
}
|
||||
@@ -495,18 +728,54 @@ void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& lin
|
||||
if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding())
|
||||
symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding;
|
||||
|
||||
// Similarly for location
|
||||
if (!symbol->getQualifier().hasLocation() && unitSymbol->getQualifier().hasLocation()) {
|
||||
symbol->getQualifier().layoutLocation = unitSymbol->getQualifier().layoutLocation;
|
||||
}
|
||||
|
||||
// Update implicit array sizes
|
||||
mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType());
|
||||
|
||||
// Check for consistent types/qualification/initializers etc.
|
||||
mergeErrorCheck(infoSink, *symbol, *unitSymbol, false);
|
||||
mergeErrorCheck(infoSink, *symbol, *unitSymbol, unitStage);
|
||||
}
|
||||
// If different symbols, verify they arn't push_constant since there can only be one per stage
|
||||
else if (symbol->getQualifier().isPushConstant() && unitSymbol->getQualifier().isPushConstant())
|
||||
else if (symbol->getQualifier().isPushConstant() && unitSymbol->getQualifier().isPushConstant() && getStage() == unitStage)
|
||||
error(infoSink, "Only one push_constant block is allowed per stage");
|
||||
}
|
||||
if (merge)
|
||||
if (merge) {
|
||||
linkerObjects.push_back(unitLinkerObjects[unitLinkObj]);
|
||||
|
||||
// for anonymous blocks, check that their members don't conflict with other names
|
||||
if (unitLinkerObjects[unitLinkObj]->getAsSymbolNode()->getBasicType() == EbtBlock &&
|
||||
IsAnonymous(unitLinkerObjects[unitLinkObj]->getAsSymbolNode()->getName())) {
|
||||
for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) {
|
||||
TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode();
|
||||
TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode();
|
||||
assert(symbol && unitSymbol);
|
||||
|
||||
auto checkName = [this, unitSymbol, &infoSink](const TString& name) {
|
||||
for (unsigned int i = 0; i < unitSymbol->getType().getStruct()->size(); ++i) {
|
||||
if (name == (*unitSymbol->getType().getStruct())[i].type->getFieldName()) {
|
||||
error(infoSink, "Anonymous member name used for global variable or other anonymous member: ");
|
||||
infoSink.info << (*unitSymbol->getType().getStruct())[i].type->getCompleteString() << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (isSameInterface(symbol, getStage(), unitSymbol, unitStage)) {
|
||||
checkName(symbol->getName());
|
||||
|
||||
// check members of other anonymous blocks
|
||||
if (symbol->getBasicType() == EbtBlock && IsAnonymous(symbol->getName())) {
|
||||
for (unsigned int i = 0; i < symbol->getType().getStruct()->size(); ++i) {
|
||||
checkName((*symbol->getType().getStruct())[i].type->getFieldName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,26 +807,74 @@ void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType)
|
||||
//
|
||||
// This function only does one of intra- or cross-stage matching per call.
|
||||
//
|
||||
void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage)
|
||||
void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, EShLanguage unitStage)
|
||||
{
|
||||
#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
|
||||
bool crossStage = getStage() != unitStage;
|
||||
bool writeTypeComparison = false;
|
||||
|
||||
// Types have to match
|
||||
if (symbol.getType() != unitSymbol.getType()) {
|
||||
{
|
||||
// but, we make an exception if one is an implicit array and the other is sized
|
||||
if (! (symbol.getType().isArray() && unitSymbol.getType().isArray() &&
|
||||
symbol.getType().sameElementType(unitSymbol.getType()) &&
|
||||
(symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray()))) {
|
||||
error(infoSink, "Types must match:");
|
||||
// or if the array sizes differ because of the extra array dimension on some in/out boundaries
|
||||
bool arraysMatch = false;
|
||||
if (isIoResizeArray(symbol.getType(), getStage()) || isIoResizeArray(unitSymbol.getType(), unitStage)) {
|
||||
// if the arrays have an extra dimension because of the stage.
|
||||
// compare dimensions while ignoring the outer dimension
|
||||
unsigned int firstDim = isIoResizeArray(symbol.getType(), getStage()) ? 1 : 0;
|
||||
unsigned int numDim = symbol.getArraySizes()
|
||||
? symbol.getArraySizes()->getNumDims() : 0;
|
||||
unsigned int unitFirstDim = isIoResizeArray(unitSymbol.getType(), unitStage) ? 1 : 0;
|
||||
unsigned int unitNumDim = unitSymbol.getArraySizes()
|
||||
? unitSymbol.getArraySizes()->getNumDims() : 0;
|
||||
arraysMatch = (numDim - firstDim) == (unitNumDim - unitFirstDim);
|
||||
// check that array sizes match as well
|
||||
for (unsigned int i = 0; i < (numDim - firstDim) && arraysMatch; i++) {
|
||||
if (symbol.getArraySizes()->getDimSize(firstDim + i) !=
|
||||
unitSymbol.getArraySizes()->getDimSize(unitFirstDim + i)) {
|
||||
arraysMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
arraysMatch = symbol.getType().sameArrayness(unitSymbol.getType()) ||
|
||||
(symbol.getType().isArray() && unitSymbol.getType().isArray() &&
|
||||
(symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray()));
|
||||
}
|
||||
|
||||
if (!symbol.getType().sameElementType(unitSymbol.getType()) ||
|
||||
!symbol.getType().sameTypeParameters(unitSymbol.getType()) ||
|
||||
!arraysMatch ) {
|
||||
writeTypeComparison = true;
|
||||
error(infoSink, "Types must match:");
|
||||
}
|
||||
}
|
||||
|
||||
// Interface block member-wise layout qualifiers have to match
|
||||
if (symbol.getType().getBasicType() == EbtBlock && unitSymbol.getType().getBasicType() == EbtBlock &&
|
||||
symbol.getType().getStruct() && unitSymbol.getType().getStruct() &&
|
||||
symbol.getType().sameStructType(unitSymbol.getType())) {
|
||||
for (unsigned int i = 0; i < symbol.getType().getStruct()->size(); ++i) {
|
||||
const TQualifier& qualifier = (*symbol.getType().getStruct())[i].type->getQualifier();
|
||||
const TQualifier& unitQualifier = (*unitSymbol.getType().getStruct())[i].type->getQualifier();
|
||||
if (qualifier.layoutMatrix != unitQualifier.layoutMatrix ||
|
||||
qualifier.layoutOffset != unitQualifier.layoutOffset ||
|
||||
qualifier.layoutAlign != unitQualifier.layoutAlign ||
|
||||
qualifier.layoutLocation != unitQualifier.layoutLocation ||
|
||||
qualifier.layoutComponent != unitQualifier.layoutComponent) {
|
||||
error(infoSink, "Interface block member layout qualifiers must match:");
|
||||
writeTypeComparison = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Qualifiers have to (almost) match
|
||||
|
||||
// Storage...
|
||||
if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) {
|
||||
if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage &&
|
||||
!((crossStage && symbol.getQualifier().storage == EvqVaryingIn && unitSymbol.getQualifier().storage == EvqVaryingOut) ||
|
||||
(crossStage && symbol.getQualifier().storage == EvqVaryingOut && unitSymbol.getQualifier().storage == EvqVaryingIn))) {
|
||||
error(infoSink, "Storage qualifiers must match:");
|
||||
writeTypeComparison = true;
|
||||
}
|
||||
@@ -597,12 +914,16 @@ void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& sy
|
||||
}
|
||||
|
||||
// Auxiliary and interpolation...
|
||||
if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid ||
|
||||
// "interpolation qualification (e.g., flat) and auxiliary qualification (e.g. centroid) may differ.
|
||||
// These mismatches are allowed between any pair of stages ...
|
||||
// those provided in the fragment shader supersede those provided in previous stages."
|
||||
if (!crossStage &&
|
||||
(symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid ||
|
||||
symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth ||
|
||||
symbol.getQualifier().flat != unitSymbol.getQualifier().flat ||
|
||||
symbol.getQualifier().isSample()!= unitSymbol.getQualifier().isSample() ||
|
||||
symbol.getQualifier().isPatch() != unitSymbol.getQualifier().isPatch() ||
|
||||
symbol.getQualifier().isNonPerspective() != unitSymbol.getQualifier().isNonPerspective()) {
|
||||
symbol.getQualifier().isNonPerspective() != unitSymbol.getQualifier().isNonPerspective())) {
|
||||
error(infoSink, "Interpolation and auxiliary storage qualifiers must match:");
|
||||
writeTypeComparison = true;
|
||||
}
|
||||
@@ -1830,4 +2151,17 @@ int TIntermediate::computeBufferReferenceTypeSize(const TType& type)
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifndef GLSLANG_WEB
|
||||
bool TIntermediate::isIoResizeArray(const TType& type, EShLanguage language) {
|
||||
return type.isArray() &&
|
||||
((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) ||
|
||||
(language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut &&
|
||||
! type.getQualifier().patch) ||
|
||||
(language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn &&
|
||||
type.getQualifier().pervertexNV) ||
|
||||
(language == EShLangMeshNV && type.getQualifier().storage == EvqVaryingOut &&
|
||||
!type.getQualifier().perTaskNV));
|
||||
}
|
||||
#endif // not GLSLANG_WEB
|
||||
|
||||
} // end namespace glslang
|
||||
|
||||
@@ -293,7 +293,12 @@ public:
|
||||
useStorageBuffer(false),
|
||||
nanMinMaxClamp(false),
|
||||
depthReplacing(false),
|
||||
uniqueId(0)
|
||||
uniqueId(0),
|
||||
globalUniformBlockName(""),
|
||||
atomicCounterBlockName(""),
|
||||
globalUniformBlockSet(TQualifier::layoutSetEnd),
|
||||
globalUniformBlockBinding(TQualifier::layoutBindingEnd),
|
||||
atomicCounterBlockSet(TQualifier::layoutSetEnd)
|
||||
#ifndef GLSLANG_WEB
|
||||
,
|
||||
implicitThisName("@this"), implicitCounterName("@count"),
|
||||
@@ -537,6 +542,19 @@ public:
|
||||
void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&);
|
||||
TIntermAggregate* findLinkerObjects() const;
|
||||
|
||||
void setGlobalUniformBlockName(const char* name) { globalUniformBlockName = std::string(name); }
|
||||
const char* getGlobalUniformBlockName() const { return globalUniformBlockName.c_str(); }
|
||||
void setGlobalUniformSet(unsigned int set) { globalUniformBlockSet = set; }
|
||||
unsigned int getGlobalUniformSet() const { return globalUniformBlockSet; }
|
||||
void setGlobalUniformBinding(unsigned int binding) { globalUniformBlockBinding = binding; }
|
||||
unsigned int getGlobalUniformBinding() const { return globalUniformBlockBinding; }
|
||||
|
||||
void setAtomicCounterBlockName(const char* name) { atomicCounterBlockName = std::string(name); }
|
||||
const char* getAtomicCounterBlockName() const { return atomicCounterBlockName.c_str(); }
|
||||
void setAtomicCounterBlockSet(unsigned int set) { atomicCounterBlockSet = set; }
|
||||
unsigned int getAtomicCounterBlockSet() const { return atomicCounterBlockSet; }
|
||||
|
||||
|
||||
void setUseStorageBuffer() { useStorageBuffer = true; }
|
||||
bool usingStorageBuffer() const { return useStorageBuffer; }
|
||||
void setDepthReplacing() { depthReplacing = true; }
|
||||
@@ -848,6 +866,20 @@ public:
|
||||
bool getBinaryDoubleOutput() { return binaryDoubleOutput; }
|
||||
#endif // GLSLANG_WEB
|
||||
|
||||
void addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing)
|
||||
{
|
||||
std::string name(nameStr);
|
||||
blockBackingOverrides[name] = backing;
|
||||
}
|
||||
TBlockStorageClass getBlockStorageOverride(const char* nameStr) const
|
||||
{
|
||||
std::string name = nameStr;
|
||||
auto pos = blockBackingOverrides.find(name);
|
||||
if (pos == blockBackingOverrides.end())
|
||||
return EbsNone;
|
||||
else
|
||||
return pos->second;
|
||||
}
|
||||
#ifdef ENABLE_HLSL
|
||||
void setHlslFunctionality1() { hlslFunctionality1 = true; }
|
||||
bool getHlslFunctionality1() const { return hlslFunctionality1; }
|
||||
@@ -883,6 +915,10 @@ public:
|
||||
void merge(TInfoSink&, TIntermediate&);
|
||||
void finalCheck(TInfoSink&, bool keepUncalled);
|
||||
|
||||
void mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit);
|
||||
void mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit);
|
||||
void checkStageIO(TInfoSink&, TIntermediate&);
|
||||
|
||||
bool buildConvertOp(TBasicType dst, TBasicType src, TOperator& convertOp) const;
|
||||
TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const;
|
||||
|
||||
@@ -906,6 +942,8 @@ public:
|
||||
static int getOffset(const TType& type, int index);
|
||||
static int getBlockSize(const TType& blockType);
|
||||
static int computeBufferReferenceTypeSize(const TType&);
|
||||
static bool isIoResizeArray(const TType& type, EShLanguage language);
|
||||
|
||||
bool promote(TIntermOperator*);
|
||||
void setNanMinMaxClamp(bool setting) { nanMinMaxClamp = setting; }
|
||||
bool getNanMinMaxClamp() const { return nanMinMaxClamp; }
|
||||
@@ -963,9 +1001,10 @@ protected:
|
||||
void seedIdMap(TIdMaps& idMaps, long long& IdShift);
|
||||
void remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate&);
|
||||
void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals);
|
||||
void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects);
|
||||
void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage);
|
||||
void mergeBlockDefinitions(TInfoSink&, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unitRoot);
|
||||
void mergeImplicitArraySizes(TType&, const TType&);
|
||||
void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage);
|
||||
void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, EShLanguage);
|
||||
void checkCallGraphCycles(TInfoSink&);
|
||||
void checkCallGraphBodies(TInfoSink&, bool keepUncalled);
|
||||
void inOutLocationCheck(TInfoSink&);
|
||||
@@ -1015,6 +1054,13 @@ protected:
|
||||
bool localSizeNotDefault[3];
|
||||
int localSizeSpecId[3];
|
||||
unsigned long long uniqueId;
|
||||
|
||||
std::string globalUniformBlockName;
|
||||
std::string atomicCounterBlockName;
|
||||
unsigned int globalUniformBlockSet;
|
||||
unsigned int globalUniformBlockBinding;
|
||||
unsigned int atomicCounterBlockSet;
|
||||
|
||||
#ifndef GLSLANG_WEB
|
||||
public:
|
||||
const char* const implicitThisName;
|
||||
@@ -1075,6 +1121,7 @@ protected:
|
||||
int uniformLocationBase;
|
||||
TNumericFeatures numericFeatures;
|
||||
#endif
|
||||
std::unordered_map<std::string, TBlockStorageClass> blockBackingOverrides;
|
||||
|
||||
std::unordered_set<int> usedConstantId; // specialization constant ids used
|
||||
std::vector<TOffsetRange> usedAtomics; // sets of bindings used by atomic counters
|
||||
|
||||
@@ -187,6 +187,7 @@ struct TInputLanguage {
|
||||
EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone
|
||||
EShClient dialect;
|
||||
int dialectVersion; // version of client's language definition, not the client (when not EShClientNone)
|
||||
bool VulkanRulesRelaxed = false;
|
||||
};
|
||||
|
||||
struct TClient {
|
||||
@@ -427,6 +428,14 @@ enum TResourceType {
|
||||
EResCount
|
||||
};
|
||||
|
||||
enum TBlockStorageClass
|
||||
{
|
||||
EbsUniform = 0,
|
||||
EbsStorageBuffer,
|
||||
EbsPushConstant,
|
||||
EbsNone, // not a uniform or buffer variable
|
||||
EbsCount,
|
||||
};
|
||||
|
||||
// Make one TShader per shader that you will link into a program. Then
|
||||
// - provide the shader through setStrings() or setStringsWithLengths()
|
||||
@@ -483,6 +492,14 @@ public:
|
||||
GLSLANG_EXPORT void setNoStorageFormat(bool useUnknownFormat);
|
||||
GLSLANG_EXPORT void setNanMinMaxClamp(bool nanMinMaxClamp);
|
||||
GLSLANG_EXPORT void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode);
|
||||
GLSLANG_EXPORT void addBlockStorageOverride(const char* nameStr, glslang::TBlockStorageClass backing);
|
||||
|
||||
GLSLANG_EXPORT void setGlobalUniformBlockName(const char* name);
|
||||
GLSLANG_EXPORT void setAtomicCounterBlockName(const char* name);
|
||||
GLSLANG_EXPORT void setGlobalUniformSet(unsigned int set);
|
||||
GLSLANG_EXPORT void setGlobalUniformBinding(unsigned int binding);
|
||||
GLSLANG_EXPORT void setAtomicCounterBlockSet(unsigned int set);
|
||||
GLSLANG_EXPORT void setAtomicCounterBlockBinding(unsigned int binding);
|
||||
|
||||
// For setting up the environment (cleared to nothingness in the constructor).
|
||||
// These must be called so that parsing is done for the right source language and
|
||||
@@ -539,6 +556,9 @@ public:
|
||||
bool getEnvTargetHlslFunctionality1() const { return false; }
|
||||
#endif
|
||||
|
||||
void setEnvInputVulkanRulesRelaxed() { environment.input.VulkanRulesRelaxed = true; }
|
||||
bool getEnvInputVulkanRulesRelaxed() const { return environment.input.VulkanRulesRelaxed; }
|
||||
|
||||
// Interface to #include handlers.
|
||||
//
|
||||
// To support #include, a client of Glslang does the following:
|
||||
@@ -806,7 +826,7 @@ public:
|
||||
// Called by TSlotCollector to resolve resource locations or bindings
|
||||
virtual void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0;
|
||||
// Called by mapIO.addStage to set shader stage mask to mark a stage be added to this pipeline
|
||||
virtual void addStage(EShLanguage stage) = 0;
|
||||
virtual void addStage(EShLanguage stage, TIntermediate& stageIntermediate) = 0;
|
||||
};
|
||||
|
||||
#endif // !GLSLANG_WEB && !GLSLANG_ANGLE
|
||||
@@ -928,6 +948,7 @@ public:
|
||||
|
||||
protected:
|
||||
GLSLANG_EXPORT bool linkStage(EShLanguage, EShMessages);
|
||||
GLSLANG_EXPORT bool crossStageCheck(EShMessages);
|
||||
|
||||
TPoolAllocator* pool;
|
||||
std::list<TShader*> stages[EShLangCount];
|
||||
|
||||
Reference in New Issue
Block a user