int resolveInOutLocation(EShLanguage /*stage*/, const char* /*name*/, const TType& type, bool /*is_live*/) override { // kick out of not doing this if (!doAutoLocationMapping) return -1; // no locations added if already present, or a built-in variable if (type.getQualifier().hasLocation() || type.isBuiltIn()) return -1; // no locations on blocks of built-in variables if (type.isStruct()) { if (type.getStruct()->size() < 1) return -1; if ((*type.getStruct())[0].type->isBuiltIn()) return -1; } // Placeholder. // TODO: It would be nice to flesh this out using // intermediate->computeTypeLocationSize(type), or functions that call it like // intermediate->addUsedLocation() // These in turn would want the intermediate, which is not available here, but // is available in many places, and a lot of copying from it could be saved if // it were just available. return 0; }
void TOutputGLSLBase::writeVariableType(const TType &type) { TInfoSinkBase &out = objSink(); TQualifier qualifier = type.getQualifier(); if (qualifier != EvqTemporary && qualifier != EvqGlobal) { out << type.getQualifierString() << " "; } // Declare the struct if we have not done so already. if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct())) { TStructure *structure = type.getStruct(); declareStruct(structure); if (!structure->name().empty()) { mDeclaredStructs.insert(structure->uniqueId()); } } else { if (writeVariablePrecision(type.getPrecision())) out << " "; out << getTypeName(type); } }
void TOutputGLSLBase::writeVariableType(const TType &type) { TInfoSinkBase &out = objSink(); if (type.isInvariant()) { out << "invariant "; } TQualifier qualifier = type.getQualifier(); if (qualifier != EvqTemporary && qualifier != EvqGlobal) { if (IsGLSL130OrNewer(mOutput)) { switch (qualifier) { case EvqAttribute: out << "in "; break; case EvqVaryingIn: out << "in "; break; case EvqVaryingOut: out << "out "; break; default: out << type.getQualifierString() << " "; break; } } else { out << type.getQualifierString() << " "; } } // Declare the struct if we have not done so already. if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct())) { TStructure *structure = type.getStruct(); declareStruct(structure); if (!structure->name().empty()) { mDeclaredStructs.insert(structure->uniqueId()); } } else { if (writeVariablePrecision(type.getPrecision())) out << " "; out << getTypeName(type); } }
// Recursively merge the implicit array sizes through the objects' respective type trees. void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) { if (type.isImplicitlySizedArray() && unitType.isArray()) { int newImplicitArraySize = unitType.isImplicitlySizedArray() ? unitType.getImplicitArraySize() : unitType.getOuterArraySize(); if (newImplicitArraySize > type.getImplicitArraySize ()) type.setImplicitArraySize(newImplicitArraySize); } // Type mismatches are caught and reported after this, just be careful for now. if (! type.isStruct() || ! unitType.isStruct() || type.getStruct()->size() != unitType.getStruct()->size()) return; for (int i = 0; i < (int)type.getStruct()->size(); ++i) mergeImplicitArraySizes(*(*type.getStruct())[i].type, *(*unitType.getStruct())[i].type); }
TString TOutputGLSLBase::getTypeName(const TType &type) { TInfoSinkBase out; if (type.isMatrix()) { out << "mat"; out << type.getNominalSize(); } else if (type.isVector()) { switch (type.getBasicType()) { case EbtFloat: out << "vec"; break; case EbtInt: out << "ivec"; break; case EbtBool: out << "bvec"; break; default: UNREACHABLE(); } out << type.getNominalSize(); } else { if (type.getBasicType() == EbtStruct) out << hashName(type.getStruct()->name()); else out << type.getBasicString(); } return TString(out.c_str()); }
bool OutputSpecification(TIntermSpecification* node, TIntermTraverser* it) { TOutputTraverser* oit = static_cast<TOutputTraverser*>(it); TInfoSink& out = oit->infoSink; OutputExtensionText(out, node); OutputTreeText(out, node, oit->depth); TType* t = node->getType(); out.debug << "specify '" << t->getTypeName().c_str() << "' (" << t->getCompleteString() << ")\n"; return true; #if 0 TTypeList* tl = t->getStruct(); TTypeList::iterator iter = tl->begin(); for(; iter < tl->end(); iter++) { out.debug << FormatSourceRange(iter->line); for (i = 0; i < (oit->depth+1); ++i) out.debug << " "; out.debug << "'" << iter->type->getFieldName().c_str() << "' (" << iter->type->getCompleteString().c_str() << ")\n"; } #endif }
TString TOutputGLSLBase::getTypeName(const TType &type) { if (type.getBasicType() == EbtStruct) return hashName(type.getStruct()->name()); else return type.getBuiltInTypeNameString(); }
void GetVariableTraverser::traverse(const TType &type, const TString &name, std::vector<VarT> *output) { const TStructure *structure = type.getStruct(); VarT variable; variable.name = name.c_str(); variable.arraySize = static_cast<unsigned int>(type.getArraySize()); if (!structure) { variable.type = GLVariableType(type); variable.precision = GLVariablePrecision(type); } else { // Note: this enum value is not exposed outside ANGLE variable.type = GL_STRUCT_ANGLEX; variable.structName = structure->name().c_str(); const TFieldList &fields = structure->fields(); for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) { TField *field = fields[fieldIndex]; traverse(*field->type(), field->name(), &variable.fields); } } visitVariable(&variable); ASSERT(output); output->push_back(variable); }
void TOutputGLSLBase::writeVariableType(const TType& type) { TInfoSinkBase& out = objSink(); TQualifier qualifier = type.getQualifier(); // TODO(alokp): Validate qualifier for variable declarations. if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal)) out << type.getQualifierString() << " "; // Declare the struct if we have not done so already. if ((type.getBasicType() == EbtStruct) && (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end())) { out << "struct " << type.getTypeName() << "{\n"; const TTypeList* structure = type.getStruct(); ASSERT(structure != NULL); for (size_t i = 0; i < structure->size(); ++i) { const TType* fieldType = (*structure)[i].type; ASSERT(fieldType != NULL); if (writeVariablePrecision(fieldType->getPrecision())) out << " "; out << getTypeName(*fieldType) << " " << fieldType->getFieldName(); if (fieldType->isArray()) out << arrayBrackets(*fieldType); out << ";\n"; } out << "}"; mDeclaredStructs.insert(type.getTypeName()); } else { if (writeVariablePrecision(type.getPrecision())) out << " "; out << getTypeName(type); } }
// Recursively figure out how many bytes of xfb buffer are used by the given type. // Return the size of type, in bytes. // Sets containsDouble to true if the type contains a double. // N.B. Caller must set containsDouble to false before calling. unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& containsDouble) const { // "...if applied to an aggregate containing a double, the offset must also be a multiple of 8, // and the space taken in the buffer will be a multiple of 8. // ...within the qualified entity, subsequent components are each // assigned, in order, to the next available offset aligned to a multiple of // that component's size. Aggregate types are flattened down to the component // level to get this sequence of components." if (type.isArray()) { // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness assert(type.isExplicitlySizedArray()); TType elementType(type, 0); return type.getOuterArraySize() * computeTypeXfbSize(elementType, containsDouble); } if (type.isStruct()) { unsigned int size = 0; bool structContainsDouble = false; for (int member = 0; member < (int)type.getStruct()->size(); ++member) { TType memberType(type, member); // "... if applied to // an aggregate containing a double, the offset must also be a multiple of 8, // and the space taken in the buffer will be a multiple of 8." bool memberContainsDouble = false; int memberSize = computeTypeXfbSize(memberType, memberContainsDouble); if (memberContainsDouble) { structContainsDouble = true; RoundToPow2(size, 8); } size += memberSize; } if (structContainsDouble) { containsDouble = true; RoundToPow2(size, 8); } return size; } int numComponents; if (type.isScalar()) numComponents = 1; else if (type.isVector()) numComponents = type.getVectorSize(); else if (type.isMatrix()) numComponents = type.getMatrixCols() * type.getMatrixRows(); else { assert(0); numComponents = 1; } if (type.getBasicType() == EbtDouble) { containsDouble = true; return 8 * numComponents; } else return 4 * numComponents; }
void TOutputGLSLBase::writeVariableType(const TType& type) { TInfoSinkBase& out = objSink(); TQualifier qualifier = type.getQualifier(); // TODO(alokp): Validate qualifier for variable declarations. if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal)) out << type.getQualifierString() << " "; // Declare the struct if we have not done so already. if ((type.getBasicType() == EbtStruct) && !structDeclared(type.getStruct())) { declareStruct(type.getStruct()); } else { if (writeVariablePrecision(type.getPrecision())) out << " "; out << getTypeName(type); } }
// Recursively figure out how many locations are used up by an input or output type. // Return the size of type, as measured by "locations". int TIntermediate::computeTypeLocationSize(const TType& type) const { // "If the declared input is an array of size n and each element takes m locations, it will be assigned m * n // consecutive locations..." if (type.isArray()) { // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness TType elementType(type, 0); if (type.isImplicitlySizedArray()) { // TODO: are there valid cases of having an implicitly-sized array with a location? If so, running this code too early. return computeTypeLocationSize(elementType); } else return type.getOuterArraySize() * computeTypeLocationSize(elementType); } // "The locations consumed by block and structure members are determined by applying the rules above // recursively..." if (type.isStruct()) { int size = 0; for (int member = 0; member < (int)type.getStruct()->size(); ++member) { TType memberType(type, member); size += computeTypeLocationSize(memberType); } return size; } // ES: "If a shader input is any scalar or vector type, it will consume a single location." // Desktop: "If a vertex shader input is any scalar or vector type, it will consume a single location. If a non-vertex // shader input is a scalar or vector type other than dvec3 or dvec4, it will consume a single location, while // types dvec3 or dvec4 will consume two consecutive locations. Inputs of type double and dvec2 will // consume only a single location, in all stages." if (type.isScalar()) return 1; if (type.isVector()) { if (language == EShLangVertex && type.getQualifier().isPipeInput()) return 1; if (type.getBasicType() == EbtDouble && type.getVectorSize() > 2) return 2; else return 1; } // "If the declared input is an n x m single- or double-precision matrix, ... // The number of locations assigned for each matrix will be the same as // for an n-element array of m-component vectors..." if (type.isMatrix()) { TType columnType(type, 0); return type.getMatrixCols() * computeTypeLocationSize(columnType); } assert(0); return 1; }
void RegenerateStructNames::visitSymbol(TIntermSymbol *symbol) { ASSERT(symbol); TType *type = symbol->getTypePointer(); ASSERT(type); TStructure *userType = type->getStruct(); if (!userType) return; if (mSymbolTable.findBuiltIn(userType->name(), mShaderVersion)) { // Built-in struct, do not touch it. return; } int uniqueId = userType->uniqueId(); ASSERT(mScopeDepth > 0); if (mScopeDepth == 1) { // If a struct is defined at global scope, we don't map its name. // This is because at global level, the struct might be used to // declare a uniform, so the same name needs to stay the same for // vertex/fragment shaders. However, our mapping uses internal ID, // which will be different for the same struct in vertex/fragment // shaders. // This is OK because names for any structs defined in other scopes // will begin with "_webgl", which is reserved. So there will be // no conflicts among unmapped struct names from global scope and // mapped struct names from other scopes. // However, we need to keep track of these global structs, so if a // variable is used in a local scope, we don't try to modify the // struct name through that variable. mDeclaredGlobalStructs.insert(uniqueId); return; } if (mDeclaredGlobalStructs.count(uniqueId) > 0) return; // Map {name} to _webgl_struct_{uniqueId}_{name}. const char kPrefix[] = "_webgl_struct_"; if (userType->name().find(kPrefix) == 0) { // The name has already been regenerated. return; } std::string id = Str(uniqueId); TString tmp = kPrefix + TString(id.c_str()); tmp += "_" + userType->name(); userType->setName(tmp); }
int resolveInOutLocation(EShLanguage stage, const char* /*name*/, const TType& type, bool /*is_live*/) override { // kick out of not doing this if (!doAutoLocationMapping()) return -1; // no locations added if already present, or a built-in variable if (type.getQualifier().hasLocation() || type.isBuiltIn()) return -1; // no locations on blocks of built-in variables if (type.isStruct()) { if (type.getStruct()->size() < 1) return -1; if ((*type.getStruct())[0].type->isBuiltIn()) return -1; } // point to the right input or output location counter int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation; // Placeholder. This does not do proper cross-stage lining up, nor // work with mixed location/no-location declarations. int location = nextLocation; int typeLocationSize; // Don’t take into account the outer-most array if the stage’s // interface is automatically an array. if (type.getQualifier().isArrayedIo(stage)) { TType elementType(type, 0); typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage); } else { typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage); } nextLocation += typeLocationSize; return location; }
bool TParseContext::containsSampler(TType& type) { if (IsSampler(type.getBasicType())) return true; if (type.getBasicType() == EbtStruct) { TTypeList& structure = *type.getStruct(); for (unsigned int i = 0; i < structure.size(); ++i) { if (containsSampler(*structure[i].type)) return true; } } return false; }
TString Std140PaddingHelper::postPaddingString(const TType &type, bool useHLSLRowMajorPacking) { if (!type.isMatrix() && !type.isArray() && type.getBasicType() != EbtStruct) { return ""; } int numComponents = 0; TStructure *structure = type.getStruct(); if (type.isMatrix()) { // This method can also be called from structureString, which does not use layout qualifiers. // Thus, use the method parameter for determining the matrix packing. // // Note HLSL row major packing corresponds to GL API column-major, and vice-versa, since we // wish to always transpose GL matrices to play well with HLSL's matrix array indexing. // const bool isRowMajorMatrix = !useHLSLRowMajorPacking; const GLenum glType = GLVariableType(type); numComponents = gl::MatrixComponentCount(glType, isRowMajorMatrix); } else if (structure) { const TString &structName = QualifiedStructNameString(*structure, useHLSLRowMajorPacking, true); numComponents = mStructElementIndexes->find(structName)->second; if (numComponents == 0) { return ""; } } else { const GLenum glType = GLVariableType(type); numComponents = gl::VariableComponentCount(glType); } TString padding; for (int paddingOffset = numComponents; paddingOffset < 4; paddingOffset++) { padding += " float pad_" + next() + ";\n"; } return padding; }
bool CompareStruct(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray) { const TFieldList& fields = leftNodeType.getStruct()->fields(); size_t structSize = fields.size(); size_t index = 0; for (size_t j = 0; j < structSize; j++) { size_t size = fields[j]->type()->getObjectSize(); for (size_t i = 0; i < size; i++) { if (fields[j]->type()->getBasicType() == EbtStruct) { if (!CompareStructure(*(fields[j]->type()), &rightUnionArray[index], &leftUnionArray[index])) return false; } else { if (leftUnionArray[index] != rightUnionArray[index]) return false; index++; } } } return true; }
bool CompareStruct(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray) { const TTypeList* fields = leftNodeType.getStruct(); size_t structSize = fields->size(); int index = 0; for (size_t j = 0; j < structSize; j++) { int size = (*fields)[j].type->getObjectSize(); for (int i = 0; i < size; i++) { if ((*fields)[j].type->getBasicType() == EbtStruct) { if (!CompareStructure(*(*fields)[j].type, &rightUnionArray[index], &leftUnionArray[index])) return false; } else { if (leftUnionArray[index] != rightUnionArray[index]) return false; index++; } } } return true; }
void UniformHLSL::outputUniform(TInfoSinkBase &out, const TType &type, const TName &name, const unsigned int registerIndex) { const TStructure *structure = type.getStruct(); // If this is a nameless struct, we need to use its full definition, rather than its (empty) // name. // TypeString() will invoke defineNameless in this case; qualifier prefixes are unnecessary for // nameless structs in ES, as nameless structs cannot be used anywhere that layout qualifiers // are permitted. const TString &typeName = ((structure && !structure->name().empty()) ? QualifiedStructNameString(*structure, false, false) : TypeString(type)); const TString ®isterString = TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")"; out << "uniform " << typeName << " "; out << DecorateUniform(name, type); out << ArrayString(type) << " : " << registerString << ";\n"; }
void StructureHLSL::addConstructor(const TType &type, const TString &name, const TIntermSequence *parameters) { if (name == "") { return; // Nameless structures don't have constructors } if (type.getStruct() && mStructNames.find(name) != mStructNames.end()) { return; // Already added } TType ctorType = type; ctorType.clearArrayness(); ctorType.setPrecision(EbpHigh); ctorType.setQualifier(EvqTemporary); typedef std::vector<TType> ParameterArray; ParameterArray ctorParameters; const TStructure* structure = type.getStruct(); if (structure) { mStructNames.insert(name); // Add element index storeStd140ElementIndex(*structure, false); storeStd140ElementIndex(*structure, true); const TString &structString = defineQualified(*structure, false, false); if (std::find(mStructDeclarations.begin(), mStructDeclarations.end(), structString) == mStructDeclarations.end()) { // Add row-major packed struct for interface blocks TString rowMajorString = "#pragma pack_matrix(row_major)\n" + defineQualified(*structure, true, false) + "#pragma pack_matrix(column_major)\n"; TString std140String = defineQualified(*structure, false, true); TString std140RowMajorString = "#pragma pack_matrix(row_major)\n" + defineQualified(*structure, true, true) + "#pragma pack_matrix(column_major)\n"; mStructDeclarations.push_back(structString); mStructDeclarations.push_back(rowMajorString); mStructDeclarations.push_back(std140String); mStructDeclarations.push_back(std140RowMajorString); } const TFieldList &fields = structure->fields(); for (unsigned int i = 0; i < fields.size(); i++) { ctorParameters.push_back(*fields[i]->type()); } } else if (parameters) { for (TIntermSequence::const_iterator parameter = parameters->begin(); parameter != parameters->end(); parameter++) { ctorParameters.push_back((*parameter)->getAsTyped()->getType()); } } else UNREACHABLE(); TString constructor; if (ctorType.getStruct()) { constructor += name + " " + name + "_ctor("; } else // Built-in type { constructor += TypeString(ctorType) + " " + name + "("; } for (unsigned int parameter = 0; parameter < ctorParameters.size(); parameter++) { const TType ¶mType = ctorParameters[parameter]; constructor += TypeString(paramType) + " x" + str(parameter) + ArrayString(paramType); if (parameter < ctorParameters.size() - 1) { constructor += ", "; } } constructor += ")\n" "{\n"; if (ctorType.getStruct()) { constructor += " " + name + " structure = {"; } else { constructor += " return " + TypeString(ctorType) + "("; } if (ctorType.isMatrix() && ctorParameters.size() == 1) { int rows = ctorType.getRows(); int cols = ctorType.getCols(); const TType ¶meter = ctorParameters[0]; if (parameter.isScalar()) { for (int col = 0; col < cols; col++) { for (int row = 0; row < rows; row++) { constructor += TString((row == col) ? "x0" : "0.0"); if (row < rows - 1 || col < cols - 1) { constructor += ", "; } } } } else if (parameter.isMatrix()) { for (int col = 0; col < cols; col++) { for (int row = 0; row < rows; row++) { if (row < parameter.getRows() && col < parameter.getCols()) { constructor += TString("x0") + "[" + str(col) + "][" + str(row) + "]"; } else { constructor += TString((row == col) ? "1.0" : "0.0"); } if (row < rows - 1 || col < cols - 1) { constructor += ", "; } } } } else { ASSERT(rows == 2 && cols == 2 && parameter.isVector() && parameter.getNominalSize() == 4); constructor += "x0"; } } else { size_t remainingComponents = ctorType.getObjectSize(); size_t parameterIndex = 0; while (remainingComponents > 0) { const TType ¶meter = ctorParameters[parameterIndex]; const size_t parameterSize = parameter.getObjectSize(); bool moreParameters = parameterIndex + 1 < ctorParameters.size(); constructor += "x" + str(parameterIndex); if (ctorType.getStruct()) { ASSERT(remainingComponents == parameterSize || moreParameters); ASSERT(parameterSize <= remainingComponents); remainingComponents -= parameterSize; } else if (parameter.isScalar()) { remainingComponents -= parameter.getObjectSize(); } else if (parameter.isVector()) { if (remainingComponents == parameterSize || moreParameters) { ASSERT(parameterSize <= remainingComponents); remainingComponents -= parameterSize; } else if (remainingComponents < static_cast<size_t>(parameter.getNominalSize())) { switch (remainingComponents) { case 1: constructor += ".x"; break; case 2: constructor += ".xy"; break; case 3: constructor += ".xyz"; break; case 4: constructor += ".xyzw"; break; default: UNREACHABLE(); } remainingComponents = 0; } else UNREACHABLE(); } else if (parameter.isMatrix()) { int column = 0; while (remainingComponents > 0 && column < parameter.getCols()) { constructor += "[" + str(column) + "]"; if (remainingComponents < static_cast<size_t>(parameter.getRows())) { switch (remainingComponents) { case 1: constructor += ".x"; break; case 2: constructor += ".xy"; break; case 3: constructor += ".xyz"; break; default: UNREACHABLE(); } remainingComponents = 0; } else { remainingComponents -= parameter.getRows(); if (remainingComponents > 0) { constructor += ", x" + str(parameterIndex); } } column++; } } else UNREACHABLE(); if (moreParameters) { parameterIndex++; } if (remainingComponents) { constructor += ", "; } } } if (ctorType.getStruct()) { constructor += "};\n" " return structure;\n" "}\n"; } else { constructor += ");\n" "}\n"; } mConstructors.insert(constructor); }
TString TypeString(const TType &type) { const TStructure *structure = type.getStruct(); if (structure) { if (structure->symbolType() != SymbolType::Empty) { return StructNameString(*structure); } else // Nameless structure, define in place { return StructureHLSL::defineNameless(*structure); } } else if (type.isMatrix()) { int cols = type.getCols(); int rows = type.getRows(); return "float" + str(cols) + "x" + str(rows); } else { switch (type.getBasicType()) { case EbtFloat: switch (type.getNominalSize()) { case 1: return "float"; case 2: return "float2"; case 3: return "float3"; case 4: return "float4"; } case EbtInt: switch (type.getNominalSize()) { case 1: return "int"; case 2: return "int2"; case 3: return "int3"; case 4: return "int4"; } case EbtUInt: switch (type.getNominalSize()) { case 1: return "uint"; case 2: return "uint2"; case 3: return "uint3"; case 4: return "uint4"; } case EbtBool: switch (type.getNominalSize()) { case 1: return "bool"; case 2: return "bool2"; case 3: return "bool3"; case 4: return "bool4"; } case EbtVoid: return "void"; case EbtSampler2D: case EbtISampler2D: case EbtUSampler2D: case EbtSampler2DArray: case EbtISampler2DArray: case EbtUSampler2DArray: return "sampler2D"; case EbtSamplerCube: case EbtISamplerCube: case EbtUSamplerCube: return "samplerCUBE"; case EbtSamplerExternalOES: return "sampler2D"; case EbtAtomicCounter: // Multiple atomic_uints will be implemented as a single RWByteAddressBuffer return "RWByteAddressBuffer"; default: break; } } UNREACHABLE(); return "<unknown type>"; }
TString TypeString(const TType &type) { const TStructure* structure = type.getStruct(); if (structure) { const TString& typeName = structure->name(); if (typeName != "") { return StructNameString(*structure); } else // Nameless structure, define in place { return StructureHLSL::defineNameless(*structure); } } else if (type.isMatrix()) { int cols = type.getCols(); int rows = type.getRows(); return "float" + str(cols) + "x" + str(rows); } else { switch (type.getBasicType()) { case EbtFloat: switch (type.getNominalSize()) { case 1: return "float"; case 2: return "float2"; case 3: return "float3"; case 4: return "float4"; } case EbtInt: switch (type.getNominalSize()) { case 1: return "int"; case 2: return "int2"; case 3: return "int3"; case 4: return "int4"; } case EbtUInt: switch (type.getNominalSize()) { case 1: return "uint"; case 2: return "uint2"; case 3: return "uint3"; case 4: return "uint4"; } case EbtBool: switch (type.getNominalSize()) { case 1: return "bool"; case 2: return "bool2"; case 3: return "bool3"; case 4: return "bool4"; } case EbtVoid: return "void"; case EbtSampler2D: case EbtISampler2D: case EbtUSampler2D: case EbtSampler2DArray: case EbtISampler2DArray: case EbtUSampler2DArray: return "sampler2D"; case EbtSamplerCube: case EbtISamplerCube: case EbtUSamplerCube: return "samplerCUBE"; case EbtSamplerExternalOES: return "sampler2D"; default: break; } } UNREACHABLE(); return "<unknown type>"; }
// Implement base-alignment and size rules from section 7.6.2.2 Standard Uniform Block Layout // Operates recursively. // // If std140 is true, it does the rounding up to vec4 size required by std140, // otherwise it does not, yielding std430 rules. // // The size is returned in the 'size' parameter // // The stride is only non-0 for arrays or matrices, and is the stride of the // top-level object nested within the type. E.g., for an array of matrices, // it is the distances needed between matrices, despite the rules saying the // stride comes from the flattening down to vectors. // // Return value is the alignment of the type. int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, bool std140, bool rowMajor) { int alignment; // When using the std140 storage layout, structures will be laid out in buffer // storage with its members stored in monotonically increasing order based on their // location in the declaration. A structure and each structure member have a base // offset and a base alignment, from which an aligned offset is computed by rounding // the base offset up to a multiple of the base alignment. The base offset of the first // member of a structure is taken from the aligned offset of the structure itself. The // base offset of all other structure members is derived by taking the offset of the // last basic machine unit consumed by the previous member and adding one. Each // structure member is stored in memory at its aligned offset. The members of a top- // level uniform block are laid out in buffer storage by treating the uniform block as // a structure with a base offset of zero. // // 1. If the member is a scalar consuming N basic machine units, the base alignment is N. // // 2. If the member is a two- or four-component vector with components consuming N basic // machine units, the base alignment is 2N or 4N, respectively. // // 3. If the member is a three-component vector with components consuming N // basic machine units, the base alignment is 4N. // // 4. If the member is an array of scalars or vectors, the base alignment and array // stride are set to match the base alignment of a single array element, according // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The // array may have padding at the end; the base offset of the member following // the array is rounded up to the next multiple of the base alignment. // // 5. If the member is a column-major matrix with C columns and R rows, the // matrix is stored identically to an array of C column vectors with R // components each, according to rule (4). // // 6. If the member is an array of S column-major matrices with C columns and // R rows, the matrix is stored identically to a row of S C column vectors // with R components each, according to rule (4). // // 7. If the member is a row-major matrix with C columns and R rows, the matrix // is stored identically to an array of R row vectors with C components each, // according to rule (4). // // 8. If the member is an array of S row-major matrices with C columns and R // rows, the matrix is stored identically to a row of S R row vectors with C // components each, according to rule (4). // // 9. If the member is a structure, the base alignment of the structure is N , where // N is the largest base alignment value of any of its members, and rounded // up to the base alignment of a vec4. The individual members of this substructure // are then assigned offsets by applying this set of rules recursively, // where the base offset of the first member of the sub-structure is equal to the // aligned offset of the structure. The structure may have padding at the end; // the base offset of the member following the sub-structure is rounded up to // the next multiple of the base alignment of the structure. // // 10. If the member is an array of S structures, the S elements of the array are laid // out in order, according to rule (9). // // Assuming, for rule 10: The stride is the same as the size of an element. stride = 0; int dummyStride; // rules 4, 6, 8, and 10 if (type.isArray()) { // TODO: perf: this might be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness TType derefType(type, 0); alignment = getBaseAlignment(derefType, size, dummyStride, std140, rowMajor); if (std140) alignment = std::max(baseAlignmentVec4Std140, alignment); RoundToPow2(size, alignment); stride = size; // uses full matrix size for stride of an array of matrices (not quite what rule 6/8, but what's expected) // uses the assumption for rule 10 in the comment above size = stride * type.getOuterArraySize(); return alignment; } // rule 9 if (type.getBasicType() == EbtStruct) { const TTypeList& memberList = *type.getStruct(); size = 0; int maxAlignment = std140 ? baseAlignmentVec4Std140 : 0; for (size_t m = 0; m < memberList.size(); ++m) { int memberSize; // modify just the children's view of matrix layout, if there is one for this member TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; int memberAlignment = getBaseAlignment(*memberList[m].type, memberSize, dummyStride, std140, (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); maxAlignment = std::max(maxAlignment, memberAlignment); RoundToPow2(size, memberAlignment); size += memberSize; } // The structure may have padding at the end; the base offset of // the member following the sub-structure is rounded up to the next // multiple of the base alignment of the structure. RoundToPow2(size, maxAlignment); return maxAlignment; } // rule 1 if (type.isScalar()) return getBaseAlignmentScalar(type, size); // rules 2 and 3 if (type.isVector()) { int scalarAlign = getBaseAlignmentScalar(type, size); switch (type.getVectorSize()) { case 2: size *= 2; return 2 * scalarAlign; default: size *= type.getVectorSize(); return 4 * scalarAlign; } } // rules 5 and 7 if (type.isMatrix()) { // rule 5: deref to row, not to column, meaning the size of vector is num columns instead of num rows TType derefType(type, 0, rowMajor); alignment = getBaseAlignment(derefType, size, dummyStride, std140, rowMajor); if (std140) alignment = std::max(baseAlignmentVec4Std140, alignment); RoundToPow2(size, alignment); stride = size; // use intra-matrix stride for stride of a just a matrix if (rowMajor) size = stride * type.getMatrixRows(); else size = stride * type.getMatrixCols(); return alignment; } assert(0); // all cases should be covered above size = baseAlignmentVec4Std140; return baseAlignmentVec4Std140; }