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