ValidateVaryingLocations.cpp (12799B)
1 // 2 // Copyright 2002 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // The ValidateVaryingLocations function checks if there exists location conflicts on shader 7 // varyings. 8 // 9 10 #include "ValidateVaryingLocations.h" 11 12 #include "compiler/translator/Diagnostics.h" 13 #include "compiler/translator/SymbolTable.h" 14 #include "compiler/translator/tree_util/IntermTraverse.h" 15 #include "compiler/translator/util.h" 16 17 namespace sh 18 { 19 20 namespace 21 { 22 23 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics) 24 { 25 diagnostics->error(symbol.getLine(), reason, symbol.getName().data()); 26 } 27 28 int GetStructLocationCount(const TStructure *structure); 29 30 int GetFieldLocationCount(const TField *field) 31 { 32 int field_size = 0; 33 const TType *fieldType = field->type(); 34 35 if (fieldType->getStruct() != nullptr) 36 { 37 field_size = GetStructLocationCount(fieldType->getStruct()); 38 } 39 else if (fieldType->isMatrix()) 40 { 41 field_size = fieldType->getNominalSize(); 42 } 43 else 44 { 45 ASSERT(fieldType->getSecondarySize() == 1); 46 field_size = 1; 47 } 48 49 if (fieldType->isArray()) 50 { 51 field_size *= fieldType->getArraySizeProduct(); 52 } 53 54 return field_size; 55 } 56 57 int GetStructLocationCount(const TStructure *structure) 58 { 59 int totalLocation = 0; 60 for (const TField *field : structure->fields()) 61 { 62 totalLocation += GetFieldLocationCount(field); 63 } 64 return totalLocation; 65 } 66 67 int GetInterfaceBlockLocationCount(const TType &varyingType, bool ignoreVaryingArraySize) 68 { 69 int totalLocation = 0; 70 for (const TField *field : varyingType.getInterfaceBlock()->fields()) 71 { 72 totalLocation += GetFieldLocationCount(field); 73 } 74 75 if (!ignoreVaryingArraySize && varyingType.isArray()) 76 { 77 totalLocation *= varyingType.getArraySizeProduct(); 78 } 79 return totalLocation; 80 } 81 82 int GetLocationCount(const TType &varyingType, bool ignoreVaryingArraySize) 83 { 84 ASSERT(!varyingType.isInterfaceBlock()); 85 86 if (varyingType.getStruct() != nullptr) 87 { 88 int totalLocation = 0; 89 for (const TField *field : varyingType.getStruct()->fields()) 90 { 91 const TType *fieldType = field->type(); 92 ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray()); 93 94 totalLocation += GetFieldLocationCount(field); 95 } 96 return totalLocation; 97 } 98 99 ASSERT(varyingType.isMatrix() || varyingType.getSecondarySize() == 1); 100 int elementLocationCount = varyingType.isMatrix() ? varyingType.getNominalSize() : 1; 101 102 // [GL_EXT_shader_io_blocks SPEC Chapter 4.4.1] 103 // Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation 104 // evaluation inputs all have an additional level of arrayness relative to other shader inputs 105 // and outputs. This outer array level is removed from the type before considering how many 106 // locations the type consumes. 107 if (ignoreVaryingArraySize) 108 { 109 // Array-of-arrays cannot be inputs or outputs of a geometry shader. 110 // (GL_EXT_geometry_shader SPEC issues(5)) 111 ASSERT(!varyingType.isArrayOfArrays()); 112 return elementLocationCount; 113 } 114 115 return elementLocationCount * varyingType.getArraySizeProduct(); 116 } 117 118 bool ShouldIgnoreVaryingArraySize(TQualifier qualifier, GLenum shaderType) 119 { 120 bool isVaryingIn = IsShaderIn(qualifier) && qualifier != EvqPatchIn; 121 122 switch (shaderType) 123 { 124 case GL_GEOMETRY_SHADER: 125 case GL_TESS_EVALUATION_SHADER: 126 return isVaryingIn; 127 case GL_TESS_CONTROL_SHADER: 128 return (IsShaderOut(qualifier) && qualifier != EvqPatchOut) || isVaryingIn; 129 default: 130 return false; 131 } 132 } 133 134 struct SymbolAndField 135 { 136 const TIntermSymbol *symbol; 137 const TField *field; 138 }; 139 using LocationMap = std::map<int, SymbolAndField>; 140 141 void MarkVaryingLocations(TDiagnostics *diagnostics, 142 const TIntermSymbol *varying, 143 const TField *field, 144 int location, 145 int elementCount, 146 LocationMap *locationMap) 147 { 148 for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex) 149 { 150 const int offsetLocation = location + elementIndex; 151 auto conflict = locationMap->find(offsetLocation); 152 if (conflict != locationMap->end()) 153 { 154 std::stringstream strstr = sh::InitializeStream<std::stringstream>(); 155 strstr << "'" << varying->getName(); 156 if (field) 157 { 158 strstr << "." << field->name(); 159 } 160 strstr << "' conflicting location with '" << conflict->second.symbol->getName(); 161 if (conflict->second.field) 162 { 163 strstr << "." << conflict->second.field->name(); 164 } 165 strstr << "'"; 166 error(*varying, strstr.str().c_str(), diagnostics); 167 } 168 else 169 { 170 (*locationMap)[offsetLocation] = {varying, field}; 171 } 172 } 173 } 174 175 using VaryingVector = std::vector<const TIntermSymbol *>; 176 177 void ValidateShaderInterfaceAndAssignLocations(TDiagnostics *diagnostics, 178 const VaryingVector &varyingVector, 179 GLenum shaderType) 180 { 181 // Location conflicts can only happen when there are two or more varyings in varyingVector. 182 if (varyingVector.size() <= 1) 183 { 184 return; 185 } 186 187 LocationMap locationMap; 188 for (const TIntermSymbol *varying : varyingVector) 189 { 190 const TType &varyingType = varying->getType(); 191 const int location = varyingType.getLayoutQualifier().location; 192 ASSERT(location >= 0); 193 194 bool ignoreVaryingArraySize = 195 ShouldIgnoreVaryingArraySize(varying->getQualifier(), shaderType); 196 197 // A varying is either: 198 // 199 // - A vector or matrix, which can take a number of contiguous locations 200 // - A struct, which also takes a number of contiguous locations 201 // - An interface block. 202 // 203 // Interface blocks can assign arbitrary locations to their fields, for example: 204 // 205 // layout(location = 4) in block { 206 // vec4 a; // gets location 4 207 // vec4 b; // gets location 5 208 // layout(location = 7) vec4 c; // gets location 7 209 // vec4 d; // gets location 8 210 // layout (location = 1) vec4 e; // gets location 1 211 // vec4 f; // gets location 2 212 // }; 213 // 214 // The following code therefore takes two paths. For non-interface-block types, the number 215 // of locations for the varying is calculated (elementCount), and all locations in 216 // [location, location + elementCount) are marked as occupied. 217 // 218 // For interface blocks, a similar algorithm is implemented except each field is 219 // individually marked with the location either advancing automatically or taking its value 220 // from the field's layout qualifier. 221 222 if (varyingType.isInterfaceBlock()) 223 { 224 int currentLocation = location; 225 bool anyFieldWithLocation = false; 226 227 for (const TField *field : varyingType.getInterfaceBlock()->fields()) 228 { 229 const int fieldLocation = field->type()->getLayoutQualifier().location; 230 if (fieldLocation >= 0) 231 { 232 currentLocation = fieldLocation; 233 anyFieldWithLocation = true; 234 } 235 236 const int fieldLocationCount = GetFieldLocationCount(field); 237 MarkVaryingLocations(diagnostics, varying, field, currentLocation, 238 fieldLocationCount, &locationMap); 239 240 currentLocation += fieldLocationCount; 241 } 242 243 // Array interface blocks can't have location qualifiers on fields. 244 ASSERT(ignoreVaryingArraySize || !anyFieldWithLocation || !varyingType.isArray()); 245 246 if (!ignoreVaryingArraySize && varyingType.isArray()) 247 { 248 // This is only reached if the varying is an array of interface blocks, with only a 249 // layout qualifier on the block itself, for example: 250 // 251 // layout(location = 4) in block { 252 // vec4 a; 253 // vec4 b; 254 // vec4 c; 255 // vec4 d; 256 // } instance[N]; 257 // 258 // The locations for instance[0] are already marked by the above code, so we need to 259 // further mark locations occupied by instances [1, N). |currentLocation| is 260 // already just past the end of instance[0], which is the beginning of instance[1]. 261 // 262 int remainingLocations = currentLocation * (varyingType.getArraySizeProduct() - 1); 263 MarkVaryingLocations(diagnostics, varying, nullptr, currentLocation, 264 remainingLocations, &locationMap); 265 } 266 } 267 else 268 { 269 const int elementCount = GetLocationCount(varying->getType(), ignoreVaryingArraySize); 270 MarkVaryingLocations(diagnostics, varying, nullptr, location, elementCount, 271 &locationMap); 272 } 273 } 274 } 275 276 class ValidateVaryingLocationsTraverser : public TIntermTraverser 277 { 278 public: 279 ValidateVaryingLocationsTraverser(GLenum shaderType); 280 void validate(TDiagnostics *diagnostics); 281 282 private: 283 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; 284 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; 285 286 VaryingVector mInputVaryingsWithLocation; 287 VaryingVector mOutputVaryingsWithLocation; 288 GLenum mShaderType; 289 }; 290 291 ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType) 292 : TIntermTraverser(true, false, false), mShaderType(shaderType) 293 {} 294 295 bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node) 296 { 297 const TIntermSequence &sequence = *(node->getSequence()); 298 ASSERT(!sequence.empty()); 299 300 const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode(); 301 if (symbol == nullptr) 302 { 303 return false; 304 } 305 306 if (symbol->variable().symbolType() == SymbolType::Empty) 307 { 308 return false; 309 } 310 311 // Collect varyings that have explicit 'location' qualifiers. 312 const TQualifier qualifier = symbol->getQualifier(); 313 if (symbol->getType().getLayoutQualifier().location != -1) 314 { 315 if (IsVaryingIn(qualifier)) 316 { 317 mInputVaryingsWithLocation.push_back(symbol); 318 } 319 else if (IsVaryingOut(qualifier)) 320 { 321 mOutputVaryingsWithLocation.push_back(symbol); 322 } 323 } 324 325 return false; 326 } 327 328 bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit, 329 TIntermFunctionDefinition *node) 330 { 331 // We stop traversing function definitions because varyings cannot be defined in a function. 332 return false; 333 } 334 335 void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics) 336 { 337 ASSERT(diagnostics); 338 339 ValidateShaderInterfaceAndAssignLocations(diagnostics, mInputVaryingsWithLocation, mShaderType); 340 ValidateShaderInterfaceAndAssignLocations(diagnostics, mOutputVaryingsWithLocation, 341 mShaderType); 342 } 343 344 } // anonymous namespace 345 346 unsigned int CalculateVaryingLocationCount(const TType &varyingType, GLenum shaderType) 347 { 348 const TQualifier qualifier = varyingType.getQualifier(); 349 const bool ignoreVaryingArraySize = ShouldIgnoreVaryingArraySize(qualifier, shaderType); 350 351 if (varyingType.isInterfaceBlock()) 352 { 353 return GetInterfaceBlockLocationCount(varyingType, ignoreVaryingArraySize); 354 } 355 356 return GetLocationCount(varyingType, ignoreVaryingArraySize); 357 } 358 359 bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType) 360 { 361 ValidateVaryingLocationsTraverser varyingValidator(shaderType); 362 root->traverse(&varyingValidator); 363 int numErrorsBefore = diagnostics->numErrors(); 364 varyingValidator.validate(diagnostics); 365 return (diagnostics->numErrors() == numErrorsBefore); 366 } 367 368 } // namespace sh