| File: | root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp |
| Warning: | line 394, column 9 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // | |||
| 2 | // Copyright 2018 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 | // RewriteStructSamplers: Extract samplers from structs. | |||
| 7 | // | |||
| 8 | ||||
| 9 | #include "compiler/translator/tree_ops/RewriteStructSamplers.h" | |||
| 10 | ||||
| 11 | #include "GLSLANG/ShaderVars.h" | |||
| 12 | #include "common/hash_containers.h" | |||
| 13 | #include "common/span.h" | |||
| 14 | #include "compiler/translator/Compiler.h" | |||
| 15 | #include "compiler/translator/ImmutableString.h" | |||
| 16 | #include "compiler/translator/ImmutableStringBuilder.h" | |||
| 17 | #include "compiler/translator/SymbolTable.h" | |||
| 18 | #include "compiler/translator/tree_util/IntermNode_util.h" | |||
| 19 | #include "compiler/translator/tree_util/IntermTraverse.h" | |||
| 20 | ||||
| 21 | namespace sh | |||
| 22 | { | |||
| 23 | namespace | |||
| 24 | { | |||
| 25 | ||||
| 26 | // Used to map one structure type to another (one where the samplers are removed). | |||
| 27 | struct StructureData | |||
| 28 | { | |||
| 29 | // The structure this was replaced with. If nullptr, it means the structure is removed (because | |||
| 30 | // it had all samplers). | |||
| 31 | // | |||
| 32 | // ParseContext reorders the samplers to the end of the struct, so the EOpIndexDirectStruct | |||
| 33 | // expressions that select non-sampler members don't have to change when they are moved out of | |||
| 34 | // the struct. | |||
| 35 | const TStructure *modified; | |||
| 36 | }; | |||
| 37 | ||||
| 38 | using StructureMap = angle::HashMap<const TStructure *, StructureData>; | |||
| 39 | using StructureUniformMap = angle::HashMap<const TVariable *, const TVariable *>; | |||
| 40 | using ExtractedSamplerMap = angle::HashMap<std::string, const TVariable *>; | |||
| 41 | ||||
| 42 | TIntermTyped *RewriteModifiedStructFieldSelectionExpression( | |||
| 43 | TCompiler *compiler, | |||
| 44 | TIntermBinary *node, | |||
| 45 | const StructureMap &structureMap, | |||
| 46 | const StructureUniformMap &structureUniformMap, | |||
| 47 | const ExtractedSamplerMap &extractedSamplers); | |||
| 48 | ||||
| 49 | TIntermTyped *RewriteExpressionVisitBinaryHelper(TCompiler *compiler, | |||
| 50 | TIntermBinary *node, | |||
| 51 | const StructureMap &structureMap, | |||
| 52 | const StructureUniformMap &structureUniformMap, | |||
| 53 | const ExtractedSamplerMap &extractedSamplers) | |||
| 54 | { | |||
| 55 | // Only interested in EOpIndex* binary nodes. | |||
| 56 | switch (node->getOp()) | |||
| 57 | { | |||
| 58 | case EOpIndexDirectInterfaceBlock: | |||
| 59 | case EOpIndexIndirect: | |||
| 60 | case EOpIndexDirect: | |||
| 61 | case EOpIndexDirectStruct: | |||
| 62 | break; | |||
| 63 | default: | |||
| 64 | return nullptr; | |||
| 65 | } | |||
| 66 | ||||
| 67 | const TStructure *structure = node->getLeft()->getType().getStruct(); | |||
| 68 | if (structure == nullptr) | |||
| 69 | { | |||
| 70 | return nullptr; | |||
| 71 | } | |||
| 72 | ||||
| 73 | // If the result of the index is not a sampler and the struct is not replaced, there's nothing | |||
| 74 | // to do. | |||
| 75 | if (!node->getType().isSampler() && structureMap.find(structure) == structureMap.end()) | |||
| 76 | { | |||
| 77 | return nullptr; | |||
| 78 | } | |||
| 79 | ||||
| 80 | // Otherwise, replace the whole expression such that: | |||
| 81 | // | |||
| 82 | // - if sampler, it's indexed with whatever indices the parent structs were indexed with, | |||
| 83 | // - otherwise, the chain of field selections is rewritten by modifying the base uniform so all | |||
| 84 | // the intermediate nodes would have the correct type (and therefore fields). | |||
| 85 | ASSERT(structureMap.find(structure) != structureMap.end())(structureMap.find(structure) != structureMap.end() ? static_cast <void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 85, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 85 << "): " << "structureMap.find(structure) != structureMap.end()" )); | |||
| 86 | ||||
| 87 | return RewriteModifiedStructFieldSelectionExpression(compiler, node, structureMap, | |||
| 88 | structureUniformMap, extractedSamplers); | |||
| 89 | } | |||
| 90 | ||||
| 91 | // Given an expression, this traverser calculates a new expression where sampler-in-structs are | |||
| 92 | // replaced with their extracted ones, and field indices are adjusted for the rest of the fields. | |||
| 93 | // In particular, this is run on the right node of EOpIndexIndirect binary nodes, so that the | |||
| 94 | // expression in the index gets a chance to go through this transformation. | |||
| 95 | class RewriteExpressionTraverser final : public TIntermTraverser | |||
| 96 | { | |||
| 97 | public: | |||
| 98 | explicit RewriteExpressionTraverser(TCompiler *compiler, | |||
| 99 | const StructureMap &structureMap, | |||
| 100 | const StructureUniformMap &structureUniformMap, | |||
| 101 | const ExtractedSamplerMap &extractedSamplers) | |||
| 102 | : TIntermTraverser(true, false, false), | |||
| 103 | mCompiler(compiler), | |||
| 104 | mStructureMap(structureMap), | |||
| 105 | mStructureUniformMap(structureUniformMap), | |||
| 106 | mExtractedSamplers(extractedSamplers) | |||
| 107 | {} | |||
| 108 | ||||
| 109 | bool visitBinary(Visit visit, TIntermBinary *node) override | |||
| 110 | { | |||
| 111 | TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper( | |||
| 112 | mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers); | |||
| 113 | ||||
| 114 | if (rewritten == nullptr) | |||
| 115 | { | |||
| 116 | return true; | |||
| 117 | } | |||
| 118 | ||||
| 119 | queueReplacement(rewritten, OriginalNode::IS_DROPPED); | |||
| 120 | ||||
| 121 | // Don't iterate as the expression is rewritten. | |||
| 122 | return false; | |||
| 123 | } | |||
| 124 | ||||
| 125 | void visitSymbol(TIntermSymbol *node) override | |||
| 126 | { | |||
| 127 | // It's impossible to reach here with a symbol that needs replacement. | |||
| 128 | // MonomorphizeUnsupportedFunctions makes sure that whole structs containing | |||
| 129 | // samplers are not passed to functions, so any instance of the struct uniform is | |||
| 130 | // necessarily indexed right away. visitBinary should have already taken care of it. | |||
| 131 | ASSERT(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end())(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap .end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 131, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 131 << "): " << "mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end()" )); | |||
| 132 | } | |||
| 133 | ||||
| 134 | private: | |||
| 135 | TCompiler *mCompiler; | |||
| 136 | ||||
| 137 | // See RewriteStructSamplersTraverser. | |||
| 138 | const StructureMap &mStructureMap; | |||
| 139 | const StructureUniformMap &mStructureUniformMap; | |||
| 140 | const ExtractedSamplerMap &mExtractedSamplers; | |||
| 141 | }; | |||
| 142 | ||||
| 143 | // Rewrite the index of an EOpIndexIndirect expression. The root can never need replacing, because | |||
| 144 | // it cannot be a sampler itself or of a struct type. | |||
| 145 | void RewriteIndexExpression(TCompiler *compiler, | |||
| 146 | TIntermTyped *expression, | |||
| 147 | const StructureMap &structureMap, | |||
| 148 | const StructureUniformMap &structureUniformMap, | |||
| 149 | const ExtractedSamplerMap &extractedSamplers) | |||
| 150 | { | |||
| 151 | RewriteExpressionTraverser traverser(compiler, structureMap, structureUniformMap, | |||
| 152 | extractedSamplers); | |||
| 153 | expression->traverse(&traverser); | |||
| 154 | bool valid = traverser.updateTree(compiler, expression); | |||
| 155 | ASSERT(valid)(valid ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 155, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 155 << "): " << "valid")); | |||
| 156 | } | |||
| 157 | ||||
| 158 | // Given an expression such as the following: | |||
| 159 | // | |||
| 160 | // EOpIndexDirectStruct (sampler) | |||
| 161 | // / \ | |||
| 162 | // EOpIndex* field index | |||
| 163 | // / \ | |||
| 164 | // EOpIndexDirectStruct index 2 | |||
| 165 | // / \ | |||
| 166 | // EOpIndex* field index | |||
| 167 | // / \ | |||
| 168 | // EOpIndexDirectStruct index 1 | |||
| 169 | // / \ | |||
| 170 | // Uniform Struct field index | |||
| 171 | // | |||
| 172 | // produces: | |||
| 173 | // | |||
| 174 | // EOpIndex* | |||
| 175 | // / \ | |||
| 176 | // EOpIndex* index 2 | |||
| 177 | // / \ | |||
| 178 | // sampler index 1 | |||
| 179 | // | |||
| 180 | // If the expression is not a sampler, it only replaces the struct with the modified one, while | |||
| 181 | // still processing the EOpIndexIndirect expressions (which may contain more structs to map). | |||
| 182 | TIntermTyped *RewriteModifiedStructFieldSelectionExpression( | |||
| 183 | TCompiler *compiler, | |||
| 184 | TIntermBinary *node, | |||
| 185 | const StructureMap &structureMap, | |||
| 186 | const StructureUniformMap &structureUniformMap, | |||
| 187 | const ExtractedSamplerMap &extractedSamplers) | |||
| 188 | { | |||
| 189 | const bool isSampler = node->getType().isSampler(); | |||
| 190 | ||||
| 191 | TIntermSymbol *baseUniform = nullptr; | |||
| 192 | std::string samplerName; | |||
| 193 | ||||
| 194 | TVector<TIntermBinary *> indexNodeStack; | |||
| 195 | ||||
| 196 | // Iterate once and build the name of the sampler. | |||
| 197 | TIntermBinary *iter = node; | |||
| 198 | while (baseUniform == nullptr) | |||
| 199 | { | |||
| 200 | indexNodeStack.push_back(iter); | |||
| 201 | baseUniform = iter->getLeft()->getAsSymbolNode(); | |||
| 202 | ||||
| 203 | if (isSampler) | |||
| 204 | { | |||
| 205 | if (iter->getOp() == EOpIndexDirectStruct) | |||
| 206 | { | |||
| 207 | // When indexed into a struct, get the field name instead and construct the sampler | |||
| 208 | // name. | |||
| 209 | samplerName.insert(0, iter->getIndexStructFieldName().data()); | |||
| 210 | samplerName.insert(0, "_"); | |||
| 211 | } | |||
| 212 | ||||
| 213 | if (baseUniform) | |||
| 214 | { | |||
| 215 | // If left is a symbol, we have reached the end of the chain. Use the struct name | |||
| 216 | // to finish building the name of the sampler. | |||
| 217 | samplerName.insert(0, baseUniform->variable().name().data()); | |||
| 218 | } | |||
| 219 | } | |||
| 220 | ||||
| 221 | iter = iter->getLeft()->getAsBinaryNode(); | |||
| 222 | } | |||
| 223 | ||||
| 224 | TIntermTyped *rewritten = nullptr; | |||
| 225 | ||||
| 226 | if (isSampler) | |||
| 227 | { | |||
| 228 | ASSERT(extractedSamplers.find(samplerName) != extractedSamplers.end())(extractedSamplers.find(samplerName) != extractedSamplers.end () ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 228, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 228 << "): " << "extractedSamplers.find(samplerName) != extractedSamplers.end()" )); | |||
| 229 | rewritten = new TIntermSymbol(extractedSamplers.at(samplerName)); | |||
| 230 | } | |||
| 231 | else | |||
| 232 | { | |||
| 233 | const TVariable *baseUniformVar = &baseUniform->variable(); | |||
| 234 | ASSERT(structureUniformMap.find(baseUniformVar) != structureUniformMap.end())(structureUniformMap.find(baseUniformVar) != structureUniformMap .end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 234, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 234 << "): " << "structureUniformMap.find(baseUniformVar) != structureUniformMap.end()" )); | |||
| 235 | rewritten = new TIntermSymbol(structureUniformMap.at(baseUniformVar)); | |||
| 236 | } | |||
| 237 | ||||
| 238 | // Iterate again and build the expression from bottom up. | |||
| 239 | for (auto it = indexNodeStack.rbegin(); it != indexNodeStack.rend(); ++it) | |||
| 240 | { | |||
| 241 | TIntermBinary *indexNode = *it; | |||
| 242 | ||||
| 243 | switch (indexNode->getOp()) | |||
| 244 | { | |||
| 245 | case EOpIndexDirectStruct: | |||
| 246 | if (!isSampler) | |||
| 247 | { | |||
| 248 | rewritten = | |||
| 249 | new TIntermBinary(EOpIndexDirectStruct, rewritten, indexNode->getRight()); | |||
| 250 | } | |||
| 251 | break; | |||
| 252 | ||||
| 253 | case EOpIndexDirect: | |||
| 254 | rewritten = new TIntermBinary(EOpIndexDirect, rewritten, indexNode->getRight()); | |||
| 255 | break; | |||
| 256 | ||||
| 257 | case EOpIndexIndirect: | |||
| 258 | { | |||
| 259 | // Run RewriteExpressionTraverser on the right node. It may itself be an expression | |||
| 260 | // with a sampler inside that needs to be rewritten, or simply use a field of a | |||
| 261 | // struct that's remapped. | |||
| 262 | TIntermTyped *indexExpression = indexNode->getRight(); | |||
| 263 | RewriteIndexExpression(compiler, indexExpression, structureMap, structureUniformMap, | |||
| 264 | extractedSamplers); | |||
| 265 | rewritten = new TIntermBinary(EOpIndexIndirect, rewritten, indexExpression); | |||
| 266 | break; | |||
| 267 | } | |||
| 268 | ||||
| 269 | default: | |||
| 270 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 270, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 270 << ")"; } while (0); | |||
| 271 | break; | |||
| 272 | } | |||
| 273 | } | |||
| 274 | ||||
| 275 | return rewritten; | |||
| 276 | } | |||
| 277 | ||||
| 278 | class RewriteStructSamplersTraverser final : public TIntermTraverser | |||
| 279 | { | |||
| 280 | public: | |||
| 281 | explicit RewriteStructSamplersTraverser(TCompiler *compiler, TSymbolTable *symbolTable) | |||
| 282 | : TIntermTraverser(true, false, false, symbolTable), | |||
| 283 | mCompiler(compiler), | |||
| 284 | mRemovedUniformsCount(0) | |||
| 285 | {} | |||
| 286 | ||||
| 287 | int removedUniformsCount() const { return mRemovedUniformsCount; } | |||
| 288 | ||||
| 289 | // Each struct sampler declaration is stripped of its samplers. New uniforms are added for each | |||
| 290 | // stripped struct sampler. | |||
| 291 | bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override | |||
| 292 | { | |||
| 293 | if (!mInGlobalScope) | |||
| ||||
| 294 | { | |||
| 295 | return true; | |||
| 296 | } | |||
| 297 | ||||
| 298 | const TIntermSequence &sequence = *(decl->getSequence()); | |||
| 299 | TIntermTyped *declarator = sequence.front()->getAsTyped(); | |||
| 300 | const TType &type = declarator->getType(); | |||
| 301 | ||||
| 302 | if (!type.isStructureContainingSamplers()) | |||
| 303 | { | |||
| 304 | return false; | |||
| 305 | } | |||
| 306 | ||||
| 307 | TIntermSequence newSequence; | |||
| 308 | ||||
| 309 | if (type.isStructSpecifier()) | |||
| 310 | { | |||
| 311 | // If this is just a struct definition (not a uniform variable declaration of a | |||
| 312 | // struct type), just remove the samplers. They are not instantiated yet. | |||
| 313 | const TStructure *structure = type.getStruct(); | |||
| 314 | ASSERT(structure && mStructureMap.find(structure) == mStructureMap.end())(structure && mStructureMap.find(structure) == mStructureMap .end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 314, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 314 << "): " << "structure && mStructureMap.find(structure) == mStructureMap.end()" )); | |||
| 315 | ||||
| 316 | stripStructSpecifierSamplers(structure, &newSequence); | |||
| 317 | } | |||
| 318 | else | |||
| 319 | { | |||
| 320 | const TStructure *structure = type.getStruct(); | |||
| 321 | ||||
| 322 | // If the structure is defined at the same time, create the mapping to the stripped | |||
| 323 | // version first. | |||
| 324 | if (mStructureMap.find(structure) == mStructureMap.end()) | |||
| 325 | { | |||
| 326 | stripStructSpecifierSamplers(structure, &newSequence); | |||
| 327 | } | |||
| 328 | ||||
| 329 | // Then, extract the samplers from the struct and create global-scope variables instead. | |||
| 330 | TIntermSymbol *asSymbol = declarator->getAsSymbolNode(); | |||
| 331 | ASSERT(asSymbol)(asSymbol ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 331, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 331 << "): " << "asSymbol" )); | |||
| 332 | const TVariable &variable = asSymbol->variable(); | |||
| 333 | ASSERT(variable.symbolType() != SymbolType::Empty)(variable.symbolType() != SymbolType::Empty ? static_cast< void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage( ::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv:: LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 333, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 333 << "): " << "variable.symbolType() != SymbolType::Empty" )); | |||
| 334 | ||||
| 335 | extractStructSamplerUniforms(variable, structure, &newSequence); | |||
| 336 | } | |||
| 337 | ||||
| 338 | mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl, | |||
| 339 | std::move(newSequence)); | |||
| 340 | ||||
| 341 | return false; | |||
| 342 | } | |||
| 343 | ||||
| 344 | // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root. | |||
| 345 | bool visitBinary(Visit visit, TIntermBinary *node) override | |||
| 346 | { | |||
| 347 | TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper( | |||
| 348 | mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers); | |||
| 349 | ||||
| 350 | if (rewritten == nullptr) | |||
| 351 | { | |||
| 352 | return true; | |||
| 353 | } | |||
| 354 | ||||
| 355 | queueReplacement(rewritten, OriginalNode::IS_DROPPED); | |||
| 356 | ||||
| 357 | // Don't iterate as the expression is rewritten. | |||
| 358 | return false; | |||
| 359 | } | |||
| 360 | ||||
| 361 | // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root. | |||
| 362 | void visitSymbol(TIntermSymbol *node) override | |||
| 363 | { | |||
| 364 | auto replacement = mStructureUniformMap.find(&node->variable()); | |||
| 365 | if (replacement != mStructureUniformMap.end()) | |||
| 366 | { | |||
| 367 | // This is a reference to the whole struct, just replace it with its replacement. | |||
| 368 | queueReplacement(new TIntermSymbol(replacement->second), OriginalNode::IS_DROPPED); | |||
| 369 | } | |||
| 370 | } | |||
| 371 | ||||
| 372 | private: | |||
| 373 | bool isActiveUniform(const ImmutableString &rootStructureName) | |||
| 374 | { | |||
| 375 | if (!mActiveUniforms) | |||
| 376 | { | |||
| 377 | mActiveUniforms = new TSet<ImmutableString>(); | |||
| 378 | for (const ShaderVariable &uniform : mCompiler->getUniforms()) | |||
| 379 | { | |||
| 380 | if (uniform.active) | |||
| 381 | { | |||
| 382 | mActiveUniforms->insert(uniform.name); | |||
| 383 | } | |||
| 384 | } | |||
| 385 | } | |||
| 386 | ||||
| 387 | return mActiveUniforms->count(rootStructureName) > 0; | |||
| 388 | } | |||
| 389 | ||||
| 390 | // Removes all samplers from a struct specifier. | |||
| 391 | void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence) | |||
| 392 | { | |||
| 393 | TFieldList *newFieldList = new TFieldList; | |||
| 394 | ASSERT(structure->containsSamplers())(structure->containsSamplers() ? static_cast<void>(0 ) : (!((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 394, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 394 << "): " << "structure->containsSamplers()" )); | |||
| ||||
| 395 | ||||
| 396 | // Add this struct to the struct map | |||
| 397 | ASSERT(mStructureMap.find(structure) == mStructureMap.end())(mStructureMap.find(structure) == mStructureMap.end() ? static_cast <void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 397, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 397 << "): " << "mStructureMap.find(structure) == mStructureMap.end()" )); | |||
| 398 | StructureData *modifiedData = &mStructureMap[structure]; | |||
| 399 | ||||
| 400 | modifiedData->modified = nullptr; | |||
| 401 | ||||
| 402 | for (size_t fieldIndex = 0; fieldIndex < structure->fields().size(); ++fieldIndex) | |||
| 403 | { | |||
| 404 | const TField *field = structure->fields()[fieldIndex]; | |||
| 405 | const TType &fieldType = *field->type(); | |||
| 406 | ||||
| 407 | // If the field is a sampler, or a struct that's entirely removed, skip it. | |||
| 408 | if (!fieldType.isSampler() && !isRemovedStructType(fieldType)) | |||
| 409 | { | |||
| 410 | TType *newType = nullptr; | |||
| 411 | ||||
| 412 | // Otherwise, if it's a struct that's replaced, create a new field of the replaced | |||
| 413 | // type. | |||
| 414 | if (fieldType.isStructureContainingSamplers()) | |||
| 415 | { | |||
| 416 | const TStructure *fieldStruct = fieldType.getStruct(); | |||
| 417 | ASSERT(mStructureMap.find(fieldStruct) != mStructureMap.end())(mStructureMap.find(fieldStruct) != mStructureMap.end() ? static_cast <void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 417, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 417 << "): " << "mStructureMap.find(fieldStruct) != mStructureMap.end()" )); | |||
| 418 | ||||
| 419 | const TStructure *modifiedStruct = mStructureMap[fieldStruct].modified; | |||
| 420 | ASSERT(modifiedStruct)(modifiedStruct ? static_cast<void>(0) : (!((::gl::priv ::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 420, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 420 << "): " << "modifiedStruct" )); | |||
| 421 | ||||
| 422 | newType = new TType(modifiedStruct, true); | |||
| 423 | if (fieldType.isArray()) | |||
| 424 | { | |||
| 425 | newType->makeArrays(fieldType.getArraySizes()); | |||
| 426 | } | |||
| 427 | } | |||
| 428 | else | |||
| 429 | { | |||
| 430 | // If not, duplicate the field as is. | |||
| 431 | newType = new TType(fieldType); | |||
| 432 | } | |||
| 433 | ||||
| 434 | TField *newField = | |||
| 435 | new TField(newType, field->name(), field->line(), field->symbolType()); | |||
| 436 | newFieldList->push_back(newField); | |||
| 437 | } | |||
| 438 | } | |||
| 439 | ||||
| 440 | // Prune empty structs. | |||
| 441 | if (newFieldList->empty()) | |||
| 442 | { | |||
| 443 | return; | |||
| 444 | } | |||
| 445 | ||||
| 446 | // Declare a new struct with the same name and the new fields. | |||
| 447 | modifiedData->modified = | |||
| 448 | new TStructure(mSymbolTable, | |||
| 449 | structure->symbolType() == SymbolType::Empty ? kEmptyImmutableString | |||
| 450 | : structure->name(), | |||
| 451 | newFieldList, structure->symbolType()); | |||
| 452 | TType *newStructType = new TType(modifiedData->modified, true); | |||
| 453 | TVariable *newStructVar = | |||
| 454 | new TVariable(mSymbolTable, kEmptyImmutableString, newStructType, SymbolType::Empty); | |||
| 455 | TIntermSymbol *newStructRef = new TIntermSymbol(newStructVar); | |||
| 456 | ||||
| 457 | TIntermDeclaration *structDecl = new TIntermDeclaration; | |||
| 458 | structDecl->appendDeclarator(newStructRef); | |||
| 459 | ||||
| 460 | newSequence->push_back(structDecl); | |||
| 461 | } | |||
| 462 | ||||
| 463 | // Returns true if the type is a struct that was removed because we extracted all the members. | |||
| 464 | bool isRemovedStructType(const TType &type) const | |||
| 465 | { | |||
| 466 | const TStructure *structure = type.getStruct(); | |||
| 467 | if (structure == nullptr) | |||
| 468 | { | |||
| 469 | // Not a struct | |||
| 470 | return false; | |||
| 471 | } | |||
| 472 | ||||
| 473 | // A struct is removed if it is in the map, but doesn't have a replacement struct. | |||
| 474 | auto iter = mStructureMap.find(structure); | |||
| 475 | return iter != mStructureMap.end() && iter->second.modified == nullptr; | |||
| 476 | } | |||
| 477 | ||||
| 478 | // Removes samplers from struct uniforms. For each sampler removed also adds a new globally | |||
| 479 | // defined sampler uniform. | |||
| 480 | void extractStructSamplerUniforms(const TVariable &variable, | |||
| 481 | const TStructure *structure, | |||
| 482 | TIntermSequence *newSequence) | |||
| 483 | { | |||
| 484 | ASSERT(structure->containsSamplers())(structure->containsSamplers() ? static_cast<void>(0 ) : (!((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 484, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 484 << "): " << "structure->containsSamplers()" )); | |||
| 485 | ASSERT(mStructureMap.find(structure) != mStructureMap.end())(mStructureMap.find(structure) != mStructureMap.end() ? static_cast <void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 485, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 485 << "): " << "mStructureMap.find(structure) != mStructureMap.end()" )); | |||
| 486 | ||||
| 487 | const TType &type = variable.getType(); | |||
| 488 | enterArray(type); | |||
| 489 | ||||
| 490 | for (const TField *field : structure->fields()) | |||
| 491 | { | |||
| 492 | extractFieldSamplers(isActiveUniform(variable.name()), variable.name().data(), field, | |||
| 493 | newSequence); | |||
| 494 | } | |||
| 495 | ||||
| 496 | // If there's a replacement structure (because there are non-sampler fields in the struct), | |||
| 497 | // add a declaration with that type. | |||
| 498 | const TStructure *modified = mStructureMap[structure].modified; | |||
| 499 | if (modified != nullptr) | |||
| 500 | { | |||
| 501 | TType *newType = new TType(modified, false); | |||
| 502 | if (type.isArray()) | |||
| 503 | { | |||
| 504 | newType->makeArrays(type.getArraySizes()); | |||
| 505 | } | |||
| 506 | newType->setQualifier(EvqUniform); | |||
| 507 | const TVariable *newVariable = | |||
| 508 | new TVariable(mSymbolTable, variable.name(), newType, variable.symbolType()); | |||
| 509 | ||||
| 510 | TIntermDeclaration *newDecl = new TIntermDeclaration(); | |||
| 511 | newDecl->appendDeclarator(new TIntermSymbol(newVariable)); | |||
| 512 | ||||
| 513 | newSequence->push_back(newDecl); | |||
| 514 | ||||
| 515 | ASSERT(mStructureUniformMap.find(&variable) == mStructureUniformMap.end())(mStructureUniformMap.find(&variable) == mStructureUniformMap .end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 515, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 515 << "): " << "mStructureUniformMap.find(&variable) == mStructureUniformMap.end()" )); | |||
| 516 | mStructureUniformMap[&variable] = newVariable; | |||
| 517 | } | |||
| 518 | else | |||
| 519 | { | |||
| 520 | mRemovedUniformsCount++; | |||
| 521 | } | |||
| 522 | ||||
| 523 | exitArray(type); | |||
| 524 | } | |||
| 525 | ||||
| 526 | // Extracts samplers from a field of a struct. Works with nested structs and arrays. | |||
| 527 | void extractFieldSamplers(bool inActiveUniform, | |||
| 528 | const std::string &prefix, | |||
| 529 | const TField *field, | |||
| 530 | TIntermSequence *newSequence) | |||
| 531 | { | |||
| 532 | const TType &fieldType = *field->type(); | |||
| 533 | if (fieldType.isSampler() || fieldType.isStructureContainingSamplers()) | |||
| 534 | { | |||
| 535 | std::string newPrefix = prefix + "_" + field->name().data(); | |||
| 536 | ||||
| 537 | if (fieldType.isSampler()) | |||
| 538 | { | |||
| 539 | if (inActiveUniform) | |||
| 540 | { | |||
| 541 | extractSampler(newPrefix, fieldType, newSequence); | |||
| 542 | } | |||
| 543 | } | |||
| 544 | else | |||
| 545 | { | |||
| 546 | enterArray(fieldType); | |||
| 547 | const TStructure *structure = fieldType.getStruct(); | |||
| 548 | for (const TField *nestedField : structure->fields()) | |||
| 549 | { | |||
| 550 | extractFieldSamplers(inActiveUniform, newPrefix, nestedField, newSequence); | |||
| 551 | } | |||
| 552 | exitArray(fieldType); | |||
| 553 | } | |||
| 554 | } | |||
| 555 | } | |||
| 556 | ||||
| 557 | void GenerateArraySizesFromStack(TVector<unsigned int> *sizesOut) | |||
| 558 | { | |||
| 559 | sizesOut->reserve(mArraySizeStack.size()); | |||
| 560 | ||||
| 561 | for (auto it = mArraySizeStack.rbegin(); it != mArraySizeStack.rend(); ++it) | |||
| 562 | { | |||
| 563 | sizesOut->push_back(*it); | |||
| 564 | } | |||
| 565 | } | |||
| 566 | ||||
| 567 | // Extracts a sampler from a struct. Declares the new extracted sampler. | |||
| 568 | void extractSampler(const std::string &newName, | |||
| 569 | const TType &fieldType, | |||
| 570 | TIntermSequence *newSequence) | |||
| 571 | { | |||
| 572 | ASSERT(fieldType.isSampler())(fieldType.isSampler() ? static_cast<void>(0) : (!((::gl ::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 572, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 572 << "): " << "fieldType.isSampler()" )); | |||
| 573 | ||||
| 574 | TType *newType = new TType(fieldType); | |||
| 575 | ||||
| 576 | // Add array dimensions accumulated so far due to struct arrays. Note that to support | |||
| 577 | // nested arrays, mArraySizeStack has the outermost size in the front. |makeArrays| thus | |||
| 578 | // expects this in reverse order. | |||
| 579 | TVector<unsigned int> parentArraySizes; | |||
| 580 | GenerateArraySizesFromStack(&parentArraySizes); | |||
| 581 | newType->makeArrays(parentArraySizes); | |||
| 582 | ||||
| 583 | ImmutableStringBuilder nameBuilder(newName.size() + 1); | |||
| 584 | nameBuilder << newName; | |||
| 585 | ||||
| 586 | newType->setQualifier(EvqUniform); | |||
| 587 | TVariable *newVariable = | |||
| 588 | new TVariable(mSymbolTable, nameBuilder, newType, SymbolType::AngleInternal); | |||
| 589 | TIntermSymbol *newSymbol = new TIntermSymbol(newVariable); | |||
| 590 | ||||
| 591 | TIntermDeclaration *samplerDecl = new TIntermDeclaration; | |||
| 592 | samplerDecl->appendDeclarator(newSymbol); | |||
| 593 | ||||
| 594 | newSequence->push_back(samplerDecl); | |||
| 595 | ||||
| 596 | // TODO: Use a temp name instead of generating a name as currently done. There is no | |||
| 597 | // guarantee that these generated names cannot clash. Create a mapping from the previous | |||
| 598 | // name to the name assigned to the temp variable so ShaderVariable::mappedName can be | |||
| 599 | // updated post-transformation. http://anglebug.com/42262930 | |||
| 600 | ASSERT(mExtractedSamplers.find(newName) == mExtractedSamplers.end())(mExtractedSamplers.find(newName) == mExtractedSamplers.end() ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" , __FUNCTION__, 600, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp" << ":" << 600 << "): " << "mExtractedSamplers.find(newName) == mExtractedSamplers.end()" )); | |||
| 601 | mExtractedSamplers[newName] = newVariable; | |||
| 602 | } | |||
| 603 | ||||
| 604 | void enterArray(const TType &arrayType) | |||
| 605 | { | |||
| 606 | const angle::Span<const unsigned int> &arraySizes = arrayType.getArraySizes(); | |||
| 607 | for (auto it = arraySizes.rbegin(); it != arraySizes.rend(); ++it) | |||
| 608 | { | |||
| 609 | unsigned int arraySize = *it; | |||
| 610 | mArraySizeStack.push_back(arraySize); | |||
| 611 | } | |||
| 612 | } | |||
| 613 | ||||
| 614 | void exitArray(const TType &arrayType) | |||
| 615 | { | |||
| 616 | mArraySizeStack.resize(mArraySizeStack.size() - arrayType.getNumArraySizes()); | |||
| 617 | } | |||
| 618 | ||||
| 619 | TCompiler *mCompiler; | |||
| 620 | int mRemovedUniformsCount; | |||
| 621 | ||||
| 622 | // Map structures with samplers to ones that have their samplers removed. | |||
| 623 | StructureMap mStructureMap; | |||
| 624 | ||||
| 625 | // Map uniform variables of structure type that are replaced with another variable. | |||
| 626 | StructureUniformMap mStructureUniformMap; | |||
| 627 | ||||
| 628 | // Map a constructed sampler name to its variable. Used to replace an expression that uses this | |||
| 629 | // sampler with the extracted one. | |||
| 630 | ExtractedSamplerMap mExtractedSamplers; | |||
| 631 | ||||
| 632 | // A stack of array sizes. Used to figure out the array dimensions of the extracted sampler, | |||
| 633 | // for example when it's nested in an array of structs in an array of structs. | |||
| 634 | TVector<unsigned int> mArraySizeStack; | |||
| 635 | ||||
| 636 | // Caches the names of all inactive uniforms. | |||
| 637 | TSet<ImmutableString> *mActiveUniforms = nullptr; | |||
| 638 | }; | |||
| 639 | } // anonymous namespace | |||
| 640 | ||||
| 641 | bool RewriteStructSamplers(TCompiler *compiler, | |||
| 642 | TIntermBlock *root, | |||
| 643 | TSymbolTable *symbolTable, | |||
| 644 | int *removedUniformsCountOut) | |||
| 645 | { | |||
| 646 | RewriteStructSamplersTraverser traverser(compiler, symbolTable); | |||
| 647 | root->traverse(&traverser); | |||
| 648 | *removedUniformsCountOut = traverser.removedUniformsCount(); | |||
| 649 | return traverser.updateTree(compiler, root); | |||
| 650 | } | |||
| 651 | } // namespace sh |