summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2018-06-26 16:56:45 +0200
committerAndre de la Rocha <andre.rocha@qt.io>2018-10-13 21:36:35 +0000
commit0a7aebadfbb3534284546aa3ca8612314c08f136 (patch)
treee94ee33ae3bb9b96fc3047c6455d47ac4920bfbf /src/3rdparty/angle/src/compiler
parent656e89f875ad2008ca16cc673b687a22daa294c9 (diff)
Update ANGLE to chromium/3280
Change-Id: I0802c0d7486f772d361f87a544d6c5af937f4ca1 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'src/3rdparty/angle/src/compiler')
-rw-r--r--src/3rdparty/angle/src/compiler/fuzz/translator_fuzzer.cpp179
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp214
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h17
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp2
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.h6
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp630
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h24
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h8
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y85
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Input.cpp31
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Input.h26
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Lexer.cpp2
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Lexer.h4
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp35
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Macro.h12
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp297
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h55
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp72
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.h20
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/SourceLocation.h12
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Token.cpp21
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Token.h29
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h17
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l55
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h18
-rw-r--r--src/3rdparty/angle/src/compiler/preprocessor/pp_utils.h18
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp155
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp59
-rw-r--r--src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.h20
-rw-r--r--src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.cpp58
-rw-r--r--src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.h22
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp227
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h12
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BaseTypes.h958
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp107
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp308
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h192
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp166
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h21
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp551
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h11
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Cache.cpp41
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Cache.h36
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CallDAG.cpp261
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CallDAG.h16
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ClampPointSize.cpp47
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ClampPointSize.h22
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CodeGen.cpp81
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CollectVariables.cpp869
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CollectVariables.h37
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Common.h73
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Compiler.cpp1111
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Compiler.h221
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ConstantUnion.cpp681
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ConstantUnion.h399
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp221
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h48
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.cpp147
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.h32
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DetectCallDepth.cpp185
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DetectCallDepth.h78
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.cpp191
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.h71
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp105
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Diagnostics.h46
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp139
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h14
-rw-r--r--src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp129
-rw-r--r--src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h31
-rw-r--r--src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp868
-rw-r--r--src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp153
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.h29
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.cpp96
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h65
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp5
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/FindMain.cpp38
-rw-r--r--src/3rdparty/angle/src/compiler/translator/FindMain.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/FindSymbolNode.cpp58
-rw-r--r--src/3rdparty/angle/src/compiler/translator/FindSymbolNode.h27
-rw-r--r--src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.cpp88
-rw-r--r--src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h37
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp97
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h53
-rw-r--r--src/3rdparty/angle/src/compiler/translator/HashNames.cpp72
-rw-r--r--src/3rdparty/angle/src/compiler/translator/HashNames.h14
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.cpp304
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.h76
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InfoSink.cpp38
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InfoSink.h78
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Initialize.cpp1252
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Initialize.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp15
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeDll.h6
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeGlobals.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeParseContext.cpp42
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp325
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeVariables.h76
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNode.cpp3818
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNode.h847
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.cpp157
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.h75
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNode_util.cpp254
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNode_util.h60
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp630
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermTraverse.h355
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Intermediate.cpp508
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Intermediate.h75
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.cpp51
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.h20
-rw-r--r--src/3rdparty/angle/src/compiler/translator/LoopInfo.cpp211
-rw-r--r--src/3rdparty/angle/src/compiler/translator/LoopInfo.h80
-rw-r--r--src/3rdparty/angle/src/compiler/translator/MMap.h56
-rw-r--r--src/3rdparty/angle/src/compiler/translator/NodeSearch.h19
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Operator.cpp554
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Operator.h126
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputESSL.cpp17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputESSL.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp80
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSL.h17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp1846
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h76
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp3540
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputHLSL.h186
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputTree.cpp682
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputTree.h22
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.cpp80
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.h34
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ParamType.h102
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ParseContext.cpp6080
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ParseContext.h767
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PoolAlloc.cpp227
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PoolAlloc.h220
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Pragma.h9
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp81
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h15
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PruneNoOps.cpp156
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PruneNoOps.h24
-rw-r--r--src/3rdparty/angle/src/compiler/translator/QualifierAlive.cpp58
-rw-r--r--src/3rdparty/angle/src/compiler/translator/QualifierAlive.h12
-rw-r--r--src/3rdparty/angle/src/compiler/translator/QualifierTypes.cpp784
-rw-r--r--src/3rdparty/angle/src/compiler/translator/QualifierTypes.h191
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp94
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp34
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h20
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.cpp83
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.h29
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp319
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h12
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.cpp56
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.h18
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp43
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.h18
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp116
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h21
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemovePow.cpp36
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemovePow.h5
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp187
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.h42
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.cpp358
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.h24
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RenameFunction.h36
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp62
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h9
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp112
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h8
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp154
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.h28
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.cpp94
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.h19
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp112
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h20
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.cpp112
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp275
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h49
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp3
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SearchSymbol.h4
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp76
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h6
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp52
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp145
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h12
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Severity.h22
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp426
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp397
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.cpp38
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.cpp300
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.cpp171
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.h28
-rw-r--r--src/3rdparty/angle/src/compiler/translator/StructureHLSL.cpp448
-rw-r--r--src/3rdparty/angle/src/compiler/translator/StructureHLSL.h41
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp448
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SymbolTable.h494
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.cpp28
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.h36
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.cpp1322
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.h76
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp146
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h15
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp214
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp119
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h21
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.cpp173
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.h34
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Types.cpp1045
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Types.h675
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.cpp185
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.h38
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp44
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h12
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp318
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h13
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp434
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UniformHLSL.h85
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.cpp105
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.h30
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp794
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h85
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp62
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h11
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp393
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h58
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.cpp29
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.h21
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp116
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h34
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateSwitch.cpp179
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h52
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.cpp174
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp673
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VariableInfo.h77
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VariablePacker.cpp394
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VariablePacker.h39
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.cpp284
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp145
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VersionGLSL.h14
-rw-r--r--src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.cpp132
-rw-r--r--src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.h22
-rw-r--r--src/3rdparty/angle/src/compiler/translator/blocklayout.cpp191
-rw-r--r--src/3rdparty/angle/src/compiler/translator/blocklayout.h75
-rw-r--r--src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp63
-rw-r--r--src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.h32
-rw-r--r--src/3rdparty/angle/src/compiler/translator/compilerdebug.cpp37
-rw-r--r--src/3rdparty/angle/src/compiler/translator/compilerdebug.h53
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp95
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h199
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp255
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h199
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp64
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h31
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp69
-rw-r--r--src/3rdparty/angle/src/compiler/translator/emulated_builtin_function_data_hlsl.json1382
-rw-r--r--src/3rdparty/angle/src/compiler/translator/emulated_builtin_functions_hlsl_autogen.cpp859
-rw-r--r--src/3rdparty/angle/src/compiler/translator/glslang.h16
-rw-r--r--src/3rdparty/angle/src/compiler/translator/glslang.l183
-rw-r--r--src/3rdparty/angle/src/compiler/translator/glslang.y1027
-rw-r--r--src/3rdparty/angle/src/compiler/translator/intermOut.cpp626
-rw-r--r--src/3rdparty/angle/src/compiler/translator/length_limits.h7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/parseConst.cpp264
-rw-r--r--src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp130
-rw-r--r--src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h39
-rw-r--r--src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h32
-rw-r--r--src/3rdparty/angle/src/compiler/translator/util.cpp780
-rw-r--r--src/3rdparty/angle/src/compiler/translator/util.h60
277 files changed, 37767 insertions, 21222 deletions
diff --git a/src/3rdparty/angle/src/compiler/fuzz/translator_fuzzer.cpp b/src/3rdparty/angle/src/compiler/fuzz/translator_fuzzer.cpp
new file mode 100644
index 0000000000..910af8be6f
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/fuzz/translator_fuzzer.cpp
@@ -0,0 +1,179 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// translator_fuzzer.cpp: A libfuzzer fuzzer for the shader translator.
+
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <unordered_map>
+
+#include "angle_gl.h"
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/util.h"
+
+using namespace sh;
+
+struct TranslatorCacheKey
+{
+ bool operator==(const TranslatorCacheKey &other) const
+ {
+ return type == other.type && spec == other.spec && output == other.output;
+ }
+
+ uint32_t type = 0;
+ uint32_t spec = 0;
+ uint32_t output = 0;
+};
+
+namespace std
+{
+
+template <>
+struct hash<TranslatorCacheKey>
+{
+ std::size_t operator()(const TranslatorCacheKey &k) const
+ {
+ return (hash<uint32_t>()(k.type) << 1) ^ (hash<uint32_t>()(k.spec) >> 1) ^
+ hash<uint32_t>()(k.output);
+ }
+};
+} // namespace std
+
+struct TCompilerDeleter
+{
+ void operator()(TCompiler *compiler) const { DeleteCompiler(compiler); }
+};
+
+using UniqueTCompiler = std::unique_ptr<TCompiler, TCompilerDeleter>;
+
+static std::unordered_map<TranslatorCacheKey, UniqueTCompiler> translators;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ // Reserve some size for future compile options
+ const size_t kHeaderSize = 128;
+
+ if (size <= kHeaderSize)
+ {
+ return 0;
+ }
+
+ // Make sure the rest of data will be a valid C string so that we don't have to copy it.
+ if (data[size - 1] != 0)
+ {
+ return 0;
+ }
+
+ uint32_t type = *reinterpret_cast<const uint32_t *>(data);
+ uint32_t spec = *reinterpret_cast<const uint32_t *>(data + 4);
+ uint32_t output = *reinterpret_cast<const uint32_t *>(data + 8);
+ uint64_t options = *reinterpret_cast<const uint64_t *>(data + 12);
+
+ if (type != GL_FRAGMENT_SHADER && type != GL_VERTEX_SHADER)
+ {
+ return 0;
+ }
+
+ if (spec != SH_GLES2_SPEC && type != SH_WEBGL_SPEC && spec != SH_GLES3_SPEC &&
+ spec != SH_WEBGL2_SPEC)
+ {
+ return 0;
+ }
+
+ ShShaderOutput shaderOutput = static_cast<ShShaderOutput>(output);
+ if (!(IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput)) &&
+ (options & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u)
+ {
+ // This compiler option is only available in ESSL and GLSL.
+ return 0;
+ }
+
+ std::vector<uint32_t> validOutputs;
+ validOutputs.push_back(SH_ESSL_OUTPUT);
+ validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
+ validOutputs.push_back(SH_GLSL_130_OUTPUT);
+ validOutputs.push_back(SH_GLSL_140_OUTPUT);
+ validOutputs.push_back(SH_GLSL_150_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_330_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_400_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_410_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_420_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_430_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_440_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_450_CORE_OUTPUT);
+ validOutputs.push_back(SH_HLSL_3_0_OUTPUT);
+ validOutputs.push_back(SH_HLSL_4_1_OUTPUT);
+ validOutputs.push_back(SH_HLSL_4_0_FL9_3_OUTPUT);
+ bool found = false;
+ for (auto valid : validOutputs)
+ {
+ found = found || (valid == output);
+ }
+ if (!found)
+ {
+ return 0;
+ }
+
+ size -= kHeaderSize;
+ data += kHeaderSize;
+
+ if (!sh::Initialize())
+ {
+ return 0;
+ }
+
+ TranslatorCacheKey key;
+ key.type = type;
+ key.spec = spec;
+ key.output = output;
+
+ if (translators.find(key) == translators.end())
+ {
+ UniqueTCompiler translator(
+ ConstructCompiler(type, static_cast<ShShaderSpec>(spec), shaderOutput));
+
+ if (translator == nullptr)
+ {
+ return 0;
+ }
+
+ ShBuiltInResources resources;
+ sh::InitBuiltInResources(&resources);
+
+ // Enable all the extensions to have more coverage
+ resources.OES_standard_derivatives = 1;
+ resources.OES_EGL_image_external = 1;
+ resources.OES_EGL_image_external_essl3 = 1;
+ resources.NV_EGL_stream_consumer_external = 1;
+ resources.ARB_texture_rectangle = 1;
+ resources.EXT_blend_func_extended = 1;
+ resources.EXT_draw_buffers = 1;
+ resources.EXT_frag_depth = 1;
+ resources.EXT_shader_texture_lod = 1;
+ resources.WEBGL_debug_shader_precision = 1;
+ resources.EXT_shader_framebuffer_fetch = 1;
+ resources.NV_shader_framebuffer_fetch = 1;
+ resources.ARM_shader_framebuffer_fetch = 1;
+ resources.EXT_YUV_target = 1;
+ resources.MaxDualSourceDrawBuffers = 1;
+
+ if (!translator->Init(resources))
+ {
+ return 0;
+ }
+
+ translators[key] = std::move(translator);
+ }
+
+ auto &translator = translators[key];
+
+ const char *shaderStrings[] = {reinterpret_cast<const char *>(data)};
+ translator->compile(shaderStrings, 1, options);
+
+ return 0;
+}
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp
index 68c6e9cea4..c89bc9fa76 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp
@@ -4,9 +4,9 @@
// found in the LICENSE file.
//
-#include "DiagnosticsBase.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
-#include <cassert>
+#include "common/debug.h"
namespace pp
{
@@ -15,122 +15,128 @@ Diagnostics::~Diagnostics()
{
}
-void Diagnostics::report(ID id,
- const SourceLocation &loc,
- const std::string &text)
+void Diagnostics::report(ID id, const SourceLocation &loc, const std::string &text)
{
- // TODO(alokp): Keep a count of errors and warnings.
print(id, loc, text);
}
-Diagnostics::Severity Diagnostics::severity(ID id)
+bool Diagnostics::isError(ID id)
{
if ((id > PP_ERROR_BEGIN) && (id < PP_ERROR_END))
- return PP_ERROR;
+ return true;
if ((id > PP_WARNING_BEGIN) && (id < PP_WARNING_END))
- return PP_WARNING;
+ return false;
- assert(false);
- return PP_ERROR;
+ UNREACHABLE();
+ return true;
}
-std::string Diagnostics::message(ID id)
+const char *Diagnostics::message(ID id)
{
switch (id)
{
- // Errors begin.
- case PP_INTERNAL_ERROR:
- return "internal error";
- case PP_OUT_OF_MEMORY:
- return "out of memory";
- case PP_INVALID_CHARACTER:
- return "invalid character";
- case PP_INVALID_NUMBER:
- return "invalid number";
- case PP_INTEGER_OVERFLOW:
- return "integer overflow";
- case PP_FLOAT_OVERFLOW:
- return "float overflow";
- case PP_TOKEN_TOO_LONG:
- return "token too long";
- case PP_INVALID_EXPRESSION:
- return "invalid expression";
- case PP_DIVISION_BY_ZERO:
- return "division by zero";
- case PP_EOF_IN_COMMENT:
- return "unexpected end of file found in comment";
- case PP_UNEXPECTED_TOKEN:
- return "unexpected token";
- case PP_DIRECTIVE_INVALID_NAME:
- return "invalid directive name";
- case PP_MACRO_NAME_RESERVED:
- return "macro name is reserved";
- case PP_MACRO_REDEFINED:
- return "macro redefined";
- case PP_MACRO_PREDEFINED_REDEFINED:
- return "predefined macro redefined";
- case PP_MACRO_PREDEFINED_UNDEFINED:
- return "predefined macro undefined";
- case PP_MACRO_UNTERMINATED_INVOCATION:
- return "unterminated macro invocation";
- case PP_MACRO_TOO_FEW_ARGS:
- return "Not enough arguments for macro";
- case PP_MACRO_TOO_MANY_ARGS:
- return "Too many arguments for macro";
- case PP_MACRO_DUPLICATE_PARAMETER_NAMES:
- return "duplicate macro parameter name";
- case PP_CONDITIONAL_ENDIF_WITHOUT_IF:
- return "unexpected #endif found without a matching #if";
- case PP_CONDITIONAL_ELSE_WITHOUT_IF:
- return "unexpected #else found without a matching #if";
- case PP_CONDITIONAL_ELSE_AFTER_ELSE:
- return "unexpected #else found after another #else";
- case PP_CONDITIONAL_ELIF_WITHOUT_IF:
- return "unexpected #elif found without a matching #if";
- case PP_CONDITIONAL_ELIF_AFTER_ELSE:
- return "unexpected #elif found after #else";
- case PP_CONDITIONAL_UNTERMINATED:
- return "unexpected end of file found in conditional block";
- case PP_INVALID_EXTENSION_NAME:
- return "invalid extension name";
- case PP_INVALID_EXTENSION_BEHAVIOR:
- return "invalid extension behavior";
- case PP_INVALID_EXTENSION_DIRECTIVE:
- return "invalid extension directive";
- case PP_INVALID_VERSION_NUMBER:
- return "invalid version number";
- case PP_INVALID_VERSION_DIRECTIVE:
- return "invalid version directive";
- case PP_VERSION_NOT_FIRST_STATEMENT:
- return "#version directive must occur before anything else, "
- "except for comments and white space";
- case PP_VERSION_NOT_FIRST_LINE_ESSL3:
- return "#version directive must occur on the first line of the shader";
- case PP_INVALID_LINE_NUMBER:
- return "invalid line number";
- case PP_INVALID_FILE_NUMBER:
- return "invalid file number";
- case PP_INVALID_LINE_DIRECTIVE:
- return "invalid line directive";
- case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3:
- return "extension directive must occur before any non-preprocessor tokens in ESSL3";
- // Errors end.
- // Warnings begin.
- case PP_EOF_IN_DIRECTIVE:
- return "unexpected end of file found in directive";
- case PP_CONDITIONAL_UNEXPECTED_TOKEN:
- return "unexpected token after conditional expression";
- case PP_UNRECOGNIZED_PRAGMA:
- return "unrecognized pragma";
- case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1:
- return "extension directive should occur before any non-preprocessor tokens";
- case PP_WARNING_MACRO_NAME_RESERVED:
- return "macro name with a double underscore is reserved - unintented behavior is possible";
- // Warnings end.
- default:
- assert(false);
- return "";
+ // Errors begin.
+ case PP_INTERNAL_ERROR:
+ return "internal error";
+ case PP_OUT_OF_MEMORY:
+ return "out of memory";
+ case PP_INVALID_CHARACTER:
+ return "invalid character";
+ case PP_INVALID_NUMBER:
+ return "invalid number";
+ case PP_INTEGER_OVERFLOW:
+ return "integer overflow";
+ case PP_FLOAT_OVERFLOW:
+ return "float overflow";
+ case PP_TOKEN_TOO_LONG:
+ return "token too long";
+ case PP_INVALID_EXPRESSION:
+ return "invalid expression";
+ case PP_DIVISION_BY_ZERO:
+ return "division by zero";
+ case PP_EOF_IN_COMMENT:
+ return "unexpected end of file found in comment";
+ case PP_UNEXPECTED_TOKEN:
+ return "unexpected token";
+ case PP_DIRECTIVE_INVALID_NAME:
+ return "invalid directive name";
+ case PP_MACRO_NAME_RESERVED:
+ return "macro name is reserved";
+ case PP_MACRO_REDEFINED:
+ return "macro redefined";
+ case PP_MACRO_PREDEFINED_REDEFINED:
+ return "predefined macro redefined";
+ case PP_MACRO_PREDEFINED_UNDEFINED:
+ return "predefined macro undefined";
+ case PP_MACRO_UNTERMINATED_INVOCATION:
+ return "unterminated macro invocation";
+ case PP_MACRO_UNDEFINED_WHILE_INVOKED:
+ return "macro undefined while being invoked";
+ case PP_MACRO_TOO_FEW_ARGS:
+ return "Not enough arguments for macro";
+ case PP_MACRO_TOO_MANY_ARGS:
+ return "Too many arguments for macro";
+ case PP_MACRO_DUPLICATE_PARAMETER_NAMES:
+ return "duplicate macro parameter name";
+ case PP_MACRO_INVOCATION_CHAIN_TOO_DEEP:
+ return "macro invocation chain too deep";
+ case PP_CONDITIONAL_ENDIF_WITHOUT_IF:
+ return "unexpected #endif found without a matching #if";
+ case PP_CONDITIONAL_ELSE_WITHOUT_IF:
+ return "unexpected #else found without a matching #if";
+ case PP_CONDITIONAL_ELSE_AFTER_ELSE:
+ return "unexpected #else found after another #else";
+ case PP_CONDITIONAL_ELIF_WITHOUT_IF:
+ return "unexpected #elif found without a matching #if";
+ case PP_CONDITIONAL_ELIF_AFTER_ELSE:
+ return "unexpected #elif found after #else";
+ case PP_CONDITIONAL_UNTERMINATED:
+ return "unexpected end of file found in conditional block";
+ case PP_INVALID_EXTENSION_NAME:
+ return "invalid extension name";
+ case PP_INVALID_EXTENSION_BEHAVIOR:
+ return "invalid extension behavior";
+ case PP_INVALID_EXTENSION_DIRECTIVE:
+ return "invalid extension directive";
+ case PP_INVALID_VERSION_NUMBER:
+ return "invalid version number";
+ case PP_INVALID_VERSION_DIRECTIVE:
+ return "invalid version directive";
+ case PP_VERSION_NOT_FIRST_STATEMENT:
+ return "#version directive must occur before anything else, "
+ "except for comments and white space";
+ case PP_VERSION_NOT_FIRST_LINE_ESSL3:
+ return "#version directive must occur on the first line of the shader";
+ case PP_INVALID_LINE_NUMBER:
+ return "invalid line number";
+ case PP_INVALID_FILE_NUMBER:
+ return "invalid file number";
+ case PP_INVALID_LINE_DIRECTIVE:
+ return "invalid line directive";
+ case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3:
+ return "extension directive must occur before any non-preprocessor tokens in ESSL3";
+ case PP_UNDEFINED_SHIFT:
+ return "shift exponent is negative or undefined";
+ case PP_TOKENIZER_ERROR:
+ return "internal tokenizer error";
+ // Errors end.
+ // Warnings begin.
+ case PP_EOF_IN_DIRECTIVE:
+ return "unexpected end of file found in directive";
+ case PP_CONDITIONAL_UNEXPECTED_TOKEN:
+ return "unexpected token after conditional expression";
+ case PP_UNRECOGNIZED_PRAGMA:
+ return "unrecognized pragma";
+ case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1:
+ return "extension directive should occur before any non-preprocessor tokens";
+ case PP_WARNING_MACRO_NAME_RESERVED:
+ return "macro name with a double underscore is reserved - unintented behavior is "
+ "possible";
+ // Warnings end.
+ default:
+ UNREACHABLE();
+ return "";
}
}
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h
index d26c174f01..ea37614606 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h
@@ -19,11 +19,6 @@ struct SourceLocation;
class Diagnostics
{
public:
- enum Severity
- {
- PP_ERROR,
- PP_WARNING
- };
enum ID
{
PP_ERROR_BEGIN,
@@ -44,9 +39,11 @@ class Diagnostics
PP_MACRO_PREDEFINED_REDEFINED,
PP_MACRO_PREDEFINED_UNDEFINED,
PP_MACRO_UNTERMINATED_INVOCATION,
+ PP_MACRO_UNDEFINED_WHILE_INVOKED,
PP_MACRO_TOO_FEW_ARGS,
PP_MACRO_TOO_MANY_ARGS,
PP_MACRO_DUPLICATE_PARAMETER_NAMES,
+ PP_MACRO_INVOCATION_CHAIN_TOO_DEEP,
PP_CONDITIONAL_ENDIF_WITHOUT_IF,
PP_CONDITIONAL_ELSE_WITHOUT_IF,
PP_CONDITIONAL_ELSE_AFTER_ELSE,
@@ -65,6 +62,8 @@ class Diagnostics
PP_INVALID_FILE_NUMBER,
PP_INVALID_LINE_DIRECTIVE,
PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3,
+ PP_UNDEFINED_SHIFT,
+ PP_TOKENIZER_ERROR,
PP_ERROR_END,
PP_WARNING_BEGIN,
@@ -80,12 +79,10 @@ class Diagnostics
void report(ID id, const SourceLocation &loc, const std::string &text);
protected:
- Severity severity(ID id);
- std::string message(ID id);
+ bool isError(ID id);
+ const char *message(ID id);
- virtual void print(ID id,
- const SourceLocation &loc,
- const std::string &text) = 0;
+ virtual void print(ID id, const SourceLocation &loc, const std::string &text) = 0;
};
} // namespace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp
index ef35c6ed50..049dae9071 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
-#include "DirectiveHandlerBase.h"
+#include "compiler/preprocessor/DirectiveHandlerBase.h"
namespace pp
{
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.h b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.h
index cf67895764..6c81d015f5 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveHandlerBase.h
@@ -23,8 +23,7 @@ class DirectiveHandler
public:
virtual ~DirectiveHandler();
- virtual void handleError(const SourceLocation &loc,
- const std::string &msg) = 0;
+ virtual void handleError(const SourceLocation &loc, const std::string &msg) = 0;
// Handle pragma of form: #pragma name[(value)]
virtual void handlePragma(const SourceLocation &loc,
@@ -36,8 +35,7 @@ class DirectiveHandler
const std::string &name,
const std::string &behavior) = 0;
- virtual void handleVersion(const SourceLocation &loc,
- int version) = 0;
+ virtual void handleVersion(const SourceLocation &loc, int version) = 0;
};
} // namespace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp
index 2faa331378..f6c5763990 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp
@@ -4,21 +4,22 @@
// found in the LICENSE file.
//
-#include "DirectiveParser.h"
+#include "compiler/preprocessor/DirectiveParser.h"
#include <algorithm>
-#include <cassert>
#include <cstdlib>
#include <sstream>
-#include "DiagnosticsBase.h"
-#include "DirectiveHandlerBase.h"
-#include "ExpressionParser.h"
-#include "MacroExpander.h"
-#include "Token.h"
-#include "Tokenizer.h"
+#include "common/debug.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/DirectiveHandlerBase.h"
+#include "compiler/preprocessor/ExpressionParser.h"
+#include "compiler/preprocessor/MacroExpander.h"
+#include "compiler/preprocessor/Token.h"
+#include "compiler/preprocessor/Tokenizer.h"
-namespace {
+namespace
+{
enum DirectiveType
{
DIRECTIVE_NONE,
@@ -39,19 +40,19 @@ enum DirectiveType
DirectiveType getDirective(const pp::Token *token)
{
- const char kDirectiveDefine[] = "define";
- const char kDirectiveUndef[] = "undef";
- const char kDirectiveIf[] = "if";
- const char kDirectiveIfdef[] = "ifdef";
- const char kDirectiveIfndef[] = "ifndef";
- const char kDirectiveElse[] = "else";
- const char kDirectiveElif[] = "elif";
- const char kDirectiveEndif[] = "endif";
- const char kDirectiveError[] = "error";
- const char kDirectivePragma[] = "pragma";
+ const char kDirectiveDefine[] = "define";
+ const char kDirectiveUndef[] = "undef";
+ const char kDirectiveIf[] = "if";
+ const char kDirectiveIfdef[] = "ifdef";
+ const char kDirectiveIfndef[] = "ifndef";
+ const char kDirectiveElse[] = "else";
+ const char kDirectiveElif[] = "elif";
+ const char kDirectiveEndif[] = "endif";
+ const char kDirectiveError[] = "error";
+ const char kDirectivePragma[] = "pragma";
const char kDirectiveExtension[] = "extension";
- const char kDirectiveVersion[] = "version";
- const char kDirectiveLine[] = "line";
+ const char kDirectiveVersion[] = "version";
+ const char kDirectiveLine[] = "line";
if (token->type != pp::Token::IDENTIFIER)
return DIRECTIVE_NONE;
@@ -90,15 +91,15 @@ bool isConditionalDirective(DirectiveType directive)
{
switch (directive)
{
- case DIRECTIVE_IF:
- case DIRECTIVE_IFDEF:
- case DIRECTIVE_IFNDEF:
- case DIRECTIVE_ELSE:
- case DIRECTIVE_ELIF:
- case DIRECTIVE_ENDIF:
- return true;
- default:
- return false;
+ case DIRECTIVE_IF:
+ case DIRECTIVE_IFDEF:
+ case DIRECTIVE_IFNDEF:
+ case DIRECTIVE_ELSE:
+ case DIRECTIVE_ELIF:
+ case DIRECTIVE_ENDIF:
+ return true;
+ default:
+ return false;
}
}
@@ -110,7 +111,7 @@ bool isEOD(const pp::Token *token)
void skipUntilEOD(pp::Lexer *lexer, pp::Token *token)
{
- while(!isEOD(token))
+ while (!isEOD(token))
{
lexer->lex(token);
}
@@ -118,8 +119,8 @@ void skipUntilEOD(pp::Lexer *lexer, pp::Token *token)
bool isMacroNameReserved(const std::string &name)
{
- // Names prefixed with "GL_" are reserved.
- return (name.substr(0, 3) == "GL_");
+ // Names prefixed with "GL_" and the name "defined" are reserved.
+ return name == "defined" || (name.substr(0, 3) == "GL_");
}
bool hasDoubleUnderscores(const std::string &name)
@@ -127,11 +128,10 @@ bool hasDoubleUnderscores(const std::string &name)
return (name.find("__") != std::string::npos);
}
-bool isMacroPredefined(const std::string &name,
- const pp::MacroSet &macroSet)
+bool isMacroPredefined(const std::string &name, const pp::MacroSet &macroSet)
{
pp::MacroSet::const_iterator iter = macroSet.find(name);
- return iter != macroSet.end() ? iter->second.predefined : false;
+ return iter != macroSet.end() ? iter->second->predefined : false;
}
} // namespace anonymous
@@ -139,17 +139,83 @@ bool isMacroPredefined(const std::string &name,
namespace pp
{
+class DefinedParser : public Lexer
+{
+ public:
+ DefinedParser(Lexer *lexer, const MacroSet *macroSet, Diagnostics *diagnostics)
+ : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics)
+ {
+ }
+
+ protected:
+ void lex(Token *token) override
+ {
+ const char kDefined[] = "defined";
+
+ mLexer->lex(token);
+ if (token->type != Token::IDENTIFIER)
+ return;
+ if (token->text != kDefined)
+ return;
+
+ bool paren = false;
+ mLexer->lex(token);
+ if (token->type == '(')
+ {
+ paren = true;
+ mLexer->lex(token);
+ }
+
+ if (token->type != Token::IDENTIFIER)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
+ skipUntilEOD(mLexer, token);
+ return;
+ }
+ MacroSet::const_iterator iter = mMacroSet->find(token->text);
+ std::string expression = iter != mMacroSet->end() ? "1" : "0";
+
+ if (paren)
+ {
+ mLexer->lex(token);
+ if (token->type != ')')
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+ token->text);
+ skipUntilEOD(mLexer, token);
+ return;
+ }
+ }
+
+ // We have a valid defined operator.
+ // Convert the current token into a CONST_INT token.
+ token->type = Token::CONST_INT;
+ token->text = expression;
+ }
+
+ private:
+ Lexer *mLexer;
+ const MacroSet *mMacroSet;
+ Diagnostics *mDiagnostics;
+};
+
DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
MacroSet *macroSet,
Diagnostics *diagnostics,
- DirectiveHandler *directiveHandler)
+ DirectiveHandler *directiveHandler,
+ int maxMacroExpansionDepth)
: mPastFirstStatement(false),
mSeenNonPreprocessorToken(false),
mTokenizer(tokenizer),
mMacroSet(macroSet),
mDiagnostics(diagnostics),
mDirectiveHandler(directiveHandler),
- mShaderVersion(100)
+ mShaderVersion(100),
+ mMaxMacroExpansionDepth(maxMacroExpansionDepth)
+{
+}
+
+DirectiveParser::~DirectiveParser()
{
}
@@ -174,21 +240,20 @@ void DirectiveParser::lex(Token *token)
if (!mConditionalStack.empty())
{
const ConditionalBlock &block = mConditionalStack.back();
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNTERMINATED,
- block.location, block.type);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNTERMINATED, block.location,
+ block.type);
}
break;
}
- }
- while (skipping() || (token->type == '\n'));
+ } while (skipping() || (token->type == '\n'));
mPastFirstStatement = true;
}
void DirectiveParser::parseDirective(Token *token)
{
- assert(token->type == Token::PP_HASH);
+ ASSERT(token->type == Token::PP_HASH);
mTokenizer->lex(token);
if (isEOD(token))
@@ -207,86 +272,83 @@ void DirectiveParser::parseDirective(Token *token)
return;
}
- switch(directive)
+ switch (directive)
{
- case DIRECTIVE_NONE:
- mDiagnostics->report(Diagnostics::PP_DIRECTIVE_INVALID_NAME,
- token->location, token->text);
- skipUntilEOD(mTokenizer, token);
- break;
- case DIRECTIVE_DEFINE:
- parseDefine(token);
- break;
- case DIRECTIVE_UNDEF:
- parseUndef(token);
- break;
- case DIRECTIVE_IF:
- parseIf(token);
- break;
- case DIRECTIVE_IFDEF:
- parseIfdef(token);
- break;
- case DIRECTIVE_IFNDEF:
- parseIfndef(token);
- break;
- case DIRECTIVE_ELSE:
- parseElse(token);
- break;
- case DIRECTIVE_ELIF:
- parseElif(token);
- break;
- case DIRECTIVE_ENDIF:
- parseEndif(token);
- break;
- case DIRECTIVE_ERROR:
- parseError(token);
- break;
- case DIRECTIVE_PRAGMA:
- parsePragma(token);
- break;
- case DIRECTIVE_EXTENSION:
- parseExtension(token);
- break;
- case DIRECTIVE_VERSION:
- parseVersion(token);
- break;
- case DIRECTIVE_LINE:
- parseLine(token);
- break;
- default:
- assert(false);
- break;
+ case DIRECTIVE_NONE:
+ mDiagnostics->report(Diagnostics::PP_DIRECTIVE_INVALID_NAME, token->location,
+ token->text);
+ skipUntilEOD(mTokenizer, token);
+ break;
+ case DIRECTIVE_DEFINE:
+ parseDefine(token);
+ break;
+ case DIRECTIVE_UNDEF:
+ parseUndef(token);
+ break;
+ case DIRECTIVE_IF:
+ parseIf(token);
+ break;
+ case DIRECTIVE_IFDEF:
+ parseIfdef(token);
+ break;
+ case DIRECTIVE_IFNDEF:
+ parseIfndef(token);
+ break;
+ case DIRECTIVE_ELSE:
+ parseElse(token);
+ break;
+ case DIRECTIVE_ELIF:
+ parseElif(token);
+ break;
+ case DIRECTIVE_ENDIF:
+ parseEndif(token);
+ break;
+ case DIRECTIVE_ERROR:
+ parseError(token);
+ break;
+ case DIRECTIVE_PRAGMA:
+ parsePragma(token);
+ break;
+ case DIRECTIVE_EXTENSION:
+ parseExtension(token);
+ break;
+ case DIRECTIVE_VERSION:
+ parseVersion(token);
+ break;
+ case DIRECTIVE_LINE:
+ parseLine(token);
+ break;
+ default:
+ UNREACHABLE();
+ break;
}
skipUntilEOD(mTokenizer, token);
if (token->type == Token::LAST)
{
- mDiagnostics->report(Diagnostics::PP_EOF_IN_DIRECTIVE,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_EOF_IN_DIRECTIVE, token->location, token->text);
}
}
void DirectiveParser::parseDefine(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_DEFINE);
+ ASSERT(getDirective(token) == DIRECTIVE_DEFINE);
mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER)
{
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
return;
}
if (isMacroPredefined(token->text, *mMacroSet))
{
- mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_REDEFINED,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_REDEFINED, token->location,
+ token->text);
return;
}
if (isMacroNameReserved(token->text))
{
- mDiagnostics->report(Diagnostics::PP_MACRO_NAME_RESERVED,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_MACRO_NAME_RESERVED, token->location, token->text);
return;
}
// Using double underscores is allowed, but may result in unintended
@@ -300,39 +362,37 @@ void DirectiveParser::parseDefine(Token *token)
token->text);
}
- Macro macro;
- macro.type = Macro::kTypeObj;
- macro.name = token->text;
+ std::shared_ptr<Macro> macro = std::make_shared<Macro>();
+ macro->type = Macro::kTypeObj;
+ macro->name = token->text;
mTokenizer->lex(token);
if (token->type == '(' && !token->hasLeadingSpace())
{
// Function-like macro. Collect arguments.
- macro.type = Macro::kTypeFunc;
+ macro->type = Macro::kTypeFunc;
do
{
mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER)
break;
- if (std::find(macro.parameters.begin(), macro.parameters.end(), token->text) != macro.parameters.end())
+ if (std::find(macro->parameters.begin(), macro->parameters.end(), token->text) !=
+ macro->parameters.end())
{
mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES,
token->location, token->text);
return;
}
- macro.parameters.push_back(token->text);
+ macro->parameters.push_back(token->text);
mTokenizer->lex(token); // Get ','.
- }
- while (token->type == ',');
+ } while (token->type == ',');
if (token->type != ')')
{
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location,
- token->text);
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
return;
}
mTokenizer->lex(token); // Get ')'.
@@ -344,47 +404,51 @@ void DirectiveParser::parseDefine(Token *token)
// list. Resetting it also allows us to reuse Token::equals() to
// compare macros.
token->location = SourceLocation();
- macro.replacements.push_back(*token);
+ macro->replacements.push_back(*token);
mTokenizer->lex(token);
}
- if (!macro.replacements.empty())
+ if (!macro->replacements.empty())
{
// Whitespace preceding the replacement list is not considered part of
// the replacement list for either form of macro.
- macro.replacements.front().setHasLeadingSpace(false);
+ macro->replacements.front().setHasLeadingSpace(false);
}
// Check for macro redefinition.
- MacroSet::const_iterator iter = mMacroSet->find(macro.name);
- if (iter != mMacroSet->end() && !macro.equals(iter->second))
+ MacroSet::const_iterator iter = mMacroSet->find(macro->name);
+ if (iter != mMacroSet->end() && !macro->equals(*iter->second))
{
- mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED,
- token->location,
- macro.name);
+ mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED, token->location, macro->name);
return;
}
- mMacroSet->insert(std::make_pair(macro.name, macro));
+ mMacroSet->insert(std::make_pair(macro->name, macro));
}
void DirectiveParser::parseUndef(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_UNDEF);
+ ASSERT(getDirective(token) == DIRECTIVE_UNDEF);
mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER)
{
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
return;
}
MacroSet::iterator iter = mMacroSet->find(token->text);
if (iter != mMacroSet->end())
{
- if (iter->second.predefined)
+ if (iter->second->predefined)
{
- mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED, token->location,
+ token->text);
+ return;
+ }
+ else if (iter->second->expansionCount > 0)
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location,
+ token->text);
+ return;
}
else
{
@@ -395,38 +459,37 @@ void DirectiveParser::parseUndef(Token *token)
mTokenizer->lex(token);
if (!isEOD(token))
{
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
skipUntilEOD(mTokenizer, token);
}
}
void DirectiveParser::parseIf(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_IF);
+ ASSERT(getDirective(token) == DIRECTIVE_IF);
parseConditionalIf(token);
}
void DirectiveParser::parseIfdef(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_IFDEF);
+ ASSERT(getDirective(token) == DIRECTIVE_IFDEF);
parseConditionalIf(token);
}
void DirectiveParser::parseIfndef(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_IFNDEF);
+ ASSERT(getDirective(token) == DIRECTIVE_IFNDEF);
parseConditionalIf(token);
}
void DirectiveParser::parseElse(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_ELSE);
+ ASSERT(getDirective(token) == DIRECTIVE_ELSE);
if (mConditionalStack.empty())
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
return;
}
@@ -440,34 +503,34 @@ void DirectiveParser::parseElse(Token *token)
}
if (block.foundElseGroup)
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
return;
}
- block.foundElseGroup = true;
- block.skipGroup = block.foundValidGroup;
+ block.foundElseGroup = true;
+ block.skipGroup = block.foundValidGroup;
block.foundValidGroup = true;
// Check if there are extra tokens after #else.
mTokenizer->lex(token);
if (!isEOD(token))
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
}
}
void DirectiveParser::parseElif(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_ELIF);
+ ASSERT(getDirective(token) == DIRECTIVE_ELIF);
if (mConditionalStack.empty())
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
return;
}
@@ -481,8 +544,8 @@ void DirectiveParser::parseElif(Token *token)
}
if (block.foundElseGroup)
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
return;
}
@@ -495,19 +558,19 @@ void DirectiveParser::parseElif(Token *token)
return;
}
- int expression = parseExpressionIf(token);
- block.skipGroup = expression == 0;
+ int expression = parseExpressionIf(token);
+ block.skipGroup = expression == 0;
block.foundValidGroup = expression != 0;
}
void DirectiveParser::parseEndif(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_ENDIF);
+ ASSERT(getDirective(token) == DIRECTIVE_ENDIF);
if (mConditionalStack.empty())
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
return;
}
@@ -518,15 +581,15 @@ void DirectiveParser::parseEndif(Token *token)
mTokenizer->lex(token);
if (!isEOD(token))
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
}
}
void DirectiveParser::parseError(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_ERROR);
+ ASSERT(getDirective(token) == DIRECTIVE_ERROR);
std::ostringstream stream;
mTokenizer->lex(token);
@@ -541,7 +604,7 @@ void DirectiveParser::parseError(Token *token)
// Parses pragma of form: #pragma name[(value)].
void DirectiveParser::parsePragma(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_PRAGMA);
+ ASSERT(getDirective(token) == DIRECTIVE_PRAGMA);
enum State
{
@@ -563,25 +626,25 @@ void DirectiveParser::parsePragma(Token *token)
}
while ((token->type != '\n') && (token->type != Token::LAST))
{
- switch(state++)
+ switch (state++)
{
- case PRAGMA_NAME:
- name = token->text;
- valid = valid && (token->type == Token::IDENTIFIER);
- break;
- case LEFT_PAREN:
- valid = valid && (token->type == '(');
- break;
- case PRAGMA_VALUE:
- value = token->text;
- valid = valid && (token->type == Token::IDENTIFIER);
- break;
- case RIGHT_PAREN:
- valid = valid && (token->type == ')');
- break;
- default:
- valid = false;
- break;
+ case PRAGMA_NAME:
+ name = token->text;
+ valid = valid && (token->type == Token::IDENTIFIER);
+ break;
+ case LEFT_PAREN:
+ valid = valid && (token->type == '(');
+ break;
+ case PRAGMA_VALUE:
+ value = token->text;
+ valid = valid && (token->type == Token::IDENTIFIER);
+ break;
+ case RIGHT_PAREN:
+ valid = valid && (token->type == ')');
+ break;
+ default:
+ valid = false;
+ break;
}
mTokenizer->lex(token);
}
@@ -591,8 +654,7 @@ void DirectiveParser::parsePragma(Token *token)
(state == RIGHT_PAREN + 1)); // With value.
if (!valid)
{
- mDiagnostics->report(Diagnostics::PP_UNRECOGNIZED_PRAGMA,
- token->location, name);
+ mDiagnostics->report(Diagnostics::PP_UNRECOGNIZED_PRAGMA, token->location, name);
}
else if (state > PRAGMA_NAME) // Do not notify for empty pragma.
{
@@ -602,7 +664,7 @@ void DirectiveParser::parsePragma(Token *token)
void DirectiveParser::parseExtension(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_EXTENSION);
+ ASSERT(getDirective(token) == DIRECTIVE_EXTENSION);
enum State
{
@@ -620,47 +682,49 @@ void DirectiveParser::parseExtension(Token *token)
{
switch (state++)
{
- case EXT_NAME:
- if (valid && (token->type != Token::IDENTIFIER))
- {
- mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_NAME,
- token->location, token->text);
- valid = false;
- }
- if (valid) name = token->text;
- break;
- case COLON:
- if (valid && (token->type != ':'))
- {
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
- valid = false;
- }
- break;
- case EXT_BEHAVIOR:
- if (valid && (token->type != Token::IDENTIFIER))
- {
- mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_BEHAVIOR,
- token->location, token->text);
- valid = false;
- }
- if (valid) behavior = token->text;
- break;
- default:
- if (valid)
- {
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
- valid = false;
- }
- break;
+ case EXT_NAME:
+ if (valid && (token->type != Token::IDENTIFIER))
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_NAME, token->location,
+ token->text);
+ valid = false;
+ }
+ if (valid)
+ name = token->text;
+ break;
+ case COLON:
+ if (valid && (token->type != ':'))
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+ token->text);
+ valid = false;
+ }
+ break;
+ case EXT_BEHAVIOR:
+ if (valid && (token->type != Token::IDENTIFIER))
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_BEHAVIOR,
+ token->location, token->text);
+ valid = false;
+ }
+ if (valid)
+ behavior = token->text;
+ break;
+ default:
+ if (valid)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+ token->text);
+ valid = false;
+ }
+ break;
}
mTokenizer->lex(token);
}
if (valid && (state != EXT_BEHAVIOR + 1))
{
- mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_DIRECTIVE,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_DIRECTIVE, token->location,
+ token->text);
valid = false;
}
if (valid && mSeenNonPreprocessorToken)
@@ -683,12 +747,12 @@ void DirectiveParser::parseExtension(Token *token)
void DirectiveParser::parseVersion(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_VERSION);
+ ASSERT(getDirective(token) == DIRECTIVE_VERSION);
if (mPastFirstStatement)
{
- mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
return;
}
@@ -700,47 +764,47 @@ void DirectiveParser::parseVersion(Token *token)
VERSION_ENDLINE
};
- bool valid = true;
+ bool valid = true;
int version = 0;
- int state = VERSION_NUMBER;
+ int state = VERSION_NUMBER;
mTokenizer->lex(token);
while (valid && (token->type != '\n') && (token->type != Token::LAST))
{
switch (state)
{
- case VERSION_NUMBER:
- if (token->type != Token::CONST_INT)
- {
- mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER,
- token->location, token->text);
- valid = false;
- }
- if (valid && !token->iValue(&version))
- {
- mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW,
- token->location, token->text);
- valid = false;
- }
- if (valid)
- {
- state = (version < 300) ? VERSION_ENDLINE : VERSION_PROFILE;
- }
- break;
- case VERSION_PROFILE:
- if (token->type != Token::IDENTIFIER || token->text != "es")
- {
- mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE,
- token->location, token->text);
+ case VERSION_NUMBER:
+ if (token->type != Token::CONST_INT)
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER, token->location,
+ token->text);
+ valid = false;
+ }
+ if (valid && !token->iValue(&version))
+ {
+ mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, token->location,
+ token->text);
+ valid = false;
+ }
+ if (valid)
+ {
+ state = (version < 300) ? VERSION_ENDLINE : VERSION_PROFILE;
+ }
+ break;
+ case VERSION_PROFILE:
+ if (token->type != Token::IDENTIFIER || token->text != "es")
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, token->location,
+ token->text);
+ valid = false;
+ }
+ state = VERSION_ENDLINE;
+ break;
+ default:
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+ token->text);
valid = false;
- }
- state = VERSION_ENDLINE;
- break;
- default:
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
- valid = false;
- break;
+ break;
}
mTokenizer->lex(token);
@@ -748,15 +812,15 @@ void DirectiveParser::parseVersion(Token *token)
if (valid && (state != VERSION_ENDLINE))
{
- mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, token->location,
+ token->text);
valid = false;
}
if (valid && version >= 300 && token->location.line > 1)
{
- mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3, token->location,
+ token->text);
valid = false;
}
@@ -770,13 +834,13 @@ void DirectiveParser::parseVersion(Token *token)
void DirectiveParser::parseLine(Token *token)
{
- assert(getDirective(token) == DIRECTIVE_LINE);
+ ASSERT(getDirective(token) == DIRECTIVE_LINE);
- bool valid = true;
+ bool valid = true;
bool parsedFileNumber = false;
int line = 0, file = 0;
- MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, false);
+ MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mMaxMacroExpansionDepth);
// Lex the first token after "#line" so we can check it for EOD.
macroExpander.lex(token);
@@ -814,8 +878,8 @@ void DirectiveParser::parseLine(Token *token)
{
if (valid)
{
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+ token->text);
valid = false;
}
skipUntilEOD(mTokenizer, token);
@@ -835,14 +899,14 @@ bool DirectiveParser::skipping() const
if (mConditionalStack.empty())
return false;
- const ConditionalBlock& block = mConditionalStack.back();
+ const ConditionalBlock &block = mConditionalStack.back();
return block.skipBlock || block.skipGroup;
}
void DirectiveParser::parseConditionalIf(Token *token)
{
ConditionalBlock block;
- block.type = token->text;
+ block.type = token->text;
block.location = token->location;
if (skipping())
@@ -861,20 +925,20 @@ void DirectiveParser::parseConditionalIf(Token *token)
int expression = 0;
switch (directive)
{
- case DIRECTIVE_IF:
- expression = parseExpressionIf(token);
- break;
- case DIRECTIVE_IFDEF:
- expression = parseExpressionIfdef(token);
- break;
- case DIRECTIVE_IFNDEF:
- expression = parseExpressionIfdef(token) == 0 ? 1 : 0;
- break;
- default:
- assert(false);
- break;
+ case DIRECTIVE_IF:
+ expression = parseExpressionIf(token);
+ break;
+ case DIRECTIVE_IFDEF:
+ expression = parseExpressionIfdef(token);
+ break;
+ case DIRECTIVE_IFNDEF:
+ expression = parseExpressionIfdef(token) == 0 ? 1 : 0;
+ break;
+ default:
+ UNREACHABLE();
+ break;
}
- block.skipGroup = expression == 0;
+ block.skipGroup = expression == 0;
block.foundValidGroup = expression != 0;
}
mConditionalStack.push_back(block);
@@ -882,16 +946,16 @@ void DirectiveParser::parseConditionalIf(Token *token)
int DirectiveParser::parseExpressionIf(Token *token)
{
- assert((getDirective(token) == DIRECTIVE_IF) ||
- (getDirective(token) == DIRECTIVE_ELIF));
+ ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF));
- MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, true);
+ DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics);
+ MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics, mMaxMacroExpansionDepth);
ExpressionParser expressionParser(&macroExpander, mDiagnostics);
int expression = 0;
ExpressionParser::ErrorSettings errorSettings;
errorSettings.integerLiteralsMustFit32BitSignedRange = false;
- errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN;
+ errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN;
bool valid = true;
expressionParser.parse(token, &expression, false, errorSettings, &valid);
@@ -899,8 +963,8 @@ int DirectiveParser::parseExpressionIf(Token *token)
// Check if there are tokens after #if expression.
if (!isEOD(token))
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
}
@@ -909,27 +973,25 @@ int DirectiveParser::parseExpressionIf(Token *token)
int DirectiveParser::parseExpressionIfdef(Token *token)
{
- assert((getDirective(token) == DIRECTIVE_IFDEF) ||
- (getDirective(token) == DIRECTIVE_IFNDEF));
+ ASSERT((getDirective(token) == DIRECTIVE_IFDEF) || (getDirective(token) == DIRECTIVE_IFNDEF));
mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER)
{
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
skipUntilEOD(mTokenizer, token);
return 0;
}
MacroSet::const_iterator iter = mMacroSet->find(token->text);
- int expression = iter != mMacroSet->end() ? 1 : 0;
+ int expression = iter != mMacroSet->end() ? 1 : 0;
// Check if there are tokens after #ifdef expression.
mTokenizer->lex(token);
if (!isEOD(token))
{
- mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
- token->location, token->text);
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
+ token->text);
skipUntilEOD(mTokenizer, token);
}
return expression;
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h
index 2888e289ce..29c30a8239 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h
@@ -7,10 +7,9 @@
#ifndef COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
#define COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
-#include "Lexer.h"
-#include "Macro.h"
-#include "pp_utils.h"
-#include "SourceLocation.h"
+#include "compiler/preprocessor/Lexer.h"
+#include "compiler/preprocessor/Macro.h"
+#include "compiler/preprocessor/SourceLocation.h"
namespace pp
{
@@ -25,13 +24,13 @@ class DirectiveParser : public Lexer
DirectiveParser(Tokenizer *tokenizer,
MacroSet *macroSet,
Diagnostics *diagnostics,
- DirectiveHandler *directiveHandler);
+ DirectiveHandler *directiveHandler,
+ int maxMacroExpansionDepth);
+ ~DirectiveParser() override;
void lex(Token *token) override;
private:
- PP_DISALLOW_COPY_AND_ASSIGN(DirectiveParser);
-
void parseDirective(Token *token);
void parseDefine(Token *token);
void parseUndef(Token *token);
@@ -62,22 +61,21 @@ class DirectiveParser : public Lexer
bool foundElseGroup;
ConditionalBlock()
- : skipBlock(false),
- skipGroup(false),
- foundValidGroup(false),
- foundElseGroup(false)
+ : skipBlock(false), skipGroup(false), foundValidGroup(false), foundElseGroup(false)
{
}
};
bool mPastFirstStatement;
- bool mSeenNonPreprocessorToken; // Tracks if a non-preprocessor token has been seen yet. Some macros, such as
- // #extension must be declared before all shader code.
+ bool mSeenNonPreprocessorToken; // Tracks if a non-preprocessor token has been seen yet. Some
+ // macros, such as
+ // #extension must be declared before all shader code.
std::vector<ConditionalBlock> mConditionalStack;
Tokenizer *mTokenizer;
MacroSet *mMacroSet;
Diagnostics *mDiagnostics;
DirectiveHandler *mDirectiveHandler;
int mShaderVersion;
+ int mMaxMacroExpansionDepth;
};
} // namespace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h
index 841c67b61c..0f2901b878 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h
@@ -7,8 +7,8 @@
#ifndef COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
#define COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
-#include "DiagnosticsBase.h"
-#include "pp_utils.h"
+#include "common/angleutils.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
namespace pp
{
@@ -16,7 +16,7 @@ namespace pp
class Lexer;
struct Token;
-class ExpressionParser
+class ExpressionParser : angle::NonCopyable
{
public:
struct ErrorSettings
@@ -34,8 +34,6 @@ class ExpressionParser
bool *valid);
private:
- PP_DISALLOW_COPY_AND_ASSIGN(ExpressionParser);
-
Lexer *mLexer;
Diagnostics *mDiagnostics;
};
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y
index 7b5d9e9cee..68d7cc3958 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y
+++ b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y
@@ -41,17 +41,15 @@ WHICH GENERATES THE GLSL ES preprocessor expression parser.
#include <cassert>
#include <sstream>
+#include <stdint.h>
#include "DiagnosticsBase.h"
#include "Lexer.h"
#include "Token.h"
+#include "common/mathutil.h"
-#if defined(_MSC_VER)
-typedef __int64 YYSTYPE;
-#else
-#include <stdint.h>
-typedef intmax_t YYSTYPE;
-#endif // _MSC_VER
+typedef int32_t YYSTYPE;
+typedef uint32_t UNSIGNED_TYPE;
#define YYENABLE_NLS 0
#define YYLTYPE_IS_TRIVIAL 1
@@ -196,16 +194,57 @@ expression
$$ = $1 < $3;
}
| expression TOK_OP_RIGHT expression {
- $$ = $1 >> $3;
+ if ($3 < 0 || $3 > 31)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << $1 << " >> " << $3;
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ $$ = static_cast<YYSTYPE>(0);
+ }
+ else if ($1 < 0)
+ {
+ // Logical shift right.
+ $$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) >> $3);
+ }
+ else
+ {
+ $$ = $1 >> $3;
+ }
}
| expression TOK_OP_LEFT expression {
- $$ = $1 << $3;
+ if ($3 < 0 || $3 > 31)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << $1 << " << " << $3;
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ $$ = static_cast<YYSTYPE>(0);
+ }
+ else
+ {
+ // Logical shift left. Casting to unsigned is needed to ensure there's no signed integer
+ // overflow, which some tools treat as an error.
+ $$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) << $3);
+ }
}
| expression '-' expression {
- $$ = $1 - $3;
+ $$ = gl::WrappingDiff<YYSTYPE>($1, $3);
}
| expression '+' expression {
- $$ = $1 + $3;
+ $$ = gl::WrappingSum<YYSTYPE>($1, $3);
}
| expression '%' expression {
if ($3 == 0)
@@ -222,6 +261,12 @@ expression
}
$$ = static_cast<YYSTYPE>(0);
}
+ else if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
+ {
+ // Check for the special case where the minimum representable number is
+ // divided by -1. If left alone this has undefined results.
+ $$ = 0;
+ }
else
{
$$ = $1 % $3;
@@ -242,13 +287,20 @@ expression
}
$$ = static_cast<YYSTYPE>(0);
}
+ else if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
+ {
+ // Check for the special case where the minimum representable number is
+ // divided by -1. If left alone this leads to integer overflow in C++, which
+ // has undefined results.
+ $$ = std::numeric_limits<YYSTYPE>::max();
+ }
else
{
$$ = $1 / $3;
}
}
| expression '*' expression {
- $$ = $1 * $3;
+ $$ = gl::WrappingMul($1, $3);
}
| '!' expression %prec TOK_UNARY {
$$ = ! $2;
@@ -257,7 +309,16 @@ expression
$$ = ~ $2;
}
| '-' expression %prec TOK_UNARY {
- $$ = - $2;
+ // Check for negation of minimum representable integer to prevent undefined signed int
+ // overflow.
+ if ($2 == std::numeric_limits<YYSTYPE>::min())
+ {
+ $$ = std::numeric_limits<YYSTYPE>::min();
+ }
+ else
+ {
+ $$ = -$2;
+ }
}
| '+' expression %prec TOK_UNARY {
$$ = + $2;
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp
index 5541d46f72..0f2327b823 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp
@@ -4,12 +4,13 @@
// found in the LICENSE file.
//
-#include "Input.h"
+#include "compiler/preprocessor/Input.h"
#include <algorithm>
-#include <cassert>
#include <cstring>
+#include "common/debug.h"
+
namespace pp
{
@@ -17,9 +18,12 @@ Input::Input() : mCount(0), mString(0)
{
}
-Input::Input(size_t count, const char *const string[], const int length[]) :
- mCount(count),
- mString(string)
+Input::~Input()
+{
+}
+
+Input::Input(size_t count, const char *const string[], const int length[])
+ : mCount(count), mString(string)
{
mLength.reserve(mCount);
for (size_t i = 0; i < mCount; ++i)
@@ -32,7 +36,7 @@ Input::Input(size_t count, const char *const string[], const int length[]) :
const char *Input::skipChar()
{
// This function should only be called when there is a character to skip.
- assert(mReadLoc.cIndex < mLength[mReadLoc.sIndex]);
+ ASSERT(mReadLoc.cIndex < mLength[mReadLoc.sIndex]);
++mReadLoc.cIndex;
if (mReadLoc.cIndex == mLength[mReadLoc.sIndex])
{
@@ -61,6 +65,11 @@ size_t Input::read(char *buf, size_t maxSize, int *lineNo)
{
// Line continuation of backslash + newline.
skipChar();
+ // Fake an EOF if the line number would overflow.
+ if (*lineNo == INT_MAX)
+ {
+ return 0;
+ }
++(*lineNo);
}
else if (c != nullptr && (*c) == '\r')
@@ -71,6 +80,11 @@ size_t Input::read(char *buf, size_t maxSize, int *lineNo)
{
skipChar();
}
+ // Fake an EOF if the line number would overflow.
+ if (*lineNo == INT_MAX)
+ {
+ return 0;
+ }
++(*lineNo);
}
else
@@ -86,7 +100,7 @@ size_t Input::read(char *buf, size_t maxSize, int *lineNo)
while ((nRead < maxRead) && (mReadLoc.sIndex < mCount))
{
size_t size = mLength[mReadLoc.sIndex] - mReadLoc.cIndex;
- size = std::min(size, maxSize);
+ size = std::min(size, maxSize);
for (size_t i = 0; i < size; ++i)
{
// Stop if a possible line continuation is encountered.
@@ -94,7 +108,7 @@ size_t Input::read(char *buf, size_t maxSize, int *lineNo)
// and increments line number if necessary.
if (*(mString[mReadLoc.sIndex] + mReadLoc.cIndex + i) == '\\')
{
- size = i;
+ size = i;
maxRead = nRead + size; // Stop reading right before the backslash.
}
}
@@ -113,4 +127,3 @@ size_t Input::read(char *buf, size_t maxSize, int *lineNo)
}
} // namespace pp
-
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Input.h b/src/3rdparty/angle/src/compiler/preprocessor/Input.h
index a1de7ddd86..8c7c7ee19e 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Input.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Input.h
@@ -7,7 +7,7 @@
#ifndef COMPILER_PREPROCESSOR_INPUT_H_
#define COMPILER_PREPROCESSOR_INPUT_H_
-#include <stddef.h>
+#include <cstddef>
#include <vector>
namespace pp
@@ -18,20 +18,12 @@ class Input
{
public:
Input();
+ ~Input();
Input(size_t count, const char *const string[], const int length[]);
- size_t count() const
- {
- return mCount;
- }
- const char *string(size_t index) const
- {
- return mString[index];
- }
- size_t length(size_t index) const
- {
- return mLength[index];
- }
+ size_t count() const { return mCount; }
+ const char *string(size_t index) const { return mString[index]; }
+ size_t length(size_t index) const { return mLength[index]; }
size_t read(char *buf, size_t maxSize, int *lineNo);
@@ -40,11 +32,7 @@ class Input
size_t sIndex; // String index;
size_t cIndex; // Char index.
- Location()
- : sIndex(0),
- cIndex(0)
- {
- }
+ Location() : sIndex(0), cIndex(0) {}
};
const Location &readLoc() const { return mReadLoc; }
@@ -55,7 +43,7 @@ class Input
// Input.
size_t mCount;
- const char * const *mString;
+ const char *const *mString;
std::vector<size_t> mLength;
Location mReadLoc;
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Lexer.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Lexer.cpp
index 7c663ee761..89cb3cf44e 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Lexer.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Lexer.cpp
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
-#include "Lexer.h"
+#include "compiler/preprocessor/Lexer.h"
namespace pp
{
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Lexer.h b/src/3rdparty/angle/src/compiler/preprocessor/Lexer.h
index 990dc5e21d..775bc0a202 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Lexer.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Lexer.h
@@ -7,12 +7,14 @@
#ifndef COMPILER_PREPROCESSOR_LEXER_H_
#define COMPILER_PREPROCESSOR_LEXER_H_
+#include "common/angleutils.h"
+
namespace pp
{
struct Token;
-class Lexer
+class Lexer : angle::NonCopyable
{
public:
virtual ~Lexer();
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp
index 4c4d5fd2e2..52e2312fe6 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp
@@ -4,40 +4,41 @@
// found in the LICENSE file.
//
-#include "Macro.h"
+#include "compiler/preprocessor/Macro.h"
-#include <sstream>
-
-#include "Token.h"
+#include "common/angleutils.h"
+#include "compiler/preprocessor/Token.h"
namespace pp
{
+Macro::Macro() : predefined(false), disabled(false), expansionCount(0), type(kTypeObj)
+{
+}
+
+Macro::~Macro()
+{
+}
+
bool Macro::equals(const Macro &other) const
{
- return (type == other.type) &&
- (name == other.name) &&
- (parameters == other.parameters) &&
+ return (type == other.type) && (name == other.name) && (parameters == other.parameters) &&
(replacements == other.replacements);
}
void PredefineMacro(MacroSet *macroSet, const char *name, int value)
{
- std::ostringstream stream;
- stream << value;
-
Token token;
token.type = Token::CONST_INT;
- token.text = stream.str();
+ token.text = ToString(value);
- Macro macro;
- macro.predefined = true;
- macro.type = Macro::kTypeObj;
- macro.name = name;
- macro.replacements.push_back(token);
+ std::shared_ptr<Macro> macro = std::make_shared<Macro>();
+ macro->predefined = true;
+ macro->type = Macro::kTypeObj;
+ macro->name = name;
+ macro->replacements.push_back(token);
(*macroSet)[name] = macro;
}
} // namespace pp
-
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Macro.h b/src/3rdparty/angle/src/compiler/preprocessor/Macro.h
index 31ee22c26a..c42e172ef9 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Macro.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Macro.h
@@ -8,6 +8,7 @@
#define COMPILER_PREPROCESSOR_MACRO_H_
#include <map>
+#include <memory>
#include <string>
#include <vector>
@@ -26,16 +27,13 @@ struct Macro
typedef std::vector<std::string> Parameters;
typedef std::vector<Token> Replacements;
- Macro()
- : predefined(false),
- disabled(false),
- type(kTypeObj)
- {
- }
+ Macro();
+ ~Macro();
bool equals(const Macro &other) const;
bool predefined;
mutable bool disabled;
+ mutable int expansionCount;
Type type;
std::string name;
@@ -43,7 +41,7 @@ struct Macro
Replacements replacements;
};
-typedef std::map<std::string, Macro> MacroSet;
+typedef std::map<std::string, std::shared_ptr<Macro>> MacroSet;
void PredefineMacro(MacroSet *macroSet, const char *name, int value);
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp
index e878ee345a..d88d3a6853 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp
@@ -4,20 +4,25 @@
// found in the LICENSE file.
//
-#include "MacroExpander.h"
+#include "compiler/preprocessor/MacroExpander.h"
#include <algorithm>
-#include <sstream>
-#include "DiagnosticsBase.h"
-#include "Token.h"
+#include "common/debug.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/Token.h"
namespace pp
{
+namespace
+{
+
+const size_t kMaxContextTokens = 10000;
+
class TokenLexer : public Lexer
{
- public:
+ public:
typedef std::vector<Token> TokenVector;
TokenLexer(TokenVector *tokens)
@@ -39,26 +44,61 @@ class TokenLexer : public Lexer
}
}
- private:
- PP_DISALLOW_COPY_AND_ASSIGN(TokenLexer);
-
+ private:
TokenVector mTokens;
TokenVector::const_iterator mIter;
};
+} // anonymous namespace
+
+class MacroExpander::ScopedMacroReenabler final : angle::NonCopyable
+{
+ public:
+ ScopedMacroReenabler(MacroExpander *expander);
+ ~ScopedMacroReenabler();
+
+ private:
+ MacroExpander *mExpander;
+};
+
+MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expander)
+ : mExpander(expander)
+{
+ mExpander->mDeferReenablingMacros = true;
+}
+
+MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
+{
+ mExpander->mDeferReenablingMacros = false;
+ for (auto macro : mExpander->mMacrosToReenable)
+ {
+ // Copying the string here by using substr is a check for use-after-free. It detects
+ // use-after-free more reliably than just toggling the disabled flag.
+ ASSERT(macro->name.substr() != "");
+ macro->disabled = false;
+ }
+ mExpander->mMacrosToReenable.clear();
+}
+
MacroExpander::MacroExpander(Lexer *lexer,
MacroSet *macroSet,
Diagnostics *diagnostics,
- bool parseDefined)
- : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mParseDefined(parseDefined)
+ int allowedMacroExpansionDepth)
+ : mLexer(lexer),
+ mMacroSet(macroSet),
+ mDiagnostics(diagnostics),
+ mTotalTokensInContexts(0),
+ mAllowedMacroExpansionDepth(allowedMacroExpansionDepth),
+ mDeferReenablingMacros(false)
{
}
MacroExpander::~MacroExpander()
{
- for (std::size_t i = 0; i < mContextStack.size(); ++i)
+ ASSERT(mMacrosToReenable.empty());
+ for (MacroContext *context : mContextStack)
{
- delete mContextStack[i];
+ delete context;
}
}
@@ -66,54 +106,11 @@ void MacroExpander::lex(Token *token)
{
while (true)
{
- const char kDefined[] = "defined";
-
getToken(token);
if (token->type != Token::IDENTIFIER)
break;
- // Defined operator is parsed here since it may be generated by macro expansion.
- // Defined operator produced by macro expansion has undefined behavior according to C++
- // spec, which the GLSL spec references (see C++14 draft spec section 16.1.4), but this
- // behavior is needed for passing dEQP tests, which enforce stricter compatibility between
- // implementations.
- if (mParseDefined && token->text == kDefined)
- {
- bool paren = false;
- getToken(token);
- if (token->type == '(')
- {
- paren = true;
- getToken(token);
- }
- if (token->type != Token::IDENTIFIER)
- {
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
- token->text);
- break;
- }
- auto iter = mMacroSet->find(token->text);
- std::string expression = iter != mMacroSet->end() ? "1" : "0";
-
- if (paren)
- {
- getToken(token);
- if (token->type != ')')
- {
- mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
- token->text);
- break;
- }
- }
-
- // We have a valid defined operator.
- // Convert the current token into a CONST_INT token.
- token->type = Token::CONST_INT;
- token->text = expression;
- break;
- }
-
if (token->expansionDisabled())
break;
@@ -121,17 +118,22 @@ void MacroExpander::lex(Token *token)
if (iter == mMacroSet->end())
break;
- const Macro& macro = iter->second;
- if (macro.disabled)
+ std::shared_ptr<Macro> macro = iter->second;
+ if (macro->disabled)
{
// If a particular token is not expanded, it is never expanded.
token->setExpansionDisabled(true);
break;
}
- if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
+
+ // Bump the expansion count before peeking if the next token is a '('
+ // otherwise there could be a #undef of the macro before the next token.
+ macro->expansionCount++;
+ if ((macro->type == Macro::kTypeFunc) && !isNextTokenLeftParen())
{
// If the token immediately after the macro name is not a '(',
// this macro should not be expanded.
+ macro->expansionCount--;
break;
}
@@ -160,6 +162,7 @@ void MacroExpander::getToken(Token *token)
}
else
{
+ ASSERT(mTotalTokensInContexts == 0);
mLexer->lex(token);
}
}
@@ -170,11 +173,11 @@ void MacroExpander::ungetToken(const Token &token)
{
MacroContext *context = mContextStack.back();
context->unget();
- assert(context->replacements[context->index] == token);
+ ASSERT(context->replacements[context->index] == token);
}
else
{
- assert(!mReserveToken.get());
+ ASSERT(!mReserveToken.get());
mReserveToken.reset(new Token(token));
}
}
@@ -190,37 +193,48 @@ bool MacroExpander::isNextTokenLeftParen()
return lparen;
}
-bool MacroExpander::pushMacro(const Macro &macro, const Token &identifier)
+bool MacroExpander::pushMacro(std::shared_ptr<Macro> macro, const Token &identifier)
{
- assert(!macro.disabled);
- assert(!identifier.expansionDisabled());
- assert(identifier.type == Token::IDENTIFIER);
- assert(identifier.text == macro.name);
+ ASSERT(!macro->disabled);
+ ASSERT(!identifier.expansionDisabled());
+ ASSERT(identifier.type == Token::IDENTIFIER);
+ ASSERT(identifier.text == macro->name);
std::vector<Token> replacements;
- if (!expandMacro(macro, identifier, &replacements))
+ if (!expandMacro(*macro, identifier, &replacements))
return false;
// Macro is disabled for expansion until it is popped off the stack.
- macro.disabled = true;
+ macro->disabled = true;
MacroContext *context = new MacroContext;
- context->macro = &macro;
+ context->macro = macro;
context->replacements.swap(replacements);
mContextStack.push_back(context);
+ mTotalTokensInContexts += context->replacements.size();
return true;
}
void MacroExpander::popMacro()
{
- assert(!mContextStack.empty());
+ ASSERT(!mContextStack.empty());
MacroContext *context = mContextStack.back();
mContextStack.pop_back();
- assert(context->empty());
- assert(context->macro->disabled);
- context->macro->disabled = false;
+ ASSERT(context->empty());
+ ASSERT(context->macro->disabled);
+ ASSERT(context->macro->expansionCount > 0);
+ if (mDeferReenablingMacros)
+ {
+ mMacrosToReenable.push_back(context->macro);
+ }
+ else
+ {
+ context->macro->disabled = false;
+ }
+ context->macro->expansionCount--;
+ mTotalTokensInContexts -= context->replacements.size();
delete context;
}
@@ -237,33 +251,28 @@ bool MacroExpander::expandMacro(const Macro &macro,
SourceLocation replacementLocation = identifier.location;
if (macro.type == Macro::kTypeObj)
{
- replacements->assign(macro.replacements.begin(),
- macro.replacements.end());
+ replacements->assign(macro.replacements.begin(), macro.replacements.end());
if (macro.predefined)
{
const char kLine[] = "__LINE__";
const char kFile[] = "__FILE__";
- assert(replacements->size() == 1);
- Token& repl = replacements->front();
+ ASSERT(replacements->size() == 1);
+ Token &repl = replacements->front();
if (macro.name == kLine)
{
- std::ostringstream stream;
- stream << identifier.location.line;
- repl.text = stream.str();
+ repl.text = ToString(identifier.location.line);
}
else if (macro.name == kFile)
{
- std::ostringstream stream;
- stream << identifier.location.file;
- repl.text = stream.str();
+ repl.text = ToString(identifier.location.file);
}
}
}
else
{
- assert(macro.type == Macro::kTypeFunc);
+ ASSERT(macro.type == Macro::kTypeFunc);
std::vector<MacroArg> args;
args.reserve(macro.parameters.size());
if (!collectMacroArgs(macro, identifier, &args, &replacementLocation))
@@ -274,7 +283,7 @@ bool MacroExpander::expandMacro(const Macro &macro,
for (std::size_t i = 0; i < replacements->size(); ++i)
{
- Token& repl = replacements->at(i);
+ Token &repl = replacements->at(i);
if (i == 0)
{
// The first token in the replacement list inherits the padding
@@ -294,45 +303,52 @@ bool MacroExpander::collectMacroArgs(const Macro &macro,
{
Token token;
getToken(&token);
- assert(token.type == '(');
+ ASSERT(token.type == '(');
args->push_back(MacroArg());
- for (int openParens = 1; openParens != 0; )
+
+ // Defer reenabling macros until args collection is finished to avoid the possibility of
+ // infinite recursion. Otherwise infinite recursion might happen when expanding the args after
+ // macros have been popped from the context stack when parsing the args.
+ ScopedMacroReenabler deferReenablingMacros(this);
+
+ int openParens = 1;
+ while (openParens != 0)
{
getToken(&token);
if (token.type == Token::LAST)
{
- mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
- identifier.location, identifier.text);
+ mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION, identifier.location,
+ identifier.text);
// Do not lose EOF token.
ungetToken(token);
return false;
}
- bool isArg = false; // True if token is part of the current argument.
+ bool isArg = false; // True if token is part of the current argument.
switch (token.type)
{
- case '(':
- ++openParens;
- isArg = true;
- break;
- case ')':
- --openParens;
- isArg = openParens != 0;
- *closingParenthesisLocation = token.location;
- break;
- case ',':
- // The individual arguments are separated by comma tokens, but
- // the comma tokens between matching inner parentheses do not
- // seperate arguments.
- if (openParens == 1)
- args->push_back(MacroArg());
- isArg = openParens != 1;
- break;
- default:
- isArg = true;
- break;
+ case '(':
+ ++openParens;
+ isArg = true;
+ break;
+ case ')':
+ --openParens;
+ isArg = openParens != 0;
+ *closingParenthesisLocation = token.location;
+ break;
+ case ',':
+ // The individual arguments are separated by comma tokens, but
+ // the comma tokens between matching inner parentheses do not
+ // seperate arguments.
+ if (openParens == 1)
+ args->push_back(MacroArg());
+ isArg = openParens != 1;
+ break;
+ default:
+ isArg = true;
+ break;
}
if (isArg)
{
@@ -353,9 +369,9 @@ bool MacroExpander::collectMacroArgs(const Macro &macro,
// Validate the number of arguments.
if (args->size() != params.size())
{
- Diagnostics::ID id = args->size() < macro.parameters.size() ?
- Diagnostics::PP_MACRO_TOO_FEW_ARGS :
- Diagnostics::PP_MACRO_TOO_MANY_ARGS;
+ Diagnostics::ID id = args->size() < macro.parameters.size()
+ ? Diagnostics::PP_MACRO_TOO_FEW_ARGS
+ : Diagnostics::PP_MACRO_TOO_MANY_ARGS;
mDiagnostics->report(id, identifier.location, identifier.text);
return false;
}
@@ -363,11 +379,17 @@ bool MacroExpander::collectMacroArgs(const Macro &macro,
// Pre-expand each argument before substitution.
// This step expands each argument individually before they are
// inserted into the macro body.
- for (std::size_t i = 0; i < args->size(); ++i)
+ size_t numTokens = 0;
+ for (auto &arg : *args)
{
- MacroArg &arg = args->at(i);
TokenLexer lexer(&arg);
- MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mParseDefined);
+ if (mAllowedMacroExpansionDepth < 1)
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, token.location,
+ token.text);
+ return false;
+ }
+ MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mAllowedMacroExpansionDepth - 1);
arg.clear();
expander.lex(&token);
@@ -375,6 +397,12 @@ bool MacroExpander::collectMacroArgs(const Macro &macro,
{
arg.push_back(token);
expander.lex(&token);
+ numTokens++;
+ if (numTokens + mTotalTokensInContexts > kMaxContextTokens)
+ {
+ mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
+ return false;
+ }
}
}
return true;
@@ -386,6 +414,14 @@ void MacroExpander::replaceMacroParams(const Macro &macro,
{
for (std::size_t i = 0; i < macro.replacements.size(); ++i)
{
+ if (!replacements->empty() &&
+ replacements->size() + mTotalTokensInContexts > kMaxContextTokens)
+ {
+ const Token &token = replacements->back();
+ mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
+ return;
+ }
+
const Token &repl = macro.replacements[i];
if (repl.type != Token::IDENTIFIER)
{
@@ -396,15 +432,15 @@ void MacroExpander::replaceMacroParams(const Macro &macro,
// TODO(alokp): Optimize this.
// There is no need to search for macro params every time.
// The param index can be cached with the replacement token.
- Macro::Parameters::const_iterator iter = std::find(
- macro.parameters.begin(), macro.parameters.end(), repl.text);
+ Macro::Parameters::const_iterator iter =
+ std::find(macro.parameters.begin(), macro.parameters.end(), repl.text);
if (iter == macro.parameters.end())
{
replacements->push_back(repl);
continue;
}
- std::size_t iArg = std::distance(macro.parameters.begin(), iter);
+ std::size_t iArg = std::distance(macro.parameters.begin(), iter);
const MacroArg &arg = args[iArg];
if (arg.empty())
{
@@ -418,5 +454,28 @@ void MacroExpander::replaceMacroParams(const Macro &macro,
}
}
-} // namespace pp
+MacroExpander::MacroContext::MacroContext() : macro(0), index(0)
+{
+}
+MacroExpander::MacroContext::~MacroContext()
+{
+}
+
+bool MacroExpander::MacroContext::empty() const
+{
+ return index == replacements.size();
+}
+
+const Token &MacroExpander::MacroContext::get()
+{
+ return replacements[index++];
+}
+
+void MacroExpander::MacroContext::unget()
+{
+ ASSERT(index > 0);
+ --index;
+}
+
+} // namespace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h
index dc870f626f..fae7676fb0 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h
@@ -7,13 +7,11 @@
#ifndef COMPILER_PREPROCESSOR_MACROEXPANDER_H_
#define COMPILER_PREPROCESSOR_MACROEXPANDER_H_
-#include <cassert>
#include <memory>
#include <vector>
-#include "Lexer.h"
-#include "Macro.h"
-#include "pp_utils.h"
+#include "compiler/preprocessor/Lexer.h"
+#include "compiler/preprocessor/Macro.h"
namespace pp
{
@@ -24,24 +22,23 @@ struct SourceLocation;
class MacroExpander : public Lexer
{
public:
- MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics, bool parseDefined);
+ MacroExpander(Lexer *lexer,
+ MacroSet *macroSet,
+ Diagnostics *diagnostics,
+ int allowedMacroExpansionDepth);
~MacroExpander() override;
void lex(Token *token) override;
private:
- PP_DISALLOW_COPY_AND_ASSIGN(MacroExpander);
-
void getToken(Token *token);
void ungetToken(const Token &token);
bool isNextTokenLeftParen();
- bool pushMacro(const Macro &macro, const Token &identifier);
+ bool pushMacro(std::shared_ptr<Macro> macro, const Token &identifier);
void popMacro();
- bool expandMacro(const Macro &macro,
- const Token &identifier,
- std::vector<Token> *replacements);
+ bool expandMacro(const Macro &macro, const Token &identifier, std::vector<Token> *replacements);
typedef std::vector<Token> MacroArg;
bool collectMacroArgs(const Macro &macro,
@@ -54,37 +51,31 @@ class MacroExpander : public Lexer
struct MacroContext
{
- const Macro *macro;
+ MacroContext();
+ ~MacroContext();
+ bool empty() const;
+ const Token &get();
+ void unget();
+
+ std::shared_ptr<Macro> macro;
std::size_t index;
std::vector<Token> replacements;
-
- MacroContext()
- : macro(0),
- index(0)
- {
- }
- bool empty() const
- {
- return index == replacements.size();
- }
- const Token &get()
- {
- return replacements[index++];
- }
- void unget()
- {
- assert(index > 0);
- --index;
- }
};
Lexer *mLexer;
MacroSet *mMacroSet;
Diagnostics *mDiagnostics;
- bool mParseDefined;
std::unique_ptr<Token> mReserveToken;
std::vector<MacroContext *> mContextStack;
+ size_t mTotalTokensInContexts;
+
+ int mAllowedMacroExpansionDepth;
+
+ bool mDeferReenablingMacros;
+ std::vector<std::shared_ptr<Macro>> mMacrosToReenable;
+
+ class ScopedMacroReenabler;
};
} // namespace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp
index aeb9c46f9d..349c7b06c7 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp
@@ -4,16 +4,15 @@
// found in the LICENSE file.
//
-#include "Preprocessor.h"
+#include "compiler/preprocessor/Preprocessor.h"
-#include <cassert>
-
-#include "DiagnosticsBase.h"
-#include "DirectiveParser.h"
-#include "Macro.h"
-#include "MacroExpander.h"
-#include "Token.h"
-#include "Tokenizer.h"
+#include "common/debug.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/DirectiveParser.h"
+#include "compiler/preprocessor/Macro.h"
+#include "compiler/preprocessor/MacroExpander.h"
+#include "compiler/preprocessor/Token.h"
+#include "compiler/preprocessor/Tokenizer.h"
namespace pp
{
@@ -26,19 +25,26 @@ struct PreprocessorImpl
DirectiveParser directiveParser;
MacroExpander macroExpander;
- PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler)
+ PreprocessorImpl(Diagnostics *diag,
+ DirectiveHandler *directiveHandler,
+ const PreprocessorSettings &settings)
: diagnostics(diag),
tokenizer(diag),
- directiveParser(&tokenizer, &macroSet, diag, directiveHandler),
- macroExpander(&directiveParser, &macroSet, diag, false)
+ directiveParser(&tokenizer,
+ &macroSet,
+ diag,
+ directiveHandler,
+ settings.maxMacroExpansionDepth),
+ macroExpander(&directiveParser, &macroSet, diag, settings.maxMacroExpansionDepth)
{
}
};
Preprocessor::Preprocessor(Diagnostics *diagnostics,
- DirectiveHandler *directiveHandler)
+ DirectiveHandler *directiveHandler,
+ const PreprocessorSettings &settings)
{
- mImpl = new PreprocessorImpl(diagnostics, directiveHandler);
+ mImpl = new PreprocessorImpl(diagnostics, directiveHandler, settings);
}
Preprocessor::~Preprocessor()
@@ -46,9 +52,7 @@ Preprocessor::~Preprocessor()
delete mImpl;
}
-bool Preprocessor::init(size_t count,
- const char * const string[],
- const int length[])
+bool Preprocessor::init(size_t count, const char *const string[], const int length[])
{
static const int kDefaultGLSLVersion = 100;
@@ -74,23 +78,23 @@ void Preprocessor::lex(Token *token)
mImpl->macroExpander.lex(token);
switch (token->type)
{
- // We should not be returning internal preprocessing tokens.
- // Convert preprocessing tokens to compiler tokens or report
- // diagnostics.
- case Token::PP_HASH:
- assert(false);
- break;
- case Token::PP_NUMBER:
- mImpl->diagnostics->report(Diagnostics::PP_INVALID_NUMBER,
- token->location, token->text);
- break;
- case Token::PP_OTHER:
- mImpl->diagnostics->report(Diagnostics::PP_INVALID_CHARACTER,
- token->location, token->text);
- break;
- default:
- validToken = true;
- break;
+ // We should not be returning internal preprocessing tokens.
+ // Convert preprocessing tokens to compiler tokens or report
+ // diagnostics.
+ case Token::PP_HASH:
+ UNREACHABLE();
+ break;
+ case Token::PP_NUMBER:
+ mImpl->diagnostics->report(Diagnostics::PP_INVALID_NUMBER, token->location,
+ token->text);
+ break;
+ case Token::PP_OTHER:
+ mImpl->diagnostics->report(Diagnostics::PP_INVALID_CHARACTER, token->location,
+ token->text);
+ break;
+ default:
+ validToken = true;
+ break;
}
}
}
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.h b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.h
index fe25daa123..2fe504f7f9 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.h
@@ -7,9 +7,9 @@
#ifndef COMPILER_PREPROCESSOR_PREPROCESSOR_H_
#define COMPILER_PREPROCESSOR_PREPROCESSOR_H_
-#include <stddef.h>
+#include <cstddef>
-#include "pp_utils.h"
+#include "common/angleutils.h"
namespace pp
{
@@ -19,10 +19,18 @@ class DirectiveHandler;
struct PreprocessorImpl;
struct Token;
-class Preprocessor
+struct PreprocessorSettings : private angle::NonCopyable
+{
+ PreprocessorSettings() : maxMacroExpansionDepth(1000) {}
+ int maxMacroExpansionDepth;
+};
+
+class Preprocessor : angle::NonCopyable
{
public:
- Preprocessor(Diagnostics *diagnostics, DirectiveHandler *directiveHandler);
+ Preprocessor(Diagnostics *diagnostics,
+ DirectiveHandler *directiveHandler,
+ const PreprocessorSettings &settings);
~Preprocessor();
// count: specifies the number of elements in the string and length arrays.
@@ -34,7 +42,7 @@ class Preprocessor
// Each element in the length array may contain the length of the
// corresponding string or a value less than 0 to indicate that the string
// is null terminated.
- bool init(size_t count, const char * const string[], const int length[]);
+ bool init(size_t count, const char *const string[], const int length[]);
// Adds a pre-defined macro.
void predefineMacro(const char *name, int value);
@@ -44,8 +52,6 @@ class Preprocessor
void setMaxTokenSize(size_t maxTokenSize);
private:
- PP_DISALLOW_COPY_AND_ASSIGN(Preprocessor);
-
PreprocessorImpl *mImpl;
};
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/SourceLocation.h b/src/3rdparty/angle/src/compiler/preprocessor/SourceLocation.h
index af8a8d5d19..51908a3b4b 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/SourceLocation.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/SourceLocation.h
@@ -12,16 +12,8 @@ namespace pp
struct SourceLocation
{
- SourceLocation()
- : file(0),
- line(0)
- {
- }
- SourceLocation(int f, int l)
- : file(f),
- line(l)
- {
- }
+ SourceLocation() : file(0), line(0) {}
+ SourceLocation(int f, int l) : file(f), line(l) {}
bool equals(const SourceLocation &other) const
{
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Token.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Token.cpp
index d102654747..ce0ce94f49 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Token.cpp
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Token.cpp
@@ -4,28 +4,25 @@
// found in the LICENSE file.
//
-#include "Token.h"
+#include "compiler/preprocessor/Token.h"
-#include <cassert>
-
-#include "numeric_lex.h"
+#include "common/debug.h"
+#include "compiler/preprocessor/numeric_lex.h"
namespace pp
{
void Token::reset()
{
- type = 0;
- flags = 0;
+ type = 0;
+ flags = 0;
location = SourceLocation();
text.clear();
}
bool Token::equals(const Token &other) const
{
- return (type == other.type) &&
- (flags == other.flags) &&
- (location == other.location) &&
+ return (type == other.type) && (flags == other.flags) && (location == other.location) &&
(text == other.text);
}
@@ -55,19 +52,19 @@ void Token::setExpansionDisabled(bool disable)
bool Token::iValue(int *value) const
{
- assert(type == CONST_INT);
+ ASSERT(type == CONST_INT);
return numeric_lex_int(text, value);
}
bool Token::uValue(unsigned int *value) const
{
- assert(type == CONST_INT);
+ ASSERT(type == CONST_INT);
return numeric_lex_int(text, value);
}
bool Token::fValue(float *value) const
{
- assert(type == CONST_FLOAT);
+ ASSERT(type == CONST_FLOAT);
return numeric_lex_float(text, value);
}
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Token.h b/src/3rdparty/angle/src/compiler/preprocessor/Token.h
index 347c47e307..26732ab64d 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Token.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Token.h
@@ -10,7 +10,7 @@
#include <ostream>
#include <string>
-#include "SourceLocation.h"
+#include "compiler/preprocessor/SourceLocation.h"
namespace pp
{
@@ -19,7 +19,9 @@ struct Token
{
enum Type
{
- LAST = 0, // EOF.
+ // Calling this ERROR causes a conflict with wingdi.h
+ GOT_ERROR = -1,
+ LAST = 0, // EOF.
IDENTIFIER = 258,
@@ -62,33 +64,20 @@ struct Token
EXPANSION_DISABLED = 1 << 2
};
- Token()
- : type(0),
- flags(0)
- {
- }
+ Token() : type(0), flags(0) {}
void reset();
bool equals(const Token &other) const;
// Returns true if this is the first token on line.
// It disregards any leading whitespace.
- bool atStartOfLine() const
- {
- return (flags & AT_START_OF_LINE) != 0;
- }
+ bool atStartOfLine() const { return (flags & AT_START_OF_LINE) != 0; }
void setAtStartOfLine(bool start);
- bool hasLeadingSpace() const
- {
- return (flags & HAS_LEADING_SPACE) != 0;
- }
+ bool hasLeadingSpace() const { return (flags & HAS_LEADING_SPACE) != 0; }
void setHasLeadingSpace(bool space);
- bool expansionDisabled() const
- {
- return (flags & EXPANSION_DISABLED) != 0;
- }
+ bool expansionDisabled() const { return (flags & EXPANSION_DISABLED) != 0; }
void setExpansionDisabled(bool disable);
// Converts text into numeric value for CONST_INT and CONST_FLOAT token.
@@ -113,7 +102,7 @@ inline bool operator!=(const Token &lhs, const Token &rhs)
return !lhs.equals(rhs);
}
-extern std::ostream &operator<<(std::ostream &out, const Token &token);
+std::ostream &operator<<(std::ostream &out, const Token &token);
} // namepsace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h
index 49e64fa209..af4fd7ce7b 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h
@@ -7,9 +7,9 @@
#ifndef COMPILER_PREPROCESSOR_TOKENIZER_H_
#define COMPILER_PREPROCESSOR_TOKENIZER_H_
-#include "Input.h"
-#include "Lexer.h"
-#include "pp_utils.h"
+#include "common/angleutils.h"
+#include "compiler/preprocessor/Input.h"
+#include "compiler/preprocessor/Lexer.h"
namespace pp
{
@@ -34,9 +34,9 @@ class Tokenizer : public Lexer
};
Tokenizer(Diagnostics *diagnostics);
- ~Tokenizer();
+ ~Tokenizer() override;
- bool init(size_t count, const char * const string[], const int length[]);
+ bool init(size_t count, const char *const string[], const int length[]);
void setFileNumber(int file);
void setLineNumber(int line);
@@ -45,13 +45,12 @@ class Tokenizer : public Lexer
void lex(Token *token) override;
private:
- PP_DISALLOW_COPY_AND_ASSIGN(Tokenizer);
bool initScanner();
void destroyScanner();
- void *mHandle; // Scanner handle.
- Context mContext; // Scanner extra.
- size_t mMaxTokenSize; // Maximum token size
+ void *mHandle; // Scanner handle.
+ Context mContext; // Scanner extra.
+ size_t mMaxTokenSize; // Maximum token size
};
} // namespace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l
index d316da88b1..096812d45f 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l
+++ b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l
@@ -27,10 +27,10 @@ IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh.
#pragma warning(disable: 4005)
#endif
-#include "Tokenizer.h"
+#include "compiler/preprocessor/Tokenizer.h"
-#include "DiagnosticsBase.h"
-#include "Token.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/Token.h"
#if defined(__GNUC__)
// Triggered by the auto-generated yy_fatal_error function.
@@ -110,7 +110,14 @@ FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".")
"/*" { BEGIN(COMMENT); }
<COMMENT>[^*\r\n]+
<COMMENT>"*"
-<COMMENT>{NEWLINE} { ++yylineno; }
+<COMMENT>{NEWLINE} {
+ if (yylineno == INT_MAX)
+ {
+ *yylval = "Integer overflow on line number";
+ return pp::Token::GOT_ERROR;
+ }
+ ++yylineno;
+}
<COMMENT>"*/" {
yyextra->leadingSpace = true;
BEGIN(INITIAL);
@@ -237,13 +244,16 @@ FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".")
[ \t\v\f]+ { yyextra->leadingSpace = true; }
{NEWLINE} {
+ if (yylineno == INT_MAX)
+ {
+ *yylval = "Integer overflow on line number";
+ return pp::Token::GOT_ERROR;
+ }
++yylineno;
yylval->assign(1, '\n');
return '\n';
}
-\\{NEWLINE} { ++yylineno; }
-
. {
yylval->assign(1, yytext[0]);
return pp::Token::PP_OTHER;
@@ -267,11 +277,17 @@ FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".")
yylloc->line = yylineno;
yylval->clear();
- if (YY_START == COMMENT)
+ // Line number overflows fake EOFs to exit early, check for this case.
+ if (yylineno == INT_MAX) {
+ yyextra->diagnostics->report(pp::Diagnostics::PP_TOKENIZER_ERROR,
+ pp::SourceLocation(yyfileno, yylineno),
+ "Integer overflow on line number");
+ }
+ else if (YY_START == COMMENT)
{
yyextra->diagnostics->report(pp::Diagnostics::PP_EOF_IN_COMMENT,
pp::SourceLocation(yyfileno, yylineno),
- "");
+ "EOF while in a comment");
}
yyterminate();
}
@@ -280,9 +296,7 @@ FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".")
namespace pp {
-Tokenizer::Tokenizer(Diagnostics *diagnostics)
- : mHandle(0),
- mMaxTokenSize(256)
+Tokenizer::Tokenizer(Diagnostics *diagnostics) : mHandle(nullptr), mMaxTokenSize(256)
{
mContext.diagnostics = diagnostics;
}
@@ -320,7 +334,18 @@ void Tokenizer::setMaxTokenSize(size_t maxTokenSize)
void Tokenizer::lex(Token *token)
{
- token->type = yylex(&token->text, &token->location, mHandle);
+ int tokenType = yylex(&token->text, &token->location, mHandle);
+
+ if (tokenType == Token::GOT_ERROR)
+ {
+ mContext.diagnostics->report(Diagnostics::PP_TOKENIZER_ERROR, token->location, token->text);
+ token->type = Token::LAST;
+ }
+ else
+ {
+ token->type = tokenType;
+ }
+
if (token->text.size() > mMaxTokenSize)
{
mContext.diagnostics->report(Diagnostics::PP_TOKEN_TOO_LONG,
@@ -339,7 +364,7 @@ void Tokenizer::lex(Token *token)
bool Tokenizer::initScanner()
{
- if ((mHandle == NULL) && yylex_init_extra(&mContext, &mHandle))
+ if ((mHandle == nullptr) && yylex_init_extra(&mContext, &mHandle))
return false;
yyrestart(0, mHandle);
@@ -348,11 +373,11 @@ bool Tokenizer::initScanner()
void Tokenizer::destroyScanner()
{
- if (mHandle == NULL)
+ if (mHandle == nullptr)
return;
yylex_destroy(mHandle);
- mHandle = NULL;
+ mHandle = nullptr;
}
} // namespace pp
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h b/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h
index b32e42253f..6ea779ab8f 100644
--- a/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h
+++ b/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h
@@ -9,15 +9,15 @@
#ifndef COMPILER_PREPROCESSOR_NUMERICLEX_H_
#define COMPILER_PREPROCESSOR_NUMERICLEX_H_
+#include <cmath>
#include <sstream>
-namespace pp {
+namespace pp
+{
inline std::ios::fmtflags numeric_base_int(const std::string &str)
{
- if ((str.size() >= 2) &&
- (str[0] == '0') &&
- (str[1] == 'x' || str[1] == 'X'))
+ if ((str.size() >= 2) && (str[0] == '0') && (str[1] == 'x' || str[1] == 'X'))
{
return std::ios::hex;
}
@@ -33,7 +33,7 @@ inline std::ios::fmtflags numeric_base_int(const std::string &str)
// of the correct form. They can only fail if the parsed value is too big,
// in which case false is returned.
-template<typename IntType>
+template <typename IntType>
bool numeric_lex_int(const std::string &str, IntType *value)
{
std::istringstream stream(str);
@@ -45,7 +45,7 @@ bool numeric_lex_int(const std::string &str, IntType *value)
return !stream.fail();
}
-template<typename FloatType>
+template <typename FloatType>
bool numeric_lex_float(const std::string &str, FloatType *value)
{
// On 64-bit Intel Android, istringstream is broken. Until this is fixed in
@@ -63,10 +63,10 @@ bool numeric_lex_float(const std::string &str, FloatType *value)
stream.imbue(std::locale::classic());
stream >> (*value);
- return !stream.fail();
+ return !stream.fail() && std::isfinite(*value);
#endif
}
-} // namespace pp.
+} // namespace pp.
-#endif // COMPILER_PREPROCESSOR_NUMERICLEX_H_
+#endif // COMPILER_PREPROCESSOR_NUMERICLEX_H_
diff --git a/src/3rdparty/angle/src/compiler/preprocessor/pp_utils.h b/src/3rdparty/angle/src/compiler/preprocessor/pp_utils.h
deleted file mode 100644
index 9fba9385c5..0000000000
--- a/src/3rdparty/angle/src/compiler/preprocessor/pp_utils.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-// pp_utils.h: Common preprocessor utilities
-
-#ifndef COMPILER_PREPROCESSOR_PPUTILS_H_
-#define COMPILER_PREPROCESSOR_PPUTILS_H_
-
-// A macro to disallow the copy constructor and operator= functions
-// This must be used in the private: declarations for a class.
-#define PP_DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName &); \
- void operator=(const TypeName &)
-
-#endif // COMPILER_PREPROCESSOR_PPUTILS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp
index 31bfae9966..b04fc28259 100644
--- a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp
@@ -9,8 +9,12 @@
#include "compiler/translator/ASTMetadataHLSL.h"
#include "compiler/translator/CallDAG.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/SymbolTable.h"
+namespace sh
+{
+
namespace
{
@@ -29,9 +33,22 @@ class PullGradient : public TIntermTraverser
mDag(dag)
{
ASSERT(index < metadataList->size());
+
+ // ESSL 100 builtin gradient functions
+ mGradientBuiltinFunctions.insert("texture2D");
+ mGradientBuiltinFunctions.insert("texture2DProj");
+ mGradientBuiltinFunctions.insert("textureCube");
+
+ // ESSL 300 builtin gradient functions
+ mGradientBuiltinFunctions.insert("texture");
+ mGradientBuiltinFunctions.insert("textureProj");
+ mGradientBuiltinFunctions.insert("textureOffset");
+ mGradientBuiltinFunctions.insert("textureProjOffset");
+
+ // ESSL 310 doesn't add builtin gradient functions
}
- void traverse(TIntermAggregate *node)
+ void traverse(TIntermFunctionDefinition *node)
{
node->traverse(this);
ASSERT(mParents.empty());
@@ -59,7 +76,7 @@ class PullGradient : public TIntermTraverser
ASSERT(mParents.back() == node);
mParents.pop_back();
// A control flow's using a gradient means its parents are too.
- if (mMetadata->mControlFlowsContainingGradient.count(node)> 0 && !mParents.empty())
+ if (mMetadata->mControlFlowsContainingGradient.count(node) > 0 && !mParents.empty())
{
mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
}
@@ -72,9 +89,9 @@ class PullGradient : public TIntermTraverser
return true;
}
- bool visitSelection(Visit visit, TIntermSelection *selection) override
+ bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override
{
- visitControlFlow(visit, selection);
+ visitControlFlow(visit, ifElse);
return true;
}
@@ -84,11 +101,12 @@ class PullGradient : public TIntermTraverser
{
switch (node->getOp())
{
- case EOpDFdx:
- case EOpDFdy:
- onGradient();
- default:
- break;
+ case EOpDFdx:
+ case EOpDFdy:
+ case EOpFwidth:
+ onGradient();
+ default:
+ break;
}
}
@@ -99,28 +117,22 @@ class PullGradient : public TIntermTraverser
{
if (visit == PreVisit)
{
- if (node->getOp() == EOpFunctionCall)
+ if (node->getOp() == EOpCallFunctionInAST)
{
- if (node->isUserDefined())
- {
- size_t calleeIndex = mDag.findIndex(node);
- ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
- UNUSED_ASSERTION_VARIABLE(mIndex);
+ size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
- if ((*mMetadataList)[calleeIndex].mUsesGradient) {
- onGradient();
- }
+ if ((*mMetadataList)[calleeIndex].mUsesGradient)
+ {
+ onGradient();
}
- else
+ }
+ else if (node->getOp() == EOpCallBuiltInFunction)
+ {
+ if (mGradientBuiltinFunctions.find(node->getFunctionSymbolInfo()->getName()) !=
+ mGradientBuiltinFunctions.end())
{
- TString name = TFunction::unmangleName(node->getName());
-
- if (name == "texture2D" ||
- name == "texture2DProj" ||
- name == "textureCube")
- {
- onGradient();
- }
+ onGradient();
}
}
}
@@ -136,7 +148,10 @@ class PullGradient : public TIntermTraverser
// Contains a stack of the control flow nodes that are parents of the node being
// currently visited. It is used to mark control flows using a gradient.
- std::vector<TIntermNode*> mParents;
+ std::vector<TIntermNode *> mParents;
+
+ // A list of builtin functions that use gradients
+ std::set<TString> mGradientBuiltinFunctions;
};
// Traverses the AST of a function definition to compute the the discontinuous loops
@@ -157,7 +172,7 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
{
}
- void traverse(TIntermAggregate *node)
+ void traverse(TIntermFunctionDefinition *node)
{
node->traverse(this);
ASSERT(mLoopsAndSwitches.empty());
@@ -196,7 +211,7 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
return true;
}
- bool visitSelection(Visit visit, TIntermSelection *node) override
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override
{
if (visit == PreVisit)
{
@@ -222,7 +237,7 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
{
switch (node->getFlowOp())
{
- case EOpBreak:
+ case EOpBreak:
{
ASSERT(!mLoopsAndSwitches.empty());
TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
@@ -232,11 +247,11 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
}
}
break;
- case EOpContinue:
+ case EOpContinue:
{
ASSERT(!mLoopsAndSwitches.empty());
TIntermLoop *loop = nullptr;
- size_t i = mLoopsAndSwitches.size();
+ size_t i = mLoopsAndSwitches.size();
while (loop == nullptr && i > 0)
{
--i;
@@ -246,23 +261,23 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
mMetadata->mDiscontinuousLoops.insert(loop);
}
break;
- case EOpKill:
- case EOpReturn:
- // A return or discard jumps out of all the enclosing loops
- if (!mLoopsAndSwitches.empty())
- {
- for (TIntermNode *intermNode : mLoopsAndSwitches)
+ case EOpKill:
+ case EOpReturn:
+ // A return or discard jumps out of all the enclosing loops
+ if (!mLoopsAndSwitches.empty())
{
- TIntermLoop *loop = intermNode->getAsLoopNode();
- if (loop)
+ for (TIntermNode *intermNode : mLoopsAndSwitches)
{
- mMetadata->mDiscontinuousLoops.insert(loop);
+ TIntermLoop *loop = intermNode->getAsLoopNode();
+ if (loop)
+ {
+ mMetadata->mDiscontinuousLoops.insert(loop);
+ }
}
}
- }
- break;
- default:
- UNREACHABLE();
+ break;
+ default:
+ UNREACHABLE();
}
}
@@ -271,18 +286,14 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
bool visitAggregate(Visit visit, TIntermAggregate *node) override
{
- if (visit == PreVisit && node->getOp() == EOpFunctionCall)
+ if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
{
- if (node->isUserDefined())
- {
- size_t calleeIndex = mDag.findIndex(node);
- ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
- UNUSED_ASSERTION_VARIABLE(mIndex);
+ size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
- if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
- {
- onGradientLoop();
- }
+ if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
+ {
+ onGradientLoop();
}
}
@@ -309,8 +320,8 @@ class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
size_t mIndex;
const CallDAG &mDag;
- std::vector<TIntermNode*> mLoopsAndSwitches;
- std::vector<TIntermSelection*> mIfs;
+ std::vector<TIntermNode *> mLoopsAndSwitches;
+ std::vector<TIntermIfElse *> mIfs;
};
// Tags all the functions called in a discontinuous loop
@@ -327,7 +338,7 @@ class PushDiscontinuousLoops : public TIntermTraverser
{
}
- void traverse(TIntermAggregate *node)
+ void traverse(TIntermFunctionDefinition *node)
{
node->traverse(this);
ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
@@ -353,18 +364,17 @@ class PushDiscontinuousLoops : public TIntermTraverser
{
switch (node->getOp())
{
- case EOpFunctionCall:
- if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0)
- {
- size_t calleeIndex = mDag.findIndex(node);
- ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
- UNUSED_ASSERTION_VARIABLE(mIndex);
+ case EOpCallFunctionInAST:
+ if (visit == PreVisit && mNestedDiscont > 0)
+ {
+ size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
- (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
- }
- break;
- default:
- break;
+ (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
+ }
+ break;
+ default:
+ break;
}
return true;
}
@@ -377,7 +387,6 @@ class PushDiscontinuousLoops : public TIntermTraverser
int mNestedDiscont;
};
-
}
bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
@@ -385,7 +394,7 @@ bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
return mControlFlowsContainingGradient.count(node) > 0;
}
-bool ASTMetadataHLSL::hasGradientLoop(TIntermSelection *node)
+bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
{
return mIfsContainingGradientLoop.count(node) > 0;
}
@@ -449,3 +458,5 @@ MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
return metadataList;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h
index 39e671e3e0..550a522b86 100644
--- a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h
@@ -12,9 +12,12 @@
#include <set>
#include <vector>
+namespace sh
+{
+
class CallDAG;
class TIntermNode;
-class TIntermSelection;
+class TIntermIfElse;
class TIntermLoop;
struct ASTMetadataHLSL
@@ -30,21 +33,21 @@ struct ASTMetadataHLSL
// Here "something uses a gradient" means here that it either contains a
// gradient operation, or a call to a function that uses a gradient.
bool hasGradientInCallGraph(TIntermLoop *node);
- bool hasGradientLoop(TIntermSelection *node);
+ bool hasGradientLoop(TIntermIfElse *node);
// Does the function use a gradient.
bool mUsesGradient;
// Even if usesGradient is true, some control flow might not use a gradient
// so we store the set of all gradient-using control flows.
- std::set<TIntermNode*> mControlFlowsContainingGradient;
+ std::set<TIntermNode *> mControlFlowsContainingGradient;
// Remember information about the discontinuous loops and which functions
// are called in such loops.
bool mCalledInDiscontinuousLoop;
bool mHasGradientLoopInCallGraph;
- std::set<TIntermLoop*> mDiscontinuousLoops;
- std::set<TIntermSelection *> mIfsContainingGradientLoop;
+ std::set<TIntermLoop *> mDiscontinuousLoops;
+ std::set<TIntermIfElse *> mIfsContainingGradientLoop;
// Will we need to generate a Lod0 version of the function.
bool mNeedsLod0;
@@ -55,4 +58,6 @@ typedef std::vector<ASTMetadataHLSL> MetadataList;
// Return the AST analysis result, in the order defined by the call DAG
MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag);
-#endif // COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp b/src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
new file mode 100644
index 0000000000..6a05104233
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/AddAndTrueToLoopCondition.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// An AST traverser that rewrites for and while loops by replacing "condition" with
+// "condition && true" to work around condition bug on Intel Mac.
+class AddAndTrueToLoopConditionTraverser : public TIntermTraverser
+{
+ public:
+ AddAndTrueToLoopConditionTraverser() : TIntermTraverser(true, false, false) {}
+
+ bool visitLoop(Visit, TIntermLoop *loop) override
+ {
+ // do-while loop doesn't have this bug.
+ if (loop->getType() != ELoopFor && loop->getType() != ELoopWhile)
+ {
+ return true;
+ }
+
+ // For loop may not have a condition.
+ if (loop->getCondition() == nullptr)
+ {
+ return true;
+ }
+
+ // Constant true.
+ TConstantUnion *trueConstant = new TConstantUnion();
+ trueConstant->setBConst(true);
+ TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, TType(EbtBool));
+
+ // CONDITION && true.
+ TIntermBinary *andOp = new TIntermBinary(EOpLogicalAnd, loop->getCondition(), trueValue);
+ loop->setCondition(andOp);
+
+ return true;
+ }
+};
+
+} // anonymous namespace
+
+void AddAndTrueToLoopCondition(TIntermNode *root)
+{
+ AddAndTrueToLoopConditionTraverser traverser;
+ root->traverse(&traverser);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.h b/src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
new file mode 100644
index 0000000000..34debe0ed9
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
@@ -0,0 +1,20 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Rewrite condition in for and while loops to work around driver bug on Intel Mac.
+
+#ifndef COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
+#define COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void AddAndTrueToLoopCondition(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.cpp b/src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
new file mode 100644
index 0000000000..4dfe60c0bc
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AddDefaultReturnStatements.cpp: Add default return statements to functions that do not end in a
+// return.
+//
+
+#include "compiler/translator/AddDefaultReturnStatements.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+bool NeedsReturnStatement(TIntermFunctionDefinition *node, TType *returnType)
+{
+ *returnType = node->getFunctionPrototype()->getType();
+ if (returnType->getBasicType() == EbtVoid)
+ {
+ return false;
+ }
+
+ TIntermBlock *bodyNode = node->getBody();
+ TIntermBranch *returnNode = bodyNode->getSequence()->back()->getAsBranchNode();
+ if (returnNode != nullptr && returnNode->getFlowOp() == EOpReturn)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+void AddDefaultReturnStatements(TIntermBlock *root)
+{
+ TType returnType;
+ for (TIntermNode *node : *root->getSequence())
+ {
+ TIntermFunctionDefinition *definition = node->getAsFunctionDefinition();
+ if (definition != nullptr && NeedsReturnStatement(definition, &returnType))
+ {
+ TIntermBranch *branch = new TIntermBranch(EOpReturn, CreateZeroNode(returnType));
+
+ TIntermBlock *bodyNode = definition->getBody();
+ bodyNode->getSequence()->push_back(branch);
+ }
+ }
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.h b/src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.h
new file mode 100644
index 0000000000..40a70ad8c2
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AddDefaultReturnStatements.h: Add default return statements to functions that do not end in a
+// return.
+//
+
+#ifndef COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
+
+class TIntermBlock;
+
+namespace sh
+{
+
+void AddDefaultReturnStatements(TIntermBlock *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
index 510ade84c1..17721fb0dc 100644
--- a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
@@ -3,17 +3,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in
-// function definitions, prototypes, and call sites.
+// The ArrayReturnValueToOutParameter function changes return values of an array type to out
+// parameters in function definitions, prototypes, and call sites.
#include "compiler/translator/ArrayReturnValueToOutParameter.h"
-#include "compiler/translator/IntermNode.h"
+#include <map>
+
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
namespace
{
-void CopyAggregateChildren(TIntermAggregate *from, TIntermAggregate *to)
+void CopyAggregateChildren(TIntermAggregateBase *from, TIntermAggregateBase *to)
{
const TIntermSequence *fromSequence = from->getSequence();
for (size_t ii = 0; ii < fromSequence->size(); ++ii)
@@ -22,156 +28,153 @@ void CopyAggregateChildren(TIntermAggregate *from, TIntermAggregate *to)
}
}
-TIntermSymbol *CreateReturnValueSymbol(const TType &type)
+TIntermSymbol *CreateReturnValueSymbol(const TSymbolUniqueId &id, const TType &type)
{
- TIntermSymbol *node = new TIntermSymbol(0, "angle_return", type);
+ TIntermSymbol *node = new TIntermSymbol(id, "angle_return", type);
node->setInternal(true);
+ node->getTypePointer()->setQualifier(EvqOut);
return node;
}
-TIntermSymbol *CreateReturnValueOutSymbol(const TType &type)
+TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall,
+ TIntermTyped *returnValueTarget)
{
- TType outType(type);
- outType.setQualifier(EvqOut);
- return CreateReturnValueSymbol(outType);
-}
-
-TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall, TIntermTyped *returnValueTarget)
-{
- TIntermAggregate *replacementCall = new TIntermAggregate(EOpFunctionCall);
- replacementCall->setType(TType(EbtVoid));
- replacementCall->setUserDefined();
- replacementCall->setNameObj(originalCall->getNameObj());
- replacementCall->setFunctionId(originalCall->getFunctionId());
- replacementCall->setLine(originalCall->getLine());
- TIntermSequence *replacementParameters = replacementCall->getSequence();
- TIntermSequence *originalParameters = originalCall->getSequence();
- for (auto &param : *originalParameters)
+ TIntermSequence *replacementArguments = new TIntermSequence();
+ TIntermSequence *originalArguments = originalCall->getSequence();
+ for (auto &arg : *originalArguments)
{
- replacementParameters->push_back(param);
+ replacementArguments->push_back(arg);
}
- replacementParameters->push_back(returnValueTarget);
+ replacementArguments->push_back(returnValueTarget);
+ TIntermAggregate *replacementCall = TIntermAggregate::CreateFunctionCall(
+ TType(EbtVoid), originalCall->getFunctionSymbolInfo()->getId(),
+ originalCall->getFunctionSymbolInfo()->getNameObj(), replacementArguments);
+ replacementCall->setLine(originalCall->getLine());
return replacementCall;
}
class ArrayReturnValueToOutParameterTraverser : private TIntermTraverser
{
public:
- static void apply(TIntermNode *root, unsigned int *temporaryIndex);
+ static void apply(TIntermNode *root, TSymbolTable *symbolTable);
+
private:
- ArrayReturnValueToOutParameterTraverser();
+ ArrayReturnValueToOutParameterTraverser(TSymbolTable *symbolTable);
+ bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
bool visitBranch(Visit visit, TIntermBranch *node) override;
bool visitBinary(Visit visit, TIntermBinary *node) override;
- bool mInFunctionWithArrayReturnValue;
+ // Set when traversal is inside a function with array return value.
+ TIntermFunctionDefinition *mFunctionWithArrayReturnValue;
+
+ // Map from function symbol ids to array return value ids.
+ std::map<int, TSymbolUniqueId *> mReturnValueIds;
};
-void ArrayReturnValueToOutParameterTraverser::apply(TIntermNode *root, unsigned int *temporaryIndex)
+void ArrayReturnValueToOutParameterTraverser::apply(TIntermNode *root, TSymbolTable *symbolTable)
{
- ArrayReturnValueToOutParameterTraverser arrayReturnValueToOutParam;
- arrayReturnValueToOutParam.useTemporaryIndex(temporaryIndex);
+ ArrayReturnValueToOutParameterTraverser arrayReturnValueToOutParam(symbolTable);
root->traverse(&arrayReturnValueToOutParam);
arrayReturnValueToOutParam.updateTree();
}
-ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser()
- : TIntermTraverser(true, false, true),
- mInFunctionWithArrayReturnValue(false)
+ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser(
+ TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable), mFunctionWithArrayReturnValue(nullptr)
{
}
-bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+bool ArrayReturnValueToOutParameterTraverser::visitFunctionDefinition(
+ Visit visit,
+ TIntermFunctionDefinition *node)
+{
+ if (node->getFunctionPrototype()->isArray() && visit == PreVisit)
+ {
+ // Replacing the function header is done on visitFunctionPrototype().
+ mFunctionWithArrayReturnValue = node;
+ }
+ if (visit == PostVisit)
+ {
+ mFunctionWithArrayReturnValue = nullptr;
+ }
+ return true;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitFunctionPrototype(Visit visit,
+ TIntermFunctionPrototype *node)
{
- if (visit == PreVisit)
+ if (visit == PreVisit && node->isArray())
{
- if (node->isArray())
+ // Replace the whole prototype node with another node that has the out parameter
+ // added. Also set the function to return void.
+ TIntermFunctionPrototype *replacement =
+ new TIntermFunctionPrototype(TType(EbtVoid), node->getFunctionSymbolInfo()->getId());
+ CopyAggregateChildren(node, replacement);
+ const TSymbolUniqueId &functionId = node->getFunctionSymbolInfo()->getId();
+ if (mReturnValueIds.find(functionId.get()) == mReturnValueIds.end())
{
- if (node->getOp() == EOpFunction)
- {
- // Replace the parameters child node of the function definition with another node
- // that has the out parameter added.
- // Also set the function to return void.
-
- TIntermAggregate *params = node->getSequence()->front()->getAsAggregate();
- ASSERT(params != nullptr && params->getOp() == EOpParameters);
-
- TIntermAggregate *replacementParams = new TIntermAggregate;
- replacementParams->setOp(EOpParameters);
- CopyAggregateChildren(params, replacementParams);
- replacementParams->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType()));
- replacementParams->setLine(params->getLine());
-
- mReplacements.push_back(NodeUpdateEntry(node, params, replacementParams, false));
-
- node->setType(TType(EbtVoid));
-
- mInFunctionWithArrayReturnValue = true;
- }
- else if (node->getOp() == EOpPrototype)
- {
- // Replace the whole prototype node with another node that has the out parameter added.
- TIntermAggregate *replacement = new TIntermAggregate;
- replacement->setOp(EOpPrototype);
- CopyAggregateChildren(node, replacement);
- replacement->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType()));
- replacement->setUserDefined();
- replacement->setNameObj(node->getNameObj());
- replacement->setFunctionId(node->getFunctionId());
- replacement->setLine(node->getLine());
- replacement->setType(TType(EbtVoid));
-
- mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, replacement, false));
- }
- else if (node->getOp() == EOpFunctionCall)
- {
- // Handle call sites where the returned array is not assigned.
- // Examples where f() is a function returning an array:
- // 1. f();
- // 2. another_array == f();
- // 3. another_function(f());
- // 4. return f();
- // Cases 2 to 4 are already converted to simpler cases by SeparateExpressionsReturningArrays, so we
- // only need to worry about the case where a function call returning an array forms an expression by
- // itself.
- TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
- if (parentAgg != nullptr && parentAgg->getOp() == EOpSequence)
- {
- nextTemporaryIndex();
- TIntermSequence replacements;
- replacements.push_back(createTempDeclaration(node->getType()));
- TIntermSymbol *returnSymbol = createTempSymbol(node->getType());
- replacements.push_back(CreateReplacementCall(node, returnSymbol));
- mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacements));
- }
- return false;
- }
+ mReturnValueIds[functionId.get()] = new TSymbolUniqueId(mSymbolTable);
}
+ replacement->getSequence()->push_back(
+ CreateReturnValueSymbol(*mReturnValueIds[functionId.get()], node->getType()));
+ *replacement->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo();
+ replacement->setLine(node->getLine());
+
+ queueReplacement(replacement, OriginalNode::IS_DROPPED);
}
- else if (visit == PostVisit)
+ return false;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ ASSERT(!node->isArray() || node->getOp() != EOpCallInternalRawFunction);
+ if (visit == PreVisit && node->isArray() && node->getOp() == EOpCallFunctionInAST)
{
- if (node->getOp() == EOpFunction)
+ // Handle call sites where the returned array is not assigned.
+ // Examples where f() is a function returning an array:
+ // 1. f();
+ // 2. another_array == f();
+ // 3. another_function(f());
+ // 4. return f();
+ // Cases 2 to 4 are already converted to simpler cases by
+ // SeparateExpressionsReturningArrays, so we only need to worry about the case where a
+ // function call returning an array forms an expression by itself.
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ if (parentBlock)
{
- mInFunctionWithArrayReturnValue = false;
+ nextTemporaryId();
+ TIntermSequence replacements;
+ replacements.push_back(createTempDeclaration(node->getType()));
+ TIntermSymbol *returnSymbol = createTempSymbol(node->getType());
+ replacements.push_back(CreateReplacementCall(node, returnSymbol));
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
}
+ return false;
}
return true;
}
bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node)
{
- if (mInFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn)
+ if (mFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn)
{
// Instead of returning a value, assign to the out parameter and then return.
TIntermSequence replacements;
- TIntermBinary *replacementAssignment = new TIntermBinary(EOpAssign);
TIntermTyped *expression = node->getExpression();
ASSERT(expression != nullptr);
- replacementAssignment->setLeft(CreateReturnValueSymbol(expression->getType()));
- replacementAssignment->setRight(node->getExpression());
- replacementAssignment->setType(expression->getType());
+ const TSymbolUniqueId &functionId =
+ mFunctionWithArrayReturnValue->getFunctionSymbolInfo()->getId();
+ ASSERT(mReturnValueIds.find(functionId.get()) != mReturnValueIds.end());
+ const TSymbolUniqueId &returnValueId = *mReturnValueIds[functionId.get()];
+ TIntermSymbol *returnValueSymbol =
+ CreateReturnValueSymbol(returnValueId, expression->getType());
+ TIntermBinary *replacementAssignment =
+ new TIntermBinary(EOpAssign, returnValueSymbol, expression);
replacementAssignment->setLine(expression->getLine());
replacements.push_back(replacementAssignment);
@@ -179,7 +182,8 @@ bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBr
replacementBranch->setLine(node->getLine());
replacements.push_back(replacementBranch);
- mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsAggregate(), node, replacements));
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, replacements));
}
return false;
}
@@ -189,18 +193,21 @@ bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBi
if (node->getOp() == EOpAssign && node->getLeft()->isArray())
{
TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
- if (rightAgg != nullptr && rightAgg->getOp() == EOpFunctionCall && rightAgg->isUserDefined())
+ ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpCallInternalRawFunction);
+ if (rightAgg != nullptr && rightAgg->getOp() == EOpCallFunctionInAST)
{
TIntermAggregate *replacementCall = CreateReplacementCall(rightAgg, node->getLeft());
- mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, replacementCall, false));
+ queueReplacement(replacementCall, OriginalNode::IS_DROPPED);
}
}
return false;
}
-} // namespace
+} // namespace
-void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex)
+void ArrayReturnValueToOutParameter(TIntermNode *root, TSymbolTable *symbolTable)
{
- ArrayReturnValueToOutParameterTraverser::apply(root, temporaryIndex);
+ ArrayReturnValueToOutParameterTraverser::apply(root, symbolTable);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h
index 983e203e62..469c7a3b14 100644
--- a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h
+++ b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h
@@ -3,14 +3,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in
-// function definitions, prototypes and call sites.
+// The ArrayReturnValueToOutParameter function changes return values of an array type to out
+// parameters in function definitions, prototypes and call sites.
#ifndef COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_
#define COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_
+namespace sh
+{
+
class TIntermNode;
+class TSymbolTable;
+
+void ArrayReturnValueToOutParameter(TIntermNode *root, TSymbolTable *symbolTable);
-void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex);
+} // namespace sh
#endif // COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/BaseTypes.h b/src/3rdparty/angle/src/compiler/translator/BaseTypes.h
index 0ed6d0e62f..b2070f3baf 100644
--- a/src/3rdparty/angle/src/compiler/translator/BaseTypes.h
+++ b/src/3rdparty/angle/src/compiler/translator/BaseTypes.h
@@ -7,7 +7,14 @@
#ifndef COMPILER_TRANSLATOR_BASETYPES_H_
#define COMPILER_TRANSLATOR_BASETYPES_H_
+#include <algorithm>
+#include <array>
+
#include "common/debug.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
//
// Precision qualifiers
@@ -24,14 +31,18 @@ enum TPrecision
EbpLast
};
-inline const char* getPrecisionString(TPrecision p)
+inline const char *getPrecisionString(TPrecision p)
{
- switch(p)
+ switch (p)
{
- case EbpHigh: return "highp"; break;
- case EbpMedium: return "mediump"; break;
- case EbpLow: return "lowp"; break;
- default: return "mediump"; break; // Safest fallback
+ case EbpHigh:
+ return "highp";
+ case EbpMedium:
+ return "mediump";
+ case EbpLow:
+ return "lowp";
+ default:
+ return "mediump"; // Safest fallback
}
}
@@ -54,69 +65,243 @@ enum TBasicType
EbtIVec, // non type: represents ivec2, ivec3, and ivec4
EbtUVec, // non type: represents uvec2, uvec3, and uvec4
EbtBVec, // non type: represents bvec2, bvec3, and bvec4
+ EbtYuvCscStandardEXT, // Only valid if EXT_YUV_target exists.
EbtGuardSamplerBegin, // non type: see implementation of IsSampler()
EbtSampler2D,
EbtSampler3D,
EbtSamplerCube,
EbtSampler2DArray,
- EbtSamplerExternalOES, // Only valid if OES_EGL_image_external exists.
- EbtSampler2DRect, // Only valid if GL_ARB_texture_rectangle exists.
+ EbtSamplerExternalOES, // Only valid if OES_EGL_image_external exists.
+ EbtSamplerExternal2DY2YEXT, // Only valid if GL_EXT_YUV_target exists.
+ EbtSampler2DRect, // Only valid if GL_ARB_texture_rectangle exists.
+ EbtSampler2DMS,
EbtISampler2D,
EbtISampler3D,
EbtISamplerCube,
EbtISampler2DArray,
+ EbtISampler2DMS,
EbtUSampler2D,
EbtUSampler3D,
EbtUSamplerCube,
EbtUSampler2DArray,
+ EbtUSampler2DMS,
EbtSampler2DShadow,
EbtSamplerCubeShadow,
EbtSampler2DArrayShadow,
- EbtGuardSamplerEnd, // non type: see implementation of IsSampler()
- EbtGSampler2D, // non type: represents sampler2D, isampler2D, and usampler2D
- EbtGSampler3D, // non type: represents sampler3D, isampler3D, and usampler3D
- EbtGSamplerCube, // non type: represents samplerCube, isamplerCube, and usamplerCube
- EbtGSampler2DArray, // non type: represents sampler2DArray, isampler2DArray, and usampler2DArray
+ EbtGuardSamplerEnd, // non type: see implementation of IsSampler()
+ EbtGSampler2D, // non type: represents sampler2D, isampler2D, and usampler2D
+ EbtGSampler3D, // non type: represents sampler3D, isampler3D, and usampler3D
+ EbtGSamplerCube, // non type: represents samplerCube, isamplerCube, and usamplerCube
+ EbtGSampler2DArray, // non type: represents sampler2DArray, isampler2DArray, and
+ // usampler2DArray
+ EbtGSampler2DMS, // non type: represents sampler2DMS, isampler2DMS, and usampler2DMS
+
+ // images
+ EbtGuardImageBegin,
+ EbtImage2D,
+ EbtIImage2D,
+ EbtUImage2D,
+ EbtImage3D,
+ EbtIImage3D,
+ EbtUImage3D,
+ EbtImage2DArray,
+ EbtIImage2DArray,
+ EbtUImage2DArray,
+ EbtImageCube,
+ EbtIImageCube,
+ EbtUImageCube,
+ EbtGuardImageEnd,
+
+ EbtGuardGImageBegin,
+ EbtGImage2D, // non type: represents image2D, uimage2D, iimage2D
+ EbtGImage3D, // non type: represents image3D, uimage3D, iimage3D
+ EbtGImage2DArray, // non type: represents image2DArray, uimage2DArray, iimage2DArray
+ EbtGImageCube, // non type: represents imageCube, uimageCube, iimageCube
+ EbtGuardGImageEnd,
+
EbtStruct,
EbtInterfaceBlock,
- EbtAddress, // should be deprecated??
+ EbtAddress, // should be deprecated??
+
+ EbtAtomicCounter,
// end of list
EbtLast
};
-const char* getBasicString(TBasicType t);
+inline TBasicType convertGImageToFloatImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtGImage2D:
+ return EbtImage2D;
+ case EbtGImage3D:
+ return EbtImage3D;
+ case EbtGImage2DArray:
+ return EbtImage2DArray;
+ case EbtGImageCube:
+ return EbtImageCube;
+ default:
+ UNREACHABLE();
+ }
+ return EbtLast;
+}
+
+inline TBasicType convertGImageToIntImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtGImage2D:
+ return EbtIImage2D;
+ case EbtGImage3D:
+ return EbtIImage3D;
+ case EbtGImage2DArray:
+ return EbtIImage2DArray;
+ case EbtGImageCube:
+ return EbtIImageCube;
+ default:
+ UNREACHABLE();
+ }
+ return EbtLast;
+}
+
+inline TBasicType convertGImageToUnsignedImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtGImage2D:
+ return EbtUImage2D;
+ case EbtGImage3D:
+ return EbtUImage3D;
+ case EbtGImage2DArray:
+ return EbtUImage2DArray;
+ case EbtGImageCube:
+ return EbtUImageCube;
+ default:
+ UNREACHABLE();
+ }
+ return EbtLast;
+}
+
+const char *getBasicString(TBasicType t);
inline bool IsSampler(TBasicType type)
{
return type > EbtGuardSamplerBegin && type < EbtGuardSamplerEnd;
}
+inline bool IsImage(TBasicType type)
+{
+ return type > EbtGuardImageBegin && type < EbtGuardImageEnd;
+}
+
+inline bool IsGImage(TBasicType type)
+{
+ return type > EbtGuardGImageBegin && type < EbtGuardGImageEnd;
+}
+
+inline bool IsAtomicCounter(TBasicType type)
+{
+ return type == EbtAtomicCounter;
+}
+
+inline bool IsOpaqueType(TBasicType type)
+{
+ return IsSampler(type) || IsImage(type) || IsAtomicCounter(type);
+}
+
inline bool IsIntegerSampler(TBasicType type)
{
switch (type)
{
- case EbtISampler2D:
- case EbtISampler3D:
- case EbtISamplerCube:
- case EbtISampler2DArray:
- case EbtUSampler2D:
- case EbtUSampler3D:
- case EbtUSamplerCube:
- case EbtUSampler2DArray:
- return true;
- case EbtSampler2D:
- case EbtSampler3D:
- case EbtSamplerCube:
- case EbtSamplerExternalOES:
- case EbtSampler2DRect:
- case EbtSampler2DArray:
- case EbtSampler2DShadow:
- case EbtSamplerCubeShadow:
- case EbtSampler2DArrayShadow:
- return false;
- default:
- assert(!IsSampler(type));
+ case EbtISampler2D:
+ case EbtISampler3D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ case EbtISampler2DMS:
+ case EbtUSampler2D:
+ case EbtUSampler3D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ case EbtUSampler2DMS:
+ return true;
+ case EbtSampler2D:
+ case EbtSampler3D:
+ case EbtSamplerCube:
+ case EbtSamplerExternalOES:
+ case EbtSamplerExternal2DY2YEXT:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ case EbtSampler2DMS:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsSampler2DMS(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtSampler2DMS:
+ case EbtISampler2DMS:
+ case EbtUSampler2DMS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline bool IsFloatImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtImage2D:
+ case EbtImage3D:
+ case EbtImage2DArray:
+ case EbtImageCube:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+inline bool IsIntegerImage(TBasicType type)
+{
+
+ switch (type)
+ {
+ case EbtIImage2D:
+ case EbtIImage3D:
+ case EbtIImage2DArray:
+ case EbtIImageCube:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+inline bool IsUnsignedImage(TBasicType type)
+{
+
+ switch (type)
+ {
+ case EbtUImage2D:
+ case EbtUImage3D:
+ case EbtUImage2DArray:
+ case EbtUImageCube:
+ return true;
+ default:
+ break;
}
return false;
@@ -126,27 +311,31 @@ inline bool IsSampler2D(TBasicType type)
{
switch (type)
{
- case EbtSampler2D:
- case EbtISampler2D:
- case EbtUSampler2D:
- case EbtSampler2DArray:
- case EbtISampler2DArray:
- case EbtUSampler2DArray:
- case EbtSampler2DRect:
- case EbtSamplerExternalOES:
- case EbtSampler2DShadow:
- case EbtSampler2DArrayShadow:
- return true;
- case EbtSampler3D:
- case EbtISampler3D:
- case EbtUSampler3D:
- case EbtISamplerCube:
- case EbtUSamplerCube:
- case EbtSamplerCube:
- case EbtSamplerCubeShadow:
- return false;
- default:
- assert(!IsSampler(type));
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DRect:
+ case EbtSamplerExternalOES:
+ case EbtSamplerExternal2DY2YEXT:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ case EbtSampler2DMS:
+ case EbtISampler2DMS:
+ case EbtUSampler2DMS:
+ return true;
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCube:
+ case EbtSamplerCubeShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
}
return false;
@@ -156,27 +345,31 @@ inline bool IsSamplerCube(TBasicType type)
{
switch (type)
{
- case EbtSamplerCube:
- case EbtISamplerCube:
- case EbtUSamplerCube:
- case EbtSamplerCubeShadow:
- return true;
- case EbtSampler2D:
- case EbtSampler3D:
- case EbtSamplerExternalOES:
- case EbtSampler2DRect:
- case EbtSampler2DArray:
- case EbtISampler2D:
- case EbtISampler3D:
- case EbtISampler2DArray:
- case EbtUSampler2D:
- case EbtUSampler3D:
- case EbtUSampler2DArray:
- case EbtSampler2DShadow:
- case EbtSampler2DArrayShadow:
- return false;
- default:
- assert(!IsSampler(type));
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCubeShadow:
+ return true;
+ case EbtSampler2D:
+ case EbtSampler3D:
+ case EbtSamplerExternalOES:
+ case EbtSamplerExternal2DY2YEXT:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ case EbtSampler2DMS:
+ case EbtISampler2D:
+ case EbtISampler3D:
+ case EbtISampler2DArray:
+ case EbtISampler2DMS:
+ case EbtUSampler2D:
+ case EbtUSampler3D:
+ case EbtUSampler2DArray:
+ case EbtUSampler2DMS:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
}
return false;
@@ -186,27 +379,31 @@ inline bool IsSampler3D(TBasicType type)
{
switch (type)
{
- case EbtSampler3D:
- case EbtISampler3D:
- case EbtUSampler3D:
- return true;
- case EbtSampler2D:
- case EbtSamplerCube:
- case EbtSamplerExternalOES:
- case EbtSampler2DRect:
- case EbtSampler2DArray:
- case EbtISampler2D:
- case EbtISamplerCube:
- case EbtISampler2DArray:
- case EbtUSampler2D:
- case EbtUSamplerCube:
- case EbtUSampler2DArray:
- case EbtSampler2DShadow:
- case EbtSamplerCubeShadow:
- case EbtSampler2DArrayShadow:
- return false;
- default:
- assert(!IsSampler(type));
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ return true;
+ case EbtSampler2D:
+ case EbtSamplerCube:
+ case EbtSamplerExternalOES:
+ case EbtSamplerExternal2DY2YEXT:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ case EbtSampler2DMS:
+ case EbtISampler2D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ case EbtISampler2DMS:
+ case EbtUSampler2D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ case EbtUSampler2DMS:
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
}
return false;
@@ -216,27 +413,31 @@ inline bool IsSamplerArray(TBasicType type)
{
switch (type)
{
- case EbtSampler2DArray:
- case EbtISampler2DArray:
- case EbtUSampler2DArray:
- case EbtSampler2DArrayShadow:
- return true;
- case EbtSampler2D:
- case EbtISampler2D:
- case EbtUSampler2D:
- case EbtSampler2DRect:
- case EbtSamplerExternalOES:
- case EbtSampler3D:
- case EbtISampler3D:
- case EbtUSampler3D:
- case EbtISamplerCube:
- case EbtUSamplerCube:
- case EbtSamplerCube:
- case EbtSampler2DShadow:
- case EbtSamplerCubeShadow:
- return false;
- default:
- assert(!IsSampler(type));
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DArrayShadow:
+ return true;
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DRect:
+ case EbtSamplerExternalOES:
+ case EbtSamplerExternal2DY2YEXT:
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCube:
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DMS:
+ case EbtISampler2DMS:
+ case EbtUSampler2DMS:
+ return false;
+ default:
+ assert(!IsSampler(type));
}
return false;
@@ -246,27 +447,131 @@ inline bool IsShadowSampler(TBasicType type)
{
switch (type)
{
- case EbtSampler2DShadow:
- case EbtSamplerCubeShadow:
- case EbtSampler2DArrayShadow:
- return true;
- case EbtISampler2D:
- case EbtISampler3D:
- case EbtISamplerCube:
- case EbtISampler2DArray:
- case EbtUSampler2D:
- case EbtUSampler3D:
- case EbtUSamplerCube:
- case EbtUSampler2DArray:
- case EbtSampler2D:
- case EbtSampler3D:
- case EbtSamplerCube:
- case EbtSamplerExternalOES:
- case EbtSampler2DRect:
- case EbtSampler2DArray:
- return false;
- default:
- assert(!IsSampler(type));
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ return true;
+ case EbtISampler2D:
+ case EbtISampler3D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ case EbtISampler2DMS:
+ case EbtUSampler2D:
+ case EbtUSampler3D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ case EbtUSampler2DMS:
+ case EbtSampler2D:
+ case EbtSampler3D:
+ case EbtSamplerCube:
+ case EbtSamplerExternalOES:
+ case EbtSamplerExternal2DY2YEXT:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ case EbtSampler2DMS:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsImage2D(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtImage2D:
+ case EbtIImage2D:
+ case EbtUImage2D:
+ return true;
+ case EbtImage3D:
+ case EbtIImage3D:
+ case EbtUImage3D:
+ case EbtImage2DArray:
+ case EbtIImage2DArray:
+ case EbtUImage2DArray:
+ case EbtImageCube:
+ case EbtIImageCube:
+ case EbtUImageCube:
+ return false;
+ default:
+ assert(!IsImage(type));
+ }
+
+ return false;
+}
+
+inline bool IsImage3D(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtImage3D:
+ case EbtIImage3D:
+ case EbtUImage3D:
+ return true;
+ case EbtImage2D:
+ case EbtIImage2D:
+ case EbtUImage2D:
+ case EbtImage2DArray:
+ case EbtIImage2DArray:
+ case EbtUImage2DArray:
+ case EbtImageCube:
+ case EbtIImageCube:
+ case EbtUImageCube:
+ return false;
+ default:
+ assert(!IsImage(type));
+ }
+
+ return false;
+}
+
+inline bool IsImage2DArray(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtImage2DArray:
+ case EbtIImage2DArray:
+ case EbtUImage2DArray:
+ return true;
+ case EbtImage2D:
+ case EbtIImage2D:
+ case EbtUImage2D:
+ case EbtImage3D:
+ case EbtIImage3D:
+ case EbtUImage3D:
+ case EbtImageCube:
+ case EbtIImageCube:
+ case EbtUImageCube:
+ return false;
+ default:
+ assert(!IsImage(type));
+ }
+
+ return false;
+}
+
+inline bool IsImageCube(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtImageCube:
+ case EbtIImageCube:
+ case EbtUImageCube:
+ return true;
+ case EbtImage2D:
+ case EbtIImage2D:
+ case EbtUImage2D:
+ case EbtImage3D:
+ case EbtIImage3D:
+ case EbtUImage3D:
+ case EbtImage2DArray:
+ case EbtIImage2DArray:
+ case EbtUImage2DArray:
+ return false;
+ default:
+ assert(!IsImage(type));
}
return false;
@@ -279,7 +584,7 @@ inline bool IsInteger(TBasicType type)
inline bool SupportsPrecision(TBasicType type)
{
- return type == EbtFloat || type == EbtInt || type == EbtUInt || IsSampler(type);
+ return type == EbtFloat || type == EbtInt || type == EbtUInt || IsOpaqueType(type);
}
//
@@ -297,6 +602,7 @@ enum TQualifier
EvqVaryingIn, // readonly, fragment shaders only
EvqVaryingOut, // vertex shaders only read/write
EvqUniform, // Readonly, vertex and fragment
+ EvqBuffer, // read/write, vertex, fragment and compute shader
EvqVertexIn, // Vertex shader input
EvqFragmentOut, // Fragment shader output
@@ -311,6 +617,7 @@ enum TQualifier
// built-ins read by vertex shader
EvqInstanceID,
+ EvqVertexID,
// built-ins written by vertex shader
EvqPosition,
@@ -331,24 +638,77 @@ enum TQualifier
EvqSecondaryFragColorEXT, // EXT_blend_func_extended
EvqSecondaryFragDataEXT, // EXT_blend_func_extended
+ EvqViewIDOVR, // OVR_multiview
+ EvqViewportIndex, // gl_ViewportIndex
+
// built-ins written by the shader_framebuffer_fetch extension(s)
EvqLastFragColor,
EvqLastFragData,
// GLSL ES 3.0 vertex output and fragment input
- EvqSmooth, // Incomplete qualifier, smooth is the default
- EvqFlat, // Incomplete qualifier
- EvqSmoothOut = EvqSmooth,
- EvqFlatOut = EvqFlat,
+ EvqSmooth, // Incomplete qualifier, smooth is the default
+ EvqFlat, // Incomplete qualifier
+ EvqCentroid, // Incomplete qualifier
+ EvqSmoothOut,
+ EvqFlatOut,
EvqCentroidOut, // Implies smooth
EvqSmoothIn,
EvqFlatIn,
EvqCentroidIn, // Implies smooth
+ // GLSL ES 3.1 compute shader special variables
+ EvqShared,
+ EvqComputeIn,
+ EvqNumWorkGroups,
+ EvqWorkGroupSize,
+ EvqWorkGroupID,
+ EvqLocalInvocationID,
+ EvqGlobalInvocationID,
+ EvqLocalInvocationIndex,
+
+ // GLSL ES 3.1 memory qualifiers
+ EvqReadOnly,
+ EvqWriteOnly,
+ EvqCoherent,
+ EvqRestrict,
+ EvqVolatile,
+
+ // GLSL ES 3.1 extension OES_geometry_shader qualifiers
+ EvqGeometryIn,
+ EvqGeometryOut,
+ EvqPerVertexIn, // gl_in
+ EvqPrimitiveIDIn, // gl_PrimitiveIDIn
+ EvqInvocationID, // gl_InvocationID
+ EvqPrimitiveID, // gl_PrimitiveID
+ EvqLayer, // gl_Layer
+
// end of list
EvqLast
};
+inline bool IsQualifierUnspecified(TQualifier qualifier)
+{
+ return (qualifier == EvqTemporary || qualifier == EvqGlobal);
+}
+
+enum TLayoutImageInternalFormat
+{
+ EiifUnspecified,
+ EiifRGBA32F,
+ EiifRGBA16F,
+ EiifR32F,
+ EiifRGBA32UI,
+ EiifRGBA16UI,
+ EiifRGBA8UI,
+ EiifR32UI,
+ EiifRGBA32I,
+ EiifRGBA16I,
+ EiifRGBA8I,
+ EiifR32I,
+ EiifRGBA8,
+ EiifRGBA8_SNORM
+};
+
enum TLayoutMatrixPacking
{
EmpUnspecified,
@@ -361,36 +721,165 @@ enum TLayoutBlockStorage
EbsUnspecified,
EbsShared,
EbsPacked,
- EbsStd140
+ EbsStd140,
+ EbsStd430
+};
+
+enum TYuvCscStandardEXT
+{
+ EycsUndefined,
+ EycsItu601,
+ EycsItu601FullRange,
+ EycsItu709
+};
+
+enum TLayoutPrimitiveType
+{
+ EptUndefined,
+ EptPoints,
+ EptLines,
+ EptLinesAdjacency,
+ EptTriangles,
+ EptTrianglesAdjacency,
+ EptLineStrip,
+ EptTriangleStrip
};
struct TLayoutQualifier
{
+ // Must have a trivial default constructor since it is used in YYSTYPE.
+ TLayoutQualifier() = default;
+
+ constexpr static TLayoutQualifier Create() { return TLayoutQualifier(0); }
+
+ bool isEmpty() const
+ {
+ return location == -1 && binding == -1 && offset == -1 && numViews == -1 && yuv == false &&
+ matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified &&
+ !localSize.isAnyValueSet() && imageInternalFormat == EiifUnspecified &&
+ primitiveType == EptUndefined && invocations == 0 && maxVertices == -1;
+ }
+
+ bool isCombinationValid() const
+ {
+ bool workSizeSpecified = localSize.isAnyValueSet();
+ bool numViewsSet = (numViews != -1);
+ bool geometryShaderSpecified =
+ (primitiveType != EptUndefined) || (invocations != 0) || (maxVertices != -1);
+ bool otherLayoutQualifiersSpecified =
+ (location != -1 || binding != -1 || matrixPacking != EmpUnspecified ||
+ blockStorage != EbsUnspecified || imageInternalFormat != EiifUnspecified);
+
+ // we can have either the work group size specified, or number of views,
+ // or yuv layout qualifier, or the other layout qualifiers.
+ return (workSizeSpecified ? 1 : 0) + (numViewsSet ? 1 : 0) + (yuv ? 1 : 0) +
+ (otherLayoutQualifiersSpecified ? 1 : 0) + (geometryShaderSpecified ? 1 : 0) <=
+ 1;
+ }
+
+ bool isLocalSizeEqual(const sh::WorkGroupSize &localSizeIn) const
+ {
+ return localSize.isWorkGroupSizeMatching(localSizeIn);
+ }
+
int location;
+ unsigned int locationsSpecified;
TLayoutMatrixPacking matrixPacking;
TLayoutBlockStorage blockStorage;
- static TLayoutQualifier create()
- {
- TLayoutQualifier layoutQualifier;
+ // Compute shader layout qualifiers.
+ sh::WorkGroupSize localSize;
+
+ int binding;
+ int offset;
+
+ // Image format layout qualifier
+ TLayoutImageInternalFormat imageInternalFormat;
+
+ // OVR_multiview num_views.
+ int numViews;
+
+ // EXT_YUV_target yuv layout qualifier.
+ bool yuv;
- layoutQualifier.location = -1;
- layoutQualifier.matrixPacking = EmpUnspecified;
- layoutQualifier.blockStorage = EbsUnspecified;
+ // OES_geometry_shader layout qualifiers.
+ TLayoutPrimitiveType primitiveType;
+ int invocations;
+ int maxVertices;
- return layoutQualifier;
+ private:
+ explicit constexpr TLayoutQualifier(int /*placeholder*/)
+ : location(-1),
+ locationsSpecified(0),
+ matrixPacking(EmpUnspecified),
+ blockStorage(EbsUnspecified),
+ localSize(-1),
+ binding(-1),
+ offset(-1),
+ imageInternalFormat(EiifUnspecified),
+ numViews(-1),
+ yuv(false),
+ primitiveType(EptUndefined),
+ invocations(0),
+ maxVertices(-1)
+ {
}
+};
+
+struct TMemoryQualifier
+{
+ // Must have a trivial default constructor since it is used in YYSTYPE.
+ TMemoryQualifier() = default;
bool isEmpty() const
{
- return location == -1 && matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified;
+ return !readonly && !writeonly && !coherent && !restrictQualifier && !volatileQualifier;
+ }
+
+ constexpr static TMemoryQualifier Create() { return TMemoryQualifier(0); }
+
+ // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+ // An image can be qualified as both readonly and writeonly. It still can be can be used with
+ // imageSize().
+ bool readonly;
+ bool writeonly;
+ bool coherent;
+
+ // restrict and volatile are reserved keywords in C/C++
+ bool restrictQualifier;
+ bool volatileQualifier;
+
+ private:
+ explicit constexpr TMemoryQualifier(int /*placeholder*/)
+ : readonly(false),
+ writeonly(false),
+ coherent(false),
+ restrictQualifier(false),
+ volatileQualifier(false)
+ {
}
};
+inline const char *getWorkGroupSizeString(size_t dimension)
+{
+ switch (dimension)
+ {
+ case 0u:
+ return "local_size_x";
+ case 1u:
+ return "local_size_y";
+ case 2u:
+ return "local_size_z";
+ default:
+ UNREACHABLE();
+ return "dimension out of bounds";
+ }
+}
+
//
-// This is just for debug print out, carried along with the definitions above.
+// This is just for debug and error message print out, carried along with the definitions above.
//
-inline const char* getQualifierString(TQualifier q)
+inline const char *getQualifierString(TQualifier q)
{
// clang-format off
switch(q)
@@ -402,6 +891,7 @@ inline const char* getQualifierString(TQualifier q)
case EvqVaryingIn: return "varying";
case EvqVaryingOut: return "varying";
case EvqUniform: return "uniform";
+ case EvqBuffer: return "buffer";
case EvqVertexIn: return "in";
case EvqFragmentOut: return "out";
case EvqVertexOut: return "out";
@@ -411,6 +901,7 @@ inline const char* getQualifierString(TQualifier q)
case EvqInOut: return "inout";
case EvqConstReadOnly: return "const";
case EvqInstanceID: return "InstanceID";
+ case EvqVertexID: return "VertexID";
case EvqPosition: return "Position";
case EvqPointSize: return "PointSize";
case EvqFragCoord: return "FragCoord";
@@ -422,54 +913,161 @@ inline const char* getQualifierString(TQualifier q)
case EvqFragDepth: return "FragDepth";
case EvqSecondaryFragColorEXT: return "SecondaryFragColorEXT";
case EvqSecondaryFragDataEXT: return "SecondaryFragDataEXT";
+ case EvqViewIDOVR: return "ViewIDOVR";
+ case EvqViewportIndex: return "ViewportIndex";
+ case EvqLayer: return "Layer";
case EvqLastFragColor: return "LastFragColor";
case EvqLastFragData: return "LastFragData";
case EvqSmoothOut: return "smooth out";
- case EvqCentroidOut: return "centroid out";
+ case EvqCentroidOut: return "smooth centroid out";
case EvqFlatOut: return "flat out";
case EvqSmoothIn: return "smooth in";
case EvqFlatIn: return "flat in";
- case EvqCentroidIn: return "centroid in";
+ case EvqCentroidIn: return "smooth centroid in";
+ case EvqCentroid: return "centroid";
+ case EvqFlat: return "flat";
+ case EvqSmooth: return "smooth";
+ case EvqShared: return "shared";
+ case EvqComputeIn: return "in";
+ case EvqNumWorkGroups: return "NumWorkGroups";
+ case EvqWorkGroupSize: return "WorkGroupSize";
+ case EvqWorkGroupID: return "WorkGroupID";
+ case EvqLocalInvocationID: return "LocalInvocationID";
+ case EvqGlobalInvocationID: return "GlobalInvocationID";
+ case EvqLocalInvocationIndex: return "LocalInvocationIndex";
+ case EvqReadOnly: return "readonly";
+ case EvqWriteOnly: return "writeonly";
+ case EvqGeometryIn: return "in";
+ case EvqGeometryOut: return "out";
+ case EvqPerVertexIn: return "gl_in";
default: UNREACHABLE(); return "unknown qualifier";
}
// clang-format on
}
-inline const char* getMatrixPackingString(TLayoutMatrixPacking mpq)
+inline const char *getMatrixPackingString(TLayoutMatrixPacking mpq)
{
switch (mpq)
{
- case EmpUnspecified: return "mp_unspecified";
- case EmpRowMajor: return "row_major";
- case EmpColumnMajor: return "column_major";
- default: UNREACHABLE(); return "unknown matrix packing";
+ case EmpUnspecified:
+ return "mp_unspecified";
+ case EmpRowMajor:
+ return "row_major";
+ case EmpColumnMajor:
+ return "column_major";
+ default:
+ UNREACHABLE();
+ return "unknown matrix packing";
}
}
-inline const char* getBlockStorageString(TLayoutBlockStorage bsq)
+inline const char *getBlockStorageString(TLayoutBlockStorage bsq)
{
switch (bsq)
{
- case EbsUnspecified: return "bs_unspecified";
- case EbsShared: return "shared";
- case EbsPacked: return "packed";
- case EbsStd140: return "std140";
- default: UNREACHABLE(); return "unknown block storage";
+ case EbsUnspecified:
+ return "bs_unspecified";
+ case EbsShared:
+ return "shared";
+ case EbsPacked:
+ return "packed";
+ case EbsStd140:
+ return "std140";
+ case EbsStd430:
+ return "std430";
+ default:
+ UNREACHABLE();
+ return "unknown block storage";
}
}
-inline const char* getInterpolationString(TQualifier q)
+inline const char *getImageInternalFormatString(TLayoutImageInternalFormat iifq)
{
- switch(q)
+ switch (iifq)
+ {
+ case EiifRGBA32F:
+ return "rgba32f";
+ case EiifRGBA16F:
+ return "rgba16f";
+ case EiifR32F:
+ return "r32f";
+ case EiifRGBA32UI:
+ return "rgba32ui";
+ case EiifRGBA16UI:
+ return "rgba16ui";
+ case EiifRGBA8UI:
+ return "rgba8ui";
+ case EiifR32UI:
+ return "r32ui";
+ case EiifRGBA32I:
+ return "rgba32i";
+ case EiifRGBA16I:
+ return "rgba16i";
+ case EiifRGBA8I:
+ return "rgba8i";
+ case EiifR32I:
+ return "r32i";
+ case EiifRGBA8:
+ return "rgba8";
+ case EiifRGBA8_SNORM:
+ return "rgba8_snorm";
+ default:
+ UNREACHABLE();
+ return "unknown internal image format";
+ }
+}
+
+inline TYuvCscStandardEXT getYuvCscStandardEXT(const std::string &str)
+{
+ if (str == "itu_601")
+ return EycsItu601;
+ else if (str == "itu_601_full_range")
+ return EycsItu601FullRange;
+ else if (str == "itu_709")
+ return EycsItu709;
+ return EycsUndefined;
+}
+
+inline const char *getYuvCscStandardEXTString(TYuvCscStandardEXT ycsq)
+{
+ switch (ycsq)
+ {
+ case EycsItu601:
+ return "itu_601";
+ case EycsItu601FullRange:
+ return "itu_601_full_range";
+ case EycsItu709:
+ return "itu_709";
+ default:
+ UNREACHABLE();
+ return "unknown color space conversion standard";
+ }
+}
+
+inline const char *getGeometryShaderPrimitiveTypeString(TLayoutPrimitiveType primitiveType)
+{
+ switch (primitiveType)
{
- case EvqSmoothOut: return "smooth"; break;
- case EvqCentroidOut: return "centroid"; break;
- case EvqFlatOut: return "flat"; break;
- case EvqSmoothIn: return "smooth"; break;
- case EvqCentroidIn: return "centroid"; break;
- case EvqFlatIn: return "flat"; break;
- default: UNREACHABLE(); return "unknown interpolation";
+ case EptPoints:
+ return "points";
+ case EptLines:
+ return "lines";
+ case EptTriangles:
+ return "triangles";
+ case EptLinesAdjacency:
+ return "lines_adjacency";
+ case EptTrianglesAdjacency:
+ return "triangles_adjacency";
+ case EptLineStrip:
+ return "line_strip";
+ case EptTriangleStrip:
+ return "triangle_strip";
+ default:
+ UNREACHABLE();
+ return "unknown geometry shader primitive type";
}
}
-#endif // COMPILER_TRANSLATOR_BASETYPES_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BASETYPES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp b/src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
new file mode 100644
index 0000000000..d6a1e025de
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
@@ -0,0 +1,107 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#include "BreakVariableAliasingInInnerLoops.h"
+
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+
+// A HLSL compiler developer gave us more details on the root cause and the workaround needed:
+// The root problem is that if the HLSL compiler is applying aliasing information even on
+// incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
+// that comes from a series of assignments, possibly with swizzled or ternary operators with
+// known conditionals, where the source is before the loop.
+// So, a workaround is to add a +0 term to variables the first time they are assigned to in
+// an inner loop (if they are declared in an outside scope, otherwise there is no need).
+// This will break the aliasing chain.
+
+// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
+// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
+// assignment don't need a workaround.
+
+namespace sh
+{
+
+namespace
+{
+
+class AliasingBreaker : public TIntermTraverser
+{
+ public:
+ AliasingBreaker() : TIntermTraverser(true, false, true) {}
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *binary)
+ {
+ if (visit != PreVisit)
+ {
+ return false;
+ }
+
+ if (mLoopLevel < 2 || !binary->isAssignment())
+ {
+ return true;
+ }
+
+ TIntermTyped *B = binary->getRight();
+ TType type = B->getType();
+
+ if (!type.isScalar() && !type.isVector() && !type.isMatrix())
+ {
+ return true;
+ }
+
+ if (type.isArray() || IsSampler(type.getBasicType()))
+ {
+ return true;
+ }
+
+ // We have a scalar / vector / matrix assignment with loop depth 2.
+ // Transform it from
+ // A = B
+ // to
+ // A = (B + typeof<B>(0));
+
+ TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, CreateZeroNode(type));
+ bPlusZero->setLine(B->getLine());
+
+ binary->replaceChildNode(B, bPlusZero);
+
+ return true;
+ }
+
+ bool visitLoop(Visit visit, TIntermLoop *loop)
+ {
+ if (visit == PreVisit)
+ {
+ mLoopLevel++;
+ }
+ else
+ {
+ ASSERT(mLoopLevel > 0);
+ mLoopLevel--;
+ }
+
+ return true;
+ }
+
+ private:
+ int mLoopLevel = 0;
+};
+
+} // anonymous namespace
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root)
+{
+ AliasingBreaker breaker;
+ root->traverse(&breaker);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h b/src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
new file mode 100644
index 0000000000..b1d906f919
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#ifndef COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+#define COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp
index 0c7f149ee6..905e634fd1 100644
--- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp
@@ -4,16 +4,20 @@
// found in the LICENSE file.
//
-#include "angle_gl.h"
#include "compiler/translator/BuiltInFunctionEmulator.h"
+#include "angle_gl.h"
+#include "compiler/translator/Cache.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/SymbolTable.h"
+namespace sh
+{
+
class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser
{
public:
BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator)
- : TIntermTraverser(true, false, false),
- mEmulator(emulator)
+ : TIntermTraverser(true, false, false), mEmulator(emulator)
{
}
@@ -21,7 +25,8 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr
{
if (visit == PreVisit)
{
- bool needToEmulate = mEmulator.SetFunctionCalled(node->getOp(), node->getOperand()->getType());
+ bool needToEmulate =
+ mEmulator.setFunctionCalled(node->getOp(), node->getOperand()->getType());
if (needToEmulate)
node->setUseEmulatedFunction();
}
@@ -32,48 +37,23 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr
{
if (visit == PreVisit)
{
- // Here we handle all the built-in functions instead of the ones we
+ // Here we handle all the built-in functions mapped to ops, not just the ones that are
// currently identified as problematic.
- switch (node->getOp())
+ if (node->isConstructor() || node->isFunctionCall())
{
- case EOpLessThan:
- case EOpGreaterThan:
- case EOpLessThanEqual:
- case EOpGreaterThanEqual:
- case EOpVectorEqual:
- case EOpVectorNotEqual:
- case EOpMod:
- case EOpPow:
- case EOpAtan:
- case EOpMin:
- case EOpMax:
- case EOpClamp:
- case EOpMix:
- case EOpStep:
- case EOpSmoothStep:
- case EOpDistance:
- case EOpDot:
- case EOpCross:
- case EOpFaceForward:
- case EOpReflect:
- case EOpRefract:
- case EOpOuterProduct:
- case EOpMul:
- break;
- default:
- return true;
+ return true;
}
const TIntermSequence &sequence = *(node->getSequence());
- bool needToEmulate = false;
- // Right now we only handle built-in functions with two or three parameters.
+ bool needToEmulate = false;
+ // Right now we only handle built-in functions with two to four parameters.
if (sequence.size() == 2)
{
TIntermTyped *param1 = sequence[0]->getAsTyped();
TIntermTyped *param2 = sequence[1]->getAsTyped();
if (!param1 || !param2)
return true;
- needToEmulate = mEmulator.SetFunctionCalled(
- node->getOp(), param1->getType(), param2->getType());
+ needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(),
+ param2->getType());
}
else if (sequence.size() == 3)
{
@@ -82,8 +62,20 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr
TIntermTyped *param3 = sequence[2]->getAsTyped();
if (!param1 || !param2 || !param3)
return true;
- needToEmulate = mEmulator.SetFunctionCalled(
- node->getOp(), param1->getType(), param2->getType(), param3->getType());
+ needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(),
+ param2->getType(), param3->getType());
+ }
+ else if (sequence.size() == 4)
+ {
+ TIntermTyped *param1 = sequence[0]->getAsTyped();
+ TIntermTyped *param2 = sequence[1]->getAsTyped();
+ TIntermTyped *param3 = sequence[2]->getAsTyped();
+ TIntermTyped *param4 = sequence[3]->getAsTyped();
+ if (!param1 || !param2 || !param3 || !param4)
+ return true;
+ needToEmulate =
+ mEmulator.setFunctionCalled(node->getOp(), param1->getType(), param2->getType(),
+ param3->getType(), param4->getType());
}
else
{
@@ -101,130 +93,245 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr
};
BuiltInFunctionEmulator::BuiltInFunctionEmulator()
-{}
+{
+}
-void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param,
- const char *emulatedFunctionDefinition)
+FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
+ const TType *param,
+ const char *emulatedFunctionDefinition)
{
- mEmulatedFunctions[FunctionId(op, param)] = std::string(emulatedFunctionDefinition);
+ FunctionId id(op, param);
+ mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
+ return id;
}
-void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2,
- const char *emulatedFunctionDefinition)
+FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const char *emulatedFunctionDefinition)
{
- mEmulatedFunctions[FunctionId(op, param1, param2)] = std::string(emulatedFunctionDefinition);
+ FunctionId id(op, param1, param2);
+ mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
+ return id;
}
-void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2,
- const TType *param3, const char *emulatedFunctionDefinition)
+FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency(
+ const FunctionId &dependency,
+ TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const char *emulatedFunctionDefinition)
{
- mEmulatedFunctions[FunctionId(op, param1, param2, param3)] = std::string(emulatedFunctionDefinition);
+ FunctionId id(op, param1, param2);
+ mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
+ mFunctionDependencies[id] = dependency;
+ return id;
}
-bool BuiltInFunctionEmulator::IsOutputEmpty() const
+FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const char *emulatedFunctionDefinition)
+{
+ FunctionId id(op, param1, param2, param3);
+ mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
+ return id;
+}
+
+FunctionId BuiltInFunctionEmulator::addEmulatedFunction(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const TType *param4,
+ const char *emulatedFunctionDefinition)
+{
+ FunctionId id(op, param1, param2, param3, param4);
+ mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
+ return id;
+}
+
+FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency(
+ const FunctionId &dependency,
+ TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const TType *param4,
+ const char *emulatedFunctionDefinition)
+{
+ FunctionId id(op, param1, param2, param3, param4);
+ mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
+ mFunctionDependencies[id] = dependency;
+ return id;
+}
+
+bool BuiltInFunctionEmulator::isOutputEmpty() const
{
return (mFunctions.size() == 0);
}
-void BuiltInFunctionEmulator::OutputEmulatedFunctions(TInfoSinkBase &out) const
+void BuiltInFunctionEmulator::outputEmulatedFunctions(TInfoSinkBase &out) const
{
- for (size_t i = 0; i < mFunctions.size(); ++i)
+ for (const auto &function : mFunctions)
{
- out << mEmulatedFunctions.find(mFunctions[i])->second << "\n\n";
+ const char *body = findEmulatedFunction(function);
+ ASSERT(body);
+ out << body;
+ out << "\n\n";
}
}
-bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType &param)
+bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, const TType &param)
{
- return SetFunctionCalled(FunctionId(op, &param));
+ return setFunctionCalled(FunctionId(op, &param));
}
-bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType &param1, const TType &param2)
+bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
+ const TType &param1,
+ const TType &param2)
{
- return SetFunctionCalled(FunctionId(op, &param1, &param2));
+ return setFunctionCalled(FunctionId(op, &param1, &param2));
}
-bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op,
- const TType &param1, const TType &param2, const TType &param3)
+bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
+ const TType &param1,
+ const TType &param2,
+ const TType &param3)
{
- return SetFunctionCalled(FunctionId(op, &param1, &param2, &param3));
+ return setFunctionCalled(FunctionId(op, &param1, &param2, &param3));
}
-bool BuiltInFunctionEmulator::SetFunctionCalled(const FunctionId &functionId)
+bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
+ const TType &param1,
+ const TType &param2,
+ const TType &param3,
+ const TType &param4)
{
- if (mEmulatedFunctions.find(functionId) != mEmulatedFunctions.end())
+ return setFunctionCalled(FunctionId(op, &param1, &param2, &param3, &param4));
+}
+
+const char *BuiltInFunctionEmulator::findEmulatedFunction(const FunctionId &functionId) const
+{
+ for (const auto &queryFunction : mQueryFunctions)
{
- for (size_t i = 0; i < mFunctions.size(); ++i)
+ const char *result = queryFunction(functionId);
+ if (result)
{
- if (mFunctions[i] == functionId)
- return true;
+ return result;
}
- // Copy the functionId if it needs to be stored, to make sure that the TType pointers inside
- // remain valid and constant.
- mFunctions.push_back(functionId.getCopy());
- return true;
}
- return false;
+
+ const auto &result = mEmulatedFunctions.find(functionId);
+ if (result != mEmulatedFunctions.end())
+ {
+ return result->second.c_str();
+ }
+
+ return nullptr;
+}
+
+bool BuiltInFunctionEmulator::setFunctionCalled(const FunctionId &functionId)
+{
+ if (!findEmulatedFunction(functionId))
+ {
+ return false;
+ }
+
+ for (size_t i = 0; i < mFunctions.size(); ++i)
+ {
+ if (mFunctions[i] == functionId)
+ return true;
+ }
+ // If the function depends on another, mark the dependency as called.
+ auto dependency = mFunctionDependencies.find(functionId);
+ if (dependency != mFunctionDependencies.end())
+ {
+ setFunctionCalled((*dependency).second);
+ }
+ // Copy the functionId if it needs to be stored, to make sure that the TType pointers inside
+ // remain valid and constant.
+ mFunctions.push_back(functionId.getCopy());
+ return true;
}
-void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(TIntermNode *root)
+void BuiltInFunctionEmulator::markBuiltInFunctionsForEmulation(TIntermNode *root)
{
ASSERT(root);
- if (mEmulatedFunctions.empty())
+ if (mEmulatedFunctions.empty() && mQueryFunctions.empty())
return;
BuiltInFunctionEmulationMarker marker(*this);
root->traverse(&marker);
}
-void BuiltInFunctionEmulator::Cleanup()
+void BuiltInFunctionEmulator::cleanup()
{
mFunctions.clear();
+ mFunctionDependencies.clear();
+}
+
+void BuiltInFunctionEmulator::addFunctionMap(BuiltinQueryFunc queryFunc)
+{
+ mQueryFunctions.push_back(queryFunc);
+}
+
+// static
+void BuiltInFunctionEmulator::WriteEmulatedFunctionName(TInfoSinkBase &out, const char *name)
+{
+ ASSERT(name[strlen(name) - 1] != '(');
+ out << name << "_emu";
}
-//static
-TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
- const TString &name)
+FunctionId::FunctionId()
+ : mOp(EOpNull),
+ mParam1(TCache::getType(EbtVoid)),
+ mParam2(TCache::getType(EbtVoid)),
+ mParam3(TCache::getType(EbtVoid)),
+ mParam4(TCache::getType(EbtVoid))
{
- ASSERT(name[name.length() - 1] == '(');
- return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
}
-BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param)
+FunctionId::FunctionId(TOperator op, const TType *param)
: mOp(op),
mParam1(param),
- mParam2(new TType(EbtVoid)),
- mParam3(new TType(EbtVoid))
+ mParam2(TCache::getType(EbtVoid)),
+ mParam3(TCache::getType(EbtVoid)),
+ mParam4(TCache::getType(EbtVoid))
{
}
-BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2)
+FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2)
: mOp(op),
mParam1(param1),
mParam2(param2),
- mParam3(new TType(EbtVoid))
+ mParam3(TCache::getType(EbtVoid)),
+ mParam4(TCache::getType(EbtVoid))
{
}
-BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op,
- const TType *param1, const TType *param2, const TType *param3)
- : mOp(op),
- mParam1(param1),
- mParam2(param2),
- mParam3(param3)
+FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2, const TType *param3)
+ : mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(TCache::getType(EbtVoid))
{
}
-bool BuiltInFunctionEmulator::FunctionId::operator==(const BuiltInFunctionEmulator::FunctionId &other) const
+FunctionId::FunctionId(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const TType *param4)
+ : mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(param4)
{
- return (mOp == other.mOp &&
- *mParam1 == *other.mParam1 &&
- *mParam2 == *other.mParam2 &&
- *mParam3 == *other.mParam3);
}
-bool BuiltInFunctionEmulator::FunctionId::operator<(const BuiltInFunctionEmulator::FunctionId &other) const
+bool FunctionId::operator==(const FunctionId &other) const
+{
+ return (mOp == other.mOp && *mParam1 == *other.mParam1 && *mParam2 == *other.mParam2 &&
+ *mParam3 == *other.mParam3 && *mParam4 == *other.mParam4);
+}
+
+bool FunctionId::operator<(const FunctionId &other) const
{
if (mOp != other.mOp)
return mOp < other.mOp;
@@ -233,11 +340,16 @@ bool BuiltInFunctionEmulator::FunctionId::operator<(const BuiltInFunctionEmulato
if (*mParam2 != *other.mParam2)
return *mParam2 < *other.mParam2;
if (*mParam3 != *other.mParam3)
- return *mParam3 < *other.mParam3;
- return false; // all fields are equal
+ return *mParam3 < *other.mParam3;
+ if (*mParam4 != *other.mParam4)
+ return *mParam4 < *other.mParam4;
+ return false; // all fields are equal
}
-BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::FunctionId::getCopy() const
+FunctionId FunctionId::getCopy() const
{
- return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3));
+ return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3),
+ new TType(*mParam4));
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h
index 6976edfd57..5f15f66224 100644
--- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h
+++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h
@@ -9,77 +9,175 @@
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/ParamType.h"
+
+namespace sh
+{
+
+struct MiniFunctionId
+{
+ constexpr MiniFunctionId(TOperator op = EOpNull,
+ ParamType paramType1 = ParamType::Void,
+ ParamType paramType2 = ParamType::Void,
+ ParamType paramType3 = ParamType::Void,
+ ParamType paramType4 = ParamType::Void)
+ : op(op),
+ paramType1(paramType1),
+ paramType2(paramType2),
+ paramType3(paramType3),
+ paramType4(paramType4)
+ {
+ }
+
+ TOperator op;
+ ParamType paramType1;
+ ParamType paramType2;
+ ParamType paramType3;
+ ParamType paramType4;
+};
+
+class FunctionId final
+{
+ public:
+ FunctionId();
+ FunctionId(TOperator op, const TType *param);
+ FunctionId(TOperator op, const TType *param1, const TType *param2);
+ FunctionId(TOperator op, const TType *param1, const TType *param2, const TType *param3);
+ FunctionId(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const TType *param4);
+
+ FunctionId(const FunctionId &) = default;
+ FunctionId &operator=(const FunctionId &) = default;
+
+ bool operator==(const FunctionId &other) const;
+ bool operator<(const FunctionId &other) const;
+
+ FunctionId getCopy() const;
+
+ private:
+ friend bool operator==(const MiniFunctionId &miniId, const FunctionId &functionId);
+ TOperator mOp;
+
+ // The memory that these TType objects use is freed by PoolAllocator. The
+ // BuiltInFunctionEmulator's lifetime can extend until after the memory pool is freed, but
+ // that's not an issue since this class never destructs these objects.
+ const TType *mParam1;
+ const TType *mParam2;
+ const TType *mParam3;
+ const TType *mParam4;
+};
+
+inline bool operator==(ParamType paramType, const TType *type)
+{
+ return SameParamType(paramType, type->getBasicType(), type->getNominalSize(),
+ type->getSecondarySize());
+}
+
+inline bool operator==(const MiniFunctionId &miniId, const FunctionId &functionId)
+{
+ return miniId.op == functionId.mOp && miniId.paramType1 == functionId.mParam1 &&
+ miniId.paramType2 == functionId.mParam2 && miniId.paramType3 == functionId.mParam3 &&
+ miniId.paramType4 == functionId.mParam4;
+}
+
+using BuiltinQueryFunc = const char *(const FunctionId &);
//
-// This class decides which built-in functions need to be replaced with the
-// emulated ones.
-// It can be used to work around driver bugs or implement functions that are
-// not natively implemented on a specific platform.
+// This class decides which built-in functions need to be replaced with the emulated ones. It can be
+// used to work around driver bugs or implement functions that are not natively implemented on a
+// specific platform.
//
class BuiltInFunctionEmulator
{
public:
BuiltInFunctionEmulator();
- void MarkBuiltInFunctionsForEmulation(TIntermNode *root);
+ void markBuiltInFunctionsForEmulation(TIntermNode *root);
- void Cleanup();
+ void cleanup();
- // "name(" becomes "webgl_name_emu(".
- static TString GetEmulatedFunctionName(const TString &name);
+ // "name" gets written as "name_emu".
+ static void WriteEmulatedFunctionName(TInfoSinkBase &out, const char *name);
- bool IsOutputEmpty() const;
+ bool isOutputEmpty() const;
- // Output function emulation definition. This should be before any other
- // shader source.
- void OutputEmulatedFunctions(TInfoSinkBase &out) const;
+ // Output function emulation definition. This should be before any other shader source.
+ void outputEmulatedFunctions(TInfoSinkBase &out) const;
// Add functions that need to be emulated.
- void addEmulatedFunction(TOperator op, const TType *param, const char *emulatedFunctionDefinition);
- void addEmulatedFunction(TOperator op, const TType *param1, const TType *param2,
- const char *emulatedFunctionDefinition);
- void addEmulatedFunction(TOperator op, const TType *param1, const TType *param2, const TType *param3,
- const char *emulatedFunctionDefinition);
+ FunctionId addEmulatedFunction(TOperator op,
+ const TType *param,
+ const char *emulatedFunctionDefinition);
+ FunctionId addEmulatedFunction(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const char *emulatedFunctionDefinition);
+ FunctionId addEmulatedFunction(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const char *emulatedFunctionDefinition);
+ FunctionId addEmulatedFunction(TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const TType *param4,
+ const char *emulatedFunctionDefinition);
+
+ FunctionId addEmulatedFunctionWithDependency(const FunctionId &dependency,
+ TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const char *emulatedFunctionDefinition);
+ FunctionId addEmulatedFunctionWithDependency(const FunctionId &dependency,
+ TOperator op,
+ const TType *param1,
+ const TType *param2,
+ const TType *param3,
+ const TType *param4,
+ const char *emulatedFunctionDefinition);
+
+ void addFunctionMap(BuiltinQueryFunc queryFunc);
private:
class BuiltInFunctionEmulationMarker;
- // Records that a function is called by the shader and might need to be
- // emulated. If the function is not in mEmulatedFunctions, this becomes a
- // no-op. Returns true if the function call needs to be replaced with an
- // emulated one.
- bool SetFunctionCalled(TOperator op, const TType &param);
- bool SetFunctionCalled(TOperator op, const TType &param1, const TType &param2);
- bool SetFunctionCalled(TOperator op, const TType &param1, const TType &param2, const TType &param3);
-
- class FunctionId {
- public:
- FunctionId(TOperator op, const TType *param);
- FunctionId(TOperator op, const TType *param1, const TType *param2);
- FunctionId(TOperator op, const TType *param1, const TType *param2, const TType *param3);
-
- bool operator==(const FunctionId &other) const;
- bool operator<(const FunctionId &other) const;
-
- FunctionId getCopy() const;
- private:
- TOperator mOp;
-
- // The memory that these TType objects use is freed by PoolAllocator. The BuiltInFunctionEmulator's lifetime
- // can extend until after the memory pool is freed, but that's not an issue since this class never destructs
- // these objects.
- const TType *mParam1;
- const TType *mParam2;
- const TType *mParam3;
- };
-
- bool SetFunctionCalled(const FunctionId &functionId);
+ // Records that a function is called by the shader and might need to be emulated. If the
+ // function is not in mEmulatedFunctions, this becomes a no-op. Returns true if the function
+ // call needs to be replaced with an emulated one.
+ bool setFunctionCalled(TOperator op, const TType &param);
+ bool setFunctionCalled(TOperator op, const TType &param1, const TType &param2);
+ bool setFunctionCalled(TOperator op,
+ const TType &param1,
+ const TType &param2,
+ const TType &param3);
+ bool setFunctionCalled(TOperator op,
+ const TType &param1,
+ const TType &param2,
+ const TType &param3,
+ const TType &param4);
+
+ bool setFunctionCalled(const FunctionId &functionId);
+
+ const char *findEmulatedFunction(const FunctionId &functionId) const;
// Map from function id to emulated function definition
std::map<FunctionId, std::string> mEmulatedFunctions;
+ // Map from dependent functions to their dependencies. This structure allows each function to
+ // have at most one dependency.
+ std::map<FunctionId, FunctionId> mFunctionDependencies;
+
// Called function ids
std::vector<FunctionId> mFunctions;
+
+ // Constexpr function tables.
+ std::vector<BuiltinQueryFunc *> mQueryFunctions;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
index 098560d110..27ee04da35 100644
--- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
@@ -11,47 +11,147 @@
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/VersionGLSL.h"
-void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType)
+namespace sh
{
- // we use macros here instead of function definitions to work around more GLSL
- // compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are
- // problematic because if the argument has side-effects they will be repeatedly
- // evaluated. This is unlikely to show up in real shaders, but is something to
- // consider.
+
+void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ sh::GLenum shaderType)
+{
+ if (shaderType == GL_VERTEX_SHADER)
+ {
+ const TType *int1 = TCache::getType(EbtInt);
+ emu->addEmulatedFunction(EOpAbs, int1, "int abs_emu(int x) { return x * sign(x); }");
+ }
+}
+
+void InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion)
+{
+ // isnan() is supported since GLSL 1.3.
+ if (targetGLSLVersion < GLSL_VERSION_130)
+ return;
const TType *float1 = TCache::getType(EbtFloat);
const TType *float2 = TCache::getType(EbtFloat, 2);
const TType *float3 = TCache::getType(EbtFloat, 3);
const TType *float4 = TCache::getType(EbtFloat, 4);
- if (shaderType == GL_FRAGMENT_SHADER)
+ // !(x > 0.0 || x < 0.0 || x == 0.0) will be optimized and always equal to false.
+ emu->addEmulatedFunction(
+ EOpIsNan, float1,
+ "bool isnan_emu(float x) { return (x > 0.0 || x < 0.0) ? false : x != 0.0; }");
+ emu->addEmulatedFunction(
+ EOpIsNan, float2,
+ "bvec2 isnan_emu(vec2 x)\n"
+ "{\n"
+ " bvec2 isnan;\n"
+ " for (int i = 0; i < 2; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+ emu->addEmulatedFunction(
+ EOpIsNan, float3,
+ "bvec3 isnan_emu(vec3 x)\n"
+ "{\n"
+ " bvec3 isnan;\n"
+ " for (int i = 0; i < 3; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+ emu->addEmulatedFunction(
+ EOpIsNan, float4,
+ "bvec4 isnan_emu(vec4 x)\n"
+ "{\n"
+ " bvec4 isnan;\n"
+ " for (int i = 0; i < 4; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+}
+
+void InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu)
+{
+ const TType *float1 = TCache::getType(EbtFloat);
+ auto floatFuncId = emu->addEmulatedFunction(
+ EOpAtan, float1, float1,
+ "emu_precision float atan_emu(emu_precision float y, emu_precision "
+ "float x)\n"
+ "{\n"
+ " if (x > 0.0) return atan(y / x);\n"
+ " else if (x < 0.0 && y >= 0.0) return atan(y / x) + 3.14159265;\n"
+ " else if (x < 0.0 && y < 0.0) return atan(y / x) - 3.14159265;\n"
+ " else return 1.57079632 * sign(y);\n"
+ "}\n");
+ for (int dim = 2; dim <= 4; ++dim)
{
- emu->addEmulatedFunction(EOpCos, float1, "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }");
- emu->addEmulatedFunction(EOpCos, float2, "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }");
- emu->addEmulatedFunction(EOpCos, float3, "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }");
- emu->addEmulatedFunction(EOpCos, float4, "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }");
+ const TType *floatVec = TCache::getType(EbtFloat, static_cast<unsigned char>(dim));
+ std::stringstream ss;
+ ss << "emu_precision vec" << dim << " atan_emu(emu_precision vec" << dim
+ << " y, emu_precision vec" << dim << " x)\n"
+ << "{\n"
+ " return vec"
+ << dim << "(";
+ for (int i = 0; i < dim; ++i)
+ {
+ ss << "atan_emu(y[" << i << "], x[" << i << "])";
+ if (i < dim - 1)
+ {
+ ss << ", ";
+ }
+ }
+ ss << ");\n"
+ "}\n";
+ emu->addEmulatedFunctionWithDependency(floatFuncId, EOpAtan, floatVec, floatVec,
+ ss.str().c_str());
}
- emu->addEmulatedFunction(EOpDistance, float1, float1, "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))");
- emu->addEmulatedFunction(EOpDot, float1, float1, "#define webgl_dot_emu(x, y) ((x) * (y))");
- emu->addEmulatedFunction(EOpLength, float1, "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))");
- emu->addEmulatedFunction(EOpNormalize, float1, "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))");
- emu->addEmulatedFunction(EOpReflect, float1, float1, "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))");
}
// Emulate built-in functions missing from GLSL 1.30 and higher
-void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
+void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu,
+ sh::GLenum shaderType,
int targetGLSLVersion)
{
+ // Emulate packUnorm2x16 and unpackUnorm2x16 (GLSL 4.10)
+ if (targetGLSLVersion < GLSL_VERSION_410)
+ {
+ const TType *float2 = TCache::getType(EbtFloat, 2);
+ const TType *uint1 = TCache::getType(EbtUInt);
+
+ // clang-format off
+ emu->addEmulatedFunction(EOpPackUnorm2x16, float2,
+ "uint packUnorm2x16_emu(vec2 v)\n"
+ "{\n"
+ " int x = int(round(clamp(v.x, 0.0, 1.0) * 65535.0));\n"
+ " int y = int(round(clamp(v.y, 0.0, 1.0) * 65535.0));\n"
+ " return uint((y << 16) | (x & 0xFFFF));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpUnpackUnorm2x16, uint1,
+ "vec2 unpackUnorm2x16_emu(uint u)\n"
+ "{\n"
+ " float x = float(u & 0xFFFFu) / 65535.0;\n"
+ " float y = float(u >> 16) / 65535.0;\n"
+ " return vec2(x, y);\n"
+ "}\n");
+ // clang-format on
+ }
+
// Emulate packSnorm2x16, packHalf2x16, unpackSnorm2x16, and unpackHalf2x16 (GLSL 4.20)
// by using floatBitsToInt, floatBitsToUint, intBitsToFloat, and uintBitsToFloat (GLSL 3.30).
if (targetGLSLVersion >= GLSL_VERSION_330 && targetGLSLVersion < GLSL_VERSION_420)
{
const TType *float2 = TCache::getType(EbtFloat, 2);
- const TType *uint1 = TCache::getType(EbtUInt);
+ const TType *uint1 = TCache::getType(EbtUInt);
// clang-format off
emu->addEmulatedFunction(EOpPackSnorm2x16, float2,
- "uint webgl_packSnorm2x16_emu(vec2 v)\n"
+ "uint packSnorm2x16_emu(vec2 v)\n"
"{\n"
" #if defined(GL_ARB_shading_language_packing)\n"
" return packSnorm2x16(v);\n"
@@ -63,28 +163,28 @@ void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator
"}\n");
emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1,
"#if !defined(GL_ARB_shading_language_packing)\n"
- " float webgl_fromSnorm(uint x)\n"
+ " float fromSnorm(uint x)\n"
" {\n"
" int xi = (int(x) & 0x7FFF) - (int(x) & 0x8000);\n"
" return clamp(float(xi) / 32767.0, -1.0, 1.0);\n"
" }\n"
"#endif\n"
"\n"
- "vec2 webgl_unpackSnorm2x16_emu(uint u)\n"
+ "vec2 unpackSnorm2x16_emu(uint u)\n"
"{\n"
" #if defined(GL_ARB_shading_language_packing)\n"
" return unpackSnorm2x16(u);\n"
" #else\n"
" uint y = (u >> 16);\n"
" uint x = u;\n"
- " return vec2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n"
+ " return vec2(fromSnorm(x), fromSnorm(y));\n"
" #endif\n"
"}\n");
- // Functions uint webgl_f32tof16(float val) and float webgl_f16tof32(uint val) are
+ // Functions uint f32tof16(float val) and float f16tof32(uint val) are
// based on the OpenGL redbook Appendix Session "Floating-Point Formats Used in OpenGL".
emu->addEmulatedFunction(EOpPackHalf2x16, float2,
"#if !defined(GL_ARB_shading_language_packing)\n"
- " uint webgl_f32tof16(float val)\n"
+ " uint f32tof16(float val)\n"
" {\n"
" uint f32 = floatBitsToUint(val);\n"
" uint f16 = 0u;\n"
@@ -119,19 +219,19 @@ void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator
" }\n"
"#endif\n"
"\n"
- "uint webgl_packHalf2x16_emu(vec2 v)\n"
+ "uint packHalf2x16_emu(vec2 v)\n"
"{\n"
" #if defined(GL_ARB_shading_language_packing)\n"
" return packHalf2x16(v);\n"
" #else\n"
- " uint x = webgl_f32tof16(v.x);\n"
- " uint y = webgl_f32tof16(v.y);\n"
+ " uint x = f32tof16(v.x);\n"
+ " uint y = f32tof16(v.y);\n"
" return (y << 16) | x;\n"
" #endif\n"
"}\n");
emu->addEmulatedFunction(EOpUnpackHalf2x16, uint1,
"#if !defined(GL_ARB_shading_language_packing)\n"
- " float webgl_f16tof32(uint val)\n"
+ " float f16tof32(uint val)\n"
" {\n"
" uint sign = (val & 0x8000u) << 16;\n"
" int exponent = int((val & 0x7C00u) >> 10);\n"
@@ -155,7 +255,9 @@ void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator
" float scale;\n"
" if(exponent < 0)\n"
" {\n"
- " scale = 1.0 / (1 << -exponent);\n"
+ " // The negative unary operator is buggy on OSX.\n"
+ " // Work around this by using abs instead.\n"
+ " scale = 1.0 / (1 << abs(exponent));\n"
" }\n"
" else\n"
" {\n"
@@ -174,16 +276,18 @@ void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator
" }\n"
"#endif\n"
"\n"
- "vec2 webgl_unpackHalf2x16_emu(uint u)\n"
+ "vec2 unpackHalf2x16_emu(uint u)\n"
"{\n"
" #if defined(GL_ARB_shading_language_packing)\n"
" return unpackHalf2x16(u);\n"
" #else\n"
" uint y = (u >> 16);\n"
" uint x = u & 0xFFFFu;\n"
- " return vec2(webgl_f16tof32(x), webgl_f16tof32(y));\n"
+ " return vec2(f16tof32(x), f16tof32(y));\n"
" #endif\n"
"}\n");
// clang-format on
}
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
index 56242598af..e1b4779bd5 100644
--- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
@@ -9,17 +9,32 @@
#include "GLSLANG/ShaderLang.h"
+namespace sh
+{
class BuiltInFunctionEmulator;
//
-// This is only a workaround for OpenGL driver bugs, and isn't needed in general.
+// This works around bug in Intel Mac drivers.
//
-void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType);
+void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ sh::GLenum shaderType);
+
+//
+// This works around isnan() bug in Intel Mac drivers
+//
+void InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion);
+//
+// This works around atan(y, x) bug in NVIDIA drivers.
+//
+void InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu);
//
// This function is emulating built-in functions missing from GLSL 1.30 and higher.
//
-void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
+void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu,
+ sh::GLenum shaderType,
int targetGLSLVersion);
+} // namespace sh
#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
index 50e15cbc28..e78d86d00a 100644
--- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
@@ -8,434 +8,177 @@
#include "compiler/translator/BuiltInFunctionEmulator.h"
#include "compiler/translator/BuiltInFunctionEmulatorHLSL.h"
#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/VersionGLSL.h"
-void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu)
+namespace sh
+{
+
+// Defined in emulated_builtin_functions_hlsl_autogen.cpp.
+const char *FindHLSLFunction(const FunctionId &functionID);
+
+void InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion)
{
+ if (targetGLSLVersion < GLSL_VERSION_130)
+ return;
+
TType *float1 = new TType(EbtFloat);
TType *float2 = new TType(EbtFloat, 2);
TType *float3 = new TType(EbtFloat, 3);
TType *float4 = new TType(EbtFloat, 4);
- emu->addEmulatedFunction(EOpMod, float1, float1,
- "float webgl_mod_emu(float x, float y)\n"
- "{\n"
- " return x - y * floor(x / y);\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpMod, float2, float2,
- "float2 webgl_mod_emu(float2 x, float2 y)\n"
- "{\n"
- " return x - y * floor(x / y);\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpMod, float2, float1,
- "float2 webgl_mod_emu(float2 x, float y)\n"
- "{\n"
- " return x - y * floor(x / y);\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpMod, float3, float3,
- "float3 webgl_mod_emu(float3 x, float3 y)\n"
- "{\n"
- " return x - y * floor(x / y);\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpMod, float3, float1,
- "float3 webgl_mod_emu(float3 x, float y)\n"
- "{\n"
- " return x - y * floor(x / y);\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpMod, float4, float4,
- "float4 webgl_mod_emu(float4 x, float4 y)\n"
- "{\n"
- " return x - y * floor(x / y);\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpMod, float4, float1,
- "float4 webgl_mod_emu(float4 x, float y)\n"
- "{\n"
- " return x - y * floor(x / y);\n"
- "}\n"
- "\n");
+ emu->addEmulatedFunction(EOpIsNan, float1,
+ "bool isnan_emu(float x)\n"
+ "{\n"
+ " return (x > 0.0 || x < 0.0) ? false : x != 0.0;\n"
+ "}\n"
+ "\n");
- emu->addEmulatedFunction(EOpFaceForward, float1, float1, float1,
- "float webgl_faceforward_emu(float N, float I, float Nref)\n"
+ emu->addEmulatedFunction(
+ EOpIsNan, float2,
+ "bool2 isnan_emu(float2 x)\n"
"{\n"
- " if(dot(Nref, I) >= 0)\n"
+ " bool2 isnan;\n"
+ " for (int i = 0; i < 2; i++)\n"
" {\n"
- " return -N;\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
" }\n"
- " else\n"
- " {\n"
- " return N;\n"
- " }\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpFaceForward, float2, float2, float2,
- "float2 webgl_faceforward_emu(float2 N, float2 I, float2 Nref)\n"
- "{\n"
- " if(dot(Nref, I) >= 0)\n"
- " {\n"
- " return -N;\n"
- " }\n"
- " else\n"
- " {\n"
- " return N;\n"
- " }\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpFaceForward, float3, float3, float3,
- "float3 webgl_faceforward_emu(float3 N, float3 I, float3 Nref)\n"
+ " return isnan;\n"
+ "}\n");
+
+ emu->addEmulatedFunction(
+ EOpIsNan, float3,
+ "bool3 isnan_emu(float3 x)\n"
"{\n"
- " if(dot(Nref, I) >= 0)\n"
+ " bool3 isnan;\n"
+ " for (int i = 0; i < 3; i++)\n"
" {\n"
- " return -N;\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
" }\n"
- " else\n"
- " {\n"
- " return N;\n"
- " }\n"
- "}\n"
- "\n");
- emu->addEmulatedFunction(EOpFaceForward, float4, float4, float4,
- "float4 webgl_faceforward_emu(float4 N, float4 I, float4 Nref)\n"
+ " return isnan;\n"
+ "}\n");
+
+ emu->addEmulatedFunction(
+ EOpIsNan, float4,
+ "bool4 isnan_emu(float4 x)\n"
"{\n"
- " if(dot(Nref, I) >= 0)\n"
- " {\n"
- " return -N;\n"
- " }\n"
- " else\n"
+ " bool4 isnan;\n"
+ " for (int i = 0; i < 4; i++)\n"
" {\n"
- " return N;\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
" }\n"
- "}\n"
- "\n");
-
- emu->addEmulatedFunction(EOpAtan, float1, float1,
- "float webgl_atan_emu(float y, float x)\n"
- "{\n"
- " if(x == 0 && y == 0) x = 1;\n" // Avoid producing a NaN
- " return atan2(y, x);\n"
- "}\n");
- emu->addEmulatedFunction(EOpAtan, float2, float2,
- "float2 webgl_atan_emu(float2 y, float2 x)\n"
- "{\n"
- " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
- " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
- " return float2(atan2(y[0], x[0]), atan2(y[1], x[1]));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAtan, float3, float3,
- "float3 webgl_atan_emu(float3 y, float3 x)\n"
- "{\n"
- " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
- " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
- " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
- " return float3(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAtan, float4, float4,
- "float4 webgl_atan_emu(float4 y, float4 x)\n"
- "{\n"
- " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
- " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
- " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
- " if(x[3] == 0 && y[3] == 0) x[3] = 1;\n"
- " return float4(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]), atan2(y[3], x[3]));\n"
- "}\n");
-
- emu->addEmulatedFunction(EOpAsinh, float1,
- "float webgl_asinh_emu(in float x) {\n"
- " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAsinh, float2,
- "float2 webgl_asinh_emu(in float2 x) {\n"
- " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAsinh, float3,
- "float3 webgl_asinh_emu(in float3 x) {\n"
- " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAsinh, float4,
- "float4 webgl_asinh_emu(in float4 x) {\n"
- " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
- "}\n");
-
- emu->addEmulatedFunction(EOpAcosh, float1,
- "float webgl_acosh_emu(in float x) {\n"
- " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAcosh, float2,
- "float2 webgl_acosh_emu(in float2 x) {\n"
- " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAcosh, float3,
- "float3 webgl_acosh_emu(in float3 x) {\n"
- " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAcosh, float4,
- "float4 webgl_acosh_emu(in float4 x) {\n"
- " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
- "}\n");
-
- emu->addEmulatedFunction(EOpAtanh, float1,
- "float webgl_atanh_emu(in float x) {\n"
- " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAtanh, float2,
- "float2 webgl_atanh_emu(in float2 x) {\n"
- " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ " return isnan;\n"
"}\n");
- emu->addEmulatedFunction(EOpAtanh, float3,
- "float3 webgl_atanh_emu(in float3 x) {\n"
- " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
- "}\n");
- emu->addEmulatedFunction(EOpAtanh, float4,
- "float4 webgl_atanh_emu(in float4 x) {\n"
- " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
- "}\n");
-
- emu->addEmulatedFunction(EOpRoundEven, float1,
- "float webgl_roundEven_emu(in float x) {\n"
- " return (frac(x) == 0.5 && trunc(x) % 2.0 == 0.0) ? trunc(x) : round(x);\n"
- "}\n");
- emu->addEmulatedFunction(EOpRoundEven, float2,
- "float2 webgl_roundEven_emu(in float2 x) {\n"
- " float2 v;\n"
- " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
- " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
- " return v;\n"
- "}\n");
- emu->addEmulatedFunction(EOpRoundEven, float3,
- "float3 webgl_roundEven_emu(in float3 x) {\n"
- " float3 v;\n"
- " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
- " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
- " v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
- " return v;\n"
- "}\n");
- emu->addEmulatedFunction(EOpRoundEven, float4,
- "float4 webgl_roundEven_emu(in float4 x) {\n"
- " float4 v;\n"
- " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
- " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
- " v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
- " v[3] = (frac(x[3]) == 0.5 && trunc(x[3]) % 2.0 == 0.0) ? trunc(x[3]) : round(x[3]);\n"
- " return v;\n"
- "}\n");
-
- emu->addEmulatedFunction(EOpPackSnorm2x16, float2,
- "int webgl_toSnorm(in float x) {\n"
- " return int(round(clamp(x, -1.0, 1.0) * 32767.0));\n"
- "}\n"
- "\n"
- "uint webgl_packSnorm2x16_emu(in float2 v) {\n"
- " int x = webgl_toSnorm(v.x);\n"
- " int y = webgl_toSnorm(v.y);\n"
- " return (asuint(y) << 16) | (asuint(x) & 0xffffu);\n"
- "}\n");
- emu->addEmulatedFunction(EOpPackUnorm2x16, float2,
- "uint webgl_toUnorm(in float x) {\n"
- " return uint(round(clamp(x, 0.0, 1.0) * 65535.0));\n"
- "}\n"
- "\n"
- "uint webgl_packUnorm2x16_emu(in float2 v) {\n"
- " uint x = webgl_toUnorm(v.x);\n"
- " uint y = webgl_toUnorm(v.y);\n"
- " return (y << 16) | x;\n"
- "}\n");
- emu->addEmulatedFunction(EOpPackHalf2x16, float2,
- "uint webgl_packHalf2x16_emu(in float2 v) {\n"
- " uint x = f32tof16(v.x);\n"
- " uint y = f32tof16(v.y);\n"
- " return (y << 16) | x;\n"
- "}\n");
-
- TType *uint1 = new TType(EbtUInt);
-
- emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1,
- "float webgl_fromSnorm(in uint x) {\n"
- " int xi = asint(x & 0x7fffu) - asint(x & 0x8000u);\n"
- " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n"
- "}\n"
- "\n"
- "float2 webgl_unpackSnorm2x16_emu(in uint u) {\n"
- " uint y = (u >> 16);\n"
- " uint x = u;\n"
- " return float2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n"
- "}\n");
- emu->addEmulatedFunction(EOpUnpackUnorm2x16, uint1,
- "float webgl_fromUnorm(in uint x) {\n"
- " return float(x) / 65535.0;\n"
- "}\n"
- "\n"
- "float2 webgl_unpackUnorm2x16_emu(in uint u) {\n"
- " uint y = (u >> 16);\n"
- " uint x = u & 0xffffu;\n"
- " return float2(webgl_fromUnorm(x), webgl_fromUnorm(y));\n"
- "}\n");
- emu->addEmulatedFunction(EOpUnpackHalf2x16, uint1,
- "float2 webgl_unpackHalf2x16_emu(in uint u) {\n"
- " uint y = (u >> 16);\n"
- " uint x = u & 0xffffu;\n"
- " return float2(f16tof32(x), f16tof32(y));\n"
- "}\n");
-
- // The matrix resulting from outer product needs to be transposed
- // (matrices are stored as transposed to simplify element access in HLSL).
- // So the function should return transpose(c * r) where c is a column vector
- // and r is a row vector. This can be simplified by using the following
- // formula:
- // transpose(c * r) = transpose(r) * transpose(c)
- // transpose(r) and transpose(c) are in a sense free, since to get the
- // transpose of r, we simply can build a column matrix out of the original
- // vector instead of a row matrix.
- emu->addEmulatedFunction(EOpOuterProduct, float2, float2,
- "float2x2 webgl_outerProduct_emu(in float2 c, in float2 r) {\n"
- " return mul(float2x1(r), float1x2(c));\n"
- "}\n");
- emu->addEmulatedFunction(EOpOuterProduct, float3, float3,
- "float3x3 webgl_outerProduct_emu(in float3 c, in float3 r) {\n"
- " return mul(float3x1(r), float1x3(c));\n"
- "}\n");
- emu->addEmulatedFunction(EOpOuterProduct, float4, float4,
- "float4x4 webgl_outerProduct_emu(in float4 c, in float4 r) {\n"
- " return mul(float4x1(r), float1x4(c));\n"
- "}\n");
-
- emu->addEmulatedFunction(EOpOuterProduct, float3, float2,
- "float2x3 webgl_outerProduct_emu(in float3 c, in float2 r) {\n"
- " return mul(float2x1(r), float1x3(c));\n"
- "}\n");
- emu->addEmulatedFunction(EOpOuterProduct, float2, float3,
- "float3x2 webgl_outerProduct_emu(in float2 c, in float3 r) {\n"
- " return mul(float3x1(r), float1x2(c));\n"
- "}\n");
- emu->addEmulatedFunction(EOpOuterProduct, float4, float2,
- "float2x4 webgl_outerProduct_emu(in float4 c, in float2 r) {\n"
- " return mul(float2x1(r), float1x4(c));\n"
- "}\n");
- emu->addEmulatedFunction(EOpOuterProduct, float2, float4,
- "float4x2 webgl_outerProduct_emu(in float2 c, in float4 r) {\n"
- " return mul(float4x1(r), float1x2(c));\n"
- "}\n");
- emu->addEmulatedFunction(EOpOuterProduct, float4, float3,
- "float3x4 webgl_outerProduct_emu(in float4 c, in float3 r) {\n"
- " return mul(float3x1(r), float1x4(c));\n"
- "}\n");
- emu->addEmulatedFunction(EOpOuterProduct, float3, float4,
- "float4x3 webgl_outerProduct_emu(in float3 c, in float4 r) {\n"
- " return mul(float4x1(r), float1x3(c));\n"
- "}\n");
-
- TType *mat2 = new TType(EbtFloat, 2, 2);
- TType *mat3 = new TType(EbtFloat, 3, 3);
- TType *mat4 = new TType(EbtFloat, 4, 4);
-
- // Remember here that the parameter matrix is actually the transpose
- // of the matrix that we're trying to invert, and the resulting matrix
- // should also be the transpose of the inverse.
-
- // When accessing the parameter matrix with m[a][b] it can be thought of so
- // that a is the column and b is the row of the matrix that we're inverting.
-
- // We calculate the inverse as the adjugate matrix divided by the
- // determinant of the matrix being inverted. However, as the result needs
- // to be transposed, we actually use of the transpose of the adjugate matrix
- // which happens to be the cofactor matrix. That's stored in "cof".
-
- // We don't need to care about divide-by-zero since results are undefined
- // for singular or poorly-conditioned matrices.
-
- emu->addEmulatedFunction(EOpInverse, mat2,
- "float2x2 webgl_inverse_emu(in float2x2 m) {\n"
- " float2x2 cof = { m[1][1], -m[0][1], -m[1][0], m[0][0] };\n"
- " return cof / determinant(transpose(m));\n"
- "}\n");
-
- // cofAB is the cofactor for column A and row B.
-
- emu->addEmulatedFunction(EOpInverse, mat3,
- "float3x3 webgl_inverse_emu(in float3x3 m) {\n"
- " float cof00 = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n"
- " float cof01 = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]);\n"
- " float cof02 = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n"
- " float cof10 = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]);\n"
- " float cof11 = m[0][0] * m[2][2] - m[2][0] * m[0][2];\n"
- " float cof12 = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]);\n"
- " float cof20 = m[0][1] * m[1][2] - m[1][1] * m[0][2];\n"
- " float cof21 = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]);\n"
- " float cof22 = m[0][0] * m[1][1] - m[1][0] * m[0][1];\n"
- " float3x3 cof = { cof00, cof10, cof20, cof01, cof11, cof21, cof02, cof12, cof22 };\n"
- " return cof / determinant(transpose(m));\n"
- "}\n");
-
- emu->addEmulatedFunction(EOpInverse, mat4,
- "float4x4 webgl_inverse_emu(in float4x4 m) {\n"
- " float cof00 = m[1][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[1][3] + m[3][1] * m[1][2] * m[2][3]"
- " - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] - m[3][1] * m[2][2] * m[1][3];\n"
- " float cof01 = -(m[1][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[1][3] + m[3][0] * m[1][2] * m[2][3]"
- " - m[1][0] * m[3][2] * m[2][3] - m[2][0] * m[1][2] * m[3][3] - m[3][0] * m[2][2] * m[1][3]);\n"
- " float cof02 = m[1][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[1][3] + m[3][0] * m[1][1] * m[2][3]"
- " - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] - m[3][0] * m[2][1] * m[1][3];\n"
- " float cof03 = -(m[1][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[1][2] + m[3][0] * m[1][1] * m[2][2]"
- " - m[1][0] * m[3][1] * m[2][2] - m[2][0] * m[1][1] * m[3][2] - m[3][0] * m[2][1] * m[1][2]);\n"
- " float cof10 = -(m[0][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[0][3] + m[3][1] * m[0][2] * m[2][3]"
- " - m[0][1] * m[3][2] * m[2][3] - m[2][1] * m[0][2] * m[3][3] - m[3][1] * m[2][2] * m[0][3]);\n"
- " float cof11 = m[0][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[0][3] + m[3][0] * m[0][2] * m[2][3]"
- " - m[0][0] * m[3][2] * m[2][3] - m[2][0] * m[0][2] * m[3][3] - m[3][0] * m[2][2] * m[0][3];\n"
- " float cof12 = -(m[0][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[0][3] + m[3][0] * m[0][1] * m[2][3]"
- " - m[0][0] * m[3][1] * m[2][3] - m[2][0] * m[0][1] * m[3][3] - m[3][0] * m[2][1] * m[0][3]);\n"
- " float cof13 = m[0][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[0][2] + m[3][0] * m[0][1] * m[2][2]"
- " - m[0][0] * m[3][1] * m[2][2] - m[2][0] * m[0][1] * m[3][2] - m[3][0] * m[2][1] * m[0][2];\n"
- " float cof20 = m[0][1] * m[1][2] * m[3][3] + m[1][1] * m[3][2] * m[0][3] + m[3][1] * m[0][2] * m[1][3]"
- " - m[0][1] * m[3][2] * m[1][3] - m[1][1] * m[0][2] * m[3][3] - m[3][1] * m[1][2] * m[0][3];\n"
- " float cof21 = -(m[0][0] * m[1][2] * m[3][3] + m[1][0] * m[3][2] * m[0][3] + m[3][0] * m[0][2] * m[1][3]"
- " - m[0][0] * m[3][2] * m[1][3] - m[1][0] * m[0][2] * m[3][3] - m[3][0] * m[1][2] * m[0][3]);\n"
- " float cof22 = m[0][0] * m[1][1] * m[3][3] + m[1][0] * m[3][1] * m[0][3] + m[3][0] * m[0][1] * m[1][3]"
- " - m[0][0] * m[3][1] * m[1][3] - m[1][0] * m[0][1] * m[3][3] - m[3][0] * m[1][1] * m[0][3];\n"
- " float cof23 = -(m[0][0] * m[1][1] * m[3][2] + m[1][0] * m[3][1] * m[0][2] + m[3][0] * m[0][1] * m[1][2]"
- " - m[0][0] * m[3][1] * m[1][2] - m[1][0] * m[0][1] * m[3][2] - m[3][0] * m[1][1] * m[0][2]);\n"
- " float cof30 = -(m[0][1] * m[1][2] * m[2][3] + m[1][1] * m[2][2] * m[0][3] + m[2][1] * m[0][2] * m[1][3]"
- " - m[0][1] * m[2][2] * m[1][3] - m[1][1] * m[0][2] * m[2][3] - m[2][1] * m[1][2] * m[0][3]);\n"
- " float cof31 = m[0][0] * m[1][2] * m[2][3] + m[1][0] * m[2][2] * m[0][3] + m[2][0] * m[0][2] * m[1][3]"
- " - m[0][0] * m[2][2] * m[1][3] - m[1][0] * m[0][2] * m[2][3] - m[2][0] * m[1][2] * m[0][3];\n"
- " float cof32 = -(m[0][0] * m[1][1] * m[2][3] + m[1][0] * m[2][1] * m[0][3] + m[2][0] * m[0][1] * m[1][3]"
- " - m[0][0] * m[2][1] * m[1][3] - m[1][0] * m[0][1] * m[2][3] - m[2][0] * m[1][1] * m[0][3]);\n"
- " float cof33 = m[0][0] * m[1][1] * m[2][2] + m[1][0] * m[2][1] * m[0][2] + m[2][0] * m[0][1] * m[1][2]"
- " - m[0][0] * m[2][1] * m[1][2] - m[1][0] * m[0][1] * m[2][2] - m[2][0] * m[1][1] * m[0][2];\n"
- " float4x4 cof = { cof00, cof10, cof20, cof30, cof01, cof11, cof21, cof31,"
- " cof02, cof12, cof22, cof32, cof03, cof13, cof23, cof33 };\n"
- " return cof / determinant(transpose(m));\n"
- "}\n");
-
- TType *bool1 = new TType(EbtBool);
- TType *bool2 = new TType(EbtBool, 2);
- TType *bool3 = new TType(EbtBool, 3);
- TType *bool4 = new TType(EbtBool, 4);
+}
- // Emulate ESSL3 variant of mix that takes last argument as boolean vector.
- // genType mix (genType x, genType y, genBType a): Selects which vector each returned component comes from.
- // For a component of 'a' that is false, the corresponding component of 'x' is returned.For a component of 'a' that is true,
- // the corresponding component of 'y' is returned.
- emu->addEmulatedFunction(EOpMix, float1, float1, bool1,
- "float webgl_mix_emu(float x, float y, bool a)\n"
- "{\n"
- " return a ? y : x;\n"
+void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu)
+{
+ TType *int1 = new TType(EbtInt);
+ TType *int2 = new TType(EbtInt, 2);
+ TType *int3 = new TType(EbtInt, 3);
+ TType *int4 = new TType(EbtInt, 4);
+ TType *uint1 = new TType(EbtUInt);
+ TType *uint2 = new TType(EbtUInt, 2);
+ TType *uint3 = new TType(EbtUInt, 3);
+ TType *uint4 = new TType(EbtUInt, 4);
+
+ emu->addFunctionMap(FindHLSLFunction);
+
+ // (a + b2^16) * (c + d2^16) = ac + (ad + bc) * 2^16 + bd * 2^32
+ // Also note that below, a * d + ((a * c) >> 16) is guaranteed not to overflow, because:
+ // a <= 0xffff, d <= 0xffff, ((a * c) >> 16) <= 0xffff and 0xffff * 0xffff + 0xffff = 0xffff0000
+ FunctionId umulExtendedUint1 = emu->addEmulatedFunction(
+ EOpUmulExtended, uint1, uint1, uint1, uint1,
+ "void umulExtended_emu(uint x, uint y, out uint msb, out uint lsb)\n"
+ "{\n"
+ " lsb = x * y;\n"
+ " uint a = (x & 0xffffu);\n"
+ " uint b = (x >> 16);\n"
+ " uint c = (y & 0xffffu);\n"
+ " uint d = (y >> 16);\n"
+ " uint ad = a * d + ((a * c) >> 16);\n"
+ " uint bc = b * c;\n"
+ " uint carry = uint(ad > (0xffffffffu - bc));\n"
+ " msb = ((ad + bc) >> 16) + (carry << 16) + b * d;\n"
+ "}\n");
+ emu->addEmulatedFunctionWithDependency(
+ umulExtendedUint1, EOpUmulExtended, uint2, uint2, uint2, uint2,
+ "void umulExtended_emu(uint2 x, uint2 y, out uint2 msb, out uint2 lsb)\n"
+ "{\n"
+ " umulExtended_emu(x.x, y.x, msb.x, lsb.x);\n"
+ " umulExtended_emu(x.y, y.y, msb.y, lsb.y);\n"
+ "}\n");
+ emu->addEmulatedFunctionWithDependency(
+ umulExtendedUint1, EOpUmulExtended, uint3, uint3, uint3, uint3,
+ "void umulExtended_emu(uint3 x, uint3 y, out uint3 msb, out uint3 lsb)\n"
+ "{\n"
+ " umulExtended_emu(x.x, y.x, msb.x, lsb.x);\n"
+ " umulExtended_emu(x.y, y.y, msb.y, lsb.y);\n"
+ " umulExtended_emu(x.z, y.z, msb.z, lsb.z);\n"
+ "}\n");
+ emu->addEmulatedFunctionWithDependency(
+ umulExtendedUint1, EOpUmulExtended, uint4, uint4, uint4, uint4,
+ "void umulExtended_emu(uint4 x, uint4 y, out uint4 msb, out uint4 lsb)\n"
+ "{\n"
+ " umulExtended_emu(x.x, y.x, msb.x, lsb.x);\n"
+ " umulExtended_emu(x.y, y.y, msb.y, lsb.y);\n"
+ " umulExtended_emu(x.z, y.z, msb.z, lsb.z);\n"
+ " umulExtended_emu(x.w, y.w, msb.w, lsb.w);\n"
+ "}\n");
+
+ // The imul emulation does two's complement negation on the lsb and msb manually in case the
+ // result needs to be negative.
+ // TODO(oetuaho): Note that this code doesn't take one edge case into account, where x or y is
+ // -2^31. abs(-2^31) is undefined.
+ FunctionId imulExtendedInt1 = emu->addEmulatedFunctionWithDependency(
+ umulExtendedUint1, EOpImulExtended, int1, int1, int1, int1,
+ "void imulExtended_emu(int x, int y, out int msb, out int lsb)\n"
+ "{\n"
+ " uint unsignedMsb;\n"
+ " uint unsignedLsb;\n"
+ " bool negative = (x < 0) != (y < 0);\n"
+ " umulExtended_emu(uint(abs(x)), uint(abs(y)), unsignedMsb, unsignedLsb);\n"
+ " lsb = asint(unsignedLsb);\n"
+ " msb = asint(unsignedMsb);\n"
+ " if (negative)\n"
+ " {\n"
+ " lsb = ~lsb;\n"
+ " msb = ~msb;\n"
+ " if (lsb == 0xffffffff)\n"
+ " {\n"
+ " lsb = 0;\n"
+ " msb += 1;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " lsb += 1;\n"
+ " }\n"
+ " }\n"
"}\n");
- emu->addEmulatedFunction(EOpMix, float2, float2, bool2,
- "float2 webgl_mix_emu(float2 x, float2 y, bool2 a)\n"
+ emu->addEmulatedFunctionWithDependency(
+ imulExtendedInt1, EOpImulExtended, int2, int2, int2, int2,
+ "void imulExtended_emu(int2 x, int2 y, out int2 msb, out int2 lsb)\n"
"{\n"
- " return a ? y : x;\n"
+ " imulExtended_emu(x.x, y.x, msb.x, lsb.x);\n"
+ " imulExtended_emu(x.y, y.y, msb.y, lsb.y);\n"
"}\n");
- emu->addEmulatedFunction(EOpMix, float3, float3, bool3,
- "float3 webgl_mix_emu(float3 x, float3 y, bool3 a)\n"
+ emu->addEmulatedFunctionWithDependency(
+ imulExtendedInt1, EOpImulExtended, int3, int3, int3, int3,
+ "void imulExtended_emu(int3 x, int3 y, out int3 msb, out int3 lsb)\n"
"{\n"
- " return a ? y : x;\n"
+ " imulExtended_emu(x.x, y.x, msb.x, lsb.x);\n"
+ " imulExtended_emu(x.y, y.y, msb.y, lsb.y);\n"
+ " imulExtended_emu(x.z, y.z, msb.z, lsb.z);\n"
"}\n");
- emu->addEmulatedFunction(EOpMix, float4, float4, bool4,
- "float4 webgl_mix_emu(float4 x, float4 y, bool4 a)\n"
+ emu->addEmulatedFunctionWithDependency(
+ imulExtendedInt1, EOpImulExtended, int4, int4, int4, int4,
+ "void imulExtended_emu(int4 x, int4 y, out int4 msb, out int4 lsb)\n"
"{\n"
- " return a ? y : x;\n"
+ " imulExtended_emu(x.x, y.x, msb.x, lsb.x);\n"
+ " imulExtended_emu(x.y, y.y, msb.y, lsb.y);\n"
+ " imulExtended_emu(x.z, y.z, msb.z, lsb.z);\n"
+ " imulExtended_emu(x.w, y.w, msb.w, lsb.w);\n"
"}\n");
-
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h
index 4c45a93dc4..48da73f58e 100644
--- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h
@@ -9,8 +9,19 @@
#include "GLSLANG/ShaderLang.h"
+namespace sh
+{
+
class BuiltInFunctionEmulator;
void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu);
+//
+// This works around isnan() bug on some Intel drivers.
+//
+void InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion);
+
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Cache.cpp b/src/3rdparty/angle/src/compiler/translator/Cache.cpp
index 57a43700ca..417e82403a 100644
--- a/src/3rdparty/angle/src/compiler/translator/Cache.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/Cache.cpp
@@ -12,21 +12,20 @@
#include "common/debug.h"
#include "compiler/translator/Cache.h"
+namespace sh
+{
+
namespace
{
class TScopedAllocator : angle::NonCopyable
{
public:
- TScopedAllocator(TPoolAllocator *allocator)
- : mPreviousAllocator(GetGlobalPoolAllocator())
+ TScopedAllocator(TPoolAllocator *allocator) : mPreviousAllocator(GetGlobalPoolAllocator())
{
SetGlobalPoolAllocator(allocator);
}
- ~TScopedAllocator()
- {
- SetGlobalPoolAllocator(mPreviousAllocator);
- }
+ ~TScopedAllocator() { SetGlobalPoolAllocator(mPreviousAllocator); }
private:
TPoolAllocator *mPreviousAllocator;
@@ -40,28 +39,28 @@ TCache::TypeKey::TypeKey(TBasicType basicType,
unsigned char primarySize,
unsigned char secondarySize)
{
- static_assert(sizeof(components) <= sizeof(value),
- "TypeKey::value is too small");
+ static_assert(sizeof(components) <= sizeof(value), "TypeKey::value is too small");
const size_t MaxEnumValue = std::numeric_limits<EnumComponentType>::max();
- UNUSED_ASSERTION_VARIABLE(MaxEnumValue);
// TODO: change to static_assert() once we deprecate MSVC 2013 support
- ASSERT(MaxEnumValue >= EbtLast &&
- MaxEnumValue >= EbpLast &&
- MaxEnumValue >= EvqLast &&
+ ASSERT(MaxEnumValue >= EbtLast && MaxEnumValue >= EbpLast && MaxEnumValue >= EvqLast &&
"TypeKey::EnumComponentType is too small");
- value = 0;
- components.basicType = static_cast<EnumComponentType>(basicType);
- components.precision = static_cast<EnumComponentType>(precision);
- components.qualifier = static_cast<EnumComponentType>(qualifier);
- components.primarySize = primarySize;
+ value = 0;
+ components.basicType = static_cast<EnumComponentType>(basicType);
+ components.precision = static_cast<EnumComponentType>(precision);
+ components.qualifier = static_cast<EnumComponentType>(qualifier);
+ components.primarySize = primarySize;
components.secondarySize = secondarySize;
}
TCache *TCache::sCache = nullptr;
+TCache::TCache()
+{
+}
+
void TCache::initialize()
{
if (sCache == nullptr)
@@ -81,8 +80,7 @@ const TType *TCache::getType(TBasicType basicType,
unsigned char primarySize,
unsigned char secondarySize)
{
- TypeKey key(basicType, precision, qualifier,
- primarySize, secondarySize);
+ TypeKey key(basicType, precision, qualifier, primarySize, secondarySize);
auto it = sCache->mTypes.find(key);
if (it != sCache->mTypes.end())
{
@@ -91,10 +89,11 @@ const TType *TCache::getType(TBasicType basicType,
TScopedAllocator scopedAllocator(&sCache->mAllocator);
- TType *type = new TType(basicType, precision, qualifier,
- primarySize, secondarySize);
+ TType *type = new TType(basicType, precision, qualifier, primarySize, secondarySize);
type->realize();
sCache->mTypes.insert(std::make_pair(key, type));
return type;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/Cache.h b/src/3rdparty/angle/src/compiler/translator/Cache.h
index 1d2abb77e1..a182b07f51 100644
--- a/src/3rdparty/angle/src/compiler/translator/Cache.h
+++ b/src/3rdparty/angle/src/compiler/translator/Cache.h
@@ -16,33 +16,31 @@
#include "compiler/translator/Types.h"
#include "compiler/translator/PoolAlloc.h"
+namespace sh
+{
+
class TCache
{
public:
-
static void initialize();
static void destroy();
- static const TType *getType(TBasicType basicType,
- TPrecision precision)
+ static const TType *getType(TBasicType basicType, TPrecision precision)
{
- return getType(basicType, precision, EvqTemporary,
- 1, 1);
+ return getType(basicType, precision, EvqTemporary, 1, 1);
}
static const TType *getType(TBasicType basicType,
- unsigned char primarySize = 1,
+ unsigned char primarySize = 1,
unsigned char secondarySize = 1)
{
- return getType(basicType, EbpUndefined, EvqGlobal,
- primarySize, secondarySize);
+ return getType(basicType, EbpUndefined, EvqGlobal, primarySize, secondarySize);
}
static const TType *getType(TBasicType basicType,
TQualifier qualifier,
- unsigned char primarySize = 1,
+ unsigned char primarySize = 1,
unsigned char secondarySize = 1)
{
- return getType(basicType, EbpUndefined, qualifier,
- primarySize, secondarySize);
+ return getType(basicType, EbpUndefined, qualifier, primarySize, secondarySize);
}
static const TType *getType(TBasicType basicType,
TPrecision precision,
@@ -51,12 +49,9 @@ class TCache
unsigned char secondarySize);
private:
- TCache()
- {
- }
+ TCache();
- union TypeKey
- {
+ union TypeKey {
TypeKey(TBasicType basicType,
TPrecision precision,
TQualifier qualifier,
@@ -74,12 +69,9 @@ class TCache
} components;
uint64_t value;
- bool operator < (const TypeKey &other) const
- {
- return value < other.value;
- }
+ bool operator<(const TypeKey &other) const { return value < other.value; }
};
- typedef std::map<TypeKey, const TType*> TypeMap;
+ typedef std::map<TypeKey, const TType *> TypeMap;
TypeMap mTypes;
TPoolAllocator mAllocator;
@@ -87,4 +79,6 @@ class TCache
static TCache *sCache;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_CACHE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp b/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp
index 10f0eb937c..5f54e80898 100644
--- a/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp
@@ -9,16 +9,22 @@
// order.
#include "compiler/translator/CallDAG.h"
-#include "compiler/translator/InfoSink.h"
+
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
// The CallDAGCreator does all the processing required to create the CallDAG
// structure so that the latter contains only the necessary variables.
class CallDAG::CallDAGCreator : public TIntermTraverser
{
public:
- CallDAGCreator(TInfoSinkBase *info)
+ CallDAGCreator(TDiagnostics *diagnostics)
: TIntermTraverser(true, false, true),
- mCreationInfo(info),
+ mDiagnostics(diagnostics),
mCurrentFunction(nullptr),
mCurrentIndex(0)
{
@@ -35,7 +41,6 @@ class CallDAG::CallDAGCreator : public TIntermTraverser
InitResult result = assignIndicesInternal(&it.second);
if (result != INITDAG_SUCCESS)
{
- *mCreationInfo << "\n";
return result;
}
}
@@ -44,6 +49,7 @@ class CallDAG::CallDAGCreator : public TIntermTraverser
skipped++;
}
}
+
ASSERT(mFunctions.size() == mCurrentIndex + skipped);
return INITDAG_SUCCESS;
}
@@ -75,147 +81,196 @@ class CallDAG::CallDAGCreator : public TIntermTraverser
record.callees.push_back(static_cast<int>(callee->index));
}
- (*idToIndex)[data.node->getFunctionId()] = static_cast<int>(data.index);
+ (*idToIndex)[data.node->getFunctionSymbolInfo()->getId().get()] =
+ static_cast<int>(data.index);
}
}
private:
-
struct CreatorFunctionData
{
- CreatorFunctionData()
- : node(nullptr),
- index(0),
- indexAssigned(false),
- visiting(false)
- {
- }
+ CreatorFunctionData() : node(nullptr), index(0), indexAssigned(false), visiting(false) {}
- std::set<CreatorFunctionData*> callees;
- TIntermAggregate *node;
+ std::set<CreatorFunctionData *> callees;
+ TIntermFunctionDefinition *node;
TString name;
size_t index;
bool indexAssigned;
bool visiting;
};
- // Aggregates the AST node for each function as well as the name of the functions called by it
- bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
{
- switch (node->getOp())
+ // Create the record if need be and remember the node.
+ if (visit == PreVisit)
{
- case EOpPrototype:
- if (visit == PreVisit)
+ auto it = mFunctions.find(node->getFunctionSymbolInfo()->getId().get());
+
+ if (it == mFunctions.end())
{
- // Function declaration, create an empty record.
- auto& record = mFunctions[node->getName()];
- record.name = node->getName();
+ mCurrentFunction = &mFunctions[node->getFunctionSymbolInfo()->getId().get()];
+ mCurrentFunction->name = node->getFunctionSymbolInfo()->getName();
}
- break;
- case EOpFunction:
+ else
{
- // Function definition, create the record if need be and remember the node.
- if (visit == PreVisit)
- {
- auto it = mFunctions.find(node->getName());
+ mCurrentFunction = &it->second;
+ ASSERT(mCurrentFunction->name == node->getFunctionSymbolInfo()->getName());
+ }
- if (it == mFunctions.end())
- {
- mCurrentFunction = &mFunctions[node->getName()];
- }
- else
- {
- mCurrentFunction = &it->second;
- }
+ mCurrentFunction->node = node;
+ }
+ else if (visit == PostVisit)
+ {
+ mCurrentFunction = nullptr;
+ }
+ return true;
+ }
- mCurrentFunction->node = node;
- mCurrentFunction->name = node->getName();
+ bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override
+ {
+ ASSERT(visit == PreVisit);
+ if (mCurrentFunction != nullptr)
+ {
+ return false;
+ }
- }
- else if (visit == PostVisit)
- {
- mCurrentFunction = nullptr;
- }
- break;
- }
- case EOpFunctionCall:
+ // Function declaration, create an empty record.
+ auto &record = mFunctions[node->getFunctionSymbolInfo()->getId().get()];
+ record.name = node->getFunctionSymbolInfo()->getName();
+
+ // No need to traverse the parameters.
+ return false;
+ }
+
+ // Aggregates the AST node for each function as well as the name of the functions called by it
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ {
+ if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
+ {
+ // Function call, add the callees
+ auto it = mFunctions.find(node->getFunctionSymbolInfo()->getId().get());
+ ASSERT(it != mFunctions.end());
+
+ // We might be traversing the initializer of a global variable. Even though function
+ // calls in global scope are forbidden by the parser, some subsequent AST
+ // transformations can add them to emulate particular features.
+ if (mCurrentFunction)
{
- // Function call, add the callees
- if (visit == PreVisit)
- {
- // Do not handle calls to builtin functions
- if (node->isUserDefined())
- {
- auto it = mFunctions.find(node->getName());
- ASSERT(it != mFunctions.end());
-
- // We might be in a top-level function call to set a global variable
- if (mCurrentFunction)
- {
- mCurrentFunction->callees.insert(&it->second);
- }
- }
- }
- break;
+ mCurrentFunction->callees.insert(&it->second);
}
- default:
- break;
}
return true;
}
// Recursively assigns indices to a sub DAG
- InitResult assignIndicesInternal(CreatorFunctionData *function)
+ InitResult assignIndicesInternal(CreatorFunctionData *root)
{
- ASSERT(function);
+ // Iterative implementation of the index assignment algorithm. A recursive version
+ // would be prettier but since the CallDAG creation runs before the limiting of the
+ // call depth, we might get stack overflows (computation of the call depth uses the
+ // CallDAG).
- if (!function->node)
- {
- *mCreationInfo << "Undefined function '" << function->name
- << ")' used in the following call chain:";
- return INITDAG_UNDEFINED;
- }
+ ASSERT(root);
- if (function->indexAssigned)
+ if (root->indexAssigned)
{
return INITDAG_SUCCESS;
}
- if (function->visiting)
+ // If we didn't have to detect recursion, functionsToProcess could be a simple queue
+ // in which we add the function being processed's callees. However in order to detect
+ // recursion we need to know which functions we are currently visiting. For that reason
+ // functionsToProcess will look like a concatenation of segments of the form
+ // [F visiting = true, subset of F callees with visiting = false] and the following
+ // segment (if any) will be start with a callee of F.
+ // This way we can remember when we started visiting a function, to put visiting back
+ // to false.
+ TVector<CreatorFunctionData *> functionsToProcess;
+ functionsToProcess.push_back(root);
+
+ InitResult result = INITDAG_SUCCESS;
+
+ std::stringstream errorStream;
+
+ while (!functionsToProcess.empty())
{
- if (mCreationInfo)
+ CreatorFunctionData *function = functionsToProcess.back();
+
+ if (function->visiting)
+ {
+ function->visiting = false;
+ function->index = mCurrentIndex++;
+ function->indexAssigned = true;
+
+ functionsToProcess.pop_back();
+ continue;
+ }
+
+ if (!function->node)
+ {
+ errorStream << "Undefined function '" << function->name
+ << ")' used in the following call chain:";
+ result = INITDAG_UNDEFINED;
+ break;
+ }
+
+ if (function->indexAssigned)
+ {
+ functionsToProcess.pop_back();
+ continue;
+ }
+
+ function->visiting = true;
+
+ for (auto callee : function->callees)
+ {
+ functionsToProcess.push_back(callee);
+
+ // Check if the callee is already being visited after pushing it so that it appears
+ // in the chain printed in the info log.
+ if (callee->visiting)
+ {
+ errorStream << "Recursive function call in the following call chain:";
+ result = INITDAG_RECURSION;
+ break;
+ }
+ }
+
+ if (result != INITDAG_SUCCESS)
{
- *mCreationInfo << "Recursive function call in the following call chain:" << function->name;
+ break;
}
- return INITDAG_RECURSION;
}
- function->visiting = true;
- for (auto &callee : function->callees)
+ // The call chain is made of the function we were visiting when the error was detected.
+ if (result != INITDAG_SUCCESS)
{
- InitResult result = assignIndicesInternal(callee);
- if (result != INITDAG_SUCCESS)
+ bool first = true;
+ for (auto function : functionsToProcess)
{
- // We know that there is an issue with the call chain in the AST,
- // print the link of the chain we were processing.
- if (mCreationInfo)
+ if (function->visiting)
{
- *mCreationInfo << " <- " << function->name << ")";
+ if (!first)
+ {
+ errorStream << " -> ";
+ }
+ errorStream << function->name << ")";
+ first = false;
}
- return result;
+ }
+ if (mDiagnostics)
+ {
+ std::string errorStr = errorStream.str();
+ mDiagnostics->globalError(errorStr.c_str());
}
}
- function->index = mCurrentIndex++;
- function->indexAssigned = true;
-
- function->visiting = false;
- return INITDAG_SUCCESS;
+ return result;
}
- TInfoSinkBase *mCreationInfo;
+ TDiagnostics *mDiagnostics;
- std::map<TString, CreatorFunctionData> mFunctions;
+ std::map<int, CreatorFunctionData> mFunctions;
CreatorFunctionData *mCurrentFunction;
size_t mCurrentIndex;
};
@@ -232,13 +287,9 @@ CallDAG::~CallDAG()
const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
-size_t CallDAG::findIndex(const TIntermAggregate *function) const
+size_t CallDAG::findIndex(const TFunctionSymbolInfo *functionInfo) const
{
- TOperator op = function->getOp();
- ASSERT(op == EOpPrototype || op == EOpFunction || op == EOpFunctionCall);
- UNUSED_ASSERTION_VARIABLE(op);
-
- auto it = mFunctionIdToIndex.find(function->getFunctionId());
+ auto it = mFunctionIdToIndex.find(functionInfo->getId().get());
if (it == mFunctionIdToIndex.end())
{
@@ -258,7 +309,7 @@ const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
{
- size_t index = findIndex(function);
+ size_t index = findIndex(function->getFunctionSymbolInfo());
ASSERT(index != InvalidIndex && index < mRecords.size());
return mRecords[index];
}
@@ -274,9 +325,9 @@ void CallDAG::clear()
mFunctionIdToIndex.clear();
}
-CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info)
+CallDAG::InitResult CallDAG::init(TIntermNode *root, TDiagnostics *diagnostics)
{
- CallDAGCreator creator(info);
+ CallDAGCreator creator(diagnostics);
// Creates the mapping of functions to callees
root->traverse(&creator);
@@ -291,3 +342,5 @@ CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info)
creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);
return INITDAG_SUCCESS;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/CallDAG.h b/src/3rdparty/angle/src/compiler/translator/CallDAG.h
index 06c377db00..155081c9a2 100644
--- a/src/3rdparty/angle/src/compiler/translator/CallDAG.h
+++ b/src/3rdparty/angle/src/compiler/translator/CallDAG.h
@@ -14,8 +14,9 @@
#include <map>
#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/VariableInfo.h"
+namespace sh
+{
// The translator needs to analyze the the graph of the function calls
// to run checks and analyses; since in GLSL recursion is not allowed
@@ -24,7 +25,7 @@
// can be reused by multiple analyses.
//
// It stores a vector of function records, with one record per function.
-// Records are accessed by index but a mangled function name can be converted
+// Records are accessed by index but a function symbol id can be converted
// to the index of the corresponding record. The records mostly contain the
// AST node of the function and the indices of the function's callees.
//
@@ -41,7 +42,7 @@ class CallDAG : angle::NonCopyable
struct Record
{
std::string name;
- TIntermAggregate *node;
+ TIntermFunctionDefinition *node;
std::vector<int> callees;
};
@@ -53,11 +54,11 @@ class CallDAG : angle::NonCopyable
};
// Returns INITDAG_SUCCESS if it was able to create the DAG, otherwise prints
- // the initialization error in info, if present.
- InitResult init(TIntermNode *root, TInfoSinkBase *info);
+ // the initialization error in diagnostics, if present.
+ InitResult init(TIntermNode *root, TDiagnostics *diagnostics);
// Returns InvalidIndex if the function wasn't found
- size_t findIndex(const TIntermAggregate *function) const;
+ size_t findIndex(const TFunctionSymbolInfo *functionInfo) const;
const Record &getRecordFromIndex(size_t index) const;
const Record &getRecord(const TIntermAggregate *function) const;
@@ -65,6 +66,7 @@ class CallDAG : angle::NonCopyable
void clear();
const static size_t InvalidIndex;
+
private:
std::vector<Record> mRecords;
std::map<int, int> mFunctionIdToIndex;
@@ -72,4 +74,6 @@ class CallDAG : angle::NonCopyable
class CallDAGCreator;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_CALLDAG_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ClampPointSize.cpp b/src/3rdparty/angle/src/compiler/translator/ClampPointSize.cpp
new file mode 100644
index 0000000000..8598a137f5
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ClampPointSize.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ClampPointSize.cpp: Limit the value that is written to gl_PointSize.
+//
+
+#include "compiler/translator/ClampPointSize.h"
+
+#include "compiler/translator/FindSymbolNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/RunAtTheEndOfShader.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+void ClampPointSize(TIntermBlock *root, float maxPointSize, TSymbolTable *symbolTable)
+{
+ // Only clamp gl_PointSize if it's used in the shader.
+ if (!FindSymbolNode(root, TString("gl_PointSize"), EbtFloat))
+ {
+ return;
+ }
+
+ TIntermSymbol *pointSizeNode = ReferenceBuiltInVariable("gl_PointSize", *symbolTable, 100);
+
+ TConstantUnion *maxPointSizeConstant = new TConstantUnion();
+ maxPointSizeConstant->setFConst(maxPointSize);
+ TIntermConstantUnion *maxPointSizeNode =
+ new TIntermConstantUnion(maxPointSizeConstant, TType(EbtFloat, EbpHigh, EvqConst));
+
+ // min(gl_PointSize, maxPointSize)
+ TIntermSequence *minArguments = new TIntermSequence();
+ minArguments->push_back(pointSizeNode->deepCopy());
+ minArguments->push_back(maxPointSizeNode);
+ TIntermTyped *clampedPointSize =
+ CreateBuiltInFunctionCallNode("min", minArguments, *symbolTable, 100);
+
+ // gl_PointSize = min(gl_PointSize, maxPointSize)
+ TIntermBinary *assignPointSize = new TIntermBinary(EOpAssign, pointSizeNode, clampedPointSize);
+
+ RunAtTheEndOfShader(root, assignPointSize, symbolTable);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ClampPointSize.h b/src/3rdparty/angle/src/compiler/translator/ClampPointSize.h
new file mode 100644
index 0000000000..0c71ae6b0d
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ClampPointSize.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ClampPointSize.h: Limit the value that is written to gl_PointSize.
+//
+
+#ifndef COMPILER_TRANSLATOR_CLAMPPOINTSIZE_H_
+#define COMPILER_TRANSLATOR_CLAMPPOINTSIZE_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+void ClampPointSize(TIntermBlock *root, float maxPointSize, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_CLAMPPOINTSIZE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp b/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp
index f099bccf15..3e25cc2339 100644
--- a/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp
@@ -6,71 +6,70 @@
#ifdef ANGLE_ENABLE_ESSL
#include "compiler/translator/TranslatorESSL.h"
-#endif
+#endif // ANGLE_ENABLE_ESSL
#ifdef ANGLE_ENABLE_GLSL
#include "compiler/translator/TranslatorGLSL.h"
-#endif
+#endif // ANGLE_ENABLE_GLSL
#ifdef ANGLE_ENABLE_HLSL
#include "compiler/translator/TranslatorHLSL.h"
-#endif // ANGLE_ENABLE_HLSL
+#endif // ANGLE_ENABLE_HLSL
+
+#ifdef ANGLE_ENABLE_VULKAN
+#include "compiler/translator/TranslatorVulkan.h"
+#endif // ANGLE_ENABLE_VULKAN
+
+#include "compiler/translator/util.h"
+
+namespace sh
+{
//
// This function must be provided to create the actual
// compile object used by higher level code. It returns
// a subclass of TCompiler.
//
-TCompiler* ConstructCompiler(
- sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
+TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
{
- switch (output) {
- case SH_ESSL_OUTPUT:
#ifdef ANGLE_ENABLE_ESSL
+ if (IsOutputESSL(output))
+ {
return new TranslatorESSL(type, spec);
-#else
- // This compiler is not supported in this
- // configuration. Return NULL per the ShConstructCompiler API.
- return nullptr;
-#endif // ANGLE_ENABLE_ESSL
- case SH_GLSL_130_OUTPUT:
- case SH_GLSL_140_OUTPUT:
- case SH_GLSL_150_CORE_OUTPUT:
- case SH_GLSL_330_CORE_OUTPUT:
- case SH_GLSL_400_CORE_OUTPUT:
- case SH_GLSL_410_CORE_OUTPUT:
- case SH_GLSL_420_CORE_OUTPUT:
- case SH_GLSL_430_CORE_OUTPUT:
- case SH_GLSL_440_CORE_OUTPUT:
- case SH_GLSL_450_CORE_OUTPUT:
- case SH_GLSL_COMPATIBILITY_OUTPUT:
+ }
+#endif // ANGLE_ENABLE_ESSL
+
#ifdef ANGLE_ENABLE_GLSL
+ if (IsOutputGLSL(output))
+ {
return new TranslatorGLSL(type, spec, output);
-#else
- // This compiler is not supported in this
- // configuration. Return NULL per the ShConstructCompiler API.
- return nullptr;
-#endif // ANGLE_ENABLE_GLSL
- case SH_HLSL_3_0_OUTPUT:
- case SH_HLSL_4_1_OUTPUT:
- case SH_HLSL_4_0_FL9_3_OUTPUT:
+ }
+#endif // ANGLE_ENABLE_GLSL
+
#ifdef ANGLE_ENABLE_HLSL
+ if (IsOutputHLSL(output))
+ {
return new TranslatorHLSL(type, spec, output);
-#else
- // This compiler is not supported in this
- // configuration. Return NULL per the ShConstructCompiler API.
- return nullptr;
-#endif // ANGLE_ENABLE_HLSL
- default:
- // Unknown format. Return NULL per the ShConstructCompiler API.
- return nullptr;
}
+#endif // ANGLE_ENABLE_HLSL
+
+#ifdef ANGLE_ENABLE_VULKAN
+ if (IsOutputVulkan(output))
+ {
+ return new TranslatorVulkan(type, spec);
+ }
+#endif // ANGLE_ENABLE_VULKAN
+
+ // Unsupported compiler or unknown format. Return nullptr per the sh::ConstructCompiler API.
+ return nullptr;
}
//
// Delete the compiler made by ConstructCompiler
//
-void DeleteCompiler(TCompiler* compiler)
+void DeleteCompiler(TCompiler *compiler)
{
- delete compiler;
+ SafeDelete(compiler);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/CollectVariables.cpp b/src/3rdparty/angle/src/compiler/translator/CollectVariables.cpp
new file mode 100644
index 0000000000..bd8cbc971a
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/CollectVariables.cpp
@@ -0,0 +1,869 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// CollectVariables.cpp: Collect lists of shader interface variables based on the AST.
+
+#include "compiler/translator/CollectVariables.h"
+
+#include "angle_gl.h"
+#include "common/utilities.h"
+#include "compiler/translator/HashNames.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
+{
+ switch (blockStorage)
+ {
+ case EbsPacked:
+ return BLOCKLAYOUT_PACKED;
+ case EbsShared:
+ return BLOCKLAYOUT_SHARED;
+ case EbsStd140:
+ return BLOCKLAYOUT_STD140;
+ case EbsStd430:
+ return BLOCKLAYOUT_STD430;
+ default:
+ UNREACHABLE();
+ return BLOCKLAYOUT_SHARED;
+ }
+}
+
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+BlockType GetBlockType(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqUniform:
+ return BlockType::BLOCK_UNIFORM;
+ case EvqBuffer:
+ return BlockType::BLOCK_BUFFER;
+ case EvqPerVertexIn:
+ return BlockType::BLOCK_IN;
+ default:
+ UNREACHABLE();
+ return BlockType::BLOCK_UNIFORM;
+ }
+}
+
+template <class VarT>
+VarT *FindVariable(const TString &name, std::vector<VarT> *infoList)
+{
+ // TODO(zmo): optimize this function.
+ for (size_t ii = 0; ii < infoList->size(); ++ii)
+ {
+ if ((*infoList)[ii].name.c_str() == name)
+ return &((*infoList)[ii]);
+ }
+
+ return nullptr;
+}
+
+// Note that this shouldn't be called for interface blocks - static use information is collected for
+// individual fields in case of interface blocks.
+void MarkStaticallyUsed(ShaderVariable *variable)
+{
+ if (!variable->staticUse)
+ {
+ if (variable->isStruct())
+ {
+ // Conservatively assume all fields are statically used as well.
+ for (auto &field : variable->fields)
+ {
+ MarkStaticallyUsed(&field);
+ }
+ }
+ variable->staticUse = true;
+ }
+}
+
+ShaderVariable *FindVariableInInterfaceBlock(const TString &name,
+ const TInterfaceBlock *interfaceBlock,
+ std::vector<InterfaceBlock> *infoList)
+{
+ ASSERT(interfaceBlock);
+ InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), infoList);
+ ASSERT(namedBlock);
+
+ // Set static use on the parent interface block here
+ namedBlock->staticUse = true;
+ return FindVariable(name, &namedBlock->fields);
+}
+
+// Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs,
+// and interface blocks.
+class CollectVariablesTraverser : public TIntermTraverser
+{
+ public:
+ CollectVariablesTraverser(std::vector<Attribute> *attribs,
+ std::vector<OutputVariable> *outputVariables,
+ std::vector<Uniform> *uniforms,
+ std::vector<Varying> *inputVaryings,
+ std::vector<Varying> *outputVaryings,
+ std::vector<InterfaceBlock> *uniformBlocks,
+ std::vector<InterfaceBlock> *shaderStorageBlocks,
+ std::vector<InterfaceBlock> *inBlocks,
+ ShHashFunction64 hashFunction,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ GLenum shaderType,
+ const TExtensionBehavior &extensionBehavior);
+
+ void visitSymbol(TIntermSymbol *symbol) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
+
+ private:
+ std::string getMappedName(const TName &name) const;
+
+ void setCommonVariableProperties(const TType &type,
+ const TName &name,
+ ShaderVariable *variableOut) const;
+
+ Attribute recordAttribute(const TIntermSymbol &variable) const;
+ OutputVariable recordOutputVariable(const TIntermSymbol &variable) const;
+ Varying recordVarying(const TIntermSymbol &variable) const;
+ void recordInterfaceBlock(const TType &interfaceBlockType,
+ InterfaceBlock *interfaceBlock) const;
+ Uniform recordUniform(const TIntermSymbol &variable) const;
+
+ void setBuiltInInfoFromSymbolTable(const char *name, ShaderVariable *info);
+
+ void recordBuiltInVaryingUsed(const char *name,
+ bool *addedFlag,
+ std::vector<Varying> *varyings);
+ void recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag);
+ void recordBuiltInAttributeUsed(const char *name, bool *addedFlag);
+ InterfaceBlock *recordGLInUsed(const TType &glInType);
+ InterfaceBlock *findNamedInterfaceBlock(const TString &name) const;
+
+ std::vector<Attribute> *mAttribs;
+ std::vector<OutputVariable> *mOutputVariables;
+ std::vector<Uniform> *mUniforms;
+ std::vector<Varying> *mInputVaryings;
+ std::vector<Varying> *mOutputVaryings;
+ std::vector<InterfaceBlock> *mUniformBlocks;
+ std::vector<InterfaceBlock> *mShaderStorageBlocks;
+ std::vector<InterfaceBlock> *mInBlocks;
+
+ std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
+
+ // Shader uniforms
+ bool mDepthRangeAdded;
+
+ // Vertex Shader builtins
+ bool mInstanceIDAdded;
+ bool mVertexIDAdded;
+ bool mPointSizeAdded;
+
+ // Vertex Shader and Geometry Shader builtins
+ bool mPositionAdded;
+
+ // Fragment Shader builtins
+ bool mPointCoordAdded;
+ bool mFrontFacingAdded;
+ bool mFragCoordAdded;
+ bool mLastFragDataAdded;
+ bool mFragColorAdded;
+ bool mFragDataAdded;
+ bool mFragDepthEXTAdded;
+ bool mFragDepthAdded;
+ bool mSecondaryFragColorEXTAdded;
+ bool mSecondaryFragDataEXTAdded;
+
+ // Geometry Shader builtins
+ bool mPerVertexInAdded;
+ bool mPrimitiveIDInAdded;
+ bool mInvocationIDAdded;
+
+ // Geometry Shader and Fragment Shader builtins
+ bool mPrimitiveIDAdded;
+ bool mLayerAdded;
+
+ ShHashFunction64 mHashFunction;
+
+ int mShaderVersion;
+ GLenum mShaderType;
+ const TExtensionBehavior &mExtensionBehavior;
+};
+
+CollectVariablesTraverser::CollectVariablesTraverser(
+ std::vector<sh::Attribute> *attribs,
+ std::vector<sh::OutputVariable> *outputVariables,
+ std::vector<sh::Uniform> *uniforms,
+ std::vector<sh::Varying> *inputVaryings,
+ std::vector<sh::Varying> *outputVaryings,
+ std::vector<sh::InterfaceBlock> *uniformBlocks,
+ std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
+ std::vector<sh::InterfaceBlock> *inBlocks,
+ ShHashFunction64 hashFunction,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ GLenum shaderType,
+ const TExtensionBehavior &extensionBehavior)
+ : TIntermTraverser(true, false, false, symbolTable),
+ mAttribs(attribs),
+ mOutputVariables(outputVariables),
+ mUniforms(uniforms),
+ mInputVaryings(inputVaryings),
+ mOutputVaryings(outputVaryings),
+ mUniformBlocks(uniformBlocks),
+ mShaderStorageBlocks(shaderStorageBlocks),
+ mInBlocks(inBlocks),
+ mDepthRangeAdded(false),
+ mInstanceIDAdded(false),
+ mVertexIDAdded(false),
+ mPointSizeAdded(false),
+ mPositionAdded(false),
+ mPointCoordAdded(false),
+ mFrontFacingAdded(false),
+ mFragCoordAdded(false),
+ mLastFragDataAdded(false),
+ mFragColorAdded(false),
+ mFragDataAdded(false),
+ mFragDepthEXTAdded(false),
+ mFragDepthAdded(false),
+ mSecondaryFragColorEXTAdded(false),
+ mSecondaryFragDataEXTAdded(false),
+ mPerVertexInAdded(false),
+ mPrimitiveIDInAdded(false),
+ mInvocationIDAdded(false),
+ mPrimitiveIDAdded(false),
+ mLayerAdded(false),
+ mHashFunction(hashFunction),
+ mShaderVersion(shaderVersion),
+ mShaderType(shaderType),
+ mExtensionBehavior(extensionBehavior)
+{
+}
+
+std::string CollectVariablesTraverser::getMappedName(const TName &name) const
+{
+ return HashName(name, mHashFunction, nullptr).c_str();
+}
+
+void CollectVariablesTraverser::setBuiltInInfoFromSymbolTable(const char *name,
+ ShaderVariable *info)
+{
+ TVariable *symbolTableVar =
+ reinterpret_cast<TVariable *>(mSymbolTable->findBuiltIn(name, mShaderVersion));
+ ASSERT(symbolTableVar);
+ const TType &type = symbolTableVar->getType();
+
+ info->name = name;
+ info->mappedName = name;
+ info->type = GLVariableType(type);
+ info->precision = GLVariablePrecision(type);
+ if (auto *arraySizes = type.getArraySizes())
+ {
+ info->arraySizes.assign(arraySizes->begin(), arraySizes->end());
+ }
+}
+
+void CollectVariablesTraverser::recordBuiltInVaryingUsed(const char *name,
+ bool *addedFlag,
+ std::vector<Varying> *varyings)
+{
+ ASSERT(varyings);
+ if (!(*addedFlag))
+ {
+ Varying info;
+ setBuiltInInfoFromSymbolTable(name, &info);
+ info.staticUse = true;
+ info.isInvariant = mSymbolTable->isVaryingInvariant(name);
+ varyings->push_back(info);
+ (*addedFlag) = true;
+ }
+}
+
+void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag)
+{
+ if (!(*addedFlag))
+ {
+ OutputVariable info;
+ setBuiltInInfoFromSymbolTable(name, &info);
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ (*addedFlag) = true;
+ }
+}
+
+void CollectVariablesTraverser::recordBuiltInAttributeUsed(const char *name, bool *addedFlag)
+{
+ if (!(*addedFlag))
+ {
+ Attribute info;
+ setBuiltInInfoFromSymbolTable(name, &info);
+ info.staticUse = true;
+ info.location = -1;
+ mAttribs->push_back(info);
+ (*addedFlag) = true;
+ }
+}
+
+InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
+{
+ if (!mPerVertexInAdded)
+ {
+ ASSERT(glInType.getQualifier() == EvqPerVertexIn);
+ InterfaceBlock info;
+ recordInterfaceBlock(glInType, &info);
+ info.staticUse = true;
+
+ mPerVertexInAdded = true;
+ mInBlocks->push_back(info);
+ return &mInBlocks->back();
+ }
+ else
+ {
+ return FindVariable("gl_PerVertex", mInBlocks);
+ }
+}
+
+// We want to check whether a uniform/varying is statically used
+// because we only count the used ones in packing computing.
+// Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
+// toward varying counting if they are statically used in a fragment
+// shader.
+void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
+{
+ ASSERT(symbol != nullptr);
+
+ if (symbol->getName().isInternal())
+ {
+ // Internal variables are not collected.
+ return;
+ }
+
+ ShaderVariable *var = nullptr;
+ const TString &symbolName = symbol->getName().getString();
+
+ if (IsVaryingIn(symbol->getQualifier()))
+ {
+ var = FindVariable(symbolName, mInputVaryings);
+ }
+ else if (IsVaryingOut(symbol->getQualifier()))
+ {
+ var = FindVariable(symbolName, mOutputVaryings);
+ }
+ else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
+ {
+ UNREACHABLE();
+ }
+ else if (symbolName == "gl_DepthRange")
+ {
+ ASSERT(symbol->getQualifier() == EvqUniform);
+
+ if (!mDepthRangeAdded)
+ {
+ Uniform info;
+ const char kName[] = "gl_DepthRange";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_NONE;
+ info.precision = GL_NONE;
+ info.staticUse = true;
+
+ ShaderVariable nearInfo(GL_FLOAT);
+ const char kNearName[] = "near";
+ nearInfo.name = kNearName;
+ nearInfo.mappedName = kNearName;
+ nearInfo.precision = GL_HIGH_FLOAT;
+ nearInfo.staticUse = true;
+
+ ShaderVariable farInfo(GL_FLOAT);
+ const char kFarName[] = "far";
+ farInfo.name = kFarName;
+ farInfo.mappedName = kFarName;
+ farInfo.precision = GL_HIGH_FLOAT;
+ farInfo.staticUse = true;
+
+ ShaderVariable diffInfo(GL_FLOAT);
+ const char kDiffName[] = "diff";
+ diffInfo.name = kDiffName;
+ diffInfo.mappedName = kDiffName;
+ diffInfo.precision = GL_HIGH_FLOAT;
+ diffInfo.staticUse = true;
+
+ info.fields.push_back(nearInfo);
+ info.fields.push_back(farInfo);
+ info.fields.push_back(diffInfo);
+
+ mUniforms->push_back(info);
+ mDepthRangeAdded = true;
+ }
+ }
+ else
+ {
+ switch (symbol->getQualifier())
+ {
+ case EvqAttribute:
+ case EvqVertexIn:
+ var = FindVariable(symbolName, mAttribs);
+ break;
+ case EvqFragmentOut:
+ var = FindVariable(symbolName, mOutputVariables);
+ break;
+ case EvqUniform:
+ {
+ const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
+ if (interfaceBlock)
+ {
+ var = FindVariableInInterfaceBlock(symbolName, interfaceBlock, mUniformBlocks);
+ }
+ else
+ {
+ var = FindVariable(symbolName, mUniforms);
+ }
+
+ // It's an internal error to reference an undefined user uniform
+ ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
+ }
+ break;
+ case EvqBuffer:
+ {
+ const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
+ var =
+ FindVariableInInterfaceBlock(symbolName, interfaceBlock, mShaderStorageBlocks);
+ }
+ break;
+ case EvqFragCoord:
+ recordBuiltInVaryingUsed("gl_FragCoord", &mFragCoordAdded, mInputVaryings);
+ return;
+ case EvqFrontFacing:
+ recordBuiltInVaryingUsed("gl_FrontFacing", &mFrontFacingAdded, mInputVaryings);
+ return;
+ case EvqPointCoord:
+ recordBuiltInVaryingUsed("gl_PointCoord", &mPointCoordAdded, mInputVaryings);
+ return;
+ case EvqInstanceID:
+ // Whenever the SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW option is set,
+ // gl_InstanceID is added inside expressions to initialize ViewID_OVR and
+ // InstanceID. gl_InstanceID is not added to the symbol table for ESSL1 shaders
+ // which makes it necessary to populate the type information explicitly instead of
+ // extracting it from the symbol table.
+ if (!mInstanceIDAdded)
+ {
+ Attribute info;
+ const char kName[] = "gl_InstanceID";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_INT;
+ info.precision = GL_HIGH_INT; // Defined by spec.
+ info.staticUse = true;
+ info.location = -1;
+ mAttribs->push_back(info);
+ mInstanceIDAdded = true;
+ }
+ return;
+ case EvqVertexID:
+ recordBuiltInAttributeUsed("gl_VertexID", &mVertexIDAdded);
+ return;
+ case EvqPosition:
+ recordBuiltInVaryingUsed("gl_Position", &mPositionAdded, mOutputVaryings);
+ return;
+ case EvqPointSize:
+ recordBuiltInVaryingUsed("gl_PointSize", &mPointSizeAdded, mOutputVaryings);
+ return;
+ case EvqLastFragData:
+ recordBuiltInVaryingUsed("gl_LastFragData", &mLastFragDataAdded, mInputVaryings);
+ return;
+ case EvqFragColor:
+ recordBuiltInFragmentOutputUsed("gl_FragColor", &mFragColorAdded);
+ return;
+ case EvqFragData:
+ if (!mFragDataAdded)
+ {
+ OutputVariable info;
+ setBuiltInInfoFromSymbolTable("gl_FragData", &info);
+ if (!IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers))
+ {
+ ASSERT(info.arraySizes.size() == 1u);
+ info.arraySizes.back() = 1u;
+ }
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ mFragDataAdded = true;
+ }
+ return;
+ case EvqFragDepthEXT:
+ recordBuiltInFragmentOutputUsed("gl_FragDepthEXT", &mFragDepthEXTAdded);
+ return;
+ case EvqFragDepth:
+ recordBuiltInFragmentOutputUsed("gl_FragDepth", &mFragDepthAdded);
+ return;
+ case EvqSecondaryFragColorEXT:
+ recordBuiltInFragmentOutputUsed("gl_SecondaryFragColorEXT",
+ &mSecondaryFragColorEXTAdded);
+ return;
+ case EvqSecondaryFragDataEXT:
+ recordBuiltInFragmentOutputUsed("gl_SecondaryFragDataEXT",
+ &mSecondaryFragDataEXTAdded);
+ return;
+ case EvqInvocationID:
+ recordBuiltInVaryingUsed("gl_InvocationID", &mInvocationIDAdded, mInputVaryings);
+ break;
+ case EvqPrimitiveIDIn:
+ recordBuiltInVaryingUsed("gl_PrimitiveIDIn", &mPrimitiveIDInAdded, mInputVaryings);
+ break;
+ case EvqPrimitiveID:
+ if (mShaderType == GL_GEOMETRY_SHADER_OES)
+ {
+ recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mOutputVaryings);
+ }
+ else
+ {
+ ASSERT(mShaderType == GL_FRAGMENT_SHADER);
+ recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mInputVaryings);
+ }
+ break;
+ case EvqLayer:
+ if (mShaderType == GL_GEOMETRY_SHADER_OES)
+ {
+ recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mOutputVaryings);
+ }
+ else if (mShaderType == GL_FRAGMENT_SHADER)
+ {
+ recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mInputVaryings);
+ }
+ else
+ {
+ ASSERT(mShaderType == GL_VERTEX_SHADER &&
+ IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (var)
+ {
+ MarkStaticallyUsed(var);
+ }
+}
+
+void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
+ const TName &name,
+ ShaderVariable *variableOut) const
+{
+ ASSERT(variableOut);
+
+ const TStructure *structure = type.getStruct();
+
+ if (!structure)
+ {
+ variableOut->type = GLVariableType(type);
+ variableOut->precision = GLVariablePrecision(type);
+ }
+ else
+ {
+ // Structures use a NONE type that isn't exposed outside ANGLE.
+ variableOut->type = GL_NONE;
+ variableOut->structName = structure->name().c_str();
+
+ const TFieldList &fields = structure->fields();
+
+ for (TField *field : fields)
+ {
+ // Regardless of the variable type (uniform, in/out etc.) its fields are always plain
+ // ShaderVariable objects.
+ ShaderVariable fieldVariable;
+ setCommonVariableProperties(*field->type(), TName(field->name()), &fieldVariable);
+ variableOut->fields.push_back(fieldVariable);
+ }
+ }
+ variableOut->name = name.getString().c_str();
+ variableOut->mappedName = getMappedName(name);
+
+ if (auto *arraySizes = type.getArraySizes())
+ {
+ variableOut->arraySizes.assign(arraySizes->begin(), arraySizes->end());
+ }
+}
+
+Attribute CollectVariablesTraverser::recordAttribute(const TIntermSymbol &variable) const
+{
+ const TType &type = variable.getType();
+ ASSERT(!type.getStruct());
+
+ Attribute attribute;
+ setCommonVariableProperties(type, variable.getName(), &attribute);
+
+ attribute.location = type.getLayoutQualifier().location;
+ return attribute;
+}
+
+OutputVariable CollectVariablesTraverser::recordOutputVariable(const TIntermSymbol &variable) const
+{
+ const TType &type = variable.getType();
+ ASSERT(!type.getStruct());
+
+ OutputVariable outputVariable;
+ setCommonVariableProperties(type, variable.getName(), &outputVariable);
+
+ outputVariable.location = type.getLayoutQualifier().location;
+ return outputVariable;
+}
+
+Varying CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) const
+{
+ const TType &type = variable.getType();
+
+ Varying varying;
+ setCommonVariableProperties(type, variable.getName(), &varying);
+ varying.location = type.getLayoutQualifier().location;
+
+ switch (type.getQualifier())
+ {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ case EvqVertexOut:
+ case EvqSmoothOut:
+ case EvqFlatOut:
+ case EvqCentroidOut:
+ case EvqGeometryOut:
+ if (mSymbolTable->isVaryingInvariant(std::string(variable.getSymbol().c_str())) ||
+ type.isInvariant())
+ {
+ varying.isInvariant = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ varying.interpolation = GetInterpolationType(type.getQualifier());
+ return varying;
+}
+
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+void CollectVariablesTraverser::recordInterfaceBlock(const TType &interfaceBlockType,
+ InterfaceBlock *interfaceBlock) const
+{
+ ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock);
+ ASSERT(interfaceBlock);
+
+ const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
+ ASSERT(blockType);
+
+ interfaceBlock->name = blockType->name().c_str();
+ interfaceBlock->mappedName = getMappedName(TName(blockType->name()));
+ interfaceBlock->instanceName =
+ (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
+ ASSERT(!interfaceBlockType.isArrayOfArrays()); // Disallowed by GLSL ES 3.10 section 4.3.9
+ interfaceBlock->arraySize = interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0;
+
+ interfaceBlock->blockType = GetBlockType(interfaceBlockType.getQualifier());
+ if (interfaceBlock->blockType == BlockType::BLOCK_UNIFORM ||
+ interfaceBlock->blockType == BlockType::BLOCK_BUFFER)
+ {
+ interfaceBlock->isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
+ interfaceBlock->binding = blockType->blockBinding();
+ interfaceBlock->layout = GetBlockLayoutType(blockType->blockStorage());
+ }
+
+ // Gather field information
+ for (const TField *field : blockType->fields())
+ {
+ const TType &fieldType = *field->type();
+
+ InterfaceBlockField fieldVariable;
+ setCommonVariableProperties(fieldType, TName(field->name()), &fieldVariable);
+ fieldVariable.isRowMajorLayout =
+ (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
+ interfaceBlock->fields.push_back(fieldVariable);
+ }
+}
+
+Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
+{
+ Uniform uniform;
+ setCommonVariableProperties(variable.getType(), variable.getName(), &uniform);
+ uniform.binding = variable.getType().getLayoutQualifier().binding;
+ uniform.location = variable.getType().getLayoutQualifier().location;
+ uniform.offset = variable.getType().getLayoutQualifier().offset;
+ return uniform;
+}
+
+bool CollectVariablesTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ const TIntermSequence &sequence = *(node->getSequence());
+ ASSERT(!sequence.empty());
+
+ const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
+ TQualifier qualifier = typedNode.getQualifier();
+
+ bool isShaderVariable = qualifier == EvqAttribute || qualifier == EvqVertexIn ||
+ qualifier == EvqFragmentOut || qualifier == EvqUniform ||
+ IsVarying(qualifier);
+
+ if (typedNode.getBasicType() != EbtInterfaceBlock && !isShaderVariable)
+ {
+ return true;
+ }
+
+ for (TIntermNode *variableNode : sequence)
+ {
+ // The only case in which the sequence will not contain a TIntermSymbol node is
+ // initialization. It will contain a TInterBinary node in that case. Since attributes,
+ // uniforms, varyings, outputs and interface blocks cannot be initialized in a shader, we
+ // must have only TIntermSymbol nodes in the sequence in the cases we are interested in.
+ const TIntermSymbol &variable = *variableNode->getAsSymbolNode();
+ if (variable.getName().isInternal())
+ {
+ // Internal variables are not collected.
+ continue;
+ }
+
+ // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+ if (typedNode.getBasicType() == EbtInterfaceBlock)
+ {
+ InterfaceBlock interfaceBlock;
+ recordInterfaceBlock(variable.getType(), &interfaceBlock);
+
+ switch (qualifier)
+ {
+ case EvqUniform:
+ mUniformBlocks->push_back(interfaceBlock);
+ break;
+ case EvqBuffer:
+ mShaderStorageBlocks->push_back(interfaceBlock);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ switch (qualifier)
+ {
+ case EvqAttribute:
+ case EvqVertexIn:
+ mAttribs->push_back(recordAttribute(variable));
+ break;
+ case EvqFragmentOut:
+ mOutputVariables->push_back(recordOutputVariable(variable));
+ break;
+ case EvqUniform:
+ mUniforms->push_back(recordUniform(variable));
+ break;
+ default:
+ if (IsVaryingIn(qualifier))
+ {
+ mInputVaryings->push_back(recordVarying(variable));
+ }
+ else
+ {
+ ASSERT(IsVaryingOut(qualifier));
+ mOutputVaryings->push_back(recordVarying(variable));
+ }
+ break;
+ }
+ }
+ }
+
+ // None of the recorded variables can have initializers, so we don't need to traverse the
+ // declarators.
+ return false;
+}
+
+// TODO(jiawei.shao@intel.com): add search on mInBlocks and mOutBlocks when implementing
+// GL_OES_shader_io_blocks.
+InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(const TString &blockName) const
+{
+ InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks);
+ if (!namedBlock)
+ {
+ namedBlock = FindVariable(blockName, mShaderStorageBlocks);
+ }
+ return namedBlock;
+}
+
+bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
+{
+ if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
+ {
+ // NOTE: we do not determine static use for individual blocks of an array
+ TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
+ ASSERT(blockNode);
+
+ TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
+ ASSERT(constantUnion);
+
+ InterfaceBlock *namedBlock = nullptr;
+
+ bool traverseIndexExpression = false;
+ TIntermBinary *interfaceIndexingNode = blockNode->getAsBinaryNode();
+ if (interfaceIndexingNode)
+ {
+ TIntermTyped *interfaceNode = interfaceIndexingNode->getLeft()->getAsTyped();
+ ASSERT(interfaceNode);
+
+ const TType &interfaceType = interfaceNode->getType();
+ if (interfaceType.getQualifier() == EvqPerVertexIn)
+ {
+ namedBlock = recordGLInUsed(interfaceType);
+ ASSERT(namedBlock);
+
+ // We need to continue traversing to collect useful variables in the index
+ // expression of gl_in.
+ traverseIndexExpression = true;
+ }
+ }
+
+ const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
+ if (!namedBlock)
+ {
+ namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
+ }
+ ASSERT(namedBlock);
+ namedBlock->staticUse = true;
+ unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
+ ASSERT(fieldIndex < namedBlock->fields.size());
+ namedBlock->fields[fieldIndex].staticUse = true;
+
+ if (traverseIndexExpression)
+ {
+ ASSERT(interfaceIndexingNode);
+ interfaceIndexingNode->getRight()->traverse(this);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+void CollectVariables(TIntermBlock *root,
+ std::vector<Attribute> *attributes,
+ std::vector<OutputVariable> *outputVariables,
+ std::vector<Uniform> *uniforms,
+ std::vector<Varying> *inputVaryings,
+ std::vector<Varying> *outputVaryings,
+ std::vector<InterfaceBlock> *uniformBlocks,
+ std::vector<InterfaceBlock> *shaderStorageBlocks,
+ std::vector<InterfaceBlock> *inBlocks,
+ ShHashFunction64 hashFunction,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ GLenum shaderType,
+ const TExtensionBehavior &extensionBehavior)
+{
+ CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
+ outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
+ hashFunction, symbolTable, shaderVersion, shaderType,
+ extensionBehavior);
+ root->traverse(&collect);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/CollectVariables.h b/src/3rdparty/angle/src/compiler/translator/CollectVariables.h
new file mode 100644
index 0000000000..4d0d1192e0
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/CollectVariables.h
@@ -0,0 +1,37 @@
+//
+// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// CollectVariables.h: Collect lists of shader interface variables based on the AST.
+
+#ifndef COMPILER_TRANSLATOR_COLLECTVARIABLES_H_
+#define COMPILER_TRANSLATOR_COLLECTVARIABLES_H_
+
+#include <GLSLANG/ShaderLang.h>
+
+#include "compiler/translator/ExtensionBehavior.h"
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+void CollectVariables(TIntermBlock *root,
+ std::vector<Attribute> *attributes,
+ std::vector<OutputVariable> *outputVariables,
+ std::vector<Uniform> *uniforms,
+ std::vector<Varying> *inputVaryings,
+ std::vector<Varying> *outputVaryings,
+ std::vector<InterfaceBlock> *uniformBlocks,
+ std::vector<InterfaceBlock> *shaderStorageBlocks,
+ std::vector<InterfaceBlock> *inBlocks,
+ ShHashFunction64 hashFunction,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ GLenum shaderType,
+ const TExtensionBehavior &extensionBehavior);
+}
+
+#endif // COMPILER_TRANSLATOR_COLLECTVARIABLES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Common.h b/src/3rdparty/angle/src/compiler/translator/Common.h
index 60223232af..cb3a680d85 100644
--- a/src/3rdparty/angle/src/compiler/translator/Common.h
+++ b/src/3rdparty/angle/src/compiler/translator/Common.h
@@ -10,15 +10,21 @@
#include <map>
#include <sstream>
#include <string>
+#include <unordered_map>
#include <vector>
#include <limits>
#include <stdio.h>
#include "common/angleutils.h"
#include "common/debug.h"
+#include "common/third_party/smhasher/src/PMurHash.h"
#include "compiler/translator/PoolAlloc.h"
-struct TSourceLoc {
+namespace sh
+{
+
+struct TSourceLoc
+{
int first_file;
int first_line;
int last_file;
@@ -29,25 +35,25 @@ struct TSourceLoc {
// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme.
//
#define POOL_ALLOCATOR_NEW_DELETE() \
- void* operator new(size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
- void* operator new(size_t, void *_Where) { return (_Where); } \
- void operator delete(void*) { } \
- void operator delete(void *, void *) { } \
- void* operator new[](size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
- void* operator new[](size_t, void *_Where) { return (_Where); } \
- void operator delete[](void*) { } \
- void operator delete[](void *, void *) { }
+ void *operator new(size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
+ void *operator new(size_t, void *_Where) { return (_Where); } \
+ void operator delete(void *) {} \
+ void operator delete(void *, void *) {} \
+ void *operator new[](size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
+ void *operator new[](size_t, void *_Where) { return (_Where); } \
+ void operator delete[](void *) {} \
+ void operator delete[](void *, void *) {}
//
// Pool version of string.
//
typedef pool_allocator<char> TStringAllocator;
-typedef std::basic_string <char, std::char_traits<char>, TStringAllocator> TString;
+typedef std::basic_string<char, std::char_traits<char>, TStringAllocator> TString;
typedef std::basic_ostringstream<char, std::char_traits<char>, TStringAllocator> TStringStream;
-inline TString* NewPoolTString(const char* s)
+inline TString *NewPoolTString(const char *s)
{
- void* memory = GetGlobalPoolAllocator()->allocate(sizeof(TString));
- return new(memory) TString(s);
+ void *memory = GetGlobalPoolAllocator()->allocate(sizeof(TString));
+ return new (memory) TString(s);
}
//
@@ -64,21 +70,44 @@ template <class T>
class TVector : public std::vector<T, pool_allocator<T>>
{
public:
+ POOL_ALLOCATOR_NEW_DELETE();
+
typedef typename std::vector<T, pool_allocator<T>>::size_type size_type;
TVector() : std::vector<T, pool_allocator<T>>() {}
TVector(const pool_allocator<T> &a) : std::vector<T, pool_allocator<T>>(a) {}
TVector(size_type i) : std::vector<T, pool_allocator<T>>(i) {}
};
+template <class K, class D, class H = std::hash<K>, class CMP = std::equal_to<K>>
+class TUnorderedMap : public std::unordered_map<K, D, H, CMP, pool_allocator<std::pair<const K, D>>>
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ typedef pool_allocator<std::pair<const K, D>> tAllocator;
+
+ TUnorderedMap() : std::unordered_map<K, D, H, CMP, tAllocator>() {}
+ // use correct two-stage name lookup supported in gcc 3.4 and above
+ TUnorderedMap(const tAllocator &a)
+ : std::unordered_map<K, D, H, CMP, tAllocator>(
+ std::unordered_map<K, D, H, CMP, tAllocator>::key_compare(),
+ a)
+ {
+ }
+};
+
template <class K, class D, class CMP = std::less<K>>
class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<const K, D>>>
{
public:
+ POOL_ALLOCATOR_NEW_DELETE();
typedef pool_allocator<std::pair<const K, D>> tAllocator;
TMap() : std::map<K, D, CMP, tAllocator>() {}
// use correct two-stage name lookup supported in gcc 3.4 and above
- TMap(const tAllocator& a) : std::map<K, D, CMP, tAllocator>(std::map<K, D, CMP, tAllocator>::key_compare(), a) {}
+ TMap(const tAllocator &a)
+ : std::map<K, D, CMP, tAllocator>(std::map<K, D, CMP, tAllocator>::key_compare(), a)
+ {
+ }
};
// Integer to TString conversion
@@ -92,4 +121,18 @@ inline TString str(T i)
return buffer;
}
-#endif // COMPILER_TRANSLATOR_COMMON_H_
+} // namespace sh
+
+namespace std
+{
+template <>
+struct hash<sh::TString>
+{
+ size_t operator()(const sh::TString &s) const
+ {
+ return angle::PMurHash32(0, s.data(), static_cast<int>(s.length()));
+ }
+};
+} // namespace std
+
+#endif // COMPILER_TRANSLATOR_COMMON_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Compiler.cpp b/src/3rdparty/angle/src/compiler/translator/Compiler.cpp
index 18524ce569..2f411cb58c 100644
--- a/src/3rdparty/angle/src/compiler/translator/Compiler.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/Compiler.cpp
@@ -4,91 +4,193 @@
// found in the LICENSE file.
//
-#include "compiler/translator/Cache.h"
#include "compiler/translator/Compiler.h"
+
+#include <sstream>
+
+#include "angle_gl.h"
+#include "common/utilities.h"
+#include "compiler/translator/AddAndTrueToLoopCondition.h"
+#include "compiler/translator/Cache.h"
#include "compiler/translator/CallDAG.h"
-#include "compiler/translator/ForLoopUnroll.h"
+#include "compiler/translator/ClampPointSize.h"
+#include "compiler/translator/CollectVariables.h"
+#include "compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h"
+#include "compiler/translator/DeferGlobalInitializers.h"
+#include "compiler/translator/EmulateGLFragColorBroadcast.h"
+#include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/Initialize.h"
-#include "compiler/translator/InitializeParseContext.h"
#include "compiler/translator/InitializeVariables.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/IsASTDepthBelowLimit.h"
+#include "compiler/translator/OutputTree.h"
#include "compiler/translator/ParseContext.h"
-#include "compiler/translator/PruneEmptyDeclarations.h"
+#include "compiler/translator/PruneNoOps.h"
#include "compiler/translator/RegenerateStructNames.h"
+#include "compiler/translator/RemoveArrayLengthMethod.h"
+#include "compiler/translator/RemoveEmptySwitchStatements.h"
+#include "compiler/translator/RemoveInvariantDeclaration.h"
+#include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h"
#include "compiler/translator/RemovePow.h"
-#include "compiler/translator/RenameFunction.h"
+#include "compiler/translator/RemoveUnreferencedVariables.h"
#include "compiler/translator/RewriteDoWhile.h"
#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
+#include "compiler/translator/SeparateDeclarations.h"
+#include "compiler/translator/SimplifyLoopConditions.h"
+#include "compiler/translator/SplitSequenceOperator.h"
#include "compiler/translator/UnfoldShortCircuitAST.h"
+#include "compiler/translator/UseInterfaceBlockFields.h"
#include "compiler/translator/ValidateLimitations.h"
+#include "compiler/translator/ValidateMaxParameters.h"
#include "compiler/translator/ValidateOutputs.h"
+#include "compiler/translator/ValidateVaryingLocations.h"
#include "compiler/translator/VariablePacker.h"
-#include "compiler/translator/depgraph/DependencyGraph.h"
-#include "compiler/translator/depgraph/DependencyGraphOutput.h"
-#include "compiler/translator/timing/RestrictFragmentShaderTiming.h"
-#include "compiler/translator/timing/RestrictVertexShaderTiming.h"
+#include "compiler/translator/VectorizeVectorScalarArithmetic.h"
+#include "compiler/translator/util.h"
#include "third_party/compiler/ArrayBoundsClamper.h"
-#include "angle_gl.h"
-#include "common/utilities.h"
+
+namespace sh
+{
+
+namespace
+{
+
+#if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+void DumpFuzzerCase(char const *const *shaderStrings,
+ size_t numStrings,
+ uint32_t type,
+ uint32_t spec,
+ uint32_t output,
+ uint64_t options)
+{
+ static int fileIndex = 0;
+
+ std::ostringstream o;
+ o << "corpus/" << fileIndex++ << ".sample";
+ std::string s = o.str();
+
+ // Must match the input format of the fuzzer
+ FILE *f = fopen(s.c_str(), "w");
+ fwrite(&type, sizeof(type), 1, f);
+ fwrite(&spec, sizeof(spec), 1, f);
+ fwrite(&output, sizeof(output), 1, f);
+ fwrite(&options, sizeof(options), 1, f);
+
+ char zero[128 - 20] = {0};
+ fwrite(&zero, 128 - 20, 1, f);
+
+ for (size_t i = 0; i < numStrings; i++)
+ {
+ fwrite(shaderStrings[i], sizeof(char), strlen(shaderStrings[i]), f);
+ }
+ fwrite(&zero, 1, 1, f);
+
+ fclose(f);
+}
+#endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+} // anonymous namespace
bool IsWebGLBasedSpec(ShShaderSpec spec)
{
- return (spec == SH_WEBGL_SPEC ||
- spec == SH_CSS_SHADERS_SPEC ||
- spec == SH_WEBGL2_SPEC);
+ return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
}
bool IsGLSL130OrNewer(ShShaderOutput output)
{
- return (output == SH_GLSL_130_OUTPUT ||
- output == SH_GLSL_140_OUTPUT ||
- output == SH_GLSL_150_CORE_OUTPUT ||
- output == SH_GLSL_330_CORE_OUTPUT ||
- output == SH_GLSL_400_CORE_OUTPUT ||
- output == SH_GLSL_410_CORE_OUTPUT ||
- output == SH_GLSL_420_CORE_OUTPUT ||
- output == SH_GLSL_430_CORE_OUTPUT ||
- output == SH_GLSL_440_CORE_OUTPUT ||
- output == SH_GLSL_450_CORE_OUTPUT);
+ return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT ||
+ output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT ||
+ output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT ||
+ output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT ||
+ output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT);
+}
+
+bool IsGLSL420OrNewer(ShShaderOutput output)
+{
+ return (output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT ||
+ output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT);
+}
+
+bool IsGLSL410OrOlder(ShShaderOutput output)
+{
+ return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT ||
+ output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT ||
+ output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT);
+}
+
+bool RemoveInvariant(sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput outputType,
+ ShCompileOptions compileOptions)
+{
+ if ((compileOptions & SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT) == 0 &&
+ shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType))
+ return true;
+
+ if ((compileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0 &&
+ shaderVersion >= 300 && shaderType == GL_VERTEX_SHADER)
+ return true;
+
+ return false;
}
size_t GetGlobalMaxTokenSize(ShShaderSpec spec)
{
- // WebGL defines a max token legnth of 256, while ES2 leaves max token
+ // WebGL defines a max token length of 256, while ES2 leaves max token
// size undefined. ES3 defines a max size of 1024 characters.
switch (spec)
{
- case SH_WEBGL_SPEC:
- case SH_CSS_SHADERS_SPEC:
- return 256;
- default:
- return 1024;
+ case SH_WEBGL_SPEC:
+ return 256;
+ default:
+ return 1024;
}
}
-namespace {
+int GetMaxUniformVectorsForShaderType(GLenum shaderType, const ShBuiltInResources &resources)
+{
+ switch (shaderType)
+ {
+ case GL_VERTEX_SHADER:
+ return resources.MaxVertexUniformVectors;
+ case GL_FRAGMENT_SHADER:
+ return resources.MaxFragmentUniformVectors;
+
+ // TODO (jiawei.shao@intel.com): check if we need finer-grained component counting
+ case GL_COMPUTE_SHADER:
+ return resources.MaxComputeUniformComponents / 4;
+ case GL_GEOMETRY_SHADER_OES:
+ return resources.MaxGeometryUniformComponents / 4;
+ default:
+ UNREACHABLE();
+ return -1;
+ }
+}
+
+namespace
+{
class TScopedPoolAllocator
{
public:
- TScopedPoolAllocator(TPoolAllocator* allocator) : mAllocator(allocator)
+ TScopedPoolAllocator(TPoolAllocator *allocator) : mAllocator(allocator)
{
mAllocator->push();
SetGlobalPoolAllocator(mAllocator);
}
~TScopedPoolAllocator()
{
- SetGlobalPoolAllocator(NULL);
+ SetGlobalPoolAllocator(nullptr);
mAllocator->pop();
}
private:
- TPoolAllocator* mAllocator;
+ TPoolAllocator *mAllocator;
};
class TScopedSymbolTableLevel
{
public:
- TScopedSymbolTableLevel(TSymbolTable* table) : mTable(table)
+ TScopedSymbolTableLevel(TSymbolTable *table) : mTable(table)
{
ASSERT(mTable->atBuiltInLevel());
mTable->push();
@@ -100,23 +202,25 @@ class TScopedSymbolTableLevel
}
private:
- TSymbolTable* mTable;
+ TSymbolTable *mTable;
};
int MapSpecToShaderVersion(ShShaderSpec spec)
{
switch (spec)
{
- case SH_GLES2_SPEC:
- case SH_WEBGL_SPEC:
- case SH_CSS_SHADERS_SPEC:
- return 100;
- case SH_GLES3_SPEC:
- case SH_WEBGL2_SPEC:
- return 300;
- default:
- UNREACHABLE();
- return 0;
+ case SH_GLES2_SPEC:
+ case SH_WEBGL_SPEC:
+ return 100;
+ case SH_GLES3_SPEC:
+ case SH_WEBGL2_SPEC:
+ return 300;
+ case SH_GLES3_1_SPEC:
+ case SH_WEBGL3_SPEC:
+ return 310;
+ default:
+ UNREACHABLE();
+ return 0;
}
}
@@ -130,22 +234,31 @@ TShHandleBase::TShHandleBase()
TShHandleBase::~TShHandleBase()
{
- SetGlobalPoolAllocator(NULL);
+ SetGlobalPoolAllocator(nullptr);
allocator.popAll();
}
TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
- : shaderType(type),
+ : variablesCollected(false),
+ mGLPositionInitialized(false),
+ shaderType(type),
shaderSpec(spec),
outputType(output),
maxUniformVectors(0),
maxExpressionComplexity(0),
maxCallStackDepth(0),
+ maxFunctionParameters(0),
fragmentPrecisionHigh(false),
clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
builtInFunctionEmulator(),
- mSourcePath(NULL),
- mTemporaryIndex(0)
+ mDiagnostics(infoSink.info),
+ mSourcePath(nullptr),
+ mComputeShaderLocalSizeDeclared(false),
+ mComputeShaderLocalSize(1),
+ mGeometryShaderMaxVertices(-1),
+ mGeometryShaderInvocations(0),
+ mGeometryShaderInputPrimitiveType(EptUndefined),
+ mGeometryShaderOutputPrimitiveType(EptUndefined)
{
}
@@ -153,7 +266,7 @@ TCompiler::~TCompiler()
{
}
-bool TCompiler::shouldRunLoopAndIndexingValidation(int compileOptions) const
+bool TCompiler::shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const
{
// If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API,
// validate loop and indexing as well (to verify that the shader only uses minimal functionality
@@ -162,14 +275,15 @@ bool TCompiler::shouldRunLoopAndIndexingValidation(int compileOptions) const
(compileOptions & SH_VALIDATE_LOOP_INDEXING);
}
-bool TCompiler::Init(const ShBuiltInResources& resources)
+bool TCompiler::Init(const ShBuiltInResources &resources)
{
shaderVersion = 100;
- maxUniformVectors = (shaderType == GL_VERTEX_SHADER) ?
- resources.MaxVertexUniformVectors :
- resources.MaxFragmentUniformVectors;
+
+ maxUniformVectors = GetMaxUniformVectorsForShaderType(shaderType, resources);
+
maxExpressionComplexity = resources.MaxExpressionComplexity;
- maxCallStackDepth = resources.MaxCallStackDepth;
+ maxCallStackDepth = resources.MaxCallStackDepth;
+ maxFunctionParameters = resources.MaxFunctionParameters;
SetGlobalPoolAllocator(&allocator);
@@ -187,15 +301,16 @@ bool TCompiler::Init(const ShBuiltInResources& resources)
return true;
}
-TIntermNode *TCompiler::compileTreeForTesting(const char* const shaderStrings[],
- size_t numStrings, int compileOptions)
+TIntermBlock *TCompiler::compileTreeForTesting(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions)
{
return compileTreeImpl(shaderStrings, numStrings, compileOptions);
}
-TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
- size_t numStrings,
- const int compileOptions)
+TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
+ size_t numStrings,
+ const ShCompileOptions compileOptions)
{
clearResults();
@@ -213,191 +328,347 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
++firstSource;
}
- TIntermediate intermediate(infoSink);
- TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec,
- compileOptions, true, infoSink, getResources());
+ TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec,
+ compileOptions, true, &mDiagnostics, getResources());
parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh);
- SetGlobalParseContext(&parseContext);
// We preserve symbols at the built-in level from compile-to-compile.
// Start pushing the user-defined symbols at global level.
TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable);
// Parse shader.
- bool success =
- (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, &parseContext) == 0) &&
- (parseContext.getTreeRoot() != nullptr);
+ if (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr,
+ &parseContext) != 0)
+ {
+ return nullptr;
+ }
+
+ if (parseContext.getTreeRoot() == nullptr)
+ {
+ return nullptr;
+ }
+
+ setASTMetadata(parseContext);
+
+ if (MapSpecToShaderVersion(shaderSpec) < shaderVersion)
+ {
+ mDiagnostics.globalError("unsupported shader version");
+ return nullptr;
+ }
+ TIntermBlock *root = parseContext.getTreeRoot();
+ if (!checkAndSimplifyAST(root, parseContext, compileOptions))
+ {
+ return nullptr;
+ }
+
+ return root;
+}
+
+void TCompiler::setASTMetadata(const TParseContext &parseContext)
+{
shaderVersion = parseContext.getShaderVersion();
- if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion)
+
+ mPragma = parseContext.pragma();
+ symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
+
+ mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
+ mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize();
+
+ mNumViews = parseContext.getNumViews();
+
+ // Highp might have been auto-enabled based on shader version
+ fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
+
+ if (shaderType == GL_GEOMETRY_SHADER_OES)
{
- infoSink.info.prefix(EPrefixError);
- infoSink.info << "unsupported shader version";
- success = false;
+ mGeometryShaderInputPrimitiveType = parseContext.getGeometryShaderInputPrimitiveType();
+ mGeometryShaderOutputPrimitiveType = parseContext.getGeometryShaderOutputPrimitiveType();
+ mGeometryShaderMaxVertices = parseContext.getGeometryShaderMaxVertices();
+ mGeometryShaderInvocations = parseContext.getGeometryShaderInvocations();
}
+}
- TIntermNode *root = nullptr;
+bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
+ const TParseContext &parseContext,
+ ShCompileOptions compileOptions)
+{
+ // Disallow expressions deemed too complex.
+ if ((compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY) && !limitExpressionComplexity(root))
+ {
+ return false;
+ }
- if (success)
+ // We prune no-ops to work around driver bugs and to keep AST processing and output simple.
+ // The following kinds of no-ops are pruned:
+ // 1. Empty declarations "int;".
+ // 2. Literal statements: "1.0;". The ESSL output doesn't define a default precision
+ // for float, so float literal statements would end up with no precision which is
+ // invalid ESSL.
+ // After this empty declarations are not allowed in the AST.
+ PruneNoOps(root);
+
+ // In case the last case inside a switch statement is a certain type of no-op, GLSL
+ // compilers in drivers may not accept it. In this case we clean up the dead code from the
+ // end of switch statements. This is also required because PruneNoOps may have left switch
+ // statements that only contained an empty declaration inside the final case in an invalid
+ // state. Relies on that PruneNoOps has already been run.
+ RemoveNoOpCasesFromEndOfSwitchStatements(root, &symbolTable);
+
+ // Remove empty switch statements - this makes output simpler.
+ RemoveEmptySwitchStatements(root);
+
+ // Create the function DAG and check there is no recursion
+ if (!initCallDag(root))
{
- mPragma = parseContext.pragma();
- if (mPragma.stdgl.invariantAll)
- {
- symbolTable.setGlobalInvariant();
- }
+ return false;
+ }
- root = parseContext.getTreeRoot();
- root = intermediate.postProcess(root);
+ if ((compileOptions & SH_LIMIT_CALL_STACK_DEPTH) && !checkCallDepth())
+ {
+ return false;
+ }
- // Highp might have been auto-enabled based on shader version
- fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
+ // Checks which functions are used and if "main" exists
+ functionMetadata.clear();
+ functionMetadata.resize(mCallDag.size());
+ if (!tagUsedFunctions())
+ {
+ return false;
+ }
- // Disallow expressions deemed too complex.
- if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
- success = limitExpressionComplexity(root);
+ if (!(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
+ {
+ pruneUnusedFunctions(root);
+ }
- // Create the function DAG and check there is no recursion
- if (success)
- success = initCallDag(root);
+ if (shaderVersion >= 310 && !ValidateVaryingLocations(root, &mDiagnostics, shaderType))
+ {
+ return false;
+ }
- if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH))
- success = checkCallDepth();
+ if (shaderVersion >= 300 && shaderType == GL_FRAGMENT_SHADER &&
+ !ValidateOutputs(root, getExtensionBehavior(), compileResources.MaxDrawBuffers,
+ &mDiagnostics))
+ {
+ return false;
+ }
- // Checks which functions are used and if "main" exists
- if (success)
- {
- functionMetadata.clear();
- functionMetadata.resize(mCallDag.size());
- success = tagUsedFunctions();
- }
+ if (shouldRunLoopAndIndexingValidation(compileOptions) &&
+ !ValidateLimitations(root, shaderType, &symbolTable, shaderVersion, &mDiagnostics))
+ {
+ return false;
+ }
- if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
- success = pruneUnusedFunctions(root);
+ // Fail compilation if precision emulation not supported.
+ if (getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision &&
+ !EmulatePrecision::SupportedInLanguage(outputType))
+ {
+ mDiagnostics.globalError("Precision emulation not supported for this output type.");
+ return false;
+ }
- // Prune empty declarations to work around driver bugs and to keep declaration output simple.
- if (success)
- PruneEmptyDeclarations(root);
+ // Clamping uniform array bounds needs to happen after validateLimitations pass.
+ if (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)
+ {
+ arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
+ }
- if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER)
- success = validateOutputs(root);
+ if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) &&
+ parseContext.isExtensionEnabled(TExtension::OVR_multiview) &&
+ getShaderType() != GL_COMPUTE_SHADER)
+ {
+ DeclareAndInitBuiltinsForInstancedMultiview(root, mNumViews, shaderType, compileOptions,
+ outputType, &symbolTable);
+ }
- if (success && shouldRunLoopAndIndexingValidation(compileOptions))
- success = validateLimitations(root);
+ // This pass might emit short circuits so keep it before the short circuit unfolding
+ if (compileOptions & SH_REWRITE_DO_WHILE_LOOPS)
+ RewriteDoWhile(root, &symbolTable);
- if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
- success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
+ if (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION)
+ sh::AddAndTrueToLoopCondition(root);
- if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
- rewriteCSSShader(root);
+ if (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)
+ {
+ UnfoldShortCircuitAST unfoldShortCircuit;
+ root->traverse(&unfoldShortCircuit);
+ unfoldShortCircuit.updateTree();
+ }
- // Unroll for-loop markup needs to happen after validateLimitations pass.
- if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
- {
- ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex,
- shouldRunLoopAndIndexingValidation(compileOptions));
- root->traverse(&marker);
- }
- if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX))
- {
- ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex,
- shouldRunLoopAndIndexingValidation(compileOptions));
- root->traverse(&marker);
- if (marker.samplerArrayIndexIsFloatLoopIndex())
- {
- infoSink.info.prefix(EPrefixError);
- infoSink.info << "sampler array index is float loop index";
- success = false;
- }
- }
+ if (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT)
+ {
+ RemovePow(root);
+ }
- // Built-in function emulation needs to happen after validateLimitations pass.
- if (success)
- {
- initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
- builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
- }
+ if (compileOptions & SH_REGENERATE_STRUCT_NAMES)
+ {
+ RegenerateStructNames gen(&symbolTable, shaderVersion);
+ root->traverse(&gen);
+ }
- // Clamping uniform array bounds needs to happen after validateLimitations pass.
- if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
- arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
+ if (shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 &&
+ compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 &&
+ IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
+ {
+ EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables,
+ &symbolTable, shaderVersion);
+ }
- // gl_Position is always written in compatibility output mode
- if (success && shaderType == GL_VERTEX_SHADER &&
- ((compileOptions & SH_INIT_GL_POSITION) ||
- (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
- initializeGLPosition(root);
+ // Split multi declarations and remove calls to array length().
+ // Note that SimplifyLoopConditions needs to be run before any other AST transformations
+ // that may need to generate new statements from loop conditions or loop expressions.
+ SimplifyLoopConditions(
+ root,
+ IntermNodePatternMatcher::kMultiDeclaration | IntermNodePatternMatcher::kArrayLengthMethod,
+ &getSymbolTable(), getShaderVersion());
- // This pass might emit short circuits so keep it before the short circuit unfolding
- if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS))
- RewriteDoWhile(root, getTemporaryIndex());
+ // Note that separate declarations need to be run before other AST transformations that
+ // generate new statements from expressions.
+ SeparateDeclarations(root);
- if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT))
- {
- UnfoldShortCircuitAST unfoldShortCircuit;
- root->traverse(&unfoldShortCircuit);
- unfoldShortCircuit.updateTree();
- }
+ SplitSequenceOperator(root, IntermNodePatternMatcher::kArrayLengthMethod, &getSymbolTable(),
+ getShaderVersion());
+
+ RemoveArrayLengthMethod(root);
+
+ RemoveUnreferencedVariables(root, &symbolTable);
- if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT))
+ // Built-in function emulation needs to happen after validateLimitations pass.
+ // TODO(jmadill): Remove global pool allocator.
+ GetGlobalPoolAllocator()->lock();
+ initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
+ GetGlobalPoolAllocator()->unlock();
+ builtInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
+
+ if (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS)
+ {
+ ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh, &symbolTable);
+ }
+
+ if (shouldCollectVariables(compileOptions))
+ {
+ ASSERT(!variablesCollected);
+ CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
+ &outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
+ hashFunction, &symbolTable, shaderVersion, shaderType, extensionBehavior);
+ collectInterfaceBlocks();
+ variablesCollected = true;
+ if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
{
- RemovePow(root);
+ useAllMembersInUnusedStandardAndSharedBlocks(root);
}
-
- if (success && shouldCollectVariables(compileOptions))
+ if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
{
- collectVariables(root);
- if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
+ // Returns true if, after applying the packing rules in the GLSL ES 1.00.17 spec
+ // Appendix A, section 7, the shader does not use too many uniforms.
+ if (!CheckVariablesInPackingLimits(maxUniformVectors, uniforms))
{
- success = enforcePackingRestrictions();
- if (!success)
- {
- infoSink.info.prefix(EPrefixError);
- infoSink.info << "too many uniforms";
- }
+ mDiagnostics.globalError("too many uniforms");
+ return false;
}
- if (success && shaderType == GL_VERTEX_SHADER &&
- (compileOptions & SH_INIT_VARYINGS_WITHOUT_STATIC_USE))
- initializeVaryingsWithoutStaticUse(root);
}
-
- if (success && (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS))
+ if (compileOptions & SH_INIT_OUTPUT_VARIABLES)
{
- ScalarizeVecAndMatConstructorArgs scalarizer(
- shaderType, fragmentPrecisionHigh);
- root->traverse(&scalarizer);
+ initializeOutputVariables(root);
}
+ }
+
+ // Removing invariant declarations must be done after collecting variables.
+ // Otherwise, built-in invariant declarations don't apply.
+ if (RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
+ {
+ RemoveInvariantDeclaration(root);
+ }
+
+ // gl_Position is always written in compatibility output mode.
+ // It may have been already initialized among other output variables, in that case we don't
+ // need to initialize it twice.
+ if (shaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
+ ((compileOptions & SH_INIT_GL_POSITION) || (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
+ {
+ initializeGLPosition(root);
+ mGLPositionInitialized = true;
+ }
+
+ // DeferGlobalInitializers needs to be run before other AST transformations that generate new
+ // statements from expressions. But it's fine to run DeferGlobalInitializers after the above
+ // SplitSequenceOperator and RemoveArrayLengthMethod since they only have an effect on the AST
+ // on ESSL >= 3.00, and the initializers that need to be deferred can only exist in ESSL < 3.00.
+ bool initializeLocalsAndGlobals =
+ (compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) && !IsOutputHLSL(getOutputType());
+ bool canUseLoopsToInitialize = !(compileOptions & SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES);
+ DeferGlobalInitializers(root, initializeLocalsAndGlobals, canUseLoopsToInitialize, &symbolTable);
- if (success && (compileOptions & SH_REGENERATE_STRUCT_NAMES))
+ if (initializeLocalsAndGlobals)
+ {
+ // Initialize uninitialized local variables.
+ // In some cases initializing can generate extra statements in the parent block, such as
+ // when initializing nameless structs or initializing arrays in ESSL 1.00. In that case
+ // we need to first simplify loop conditions. We've already separated declarations
+ // earlier, which is also required. If we don't follow the Appendix A limitations, loop
+ // init statements can declare arrays or nameless structs and have multiple
+ // declarations.
+
+ if (!shouldRunLoopAndIndexingValidation(compileOptions))
{
- RegenerateStructNames gen(symbolTable, shaderVersion);
- root->traverse(&gen);
+ SimplifyLoopConditions(root,
+ IntermNodePatternMatcher::kArrayDeclaration |
+ IntermNodePatternMatcher::kNamelessStructDeclaration,
+ &getSymbolTable(), getShaderVersion());
}
+
+ InitializeUninitializedLocals(root, getShaderVersion(), canUseLoopsToInitialize,
+ &getSymbolTable());
}
- SetGlobalParseContext(NULL);
- if (success)
- return root;
+ if (getShaderType() == GL_VERTEX_SHADER && (compileOptions & SH_CLAMP_POINT_SIZE))
+ {
+ ClampPointSize(root, compileResources.MaxPointSize, &getSymbolTable());
+ }
- return NULL;
+ if (compileOptions & SH_REWRITE_VECTOR_SCALAR_ARITHMETIC)
+ {
+ VectorizeVectorScalarArithmetic(root, &getSymbolTable());
+ }
+
+ return true;
}
-bool TCompiler::compile(const char* const shaderStrings[],
- size_t numStrings, int compileOptions)
+bool TCompiler::compile(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptionsIn)
{
+#if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+ DumpFuzzerCase(shaderStrings, numStrings, shaderType, shaderSpec, outputType, compileOptionsIn);
+#endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+
if (numStrings == 0)
return true;
+ ShCompileOptions compileOptions = compileOptionsIn;
+
+ // Apply key workarounds.
+ if (shouldFlattenPragmaStdglInvariantAll())
+ {
+ // This should be harmless to do in all cases, but for the moment, do it only conditionally.
+ compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL;
+ }
+
TScopedPoolAllocator scopedAlloc(&allocator);
- TIntermNode *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
+ TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
if (root)
{
if (compileOptions & SH_INTERMEDIATE_TREE)
- TIntermediate::outputTree(root, infoSink.info);
+ OutputTree(root, infoSink.info);
if (compileOptions & SH_OBJECT_CODE)
- translate(root, compileOptions);
+ {
+ PerformanceDiagnostics perfDiagnostics(&mDiagnostics);
+ translate(root, compileOptions, &perfDiagnostics);
+ }
// The IntermNode tree doesn't need to be deleted here, since the
// memory will be freed in a big chunk by the PoolAllocator.
@@ -408,37 +679,38 @@ bool TCompiler::compile(const char* const shaderStrings[],
bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
{
+ if (resources.MaxDrawBuffers < 1)
+ {
+ return false;
+ }
+ if (resources.EXT_blend_func_extended && resources.MaxDualSourceDrawBuffers < 1)
+ {
+ return false;
+ }
+
compileResources = resources;
setResourceString();
- assert(symbolTable.isEmpty());
- symbolTable.push(); // COMMON_BUILTINS
- symbolTable.push(); // ESSL1_BUILTINS
- symbolTable.push(); // ESSL3_BUILTINS
-
- TPublicType integer;
- integer.type = EbtInt;
- integer.primarySize = 1;
- integer.secondarySize = 1;
- integer.array = false;
-
- TPublicType floatingPoint;
- floatingPoint.type = EbtFloat;
- floatingPoint.primarySize = 1;
- floatingPoint.secondarySize = 1;
- floatingPoint.array = false;
-
- switch(shaderType)
- {
- case GL_FRAGMENT_SHADER:
- symbolTable.setDefaultPrecision(integer, EbpMedium);
- break;
- case GL_VERTEX_SHADER:
- symbolTable.setDefaultPrecision(integer, EbpHigh);
- symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
- break;
- default:
- assert(false && "Language not supported");
+ ASSERT(symbolTable.isEmpty());
+ symbolTable.push(); // COMMON_BUILTINS
+ symbolTable.push(); // ESSL1_BUILTINS
+ symbolTable.push(); // ESSL3_BUILTINS
+ symbolTable.push(); // ESSL3_1_BUILTINS
+ symbolTable.push(); // GLSL_BUILTINS
+
+ switch (shaderType)
+ {
+ case GL_FRAGMENT_SHADER:
+ symbolTable.setDefaultPrecision(EbtInt, EbpMedium);
+ break;
+ case GL_VERTEX_SHADER:
+ case GL_COMPUTE_SHADER:
+ case GL_GEOMETRY_SHADER_OES:
+ symbolTable.setDefaultPrecision(EbtInt, EbpHigh);
+ symbolTable.setDefaultPrecision(EbtFloat, EbpHigh);
+ break;
+ default:
+ UNREACHABLE();
}
// Set defaults for sampler types that have default precision, even those that are
// only available if an extension exists.
@@ -447,9 +719,13 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
initSamplerDefaultPrecision(EbtSamplerCube);
// SamplerExternalOES is specified in the extension to have default precision.
initSamplerDefaultPrecision(EbtSamplerExternalOES);
+ // SamplerExternal2DY2YEXT is specified in the extension to have default precision.
+ initSamplerDefaultPrecision(EbtSamplerExternal2DY2YEXT);
// It isn't specified whether Sampler2DRect has default precision.
initSamplerDefaultPrecision(EbtSampler2DRect);
+ symbolTable.setDefaultPrecision(EbtAtomicCounter, EbpHigh);
+
InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
@@ -460,87 +736,149 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType)
{
ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd);
- TPublicType sampler;
- sampler.primarySize = 1;
- sampler.secondarySize = 1;
- sampler.array = false;
- sampler.type = samplerType;
- symbolTable.setDefaultPrecision(sampler, EbpLow);
+ symbolTable.setDefaultPrecision(samplerType, EbpLow);
}
void TCompiler::setResourceString()
{
std::ostringstream strstream;
+
+ // clang-format off
strstream << ":MaxVertexAttribs:" << compileResources.MaxVertexAttribs
- << ":MaxVertexUniformVectors:" << compileResources.MaxVertexUniformVectors
- << ":MaxVaryingVectors:" << compileResources.MaxVaryingVectors
- << ":MaxVertexTextureImageUnits:" << compileResources.MaxVertexTextureImageUnits
- << ":MaxCombinedTextureImageUnits:" << compileResources.MaxCombinedTextureImageUnits
- << ":MaxTextureImageUnits:" << compileResources.MaxTextureImageUnits
- << ":MaxFragmentUniformVectors:" << compileResources.MaxFragmentUniformVectors
- << ":MaxDrawBuffers:" << compileResources.MaxDrawBuffers
- << ":OES_standard_derivatives:" << compileResources.OES_standard_derivatives
- << ":OES_EGL_image_external:" << compileResources.OES_EGL_image_external
- << ":ARB_texture_rectangle:" << compileResources.ARB_texture_rectangle
- << ":EXT_draw_buffers:" << compileResources.EXT_draw_buffers
- << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh
- << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity
- << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
- << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended
- << ":EXT_frag_depth:" << compileResources.EXT_frag_depth
- << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
- << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch
- << ":NV_shader_framebuffer_fetch:" << compileResources.NV_shader_framebuffer_fetch
- << ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch
- << ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors
- << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
- << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
- << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset
- << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers
- << ":NV_draw_buffers:" << compileResources.NV_draw_buffers
- << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision;
+ << ":MaxVertexUniformVectors:" << compileResources.MaxVertexUniformVectors
+ << ":MaxVaryingVectors:" << compileResources.MaxVaryingVectors
+ << ":MaxVertexTextureImageUnits:" << compileResources.MaxVertexTextureImageUnits
+ << ":MaxCombinedTextureImageUnits:" << compileResources.MaxCombinedTextureImageUnits
+ << ":MaxTextureImageUnits:" << compileResources.MaxTextureImageUnits
+ << ":MaxFragmentUniformVectors:" << compileResources.MaxFragmentUniformVectors
+ << ":MaxDrawBuffers:" << compileResources.MaxDrawBuffers
+ << ":OES_standard_derivatives:" << compileResources.OES_standard_derivatives
+ << ":OES_EGL_image_external:" << compileResources.OES_EGL_image_external
+ << ":OES_EGL_image_external_essl3:" << compileResources.OES_EGL_image_external_essl3
+ << ":NV_EGL_stream_consumer_external:" << compileResources.NV_EGL_stream_consumer_external
+ << ":ARB_texture_rectangle:" << compileResources.ARB_texture_rectangle
+ << ":EXT_draw_buffers:" << compileResources.EXT_draw_buffers
+ << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh
+ << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity
+ << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
+ << ":MaxFunctionParameters:" << compileResources.MaxFunctionParameters
+ << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended
+ << ":EXT_frag_depth:" << compileResources.EXT_frag_depth
+ << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
+ << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch
+ << ":NV_shader_framebuffer_fetch:" << compileResources.NV_shader_framebuffer_fetch
+ << ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch
+ << ":OVR_multiview:" << compileResources.OVR_multiview
+ << ":EXT_YUV_target:" << compileResources.EXT_YUV_target
+ << ":OES_geometry_shader:" << compileResources.OES_geometry_shader
+ << ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors
+ << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
+ << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
+ << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset
+ << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers
+ << ":MaxViewsOVR:" << compileResources.MaxViewsOVR
+ << ":NV_draw_buffers:" << compileResources.NV_draw_buffers
+ << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision
+ << ":MinProgramTextureGatherOffset:" << compileResources.MinProgramTextureGatherOffset
+ << ":MaxProgramTextureGatherOffset:" << compileResources.MaxProgramTextureGatherOffset
+ << ":MaxImageUnits:" << compileResources.MaxImageUnits
+ << ":MaxVertexImageUniforms:" << compileResources.MaxVertexImageUniforms
+ << ":MaxFragmentImageUniforms:" << compileResources.MaxFragmentImageUniforms
+ << ":MaxComputeImageUniforms:" << compileResources.MaxComputeImageUniforms
+ << ":MaxCombinedImageUniforms:" << compileResources.MaxCombinedImageUniforms
+ << ":MaxCombinedShaderOutputResources:" << compileResources.MaxCombinedShaderOutputResources
+ << ":MaxComputeWorkGroupCountX:" << compileResources.MaxComputeWorkGroupCount[0]
+ << ":MaxComputeWorkGroupCountY:" << compileResources.MaxComputeWorkGroupCount[1]
+ << ":MaxComputeWorkGroupCountZ:" << compileResources.MaxComputeWorkGroupCount[2]
+ << ":MaxComputeWorkGroupSizeX:" << compileResources.MaxComputeWorkGroupSize[0]
+ << ":MaxComputeWorkGroupSizeY:" << compileResources.MaxComputeWorkGroupSize[1]
+ << ":MaxComputeWorkGroupSizeZ:" << compileResources.MaxComputeWorkGroupSize[2]
+ << ":MaxComputeUniformComponents:" << compileResources.MaxComputeUniformComponents
+ << ":MaxComputeTextureImageUnits:" << compileResources.MaxComputeTextureImageUnits
+ << ":MaxComputeAtomicCounters:" << compileResources.MaxComputeAtomicCounters
+ << ":MaxComputeAtomicCounterBuffers:" << compileResources.MaxComputeAtomicCounterBuffers
+ << ":MaxVertexAtomicCounters:" << compileResources.MaxVertexAtomicCounters
+ << ":MaxFragmentAtomicCounters:" << compileResources.MaxFragmentAtomicCounters
+ << ":MaxCombinedAtomicCounters:" << compileResources.MaxCombinedAtomicCounters
+ << ":MaxAtomicCounterBindings:" << compileResources.MaxAtomicCounterBindings
+ << ":MaxVertexAtomicCounterBuffers:" << compileResources.MaxVertexAtomicCounterBuffers
+ << ":MaxFragmentAtomicCounterBuffers:" << compileResources.MaxFragmentAtomicCounterBuffers
+ << ":MaxCombinedAtomicCounterBuffers:" << compileResources.MaxCombinedAtomicCounterBuffers
+ << ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize
+ << ":MaxGeometryUniformComponents:" << compileResources.MaxGeometryUniformComponents
+ << ":MaxGeometryUniformBlocks:" << compileResources.MaxGeometryUniformBlocks
+ << ":MaxGeometryInputComponents:" << compileResources.MaxGeometryInputComponents
+ << ":MaxGeometryOutputComponents:" << compileResources.MaxGeometryOutputComponents
+ << ":MaxGeometryOutputVertices:" << compileResources.MaxGeometryOutputVertices
+ << ":MaxGeometryTotalOutputComponents:" << compileResources.MaxGeometryTotalOutputComponents
+ << ":MaxGeometryTextureImageUnits:" << compileResources.MaxGeometryTextureImageUnits
+ << ":MaxGeometryAtomicCounterBuffers:" << compileResources.MaxGeometryAtomicCounterBuffers
+ << ":MaxGeometryAtomicCounters:" << compileResources.MaxGeometryAtomicCounters
+ << ":MaxGeometryShaderStorageBlocks:" << compileResources.MaxGeometryShaderStorageBlocks
+ << ":MaxGeometryShaderInvocations:" << compileResources.MaxGeometryShaderInvocations
+ << ":MaxGeometryImageUniforms:" << compileResources.MaxGeometryImageUniforms;
+ // clang-format on
builtInResourcesString = strstream.str();
}
+void TCompiler::collectInterfaceBlocks()
+{
+ ASSERT(interfaceBlocks.empty());
+ interfaceBlocks.reserve(uniformBlocks.size() + shaderStorageBlocks.size() + inBlocks.size());
+ interfaceBlocks.insert(interfaceBlocks.end(), uniformBlocks.begin(), uniformBlocks.end());
+ interfaceBlocks.insert(interfaceBlocks.end(), shaderStorageBlocks.begin(),
+ shaderStorageBlocks.end());
+ interfaceBlocks.insert(interfaceBlocks.end(), inBlocks.begin(), inBlocks.end());
+}
+
void TCompiler::clearResults()
{
arrayBoundsClamper.Cleanup();
infoSink.info.erase();
infoSink.obj.erase();
infoSink.debug.erase();
+ mDiagnostics.resetErrorCount();
attributes.clear();
outputVariables.clear();
uniforms.clear();
- expandedUniforms.clear();
- varyings.clear();
+ inputVaryings.clear();
+ outputVaryings.clear();
interfaceBlocks.clear();
+ uniformBlocks.clear();
+ shaderStorageBlocks.clear();
+ inBlocks.clear();
+ variablesCollected = false;
+ mGLPositionInitialized = false;
+
+ mNumViews = -1;
- builtInFunctionEmulator.Cleanup();
+ mGeometryShaderInputPrimitiveType = EptUndefined;
+ mGeometryShaderOutputPrimitiveType = EptUndefined;
+ mGeometryShaderInvocations = 0;
+ mGeometryShaderMaxVertices = -1;
+
+ builtInFunctionEmulator.cleanup();
nameMap.clear();
- mSourcePath = NULL;
- mTemporaryIndex = 0;
+ mSourcePath = nullptr;
}
bool TCompiler::initCallDag(TIntermNode *root)
{
mCallDag.clear();
- switch (mCallDag.init(root, &infoSink.info))
+ switch (mCallDag.init(root, &mDiagnostics))
{
- case CallDAG::INITDAG_SUCCESS:
- return true;
- case CallDAG::INITDAG_RECURSION:
- infoSink.info.prefix(EPrefixError);
- infoSink.info << "Function recursion detected";
- return false;
- case CallDAG::INITDAG_UNDEFINED:
- infoSink.info.prefix(EPrefixError);
- infoSink.info << "Unimplemented function detected";
- return false;
+ case CallDAG::INITDAG_SUCCESS:
+ return true;
+ case CallDAG::INITDAG_RECURSION:
+ case CallDAG::INITDAG_UNDEFINED:
+ // Error message has already been written out.
+ ASSERT(mDiagnostics.numErrors() > 0);
+ return false;
}
UNREACHABLE();
@@ -553,7 +891,7 @@ bool TCompiler::checkCallDepth()
for (size_t i = 0; i < mCallDag.size(); i++)
{
- int depth = 0;
+ int depth = 0;
auto &record = mCallDag.getRecordFromIndex(i);
for (auto &calleeIndex : record.callees)
@@ -566,19 +904,19 @@ bool TCompiler::checkCallDepth()
if (depth >= maxCallStackDepth)
{
// Trace back the function chain to have a meaningful info log.
- infoSink.info.prefix(EPrefixError);
- infoSink.info << "Call stack too deep (larger than " << maxCallStackDepth
- << ") with the following call chain: " << record.name;
+ std::stringstream errorStream;
+ errorStream << "Call stack too deep (larger than " << maxCallStackDepth
+ << ") with the following call chain: " << record.name;
int currentFunction = static_cast<int>(i);
- int currentDepth = depth;
+ int currentDepth = depth;
while (currentFunction != -1)
{
- infoSink.info << " -> " << mCallDag.getRecordFromIndex(currentFunction).name;
+ errorStream << " -> " << mCallDag.getRecordFromIndex(currentFunction).name;
int nextFunction = -1;
- for (auto& calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees)
+ for (auto &calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees)
{
if (depths[calleeIndex] == currentDepth - 1)
{
@@ -590,6 +928,9 @@ bool TCompiler::checkCallDepth()
currentFunction = nextFunction;
}
+ std::string errorStr = errorStream.str();
+ mDiagnostics.globalError(errorStr.c_str());
+
return false;
}
}
@@ -602,15 +943,14 @@ bool TCompiler::tagUsedFunctions()
// Search from main, starting from the end of the DAG as it usually is the root.
for (size_t i = mCallDag.size(); i-- > 0;)
{
- if (mCallDag.getRecordFromIndex(i).name == "main(")
+ if (mCallDag.getRecordFromIndex(i).name == "main")
{
internalTagUsedFunction(i);
return true;
}
}
- infoSink.info.prefix(EPrefixError);
- infoSink.info << "Missing main()\n";
+ mDiagnostics.globalError("Missing main()");
return false;
}
@@ -634,30 +974,35 @@ class TCompiler::UnusedPredicate
{
public:
UnusedPredicate(const CallDAG *callDag, const std::vector<FunctionMetadata> *metadatas)
- : mCallDag(callDag),
- mMetadatas(metadatas)
+ : mCallDag(callDag), mMetadatas(metadatas)
{
}
- bool operator ()(TIntermNode *node)
+ bool operator()(TIntermNode *node)
{
- const TIntermAggregate *asAggregate = node->getAsAggregate();
+ const TIntermFunctionPrototype *asFunctionPrototype = node->getAsFunctionPrototypeNode();
+ const TIntermFunctionDefinition *asFunctionDefinition = node->getAsFunctionDefinition();
+
+ const TFunctionSymbolInfo *functionInfo = nullptr;
- if (asAggregate == nullptr)
+ if (asFunctionDefinition)
{
- return false;
+ functionInfo = asFunctionDefinition->getFunctionSymbolInfo();
}
-
- if (!(asAggregate->getOp() == EOpFunction || asAggregate->getOp() == EOpPrototype))
+ else if (asFunctionPrototype)
+ {
+ functionInfo = asFunctionPrototype->getFunctionSymbolInfo();
+ }
+ if (functionInfo == nullptr)
{
return false;
}
- size_t callDagIndex = mCallDag->findIndex(asAggregate);
+ size_t callDagIndex = mCallDag->findIndex(functionInfo);
if (callDagIndex == CallDAG::InvalidIndex)
{
// This happens only for unimplemented prototypes which are thus unused
- ASSERT(asAggregate->getOp() == EOpPrototype);
+ ASSERT(asFunctionPrototype);
return true;
}
@@ -670,157 +1015,97 @@ class TCompiler::UnusedPredicate
const std::vector<FunctionMetadata> *mMetadatas;
};
-bool TCompiler::pruneUnusedFunctions(TIntermNode *root)
+void TCompiler::pruneUnusedFunctions(TIntermBlock *root)
{
- TIntermAggregate *rootNode = root->getAsAggregate();
- ASSERT(rootNode != nullptr);
-
UnusedPredicate isUnused(&mCallDag, &functionMetadata);
- TIntermSequence *sequence = rootNode->getSequence();
+ TIntermSequence *sequence = root->getSequence();
if (!sequence->empty())
{
- sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused), sequence->end());
+ sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused),
+ sequence->end());
}
-
- return true;
-}
-
-bool TCompiler::validateOutputs(TIntermNode* root)
-{
- ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers);
- root->traverse(&validateOutputs);
- return (validateOutputs.validateAndCountErrors(infoSink.info) == 0);
-}
-
-void TCompiler::rewriteCSSShader(TIntermNode* root)
-{
- RenameFunction renamer("main(", "css_main(");
- root->traverse(&renamer);
}
-bool TCompiler::validateLimitations(TIntermNode* root)
+bool TCompiler::limitExpressionComplexity(TIntermBlock *root)
{
- ValidateLimitations validate(shaderType, &infoSink.info);
- root->traverse(&validate);
- return validate.numErrors() == 0;
-}
-
-bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
-{
- if (shaderSpec != SH_WEBGL_SPEC)
+ if (!IsASTDepthBelowLimit(root, maxExpressionComplexity))
{
- infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
+ mDiagnostics.globalError("Expression too complex.");
return false;
}
- if (shaderType == GL_FRAGMENT_SHADER)
- {
- TDependencyGraph graph(root);
-
- // Output any errors first.
- bool success = enforceFragmentShaderTimingRestrictions(graph);
-
- // Then, output the dependency graph.
- if (outputGraph)
- {
- TDependencyGraphOutput output(infoSink.info);
- output.outputAllSpanningTrees(graph);
- }
-
- return success;
- }
- else
- {
- return enforceVertexShaderTimingRestrictions(root);
- }
-}
-
-bool TCompiler::limitExpressionComplexity(TIntermNode* root)
-{
- TMaxDepthTraverser traverser(maxExpressionComplexity+1);
- root->traverse(&traverser);
-
- if (traverser.getMaxDepth() > maxExpressionComplexity)
+ if (!ValidateMaxParameters(root, maxFunctionParameters))
{
- infoSink.info << "Expression too complex.";
+ mDiagnostics.globalError("Function has too many parameters.");
return false;
}
return true;
}
-bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
+bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions)
{
- RestrictFragmentShaderTiming restrictor(infoSink.info);
- restrictor.enforceRestrictions(graph);
- return restrictor.numErrors() == 0;
+ return (compileOptions & SH_VARIABLES) != 0;
}
-bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
+bool TCompiler::wereVariablesCollected() const
{
- RestrictVertexShaderTiming restrictor(infoSink.info);
- restrictor.enforceRestrictions(root);
- return restrictor.numErrors() == 0;
+ return variablesCollected;
}
-void TCompiler::collectVariables(TIntermNode* root)
+void TCompiler::initializeGLPosition(TIntermBlock *root)
{
- sh::CollectVariables collect(&attributes,
- &outputVariables,
- &uniforms,
- &varyings,
- &interfaceBlocks,
- hashFunction,
- symbolTable);
- root->traverse(&collect);
-
- // This is for enforcePackingRestriction().
- sh::ExpandUniforms(uniforms, &expandedUniforms);
+ InitVariableList list;
+ sh::ShaderVariable var(GL_FLOAT_VEC4);
+ var.name = "gl_Position";
+ list.push_back(var);
+ InitializeVariables(root, list, &symbolTable, shaderVersion, extensionBehavior, false);
}
-bool TCompiler::enforcePackingRestrictions()
+void TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermBlock *root)
{
- VariablePacker packer;
- return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, expandedUniforms);
-}
+ sh::InterfaceBlockList list;
-void TCompiler::initializeGLPosition(TIntermNode* root)
-{
- InitializeVariables::InitVariableInfoList variables;
- InitializeVariables::InitVariableInfo var(
- "gl_Position", TType(EbtFloat, EbpUndefined, EvqPosition, 4));
- variables.push_back(var);
- InitializeVariables initializer(variables);
- root->traverse(&initializer);
+ for (auto block : uniformBlocks)
+ {
+ if (!block.staticUse &&
+ (block.layout == sh::BLOCKLAYOUT_STD140 || block.layout == sh::BLOCKLAYOUT_SHARED))
+ {
+ list.push_back(block);
+ }
+ }
+
+ sh::UseInterfaceBlockFields(root, list, symbolTable);
}
-void TCompiler::initializeVaryingsWithoutStaticUse(TIntermNode* root)
+void TCompiler::initializeOutputVariables(TIntermBlock *root)
{
- InitializeVariables::InitVariableInfoList variables;
- for (size_t ii = 0; ii < varyings.size(); ++ii)
+ InitVariableList list;
+ if (shaderType == GL_VERTEX_SHADER || shaderType == GL_GEOMETRY_SHADER_OES)
{
- const sh::Varying& varying = varyings[ii];
- if (varying.staticUse)
- continue;
- unsigned char primarySize = static_cast<unsigned char>(gl::VariableColumnCount(varying.type));
- unsigned char secondarySize = static_cast<unsigned char>(gl::VariableRowCount(varying.type));
- TType type(EbtFloat, EbpUndefined, EvqVaryingOut, primarySize, secondarySize, varying.isArray());
- TString name = varying.name.c_str();
- if (varying.isArray())
+ for (auto var : outputVaryings)
{
- type.setArraySize(varying.arraySize);
- name = name.substr(0, name.find_first_of('['));
+ list.push_back(var);
+ if (var.name == "gl_Position")
+ {
+ ASSERT(!mGLPositionInitialized);
+ mGLPositionInitialized = true;
+ }
}
-
- InitializeVariables::InitVariableInfo var(name, type);
- variables.push_back(var);
}
- InitializeVariables initializer(variables);
- root->traverse(&initializer);
+ else
+ {
+ ASSERT(shaderType == GL_FRAGMENT_SHADER);
+ for (auto var : outputVariables)
+ {
+ list.push_back(var);
+ }
+ }
+ InitializeVariables(root, list, &symbolTable, shaderVersion, extensionBehavior, false);
}
-const TExtensionBehavior& TCompiler::getExtensionBehavior() const
+const TExtensionBehavior &TCompiler::getExtensionBehavior() const
{
return extensionBehavior;
}
@@ -830,12 +1115,12 @@ const char *TCompiler::getSourcePath() const
return mSourcePath;
}
-const ShBuiltInResources& TCompiler::getResources() const
+const ShBuiltInResources &TCompiler::getResources() const
{
return compileResources;
}
-const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
+const ArrayBoundsClamper &TCompiler::getArrayBoundsClamper() const
{
return arrayBoundsClamper;
}
@@ -845,14 +1130,40 @@ ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
return clampingStrategy;
}
-const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
+const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const
{
return builtInFunctionEmulator;
}
-void TCompiler::writePragma()
+void TCompiler::writePragma(ShCompileOptions compileOptions)
+{
+ if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL))
+ {
+ TInfoSinkBase &sink = infoSink.obj;
+ if (mPragma.stdgl.invariantAll)
+ sink << "#pragma STDGL invariant(all)\n";
+ }
+}
+
+bool TCompiler::isVaryingDefined(const char *varyingName)
{
- TInfoSinkBase &sink = infoSink.obj;
- if (mPragma.stdgl.invariantAll)
- sink << "#pragma STDGL invariant(all)\n";
+ ASSERT(variablesCollected);
+ for (size_t ii = 0; ii < inputVaryings.size(); ++ii)
+ {
+ if (inputVaryings[ii].name == varyingName)
+ {
+ return true;
+ }
+ }
+ for (size_t ii = 0; ii < outputVaryings.size(); ++ii)
+ {
+ if (outputVaryings[ii].name == varyingName)
+ {
+ return true;
+ }
+ }
+
+ return false;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/Compiler.h b/src/3rdparty/angle/src/compiler/translator/Compiler.h
index c00a8f97aa..753cfb8e7b 100644
--- a/src/3rdparty/angle/src/compiler/translator/Compiler.h
+++ b/src/3rdparty/angle/src/compiler/translator/Compiler.h
@@ -14,25 +14,29 @@
// This should not be included by driver code.
//
+#include <GLSLANG/ShaderVars.h>
+
#include "compiler/translator/BuiltInFunctionEmulator.h"
#include "compiler/translator/CallDAG.h"
+#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/HashNames.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/Pragma.h"
#include "compiler/translator/SymbolTable.h"
-#include "compiler/translator/VariableInfo.h"
#include "third_party/compiler/ArrayBoundsClamper.h"
+namespace sh
+{
+
class TCompiler;
-class TDependencyGraph;
+class TParseContext;
#ifdef ANGLE_ENABLE_HLSL
class TranslatorHLSL;
-#endif // ANGLE_ENABLE_HLSL
+#endif // ANGLE_ENABLE_HLSL
//
-// Helper function to identify specs that are based on the WebGL spec,
-// like the CSS Shaders spec.
+// Helper function to identify specs that are based on the WebGL spec.
//
bool IsWebGLBasedSpec(ShShaderSpec spec);
@@ -40,20 +44,31 @@ bool IsWebGLBasedSpec(ShShaderSpec spec);
// Helper function to check if the shader type is GLSL.
//
bool IsGLSL130OrNewer(ShShaderOutput output);
+bool IsGLSL420OrNewer(ShShaderOutput output);
+bool IsGLSL410OrOlder(ShShaderOutput output);
+
+//
+// Helper function to check if the invariant qualifier can be removed.
+//
+bool RemoveInvariant(sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput outputType,
+ ShCompileOptions compileOptions);
//
// The base class used to back handles returned to the driver.
//
-class TShHandleBase {
-public:
+class TShHandleBase
+{
+ public:
TShHandleBase();
virtual ~TShHandleBase();
- virtual TCompiler* getAsCompiler() { return 0; }
+ virtual TCompiler *getAsCompiler() { return 0; }
#ifdef ANGLE_ENABLE_HLSL
- virtual TranslatorHLSL* getAsTranslatorHLSL() { return 0; }
-#endif // ANGLE_ENABLE_HLSL
+ virtual TranslatorHLSL *getAsTranslatorHLSL() { return 0; }
+#endif // ANGLE_ENABLE_HLSL
-protected:
+ protected:
// Memory allocator. Allocates and tracks memory required by the compiler.
// Deallocates all memory when compiler is destructed.
TPoolAllocator allocator;
@@ -70,20 +85,26 @@ class TCompiler : public TShHandleBase
~TCompiler() override;
TCompiler *getAsCompiler() override { return this; }
- bool Init(const ShBuiltInResources& resources);
+ bool Init(const ShBuiltInResources &resources);
// compileTreeForTesting should be used only when tests require access to
// the AST. Users of this function need to manually manage the global pool
- // allocator. Returns NULL whenever there are compilation errors.
- TIntermNode *compileTreeForTesting(const char* const shaderStrings[],
- size_t numStrings, int compileOptions);
+ // allocator. Returns nullptr whenever there are compilation errors.
+ TIntermBlock *compileTreeForTesting(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions);
- bool compile(const char* const shaderStrings[],
- size_t numStrings, int compileOptions);
+ bool compile(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions);
// Get results of the last compilation.
int getShaderVersion() const { return shaderVersion; }
- TInfoSink& getInfoSink() { return infoSink; }
+ TInfoSink &getInfoSink() { return infoSink; }
+
+ bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+ const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mComputeShaderLocalSize; }
+ int getNumViews() const { return mNumViews; }
// Clears the results from the previous compilation.
void clearResults();
@@ -91,86 +112,94 @@ class TCompiler : public TShHandleBase
const std::vector<sh::Attribute> &getAttributes() const { return attributes; }
const std::vector<sh::OutputVariable> &getOutputVariables() const { return outputVariables; }
const std::vector<sh::Uniform> &getUniforms() const { return uniforms; }
- const std::vector<sh::Varying> &getVaryings() const { return varyings; }
+ const std::vector<sh::Varying> &getInputVaryings() const { return inputVaryings; }
+ const std::vector<sh::Varying> &getOutputVaryings() const { return outputVaryings; }
const std::vector<sh::InterfaceBlock> &getInterfaceBlocks() const { return interfaceBlocks; }
+ const std::vector<sh::InterfaceBlock> &getUniformBlocks() const { return uniformBlocks; }
+ const std::vector<sh::InterfaceBlock> &getShaderStorageBlocks() const
+ {
+ return shaderStorageBlocks;
+ }
+ const std::vector<sh::InterfaceBlock> &getInBlocks() const { return inBlocks; }
ShHashFunction64 getHashFunction() const { return hashFunction; }
- NameMap& getNameMap() { return nameMap; }
- TSymbolTable& getSymbolTable() { return symbolTable; }
+ NameMap &getNameMap() { return nameMap; }
+ TSymbolTable &getSymbolTable() { return symbolTable; }
ShShaderSpec getShaderSpec() const { return shaderSpec; }
ShShaderOutput getOutputType() const { return outputType; }
const std::string &getBuiltInResourcesString() const { return builtInResourcesString; }
- bool shouldRunLoopAndIndexingValidation(int compileOptions) const;
+ bool shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const;
// Get the resources set by InitBuiltInSymbolTable
- const ShBuiltInResources& getResources() const;
+ const ShBuiltInResources &getResources() const;
+
+ int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
+ int getGeometryShaderInvocations() const { return mGeometryShaderInvocations; }
+ TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
+ {
+ return mGeometryShaderInputPrimitiveType;
+ }
+ TLayoutPrimitiveType getGeometryShaderOutputPrimitiveType() const
+ {
+ return mGeometryShaderOutputPrimitiveType;
+ }
- protected:
sh::GLenum getShaderType() const { return shaderType; }
+
+ protected:
// Initialize symbol-table with built-in symbols.
- bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
+ bool InitBuiltInSymbolTable(const ShBuiltInResources &resources);
// Compute the string representation of the built-in resources
void setResourceString();
// Return false if the call depth is exceeded.
bool checkCallDepth();
- // Returns true if a program has no conflicting or missing fragment outputs
- bool validateOutputs(TIntermNode* root);
- // Rewrites a shader's intermediate tree according to the CSS Shaders spec.
- void rewriteCSSShader(TIntermNode* root);
- // Returns true if the given shader does not exceed the minimum
- // functionality mandated in GLSL 1.0 spec Appendix A.
- bool validateLimitations(TIntermNode* root);
- // Collect info for all attribs, uniforms, varyings.
- void collectVariables(TIntermNode* root);
// Add emulated functions to the built-in function emulator.
- virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) {};
- // Translate to object code.
- virtual void translate(TIntermNode *root, int compileOptions) = 0;
- // Returns true if, after applying the packing rules in the GLSL 1.017 spec
- // Appendix A, section 7, the shader does not use too many uniforms.
- bool enforcePackingRestrictions();
- // Insert statements to initialize varyings without static use in the beginning
- // of main(). It is to work around a Mac driver where such varyings in a vertex
- // shader may be optimized out incorrectly at compile time, causing a link failure.
- // This function should only be applied to vertex shaders.
- void initializeVaryingsWithoutStaticUse(TIntermNode* root);
+ virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions){};
+ // Translate to object code. May generate performance warnings through the diagnostics.
+ virtual void translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics *perfDiagnostics) = 0;
+ // Insert statements to reference all members in unused uniform blocks with standard and shared
+ // layout. This is to work around a Mac driver that treats unused standard/shared
+ // uniform blocks as inactive.
+ void useAllMembersInUnusedStandardAndSharedBlocks(TIntermBlock *root);
+ // Insert statements to initialize output variables in the beginning of main().
+ // This is to avoid undefined behaviors.
+ void initializeOutputVariables(TIntermBlock *root);
// Insert gl_Position = vec4(0,0,0,0) to the beginning of main().
// It is to work around a Linux driver bug where missing this causes compile failure
// while spec says it is allowed.
// This function should only be applied to vertex shaders.
- void initializeGLPosition(TIntermNode* root);
- // Returns true if the shader passes the restrictions that aim to prevent timing attacks.
- bool enforceTimingRestrictions(TIntermNode* root, bool outputGraph);
- // Returns true if the shader does not use samplers.
- bool enforceVertexShaderTimingRestrictions(TIntermNode* root);
- // Returns true if the shader does not use sampler dependent values to affect control
- // flow or in operations whose time can depend on the input values.
- bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph);
+ void initializeGLPosition(TIntermBlock *root);
// Return true if the maximum expression complexity is below the limit.
- bool limitExpressionComplexity(TIntermNode* root);
+ bool limitExpressionComplexity(TIntermBlock *root);
// Get built-in extensions with default behavior.
- const TExtensionBehavior& getExtensionBehavior() const;
+ const TExtensionBehavior &getExtensionBehavior() const;
const char *getSourcePath() const;
- const TPragma& getPragma() const { return mPragma; }
- void writePragma();
- unsigned int *getTemporaryIndex() { return &mTemporaryIndex; }
+ const TPragma &getPragma() const { return mPragma; }
+ void writePragma(ShCompileOptions compileOptions);
+ // Relies on collectVariables having been called.
+ bool isVaryingDefined(const char *varyingName);
- const ArrayBoundsClamper& getArrayBoundsClamper() const;
+ const ArrayBoundsClamper &getArrayBoundsClamper() const;
ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const;
- const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
+ const BuiltInFunctionEmulator &getBuiltInFunctionEmulator() const;
+ virtual bool shouldFlattenPragmaStdglInvariantAll() = 0;
+ virtual bool shouldCollectVariables(ShCompileOptions compileOptions);
+
+ bool wereVariablesCollected() const;
std::vector<sh::Attribute> attributes;
std::vector<sh::OutputVariable> outputVariables;
std::vector<sh::Uniform> uniforms;
- std::vector<sh::ShaderVariable> expandedUniforms;
- std::vector<sh::Varying> varyings;
+ std::vector<sh::Varying> inputVaryings;
+ std::vector<sh::Varying> outputVaryings;
std::vector<sh::InterfaceBlock> interfaceBlocks;
-
- virtual bool shouldCollectVariables(int compileOptions)
- {
- return (compileOptions & SH_VARIABLES) != 0;
- }
+ std::vector<sh::InterfaceBlock> uniformBlocks;
+ std::vector<sh::InterfaceBlock> shaderStorageBlocks;
+ std::vector<sh::InterfaceBlock> inBlocks;
private:
// Creates the function call DAG for further analysis, returning false if there is a recursion
@@ -181,13 +210,28 @@ class TCompiler : public TShHandleBase
void initSamplerDefaultPrecision(TBasicType samplerType);
+ void collectInterfaceBlocks();
+
+ bool variablesCollected;
+
+ bool mGLPositionInitialized;
+
// Removes unused function declarations and prototypes from the AST
class UnusedPredicate;
- bool pruneUnusedFunctions(TIntermNode *root);
+ void pruneUnusedFunctions(TIntermBlock *root);
+
+ TIntermBlock *compileTreeImpl(const char *const shaderStrings[],
+ size_t numStrings,
+ const ShCompileOptions compileOptions);
- TIntermNode *compileTreeImpl(const char *const shaderStrings[],
- size_t numStrings,
- const int compileOptions);
+ // Fetches and stores shader metadata that is not stored within the AST itself, such as shader
+ // version.
+ void setASTMetadata(const TParseContext &parseContext);
+
+ // Does checks that need to be run after parsing is complete and returns true if they pass.
+ bool checkAndSimplifyAST(TIntermBlock *root,
+ const TParseContext &parseContext,
+ ShCompileOptions compileOptions);
sh::GLenum shaderType;
ShShaderSpec shaderSpec;
@@ -195,10 +239,7 @@ class TCompiler : public TShHandleBase
struct FunctionMetadata
{
- FunctionMetadata()
- : used(false)
- {
- }
+ FunctionMetadata() : used(false) {}
bool used;
};
@@ -208,6 +249,7 @@ class TCompiler : public TShHandleBase
int maxUniformVectors;
int maxExpressionComplexity;
int maxCallStackDepth;
+ int maxFunctionParameters;
ShBuiltInResources compileResources;
std::string builtInResourcesString;
@@ -225,16 +267,28 @@ class TCompiler : public TShHandleBase
// Results of compilation.
int shaderVersion;
- TInfoSink infoSink; // Output sink.
- const char *mSourcePath; // Path of source file or NULL
+ TInfoSink infoSink; // Output sink.
+ TDiagnostics mDiagnostics;
+ const char *mSourcePath; // Path of source file or NULL
+
+ // compute shader local group size
+ bool mComputeShaderLocalSizeDeclared;
+ sh::WorkGroupSize mComputeShaderLocalSize;
+
+ // GL_OVR_multiview num_views.
+ int mNumViews;
+
+ // geometry shader parameters.
+ int mGeometryShaderMaxVertices;
+ int mGeometryShaderInvocations;
+ TLayoutPrimitiveType mGeometryShaderInputPrimitiveType;
+ TLayoutPrimitiveType mGeometryShaderOutputPrimitiveType;
// name hashing.
ShHashFunction64 hashFunction;
NameMap nameMap;
TPragma mPragma;
-
- unsigned int mTemporaryIndex;
};
//
@@ -246,8 +300,9 @@ class TCompiler : public TShHandleBase
// destroy the machine dependent objects, which contain the
// above machine independent information.
//
-TCompiler* ConstructCompiler(
- sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
-void DeleteCompiler(TCompiler*);
+TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
+void DeleteCompiler(TCompiler *);
+
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_COMPILER_H_
+#endif // COMPILER_TRANSLATOR_COMPILER_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ConstantUnion.cpp b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.cpp
new file mode 100644
index 0000000000..7004aedee3
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.cpp
@@ -0,0 +1,681 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ConstantUnion: Constant folding helper class.
+
+#include "compiler/translator/ConstantUnion.h"
+
+#include "common/mathutil.h"
+#include "compiler/translator/Diagnostics.h"
+
+namespace sh
+{
+
+namespace
+{
+
+float CheckedSum(float lhs, float rhs, TDiagnostics *diag, const TSourceLoc &line)
+{
+ float result = lhs + rhs;
+ if (gl::isNaN(result) && !gl::isNaN(lhs) && !gl::isNaN(rhs))
+ {
+ diag->warning(line, "Constant folded undefined addition generated NaN", "+");
+ }
+ else if (gl::isInf(result) && !gl::isInf(lhs) && !gl::isInf(rhs))
+ {
+ diag->warning(line, "Constant folded addition overflowed to infinity", "+");
+ }
+ return result;
+}
+
+float CheckedDiff(float lhs, float rhs, TDiagnostics *diag, const TSourceLoc &line)
+{
+ float result = lhs - rhs;
+ if (gl::isNaN(result) && !gl::isNaN(lhs) && !gl::isNaN(rhs))
+ {
+ diag->warning(line, "Constant folded undefined subtraction generated NaN", "-");
+ }
+ else if (gl::isInf(result) && !gl::isInf(lhs) && !gl::isInf(rhs))
+ {
+ diag->warning(line, "Constant folded subtraction overflowed to infinity", "-");
+ }
+ return result;
+}
+
+float CheckedMul(float lhs, float rhs, TDiagnostics *diag, const TSourceLoc &line)
+{
+ float result = lhs * rhs;
+ if (gl::isNaN(result) && !gl::isNaN(lhs) && !gl::isNaN(rhs))
+ {
+ diag->warning(line, "Constant folded undefined multiplication generated NaN", "*");
+ }
+ else if (gl::isInf(result) && !gl::isInf(lhs) && !gl::isInf(rhs))
+ {
+ diag->warning(line, "Constant folded multiplication overflowed to infinity", "*");
+ }
+ return result;
+}
+
+bool IsValidShiftOffset(const TConstantUnion &rhs)
+{
+ return (rhs.getType() == EbtInt && (rhs.getIConst() >= 0 && rhs.getIConst() <= 31)) ||
+ (rhs.getType() == EbtUInt && rhs.getUConst() <= 31u);
+}
+
+} // anonymous namespace
+
+TConstantUnion::TConstantUnion()
+{
+ iConst = 0;
+ type = EbtVoid;
+}
+
+int TConstantUnion::getIConst() const
+{
+ ASSERT(type == EbtInt);
+ return iConst;
+}
+
+unsigned int TConstantUnion::getUConst() const
+{
+ ASSERT(type == EbtUInt);
+ return uConst;
+}
+
+float TConstantUnion::getFConst() const
+{
+ ASSERT(type == EbtFloat);
+ return fConst;
+}
+
+bool TConstantUnion::getBConst() const
+{
+ ASSERT(type == EbtBool);
+ return bConst;
+}
+
+TYuvCscStandardEXT TConstantUnion::getYuvCscStandardEXTConst() const
+{
+ ASSERT(type == EbtYuvCscStandardEXT);
+ return yuvCscStandardEXTConst;
+}
+
+bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
+{
+ switch (newType)
+ {
+ case EbtFloat:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setFConst(static_cast<float>(constant.getIConst()));
+ break;
+ case EbtUInt:
+ setFConst(static_cast<float>(constant.getUConst()));
+ break;
+ case EbtBool:
+ setFConst(static_cast<float>(constant.getBConst()));
+ break;
+ case EbtFloat:
+ setFConst(static_cast<float>(constant.getFConst()));
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtInt:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setIConst(static_cast<int>(constant.getIConst()));
+ break;
+ case EbtUInt:
+ setIConst(static_cast<int>(constant.getUConst()));
+ break;
+ case EbtBool:
+ setIConst(static_cast<int>(constant.getBConst()));
+ break;
+ case EbtFloat:
+ setIConst(static_cast<int>(constant.getFConst()));
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtUInt:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setUConst(static_cast<unsigned int>(constant.getIConst()));
+ break;
+ case EbtUInt:
+ setUConst(static_cast<unsigned int>(constant.getUConst()));
+ break;
+ case EbtBool:
+ setUConst(static_cast<unsigned int>(constant.getBConst()));
+ break;
+ case EbtFloat:
+ setUConst(static_cast<unsigned int>(constant.getFConst()));
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtBool:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setBConst(constant.getIConst() != 0);
+ break;
+ case EbtUInt:
+ setBConst(constant.getUConst() != 0);
+ break;
+ case EbtBool:
+ setBConst(constant.getBConst());
+ break;
+ case EbtFloat:
+ setBConst(constant.getFConst() != 0.0f);
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtStruct: // Struct fields don't get cast
+ switch (constant.type)
+ {
+ case EbtInt:
+ setIConst(constant.getIConst());
+ break;
+ case EbtUInt:
+ setUConst(constant.getUConst());
+ break;
+ case EbtBool:
+ setBConst(constant.getBConst());
+ break;
+ case EbtFloat:
+ setFConst(constant.getFConst());
+ break;
+ default:
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool TConstantUnion::operator==(const int i) const
+{
+ return i == iConst;
+}
+
+bool TConstantUnion::operator==(const unsigned int u) const
+{
+ return u == uConst;
+}
+
+bool TConstantUnion::operator==(const float f) const
+{
+ return f == fConst;
+}
+
+bool TConstantUnion::operator==(const bool b) const
+{
+ return b == bConst;
+}
+
+bool TConstantUnion::operator==(const TYuvCscStandardEXT s) const
+{
+ return s == yuvCscStandardEXTConst;
+}
+
+bool TConstantUnion::operator==(const TConstantUnion &constant) const
+{
+ if (constant.type != type)
+ return false;
+
+ switch (type)
+ {
+ case EbtInt:
+ return constant.iConst == iConst;
+ case EbtUInt:
+ return constant.uConst == uConst;
+ case EbtFloat:
+ return constant.fConst == fConst;
+ case EbtBool:
+ return constant.bConst == bConst;
+ case EbtYuvCscStandardEXT:
+ return constant.yuvCscStandardEXTConst == yuvCscStandardEXTConst;
+ default:
+ return false;
+ }
+}
+
+bool TConstantUnion::operator!=(const int i) const
+{
+ return !operator==(i);
+}
+
+bool TConstantUnion::operator!=(const unsigned int u) const
+{
+ return !operator==(u);
+}
+
+bool TConstantUnion::operator!=(const float f) const
+{
+ return !operator==(f);
+}
+
+bool TConstantUnion::operator!=(const bool b) const
+{
+ return !operator==(b);
+}
+
+bool TConstantUnion::operator!=(const TYuvCscStandardEXT s) const
+{
+ return !operator==(s);
+}
+
+bool TConstantUnion::operator!=(const TConstantUnion &constant) const
+{
+ return !operator==(constant);
+}
+
+bool TConstantUnion::operator>(const TConstantUnion &constant) const
+{
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ return iConst > constant.iConst;
+ case EbtUInt:
+ return uConst > constant.uConst;
+ case EbtFloat:
+ return fConst > constant.fConst;
+ default:
+ return false; // Invalid operation, handled at semantic analysis
+ }
+}
+
+bool TConstantUnion::operator<(const TConstantUnion &constant) const
+{
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ return iConst < constant.iConst;
+ case EbtUInt:
+ return uConst < constant.uConst;
+ case EbtFloat:
+ return fConst < constant.fConst;
+ default:
+ return false; // Invalid operation, handled at semantic analysis
+ }
+}
+
+// static
+TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == rhs.type);
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(gl::WrappingSum<int>(lhs.iConst, rhs.iConst));
+ break;
+ case EbtUInt:
+ returnValue.setUConst(gl::WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
+ break;
+ case EbtFloat:
+ returnValue.setFConst(CheckedSum(lhs.fConst, rhs.fConst, diag, line));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == rhs.type);
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(gl::WrappingDiff<int>(lhs.iConst, rhs.iConst));
+ break;
+ case EbtUInt:
+ returnValue.setUConst(gl::WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
+ break;
+ case EbtFloat:
+ returnValue.setFConst(CheckedDiff(lhs.fConst, rhs.fConst, diag, line));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == rhs.type);
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(gl::WrappingMul(lhs.iConst, rhs.iConst));
+ break;
+ case EbtUInt:
+ // Unsigned integer math in C++ is defined to be done in modulo 2^n, so we rely on that
+ // to implement wrapping multiplication.
+ returnValue.setUConst(lhs.uConst * rhs.uConst);
+ break;
+ case EbtFloat:
+ returnValue.setFConst(CheckedMul(lhs.fConst, rhs.fConst, diag, line));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst % constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst % constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::rshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
+ ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
+ if (!IsValidShiftOffset(rhs))
+ {
+ diag->warning(line, "Undefined shift (operand out of range)", ">>");
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(0);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(0u);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+ }
+
+ switch (lhs.type)
+ {
+ case EbtInt:
+ {
+ unsigned int shiftOffset = 0;
+ switch (rhs.type)
+ {
+ case EbtInt:
+ shiftOffset = static_cast<unsigned int>(rhs.iConst);
+ break;
+ case EbtUInt:
+ shiftOffset = rhs.uConst;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (shiftOffset > 0)
+ {
+ // ESSL 3.00.6 section 5.9: "If E1 is a signed integer, the right-shift will extend
+ // the sign bit." In C++ shifting negative integers is undefined, so we implement
+ // extending the sign bit manually.
+ int lhsSafe = lhs.iConst;
+ if (lhsSafe == std::numeric_limits<int>::min())
+ {
+ // The min integer needs special treatment because only bit it has set is the
+ // sign bit, which we clear later to implement safe right shift of negative
+ // numbers.
+ lhsSafe = -0x40000000;
+ --shiftOffset;
+ }
+ if (shiftOffset > 0)
+ {
+ bool extendSignBit = false;
+ if (lhsSafe < 0)
+ {
+ extendSignBit = true;
+ // Clear the sign bit so that bitshift right is defined in C++.
+ lhsSafe &= 0x7fffffff;
+ ASSERT(lhsSafe > 0);
+ }
+ returnValue.setIConst(lhsSafe >> shiftOffset);
+
+ // Manually fill in the extended sign bit if necessary.
+ if (extendSignBit)
+ {
+ int extendedSignBit = static_cast<int>(0xffffffffu << (31 - shiftOffset));
+ returnValue.setIConst(returnValue.getIConst() | extendedSignBit);
+ }
+ }
+ else
+ {
+ returnValue.setIConst(lhsSafe);
+ }
+ }
+ else
+ {
+ returnValue.setIConst(lhs.iConst);
+ }
+ break;
+ }
+ case EbtUInt:
+ switch (rhs.type)
+ {
+ case EbtInt:
+ returnValue.setUConst(lhs.uConst >> rhs.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(lhs.uConst >> rhs.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::lshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
+ ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
+ if (!IsValidShiftOffset(rhs))
+ {
+ diag->warning(line, "Undefined shift (operand out of range)", "<<");
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(0);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(0u);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+ }
+
+ switch (lhs.type)
+ {
+ case EbtInt:
+ switch (rhs.type)
+ {
+ // Cast to unsigned integer before shifting, since ESSL 3.00.6 section 5.9 says that
+ // lhs is "interpreted as a bit pattern". This also avoids the possibility of signed
+ // integer overflow or undefined shift of a negative integer.
+ case EbtInt:
+ returnValue.setIConst(
+ static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.iConst));
+ break;
+ case EbtUInt:
+ returnValue.setIConst(
+ static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.uConst));
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+
+ case EbtUInt:
+ switch (rhs.type)
+ {
+ case EbtInt:
+ returnValue.setUConst(lhs.uConst << rhs.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(lhs.uConst << rhs.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst & constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst & constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst | constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst | constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst ^ constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst ^ constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtBool:
+ returnValue.setBConst(bConst && constant.bConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtBool:
+ returnValue.setBConst(bConst || constant.bConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h
index a86d27f3ff..c1b3db9070 100644
--- a/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h
+++ b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h
@@ -9,340 +9,109 @@
#include <assert.h>
+#include "compiler/translator/Common.h"
#include "compiler/translator/BaseTypes.h"
-class TConstantUnion {
-public:
- POOL_ALLOCATOR_NEW_DELETE();
- TConstantUnion()
- {
- iConst = 0;
- type = EbtVoid;
- }
-
- bool cast(TBasicType newType, const TConstantUnion &constant)
- {
- switch (newType)
- {
- case EbtFloat:
- switch (constant.type)
- {
- case EbtInt: setFConst(static_cast<float>(constant.getIConst())); break;
- case EbtUInt: setFConst(static_cast<float>(constant.getUConst())); break;
- case EbtBool: setFConst(static_cast<float>(constant.getBConst())); break;
- case EbtFloat: setFConst(static_cast<float>(constant.getFConst())); break;
- default: return false;
- }
- break;
- case EbtInt:
- switch (constant.type)
- {
- case EbtInt: setIConst(static_cast<int>(constant.getIConst())); break;
- case EbtUInt: setIConst(static_cast<int>(constant.getUConst())); break;
- case EbtBool: setIConst(static_cast<int>(constant.getBConst())); break;
- case EbtFloat: setIConst(static_cast<int>(constant.getFConst())); break;
- default: return false;
- }
- break;
- case EbtUInt:
- switch (constant.type)
- {
- case EbtInt: setUConst(static_cast<unsigned int>(constant.getIConst())); break;
- case EbtUInt: setUConst(static_cast<unsigned int>(constant.getUConst())); break;
- case EbtBool: setUConst(static_cast<unsigned int>(constant.getBConst())); break;
- case EbtFloat: setUConst(static_cast<unsigned int>(constant.getFConst())); break;
- default: return false;
- }
- break;
- case EbtBool:
- switch (constant.type)
- {
- case EbtInt: setBConst(constant.getIConst() != 0); break;
- case EbtUInt: setBConst(constant.getUConst() != 0); break;
- case EbtBool: setBConst(constant.getBConst()); break;
- case EbtFloat: setBConst(constant.getFConst() != 0.0f); break;
- default: return false;
- }
- break;
- case EbtStruct: // Struct fields don't get cast
- switch (constant.type)
- {
- case EbtInt: setIConst(constant.getIConst()); break;
- case EbtUInt: setUConst(constant.getUConst()); break;
- case EbtBool: setBConst(constant.getBConst()); break;
- case EbtFloat: setFConst(constant.getFConst()); break;
- default: return false;
- }
- break;
- default:
- return false;
- }
-
- return true;
- }
-
- void setIConst(int i) {iConst = i; type = EbtInt; }
- void setUConst(unsigned int u) { uConst = u; type = EbtUInt; }
- void setFConst(float f) {fConst = f; type = EbtFloat; }
- void setBConst(bool b) {bConst = b; type = EbtBool; }
-
- int getIConst() const { return iConst; }
- unsigned int getUConst() const { return uConst; }
- float getFConst() const { return fConst; }
- bool getBConst() const { return bConst; }
+namespace sh
+{
- bool operator==(const int i) const
- {
- return i == iConst;
- }
-
- bool operator==(const unsigned int u) const
- {
- return u == uConst;
- }
-
- bool operator==(const float f) const
- {
- return f == fConst;
- }
+class TDiagnostics;
- bool operator==(const bool b) const
- {
- return b == bConst;
- }
-
- bool operator==(const TConstantUnion& constant) const
- {
- if (constant.type != type)
- return false;
+class TConstantUnion
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TConstantUnion();
- switch (type) {
- case EbtInt:
- return constant.iConst == iConst;
- case EbtUInt:
- return constant.uConst == uConst;
- case EbtFloat:
- return constant.fConst == fConst;
- case EbtBool:
- return constant.bConst == bConst;
- default:
- return false;
- }
- }
+ bool cast(TBasicType newType, const TConstantUnion &constant);
- bool operator!=(const int i) const
+ void setIConst(int i)
{
- return !operator==(i);
+ iConst = i;
+ type = EbtInt;
}
-
- bool operator!=(const unsigned int u) const
+ void setUConst(unsigned int u)
{
- return !operator==(u);
+ uConst = u;
+ type = EbtUInt;
}
-
- bool operator!=(const float f) const
+ void setFConst(float f)
{
- return !operator==(f);
+ fConst = f;
+ type = EbtFloat;
}
-
- bool operator!=(const bool b) const
+ void setBConst(bool b)
{
- return !operator==(b);
+ bConst = b;
+ type = EbtBool;
}
- bool operator!=(const TConstantUnion& constant) const
+ void setYuvCscStandardEXTConst(TYuvCscStandardEXT s)
{
- return !operator==(constant);
- }
-
- bool operator>(const TConstantUnion& constant) const
- {
- assert(type == constant.type);
- switch (type) {
- case EbtInt:
- return iConst > constant.iConst;
- case EbtUInt:
- return uConst > constant.uConst;
- case EbtFloat:
- return fConst > constant.fConst;
- default:
- return false; // Invalid operation, handled at semantic analysis
- }
- }
-
- bool operator<(const TConstantUnion& constant) const
- {
- assert(type == constant.type);
- switch (type) {
- case EbtInt:
- return iConst < constant.iConst;
- case EbtUInt:
- return uConst < constant.uConst;
- case EbtFloat:
- return fConst < constant.fConst;
- default:
- return false; // Invalid operation, handled at semantic analysis
- }
- }
-
- TConstantUnion operator+(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst + constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst + constant.uConst); break;
- case EbtFloat: returnValue.setFConst(fConst + constant.fConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator-(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst - constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst - constant.uConst); break;
- case EbtFloat: returnValue.setFConst(fConst - constant.fConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator*(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst * constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst * constant.uConst); break;
- case EbtFloat: returnValue.setFConst(fConst * constant.fConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator%(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst % constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst % constant.uConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator>>(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst >> constant.uConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator<<(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- // The signedness of the second parameter might be different, but we
- // don't care, since the result is undefined if the second parameter is
- // negative, and aliasing should not be a problem with unions.
- assert(constant.type == EbtInt || constant.type == EbtUInt);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst << constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst << constant.uConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator&(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(constant.type == EbtInt || constant.type == EbtUInt);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst & constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst & constant.uConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator|(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst | constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst | constant.uConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator^(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break;
- case EbtUInt: returnValue.setUConst(uConst ^ constant.uConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator&&(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtBool: returnValue.setBConst(bConst && constant.bConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
-
- TConstantUnion operator||(const TConstantUnion& constant) const
- {
- TConstantUnion returnValue;
- assert(type == constant.type);
- switch (type) {
- case EbtBool: returnValue.setBConst(bConst || constant.bConst); break;
- default: assert(false && "Default missing");
- }
-
- return returnValue;
- }
+ yuvCscStandardEXTConst = s;
+ type = EbtYuvCscStandardEXT;
+ }
+
+ int getIConst() const;
+ unsigned int getUConst() const;
+ float getFConst() const;
+ bool getBConst() const;
+ TYuvCscStandardEXT getYuvCscStandardEXTConst() const;
+
+ bool operator==(const int i) const;
+ bool operator==(const unsigned int u) const;
+ bool operator==(const float f) const;
+ bool operator==(const bool b) const;
+ bool operator==(const TYuvCscStandardEXT s) const;
+ bool operator==(const TConstantUnion &constant) const;
+ bool operator!=(const int i) const;
+ bool operator!=(const unsigned int u) const;
+ bool operator!=(const float f) const;
+ bool operator!=(const bool b) const;
+ bool operator!=(const TYuvCscStandardEXT s) const;
+ bool operator!=(const TConstantUnion &constant) const;
+ bool operator>(const TConstantUnion &constant) const;
+ bool operator<(const TConstantUnion &constant) const;
+ static TConstantUnion add(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ static TConstantUnion sub(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ static TConstantUnion mul(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ TConstantUnion operator%(const TConstantUnion &constant) const;
+ static TConstantUnion rshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ static TConstantUnion lshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ TConstantUnion operator&(const TConstantUnion &constant) const;
+ TConstantUnion operator|(const TConstantUnion &constant) const;
+ TConstantUnion operator^(const TConstantUnion &constant) const;
+ TConstantUnion operator&&(const TConstantUnion &constant) const;
+ TConstantUnion operator||(const TConstantUnion &constant) const;
TBasicType getType() const { return type; }
-private:
-
- union {
- int iConst; // used for ivec, scalar ints
- unsigned int uConst; // used for uvec, scalar uints
- bool bConst; // used for bvec, scalar bools
- float fConst; // used for vec, mat, scalar floats
- } ;
+ private:
+ union {
+ int iConst; // used for ivec, scalar ints
+ unsigned int uConst; // used for uvec, scalar uints
+ bool bConst; // used for bvec, scalar bools
+ float fConst; // used for vec, mat, scalar floats
+ TYuvCscStandardEXT yuvCscStandardEXTConst;
+ };
TBasicType type;
};
-#endif // COMPILER_TRANSLATOR_CONSTANTUNION_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_CONSTANTUNION_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp b/src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
new file mode 100644
index 0000000000..ce9828f1f9
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
@@ -0,0 +1,221 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Applies the necessary AST transformations to support multiview rendering through instancing.
+// Check the header file For more information.
+//
+
+#include "compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h"
+
+#include "compiler/translator/FindMain.h"
+#include "compiler/translator/InitializeVariables.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class ReplaceVariableTraverser : public TIntermTraverser
+{
+ public:
+ ReplaceVariableTraverser(const TString &symbolName, TIntermSymbol *newSymbol)
+ : TIntermTraverser(true, false, false), mSymbolName(symbolName), mNewSymbol(newSymbol)
+ {
+ }
+
+ void visitSymbol(TIntermSymbol *node) override
+ {
+ TName &name = node->getName();
+ if (name.getString() == mSymbolName)
+ {
+ queueReplacement(mNewSymbol->deepCopy(), OriginalNode::IS_DROPPED);
+ }
+ }
+
+ private:
+ TString mSymbolName;
+ TIntermSymbol *mNewSymbol;
+};
+
+TIntermSymbol *CreateGLInstanceIDSymbol(const TSymbolTable &symbolTable)
+{
+ return ReferenceBuiltInVariable("gl_InstanceID", symbolTable, 300);
+}
+
+// Adds the InstanceID and ViewID_OVR initializers to the end of the initializers' sequence.
+void InitializeViewIDAndInstanceID(TIntermTyped *viewIDSymbol,
+ TIntermTyped *instanceIDSymbol,
+ unsigned numberOfViews,
+ const TSymbolTable &symbolTable,
+ TIntermSequence *initializers)
+{
+ // Create an unsigned numberOfViews node.
+ TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion();
+ numberOfViewsUnsignedConstant->setUConst(numberOfViews);
+ TIntermConstantUnion *numberOfViewsUint =
+ new TIntermConstantUnion(numberOfViewsUnsignedConstant, TType(EbtUInt, EbpHigh, EvqConst));
+
+ // Create a uint(gl_InstanceID) node.
+ TIntermSequence *glInstanceIDSymbolCastArguments = new TIntermSequence();
+ glInstanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol(symbolTable));
+ TIntermAggregate *glInstanceIDAsUint = TIntermAggregate::CreateConstructor(
+ TType(EbtUInt, EbpHigh, EvqTemporary), glInstanceIDSymbolCastArguments);
+
+ // Create a uint(gl_InstanceID) / numberOfViews node.
+ TIntermBinary *normalizedInstanceID =
+ new TIntermBinary(EOpDiv, glInstanceIDAsUint, numberOfViewsUint);
+
+ // Create an int(uint(gl_InstanceID) / numberOfViews) node.
+ TIntermSequence *normalizedInstanceIDCastArguments = new TIntermSequence();
+ normalizedInstanceIDCastArguments->push_back(normalizedInstanceID);
+ TIntermAggregate *normalizedInstanceIDAsInt = TIntermAggregate::CreateConstructor(
+ TType(EbtInt, EbpHigh, EvqTemporary), normalizedInstanceIDCastArguments);
+
+ // Create an InstanceID = int(uint(gl_InstanceID) / numberOfViews) node.
+ TIntermBinary *instanceIDInitializer =
+ new TIntermBinary(EOpAssign, instanceIDSymbol->deepCopy(), normalizedInstanceIDAsInt);
+ initializers->push_back(instanceIDInitializer);
+
+ // Create a uint(gl_InstanceID) % numberOfViews node.
+ TIntermBinary *normalizedViewID =
+ new TIntermBinary(EOpIMod, glInstanceIDAsUint->deepCopy(), numberOfViewsUint->deepCopy());
+
+ // Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node.
+ TIntermBinary *viewIDInitializer =
+ new TIntermBinary(EOpAssign, viewIDSymbol->deepCopy(), normalizedViewID);
+ initializers->push_back(viewIDInitializer);
+}
+
+// Replaces every occurrence of a symbol with the name specified in symbolName with newSymbolNode.
+void ReplaceSymbol(TIntermBlock *root, const TString &symbolName, TIntermSymbol *newSymbolNode)
+{
+ ReplaceVariableTraverser traverser(symbolName, newSymbolNode);
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+void DeclareGlobalVariable(TIntermBlock *root, TIntermTyped *typedNode)
+{
+ TIntermSequence *globalSequence = root->getSequence();
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->appendDeclarator(typedNode->deepCopy());
+ globalSequence->insert(globalSequence->begin(), declaration);
+}
+
+// Adds a branch to write int(ViewID_OVR) to either gl_ViewportIndex or gl_Layer. The branch is
+// added to the end of the initializers' sequence.
+void SelectViewIndexInVertexShader(TIntermTyped *viewIDSymbol,
+ TIntermTyped *multiviewBaseViewLayerIndexSymbol,
+ TIntermSequence *initializers,
+ const TSymbolTable &symbolTable)
+{
+ // Create an int(ViewID_OVR) node.
+ TIntermSequence *viewIDSymbolCastArguments = new TIntermSequence();
+ viewIDSymbolCastArguments->push_back(viewIDSymbol);
+ TIntermAggregate *viewIDAsInt = TIntermAggregate::CreateConstructor(
+ TType(EbtInt, EbpHigh, EvqTemporary), viewIDSymbolCastArguments);
+
+ // Create a gl_ViewportIndex node.
+ TIntermSymbol *viewportIndexSymbol =
+ ReferenceBuiltInVariable("gl_ViewportIndex", symbolTable, 0);
+
+ // Create a { gl_ViewportIndex = int(ViewID_OVR) } node.
+ TIntermBlock *viewportIndexInitializerInBlock = new TIntermBlock();
+ viewportIndexInitializerInBlock->appendStatement(
+ new TIntermBinary(EOpAssign, viewportIndexSymbol, viewIDAsInt));
+
+ // Create a gl_Layer node.
+ TIntermSymbol *layerSymbol = ReferenceBuiltInVariable("gl_Layer", symbolTable, 0);
+
+ // Create an int(ViewID_OVR) + multiviewBaseViewLayerIndex node
+ TIntermBinary *sumOfViewIDAndBaseViewIndex =
+ new TIntermBinary(EOpAdd, viewIDAsInt->deepCopy(), multiviewBaseViewLayerIndexSymbol);
+
+ // Create a { gl_Layer = int(ViewID_OVR) + multiviewBaseViewLayerIndex } node.
+ TIntermBlock *layerInitializerInBlock = new TIntermBlock();
+ layerInitializerInBlock->appendStatement(
+ new TIntermBinary(EOpAssign, layerSymbol, sumOfViewIDAndBaseViewIndex));
+
+ // Create a node to compare whether the base view index uniform is less than zero.
+ TIntermBinary *multiviewBaseViewLayerIndexZeroComparison =
+ new TIntermBinary(EOpLessThan, multiviewBaseViewLayerIndexSymbol->deepCopy(),
+ CreateZeroNode(TType(EbtInt, EbpHigh, EvqConst)));
+
+ // Create an if-else statement to select the code path.
+ TIntermIfElse *multiviewBranch =
+ new TIntermIfElse(multiviewBaseViewLayerIndexZeroComparison,
+ viewportIndexInitializerInBlock, layerInitializerInBlock);
+
+ initializers->push_back(multiviewBranch);
+}
+
+} // namespace
+
+void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
+ unsigned numberOfViews,
+ GLenum shaderType,
+ ShCompileOptions compileOptions,
+ ShShaderOutput shaderOutput,
+ TSymbolTable *symbolTable)
+{
+ ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
+
+ TQualifier viewIDQualifier = (shaderType == GL_VERTEX_SHADER) ? EvqFlatOut : EvqFlatIn;
+ TIntermSymbol *viewIDSymbol = new TIntermSymbol(symbolTable->nextUniqueId(), "ViewID_OVR",
+ TType(EbtUInt, EbpHigh, viewIDQualifier));
+ viewIDSymbol->setInternal(true);
+
+ DeclareGlobalVariable(root, viewIDSymbol);
+ ReplaceSymbol(root, "gl_ViewID_OVR", viewIDSymbol);
+ if (shaderType == GL_VERTEX_SHADER)
+ {
+ // Replacing gl_InstanceID with InstanceID should happen before adding the initializers of
+ // InstanceID and ViewID.
+ TIntermSymbol *instanceIDSymbol = new TIntermSymbol(
+ symbolTable->nextUniqueId(), "InstanceID", TType(EbtInt, EbpHigh, EvqGlobal));
+ instanceIDSymbol->setInternal(true);
+ DeclareGlobalVariable(root, instanceIDSymbol);
+ ReplaceSymbol(root, "gl_InstanceID", instanceIDSymbol);
+
+ TIntermSequence *initializers = new TIntermSequence();
+ InitializeViewIDAndInstanceID(viewIDSymbol, instanceIDSymbol, numberOfViews, *symbolTable,
+ initializers);
+
+ // The AST transformation which adds the expression to select the viewport index should
+ // be done only for the GLSL and ESSL output.
+ const bool selectView = (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u;
+ // Assert that if the view is selected in the vertex shader, then the output is
+ // either GLSL or ESSL.
+ ASSERT(!selectView || IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput));
+ if (selectView)
+ {
+ // Add a uniform to switch between side-by-side and layered rendering.
+ TIntermSymbol *multiviewBaseViewLayerIndexSymbol =
+ new TIntermSymbol(symbolTable->nextUniqueId(), "multiviewBaseViewLayerIndex",
+ TType(EbtInt, EbpHigh, EvqUniform));
+ multiviewBaseViewLayerIndexSymbol->setInternal(true);
+ DeclareGlobalVariable(root, multiviewBaseViewLayerIndexSymbol);
+
+ // Setting a value to gl_ViewportIndex or gl_Layer should happen after ViewID_OVR's
+ // initialization.
+ SelectViewIndexInVertexShader(viewIDSymbol->deepCopy(),
+ multiviewBaseViewLayerIndexSymbol->deepCopy(),
+ initializers, *symbolTable);
+ }
+
+ // Insert initializers at the beginning of main().
+ TIntermBlock *initializersBlock = new TIntermBlock();
+ initializersBlock->getSequence()->swap(*initializers);
+ TIntermBlock *mainBody = FindMainBody(root);
+ mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initializersBlock);
+ }
+}
+
+} // namespace sh \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h b/src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
new file mode 100644
index 0000000000..b4ab05fd0e
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
@@ -0,0 +1,48 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Regardless of the shader type, the following AST transformations are applied:
+// - Add declaration of View_ID_OVR.
+// - Replace every occurrence of gl_ViewID_OVR with ViewID_OVR, mark ViewID_OVR as internal and
+// declare it as a flat varying.
+//
+// If the shader type is a vertex shader, the following AST transformations are applied:
+// - Replace every occurrence of gl_InstanceID with InstanceID, mark InstanceID as internal and set
+// its qualifier to EvqTemporary.
+// - Add initializers of ViewID_OVR and InstanceID to the beginning of the body of main. The pass
+// should be executed before any variables get collected so that usage of gl_InstanceID is recorded.
+// - If the output is ESSL or GLSL and the SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is
+// enabled, the expression
+// "if (multiviewBaseViewLayerIndex < 0) {
+// gl_ViewportIndex = int(ViewID_OVR);
+// } else {
+// gl_Layer = int(ViewID_OVR) + multiviewBaseViewLayerIndex;
+// }"
+// is added after ViewID and InstanceID are initialized. Also, MultiviewRenderPath is added as a
+// uniform.
+//
+
+#ifndef COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
+#define COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
+
+#include "GLSLANG/ShaderLang.h"
+#include "angle_gl.h"
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
+ unsigned numberOfViews,
+ GLenum shaderType,
+ ShCompileOptions compileOptions,
+ ShShaderOutput shaderOutput,
+ TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.cpp b/src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.cpp
new file mode 100644
index 0000000000..67d51ea87b
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -0,0 +1,147 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// DeferGlobalInitializers is an AST traverser that moves global initializers into a separate
+// function that is called in the beginning of main(). This enables initialization of globals with
+// uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing
+// non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be
+// done after DeferGlobalInitializers is run. Note that it's important that the function definition
+// is at the end of the shader, as some globals may be declared after main().
+//
+// It can also initialize all uninitialized globals.
+//
+
+#include "compiler/translator/DeferGlobalInitializers.h"
+
+#include "compiler/translator/FindMain.h"
+#include "compiler/translator/InitializeVariables.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void GetDeferredInitializers(TIntermDeclaration *declaration,
+ bool initializeUninitializedGlobals,
+ bool canUseLoopsToInitialize,
+ TIntermSequence *deferredInitializersOut,
+ TSymbolTable *symbolTable)
+{
+ // SeparateDeclarations should have already been run.
+ ASSERT(declaration->getSequence()->size() == 1);
+
+ TIntermNode *declarator = declaration->getSequence()->back();
+ TIntermBinary *init = declarator->getAsBinaryNode();
+ if (init)
+ {
+ TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
+ ASSERT(symbolNode);
+ TIntermTyped *expression = init->getRight();
+
+ if ((expression->getQualifier() != EvqConst ||
+ (expression->getAsConstantUnion() == nullptr &&
+ !expression->isConstructorWithOnlyConstantUnionParameters())))
+ {
+ // For variables which are not constant, defer their real initialization until
+ // after we initialize uniforms.
+ // Deferral is done also in any cases where the variable has not been constant
+ // folded, since otherwise there's a chance that HLSL output will generate extra
+ // statements from the initializer expression.
+
+ // Change const global to a regular global if its initialization is deferred.
+ // This can happen if ANGLE has not been able to fold the constant expression used
+ // as an initializer.
+ ASSERT(symbolNode->getQualifier() == EvqConst ||
+ symbolNode->getQualifier() == EvqGlobal);
+ if (symbolNode->getQualifier() == EvqConst)
+ {
+ symbolNode->getTypePointer()->setQualifier(EvqGlobal);
+ }
+
+ TIntermBinary *deferredInit =
+ new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
+ deferredInitializersOut->push_back(deferredInit);
+
+ // Remove the initializer from the global scope and just declare the global instead.
+ declaration->replaceChildNode(init, symbolNode);
+ }
+ }
+ else if (initializeUninitializedGlobals)
+ {
+ TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
+ ASSERT(symbolNode);
+
+ // Ignore ANGLE internal variables.
+ if (symbolNode->getName().isInternal())
+ return;
+
+ if (symbolNode->getQualifier() == EvqGlobal && symbolNode->getSymbol() != "")
+ {
+ TIntermSequence *initCode =
+ CreateInitCode(symbolNode, canUseLoopsToInitialize, symbolTable);
+ deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(),
+ initCode->end());
+ }
+ }
+}
+
+void InsertInitCallToMain(TIntermBlock *root,
+ TIntermSequence *deferredInitializers,
+ TSymbolTable *symbolTable)
+{
+ TIntermBlock *initGlobalsBlock = new TIntermBlock();
+ initGlobalsBlock->getSequence()->swap(*deferredInitializers);
+
+ TSymbolUniqueId initGlobalsFunctionId(symbolTable);
+
+ const char *kInitGlobalsFunctionName = "initGlobals";
+
+ TIntermFunctionPrototype *initGlobalsFunctionPrototype =
+ CreateInternalFunctionPrototypeNode(TType(), kInitGlobalsFunctionName, initGlobalsFunctionId);
+ root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype);
+ TIntermFunctionDefinition *initGlobalsFunctionDefinition = CreateInternalFunctionDefinitionNode(
+ TType(), kInitGlobalsFunctionName, initGlobalsBlock, initGlobalsFunctionId);
+ root->appendStatement(initGlobalsFunctionDefinition);
+
+ TIntermAggregate *initGlobalsCall = CreateInternalFunctionCallNode(
+ TType(), kInitGlobalsFunctionName, initGlobalsFunctionId, new TIntermSequence());
+
+ TIntermBlock *mainBody = FindMainBody(root);
+ mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall);
+}
+
+} // namespace
+
+void DeferGlobalInitializers(TIntermBlock *root,
+ bool initializeUninitializedGlobals,
+ bool canUseLoopsToInitialize,
+ TSymbolTable *symbolTable)
+{
+ TIntermSequence *deferredInitializers = new TIntermSequence();
+
+ // Loop over all global statements and process the declarations. This is simpler than using a
+ // traverser.
+ for (TIntermNode *statement : *root->getSequence())
+ {
+ TIntermDeclaration *declaration = statement->getAsDeclarationNode();
+ if (declaration)
+ {
+ GetDeferredInitializers(declaration, initializeUninitializedGlobals,
+ canUseLoopsToInitialize, deferredInitializers, symbolTable);
+ }
+ }
+
+ // Add the function with initialization and the call to that.
+ if (!deferredInitializers->empty())
+ {
+ InsertInitCallToMain(root, deferredInitializers, symbolTable);
+ }
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.h b/src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.h
new file mode 100644
index 0000000000..86930a585f
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.h
@@ -0,0 +1,32 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// DeferGlobalInitializers is an AST traverser that moves global initializers into a separate
+// function that is called in the beginning of main(). This enables initialization of globals with
+// uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing
+// non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be
+// done after DeferGlobalInitializers is run. Note that it's important that the function definition
+// is at the end of the shader, as some globals may be declared after main().
+//
+// It can also initialize all uninitialized globals.
+//
+
+#ifndef COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
+#define COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+void DeferGlobalInitializers(TIntermBlock *root,
+ bool initializeUninitializedGlobals,
+ bool canUseLoopsToInitialize,
+ TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/DetectCallDepth.cpp b/src/3rdparty/angle/src/compiler/translator/DetectCallDepth.cpp
deleted file mode 100644
index 0dc5d22709..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/DetectCallDepth.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-//
-// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/DetectCallDepth.h"
-#include "compiler/translator/InfoSink.h"
-
-DetectCallDepth::FunctionNode::FunctionNode(const TString& fname)
- : name(fname),
- visit(PreVisit)
-{
-}
-
-const TString& DetectCallDepth::FunctionNode::getName() const
-{
- return name;
-}
-
-void DetectCallDepth::FunctionNode::addCallee(
- DetectCallDepth::FunctionNode* callee)
-{
- for (size_t i = 0; i < callees.size(); ++i) {
- if (callees[i] == callee)
- return;
- }
- callees.push_back(callee);
-}
-
-int DetectCallDepth::FunctionNode::detectCallDepth(DetectCallDepth* detectCallDepth, int depth)
-{
- ASSERT(visit == PreVisit);
- ASSERT(detectCallDepth);
-
- int retMaxDepth = depth;
- visit = InVisit;
- for (size_t i = 0; i < callees.size(); ++i) {
- switch (callees[i]->visit) {
- case InVisit:
- // cycle detected, i.e., recursion detected.
- return kInfiniteCallDepth;
- case PostVisit:
- break;
- case PreVisit: {
- // Check before we recurse so we don't go too depth
- if (detectCallDepth->checkExceedsMaxDepth(depth))
- return depth;
- int callDepth = callees[i]->detectCallDepth(detectCallDepth, depth + 1);
- // Check after we recurse so we can exit immediately and provide info.
- if (detectCallDepth->checkExceedsMaxDepth(callDepth)) {
- detectCallDepth->getInfoSink().info << "<-" << callees[i]->getName();
- return callDepth;
- }
- retMaxDepth = std::max(callDepth, retMaxDepth);
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
- }
- visit = PostVisit;
- return retMaxDepth;
-}
-
-void DetectCallDepth::FunctionNode::reset()
-{
- visit = PreVisit;
-}
-
-DetectCallDepth::DetectCallDepth(TInfoSink& infoSink, bool limitCallStackDepth, int maxCallStackDepth)
- : TIntermTraverser(true, false, true, false),
- currentFunction(NULL),
- infoSink(infoSink),
- maxDepth(limitCallStackDepth ? maxCallStackDepth : FunctionNode::kInfiniteCallDepth)
-{
-}
-
-DetectCallDepth::~DetectCallDepth()
-{
- for (size_t i = 0; i < functions.size(); ++i)
- delete functions[i];
-}
-
-bool DetectCallDepth::visitAggregate(Visit visit, TIntermAggregate* node)
-{
- switch (node->getOp())
- {
- case EOpPrototype:
- // Function declaration.
- // Don't add FunctionNode here because node->getName() is the
- // unmangled function name.
- break;
- case EOpFunction: {
- // Function definition.
- if (visit == PreVisit) {
- currentFunction = findFunctionByName(node->getName());
- if (currentFunction == NULL) {
- currentFunction = new FunctionNode(node->getName());
- functions.push_back(currentFunction);
- }
- } else if (visit == PostVisit) {
- currentFunction = NULL;
- }
- break;
- }
- case EOpFunctionCall: {
- // Function call.
- if (visit == PreVisit) {
- FunctionNode* func = findFunctionByName(node->getName());
- if (func == NULL) {
- func = new FunctionNode(node->getName());
- functions.push_back(func);
- }
- if (currentFunction)
- currentFunction->addCallee(func);
- }
- break;
- }
- default:
- break;
- }
- return true;
-}
-
-bool DetectCallDepth::checkExceedsMaxDepth(int depth)
-{
- return depth >= maxDepth;
-}
-
-void DetectCallDepth::resetFunctionNodes()
-{
- for (size_t i = 0; i < functions.size(); ++i) {
- functions[i]->reset();
- }
-}
-
-DetectCallDepth::ErrorCode DetectCallDepth::detectCallDepthForFunction(FunctionNode* func)
-{
- currentFunction = NULL;
- resetFunctionNodes();
-
- int maxCallDepth = func->detectCallDepth(this, 1);
-
- if (maxCallDepth == FunctionNode::kInfiniteCallDepth)
- return kErrorRecursion;
-
- if (maxCallDepth >= maxDepth)
- return kErrorMaxDepthExceeded;
-
- return kErrorNone;
-}
-
-DetectCallDepth::ErrorCode DetectCallDepth::detectCallDepth()
-{
- if (maxDepth != FunctionNode::kInfiniteCallDepth) {
- // Check all functions because the driver may fail on them
- // TODO: Before detectingRecursion, strip unused functions.
- for (size_t i = 0; i < functions.size(); ++i) {
- ErrorCode error = detectCallDepthForFunction(functions[i]);
- if (error != kErrorNone)
- return error;
- }
- } else {
- FunctionNode* main = findFunctionByName("main(");
- if (main == NULL)
- return kErrorMissingMain;
-
- return detectCallDepthForFunction(main);
- }
-
- return kErrorNone;
-}
-
-DetectCallDepth::FunctionNode* DetectCallDepth::findFunctionByName(
- const TString& name)
-{
- for (size_t i = 0; i < functions.size(); ++i) {
- if (functions[i]->getName() == name)
- return functions[i];
- }
- return NULL;
-}
-
diff --git a/src/3rdparty/angle/src/compiler/translator/DetectCallDepth.h b/src/3rdparty/angle/src/compiler/translator/DetectCallDepth.h
deleted file mode 100644
index 8dd1391e67..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/DetectCallDepth.h
+++ /dev/null
@@ -1,78 +0,0 @@
-//
-// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_DETECTCALLDEPTH_H_
-#define COMPILER_TRANSLATOR_DETECTCALLDEPTH_H_
-
-#include <limits.h>
-#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/VariableInfo.h"
-
-class TInfoSink;
-
-// Traverses intermediate tree to detect function recursion.
-class DetectCallDepth : public TIntermTraverser {
-public:
- enum ErrorCode {
- kErrorMissingMain,
- kErrorRecursion,
- kErrorMaxDepthExceeded,
- kErrorNone
- };
-
- DetectCallDepth(TInfoSink& infoSync, bool limitCallStackDepth, int maxCallStackDepth);
- ~DetectCallDepth();
-
- virtual bool visitAggregate(Visit, TIntermAggregate*);
-
- bool checkExceedsMaxDepth(int depth);
-
- ErrorCode detectCallDepth();
-
-private:
- class FunctionNode {
- public:
- static const int kInfiniteCallDepth = INT_MAX;
-
- FunctionNode(const TString& fname);
-
- const TString& getName() const;
-
- // If a function is already in the callee list, this becomes a no-op.
- void addCallee(FunctionNode* callee);
-
- // Returns kInifinityCallDepth if recursive function calls are detected.
- int detectCallDepth(DetectCallDepth* detectCallDepth, int depth);
-
- // Reset state.
- void reset();
-
- private:
- // mangled function name is unique.
- TString name;
-
- // functions that are directly called by this function.
- TVector<FunctionNode*> callees;
-
- Visit visit;
- };
-
- ErrorCode detectCallDepthForFunction(FunctionNode* func);
- FunctionNode* findFunctionByName(const TString& name);
- void resetFunctionNodes();
-
- TInfoSink& getInfoSink() { return infoSink; }
-
- TVector<FunctionNode*> functions;
- FunctionNode* currentFunction;
- TInfoSink& infoSink;
- int maxDepth;
-
- DetectCallDepth(const DetectCallDepth&);
- void operator=(const DetectCallDepth&);
-};
-
-#endif // COMPILER_TRANSLATOR_DETECTCALLDEPTH_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.cpp b/src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.cpp
deleted file mode 100644
index f98d32b2b7..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Contains analysis utilities for dealing with HLSL's lack of support for
-// the use of intrinsic functions which (implicitly or explicitly) compute
-// gradients of functions with discontinuities.
-//
-
-#include "compiler/translator/DetectDiscontinuity.h"
-
-#include "compiler/translator/ParseContext.h"
-
-namespace sh
-{
-
-// Detect Loop Discontinuity
-
-bool DetectLoopDiscontinuity::traverse(TIntermNode *node)
-{
- mLoopDepth = 0;
- mLoopDiscontinuity = false;
- node->traverse(this);
- return mLoopDiscontinuity;
-}
-
-bool DetectLoopDiscontinuity::visitLoop(Visit visit, TIntermLoop *loop)
-{
- if (visit == PreVisit)
- {
- ++mLoopDepth;
- }
- else if (visit == PostVisit)
- {
- --mLoopDepth;
- }
-
- return true;
-}
-
-bool DetectLoopDiscontinuity::visitBranch(Visit visit, TIntermBranch *node)
-{
- if (mLoopDiscontinuity)
- {
- return false;
- }
-
- if (!mLoopDepth)
- {
- return true;
- }
-
- switch (node->getFlowOp())
- {
- case EOpKill:
- break;
- case EOpBreak:
- case EOpContinue:
- case EOpReturn:
- mLoopDiscontinuity = true;
- break;
- default: UNREACHABLE();
- }
-
- return !mLoopDiscontinuity;
-}
-
-bool DetectLoopDiscontinuity::visitAggregate(Visit visit, TIntermAggregate *node)
-{
- return !mLoopDiscontinuity;
-}
-
-bool containsLoopDiscontinuity(TIntermNode *node)
-{
- DetectLoopDiscontinuity detectLoopDiscontinuity;
- return detectLoopDiscontinuity.traverse(node);
-}
-
-// Detect Any Loop
-
-bool DetectAnyLoop::traverse(TIntermNode *node)
-{
- mHasLoop = false;
- node->traverse(this);
- return mHasLoop;
-}
-
-bool DetectAnyLoop::visitLoop(Visit visit, TIntermLoop *loop)
-{
- mHasLoop = true;
- return false;
-}
-
-// The following definitions stop all traversal when we have found a loop
-bool DetectAnyLoop::visitBinary(Visit, TIntermBinary *)
-{
- return !mHasLoop;
-}
-
-bool DetectAnyLoop::visitUnary(Visit, TIntermUnary *)
-{
- return !mHasLoop;
-}
-
-bool DetectAnyLoop::visitSelection(Visit, TIntermSelection *)
-{
- return !mHasLoop;
-}
-
-bool DetectAnyLoop::visitAggregate(Visit, TIntermAggregate *)
-{
- return !mHasLoop;
-}
-
-bool DetectAnyLoop::visitBranch(Visit, TIntermBranch *)
-{
- return !mHasLoop;
-}
-
-bool containsAnyLoop(TIntermNode *node)
-{
- DetectAnyLoop detectAnyLoop;
- return detectAnyLoop.traverse(node);
-}
-
-// Detect Gradient Operation
-
-bool DetectGradientOperation::traverse(TIntermNode *node)
-{
- mGradientOperation = false;
- node->traverse(this);
- return mGradientOperation;
-}
-
-bool DetectGradientOperation::visitUnary(Visit visit, TIntermUnary *node)
-{
- if (mGradientOperation)
- {
- return false;
- }
-
- switch (node->getOp())
- {
- case EOpDFdx:
- case EOpDFdy:
- mGradientOperation = true;
- default:
- break;
- }
-
- return !mGradientOperation;
-}
-
-bool DetectGradientOperation::visitAggregate(Visit visit, TIntermAggregate *node)
-{
- if (mGradientOperation)
- {
- return false;
- }
-
- if (node->getOp() == EOpFunctionCall)
- {
- if (!node->isUserDefined())
- {
- TString name = TFunction::unmangleName(node->getName());
-
- if (name == "texture2D" ||
- name == "texture2DProj" ||
- name == "textureCube")
- {
- mGradientOperation = true;
- }
- }
- else
- {
- // When a user defined function is called, we have to
- // conservatively assume it to contain gradient operations
- mGradientOperation = true;
- }
- }
-
- return !mGradientOperation;
-}
-
-bool containsGradientOperation(TIntermNode *node)
-{
- DetectGradientOperation detectGradientOperation;
- return detectGradientOperation.traverse(node);
-}
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.h b/src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.h
deleted file mode 100644
index 623be13533..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.h
+++ /dev/null
@@ -1,71 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Contains analysis utilities for dealing with HLSL's lack of support for
-// the use of intrinsic functions which (implicitly or explicitly) compute
-// gradients of functions with discontinuities.
-//
-
-#ifndef COMPILER_TRANSLATOR_DETECTDISCONTINUITY_H_
-#define COMPILER_TRANSLATOR_DETECTDISCONTINUITY_H_
-
-#include "compiler/translator/IntermNode.h"
-
-namespace sh
-{
-// Checks whether a loop can run for a variable number of iterations
-class DetectLoopDiscontinuity : public TIntermTraverser
-{
- public:
- bool traverse(TIntermNode *node);
-
- protected:
- bool visitBranch(Visit visit, TIntermBranch *node);
- bool visitLoop(Visit visit, TIntermLoop *loop);
- bool visitAggregate(Visit visit, TIntermAggregate *node);
-
- int mLoopDepth;
- bool mLoopDiscontinuity;
-};
-
-bool containsLoopDiscontinuity(TIntermNode *node);
-
-// Checks for the existence of any loop
-class DetectAnyLoop : public TIntermTraverser
-{
-public:
- bool traverse(TIntermNode *node);
-
-protected:
- bool visitBinary(Visit, TIntermBinary *);
- bool visitUnary(Visit, TIntermUnary *);
- bool visitSelection(Visit, TIntermSelection *);
- bool visitAggregate(Visit, TIntermAggregate *);
- bool visitLoop(Visit, TIntermLoop *);
- bool visitBranch(Visit, TIntermBranch *);
-
- bool mHasLoop;
-};
-
-bool containsAnyLoop(TIntermNode *node);
-
-// Checks for intrinsic functions which compute gradients
-class DetectGradientOperation : public TIntermTraverser
-{
- public:
- bool traverse(TIntermNode *node);
-
- protected:
- bool visitUnary(Visit visit, TIntermUnary *node);
- bool visitAggregate(Visit visit, TIntermAggregate *node);
-
- bool mGradientOperation;
-};
-
-bool containsGradientOperation(TIntermNode *node);
-
-}
-
-#endif // COMPILER_TRANSLATOR_DETECTDISCONTINUITY_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp b/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp
index 593137fb0a..1744e5ab3e 100644
--- a/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp
@@ -7,13 +7,15 @@
#include "compiler/translator/Diagnostics.h"
#include "common/debug.h"
-#include "compiler/translator/InfoSink.h"
#include "compiler/preprocessor/SourceLocation.h"
+#include "compiler/translator/Common.h"
+#include "compiler/translator/InfoSink.h"
+
+namespace sh
+{
-TDiagnostics::TDiagnostics(TInfoSink& infoSink) :
- mInfoSink(infoSink),
- mNumErrors(0),
- mNumWarnings(0)
+TDiagnostics::TDiagnostics(TInfoSinkBase &infoSink)
+ : mInfoSink(infoSink), mNumErrors(0), mNumWarnings(0)
{
}
@@ -22,37 +24,82 @@ TDiagnostics::~TDiagnostics()
}
void TDiagnostics::writeInfo(Severity severity,
- const pp::SourceLocation& loc,
- const std::string& reason,
- const std::string& token,
- const std::string& extra)
+ const pp::SourceLocation &loc,
+ const char *reason,
+ const char *token)
{
- TPrefixType prefix = EPrefixNone;
switch (severity)
{
- case PP_ERROR:
- ++mNumErrors;
- prefix = EPrefixError;
- break;
- case PP_WARNING:
- ++mNumWarnings;
- prefix = EPrefixWarning;
- break;
- default:
- UNREACHABLE();
- break;
+ case SH_ERROR:
+ ++mNumErrors;
+ break;
+ case SH_WARNING:
+ ++mNumWarnings;
+ break;
+ default:
+ UNREACHABLE();
+ break;
}
- TInfoSinkBase& sink = mInfoSink.info;
/* VC++ format: file(linenum) : error #: 'token' : extrainfo */
- sink.prefix(prefix);
- sink.location(loc.file, loc.line);
- sink << "'" << token << "' : " << reason << " " << extra << "\n";
+ mInfoSink.prefix(severity);
+ mInfoSink.location(loc.file, loc.line);
+ mInfoSink << "'" << token << "' : " << reason << "\n";
}
-void TDiagnostics::print(ID id,
- const pp::SourceLocation& loc,
- const std::string& text)
+void TDiagnostics::globalError(const char *message)
{
- writeInfo(severity(id), loc, message(id), text, "");
+ ++mNumErrors;
+ mInfoSink.prefix(SH_ERROR);
+ mInfoSink << message << "\n";
}
+
+void TDiagnostics::error(const pp::SourceLocation &loc, const char *reason, const char *token)
+{
+ writeInfo(SH_ERROR, loc, reason, token);
+}
+
+void TDiagnostics::warning(const pp::SourceLocation &loc, const char *reason, const char *token)
+{
+ writeInfo(SH_WARNING, loc, reason, token);
+}
+
+void TDiagnostics::error(const TSourceLoc &loc, const char *reason, const char *token)
+{
+ pp::SourceLocation srcLoc;
+ srcLoc.file = loc.first_file;
+ srcLoc.line = loc.first_line;
+ error(srcLoc, reason, token);
+}
+
+void TDiagnostics::warning(const TSourceLoc &loc, const char *reason, const char *token)
+{
+ pp::SourceLocation srcLoc;
+ srcLoc.file = loc.first_file;
+ srcLoc.line = loc.first_line;
+ warning(srcLoc, reason, token);
+}
+
+void TDiagnostics::print(ID id, const pp::SourceLocation &loc, const std::string &text)
+{
+ writeInfo(isError(id) ? SH_ERROR : SH_WARNING, loc, message(id), text.c_str());
+}
+
+void TDiagnostics::resetErrorCount()
+{
+ mNumErrors = 0;
+ mNumWarnings = 0;
+}
+
+PerformanceDiagnostics::PerformanceDiagnostics(TDiagnostics *diagnostics)
+ : mDiagnostics(diagnostics)
+{
+ ASSERT(diagnostics);
+}
+
+void PerformanceDiagnostics::warning(const TSourceLoc &loc, const char *reason, const char *token)
+{
+ mDiagnostics->warning(loc, reason, token);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/Diagnostics.h b/src/3rdparty/angle/src/compiler/translator/Diagnostics.h
index bc26e4584f..55b88991df 100644
--- a/src/3rdparty/angle/src/compiler/translator/Diagnostics.h
+++ b/src/3rdparty/angle/src/compiler/translator/Diagnostics.h
@@ -9,33 +9,59 @@
#include "common/angleutils.h"
#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/translator/Severity.h"
-class TInfoSink;
+namespace sh
+{
+
+class TInfoSinkBase;
+struct TSourceLoc;
class TDiagnostics : public pp::Diagnostics, angle::NonCopyable
{
public:
- TDiagnostics(TInfoSink& infoSink);
+ TDiagnostics(TInfoSinkBase &infoSink);
~TDiagnostics() override;
- TInfoSink& infoSink() { return mInfoSink; }
-
int numErrors() const { return mNumErrors; }
int numWarnings() const { return mNumWarnings; }
- void writeInfo(Severity severity,
- const pp::SourceLocation& loc,
- const std::string& reason,
- const std::string& token,
- const std::string& extra);
+ void error(const pp::SourceLocation &loc, const char *reason, const char *token);
+ void warning(const pp::SourceLocation &loc, const char *reason, const char *token);
+
+ void error(const TSourceLoc &loc, const char *reason, const char *token);
+ void warning(const TSourceLoc &loc, const char *reason, const char *token);
+
+ void globalError(const char *message);
+
+ void resetErrorCount();
protected:
+ void writeInfo(Severity severity,
+ const pp::SourceLocation &loc,
+ const char *reason,
+ const char *token);
+
void print(ID id, const pp::SourceLocation &loc, const std::string &text) override;
private:
- TInfoSink& mInfoSink;
+ TInfoSinkBase &mInfoSink;
int mNumErrors;
int mNumWarnings;
};
+// Diagnostics wrapper to use when the code is only allowed to generate warnings.
+class PerformanceDiagnostics : public angle::NonCopyable
+{
+ public:
+ PerformanceDiagnostics(TDiagnostics *diagnostics);
+
+ void warning(const TSourceLoc &loc, const char *reason, const char *token);
+
+ private:
+ TDiagnostics *mDiagnostics;
+};
+
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_DIAGNOSTICS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp
index ff8a69efa5..485e66670c 100644
--- a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp
@@ -12,17 +12,24 @@
#include "common/debug.h"
#include "compiler/translator/Diagnostics.h"
-static TBehavior getBehavior(const std::string& str)
+namespace sh
+{
+
+static TBehavior getBehavior(const std::string &str)
{
const char kRequire[] = "require";
- const char kEnable[] = "enable";
+ const char kEnable[] = "enable";
const char kDisable[] = "disable";
- const char kWarn[] = "warn";
-
- if (str == kRequire) return EBhRequire;
- else if (str == kEnable) return EBhEnable;
- else if (str == kDisable) return EBhDisable;
- else if (str == kWarn) return EBhWarn;
+ const char kWarn[] = "warn";
+
+ if (str == kRequire)
+ return EBhRequire;
+ else if (str == kEnable)
+ return EBhEnable;
+ else if (str == kDisable)
+ return EBhDisable;
+ else if (str == kWarn)
+ return EBhWarn;
return EBhUndefined;
}
@@ -43,30 +50,29 @@ TDirectiveHandler::~TDirectiveHandler()
{
}
-void TDirectiveHandler::handleError(const pp::SourceLocation& loc,
- const std::string& msg)
+void TDirectiveHandler::handleError(const pp::SourceLocation &loc, const std::string &msg)
{
- mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc, msg, "", "");
+ mDiagnostics.error(loc, msg.c_str(), "");
}
-void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
- const std::string& name,
- const std::string& value,
+void TDirectiveHandler::handlePragma(const pp::SourceLocation &loc,
+ const std::string &name,
+ const std::string &value,
bool stdgl)
{
if (stdgl)
{
const char kInvariant[] = "invariant";
- const char kAll[] = "all";
+ const char kAll[] = "all";
if (name == kInvariant && value == kAll)
{
if (mShaderVersion == 300 && mShaderType == GL_FRAGMENT_SHADER)
{
// ESSL 3.00.4 section 4.6.1
- mDiagnostics.writeInfo(
- pp::Diagnostics::PP_ERROR, loc,
- "#pragma STDGL invariant(all) can not be used in fragment shader", name, value);
+ mDiagnostics.error(
+ loc, "#pragma STDGL invariant(all) can not be used in fragment shader",
+ name.c_str());
}
mPragma.stdgl.invariantAll = true;
}
@@ -77,30 +83,39 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
}
else
{
- const char kOptimize[] = "optimize";
- const char kDebug[] = "debug";
+ const char kOptimize[] = "optimize";
+ const char kDebug[] = "debug";
const char kDebugShaderPrecision[] = "webgl_debug_shader_precision";
- const char kOn[] = "on";
- const char kOff[] = "off";
+ const char kOn[] = "on";
+ const char kOff[] = "off";
bool invalidValue = false;
if (name == kOptimize)
{
- if (value == kOn) mPragma.optimize = true;
- else if (value == kOff) mPragma.optimize = false;
- else invalidValue = true;
+ if (value == kOn)
+ mPragma.optimize = true;
+ else if (value == kOff)
+ mPragma.optimize = false;
+ else
+ invalidValue = true;
}
else if (name == kDebug)
{
- if (value == kOn) mPragma.debug = true;
- else if (value == kOff) mPragma.debug = false;
- else invalidValue = true;
+ if (value == kOn)
+ mPragma.debug = true;
+ else if (value == kOff)
+ mPragma.debug = false;
+ else
+ invalidValue = true;
}
else if (name == kDebugShaderPrecision && mDebugShaderPrecisionSupported)
{
- if (value == kOn) mPragma.debugShaderPrecision = true;
- else if (value == kOff) mPragma.debugShaderPrecision = false;
- else invalidValue = true;
+ if (value == kOn)
+ mPragma.debugShaderPrecision = true;
+ else if (value == kOff)
+ mPragma.debugShaderPrecision = false;
+ else
+ invalidValue = true;
}
else
{
@@ -110,24 +125,21 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
if (invalidValue)
{
- mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
- "invalid pragma value", value,
- "'on' or 'off' expected");
+ mDiagnostics.error(loc, "invalid pragma value - 'on' or 'off' expected", value.c_str());
}
}
}
-void TDirectiveHandler::handleExtension(const pp::SourceLocation& loc,
- const std::string& name,
- const std::string& behavior)
+void TDirectiveHandler::handleExtension(const pp::SourceLocation &loc,
+ const std::string &name,
+ const std::string &behavior)
{
const char kExtAll[] = "all";
TBehavior behaviorVal = getBehavior(behavior);
if (behaviorVal == EBhUndefined)
{
- mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
- "behavior", name, "invalid");
+ mDiagnostics.error(loc, "behavior invalid", name.c_str());
return;
}
@@ -135,15 +147,11 @@ void TDirectiveHandler::handleExtension(const pp::SourceLocation& loc,
{
if (behaviorVal == EBhRequire)
{
- mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
- "extension", name,
- "cannot have 'require' behavior");
+ mDiagnostics.error(loc, "extension cannot have 'require' behavior", name.c_str());
}
else if (behaviorVal == EBhEnable)
{
- mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
- "extension", name,
- "cannot have 'enable' behavior");
+ mDiagnostics.error(loc, "extension cannot have 'enable' behavior", name.c_str());
}
else
{
@@ -154,36 +162,32 @@ void TDirectiveHandler::handleExtension(const pp::SourceLocation& loc,
return;
}
- TExtensionBehavior::iterator iter = mExtensionBehavior.find(name);
+ TExtensionBehavior::iterator iter = mExtensionBehavior.find(GetExtensionByName(name.c_str()));
if (iter != mExtensionBehavior.end())
{
iter->second = behaviorVal;
return;
}
- pp::Diagnostics::Severity severity = pp::Diagnostics::PP_ERROR;
- switch (behaviorVal) {
- case EBhRequire:
- severity = pp::Diagnostics::PP_ERROR;
- break;
- case EBhEnable:
- case EBhWarn:
- case EBhDisable:
- severity = pp::Diagnostics::PP_WARNING;
- break;
- default:
- UNREACHABLE();
- break;
+ switch (behaviorVal)
+ {
+ case EBhRequire:
+ mDiagnostics.error(loc, "extension is not supported", name.c_str());
+ break;
+ case EBhEnable:
+ case EBhWarn:
+ case EBhDisable:
+ mDiagnostics.warning(loc, "extension is not supported", name.c_str());
+ break;
+ default:
+ UNREACHABLE();
+ break;
}
- mDiagnostics.writeInfo(severity, loc,
- "extension", name, "is not supported");
}
-void TDirectiveHandler::handleVersion(const pp::SourceLocation& loc,
- int version)
+void TDirectiveHandler::handleVersion(const pp::SourceLocation &loc, int version)
{
- if (version == 100 ||
- version == 300)
+ if (version == 100 || version == 300 || version == 310)
{
mShaderVersion = version;
}
@@ -192,7 +196,8 @@ void TDirectiveHandler::handleVersion(const pp::SourceLocation& loc,
std::stringstream stream;
stream << version;
std::string str = stream.str();
- mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
- "version number", str, "not supported");
+ mDiagnostics.error(loc, "version number not supported", str.c_str());
}
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h
index 00eb49114e..8e8cb9bbf6 100644
--- a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h
+++ b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h
@@ -13,6 +13,8 @@
#include "compiler/preprocessor/DirectiveHandlerBase.h"
#include "GLSLANG/ShaderLang.h"
+namespace sh
+{
class TDiagnostics;
class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable
@@ -25,8 +27,8 @@ class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable
bool debugShaderPrecisionSupported);
~TDirectiveHandler() override;
- const TPragma& pragma() const { return mPragma; }
- const TExtensionBehavior& extensionBehavior() const { return mExtensionBehavior; }
+ const TPragma &pragma() const { return mPragma; }
+ const TExtensionBehavior &extensionBehavior() const { return mExtensionBehavior; }
void handleError(const pp::SourceLocation &loc, const std::string &msg) override;
@@ -43,11 +45,13 @@ class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable
private:
TPragma mPragma;
- TExtensionBehavior& mExtensionBehavior;
- TDiagnostics& mDiagnostics;
- int& mShaderVersion;
+ TExtensionBehavior &mExtensionBehavior;
+ TDiagnostics &mDiagnostics;
+ int &mShaderVersion;
sh::GLenum mShaderType;
bool mDebugShaderPrecisionSupported;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_DIRECTIVEHANDLER_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp b/src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
new file mode 100644
index 0000000000..189ea341eb
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
@@ -0,0 +1,129 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// gl_FragColor needs to broadcast to all color buffers in ES2 if
+// GL_EXT_draw_buffers is explicitly enabled in a fragment shader.
+//
+// We emulate this by replacing all gl_FragColor with gl_FragData[0], and in the end
+// of main() function, assigning gl_FragData[1], ..., gl_FragData[maxDrawBuffers-1]
+// with gl_FragData[0].
+//
+
+#include "compiler/translator/EmulateGLFragColorBroadcast.h"
+
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/RunAtTheEndOfShader.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class GLFragColorBroadcastTraverser : public TIntermTraverser
+{
+ public:
+ GLFragColorBroadcastTraverser(int maxDrawBuffers, TSymbolTable *symbolTable, int shaderVersion)
+ : TIntermTraverser(true, false, false, symbolTable),
+ mGLFragColorUsed(false),
+ mMaxDrawBuffers(maxDrawBuffers),
+ mShaderVersion(shaderVersion)
+ {
+ }
+
+ void broadcastGLFragColor(TIntermBlock *root);
+
+ bool isGLFragColorUsed() const { return mGLFragColorUsed; }
+
+ protected:
+ void visitSymbol(TIntermSymbol *node) override;
+
+ TIntermBinary *constructGLFragDataNode(int index) const;
+ TIntermBinary *constructGLFragDataAssignNode(int index) const;
+
+ private:
+ bool mGLFragColorUsed;
+ int mMaxDrawBuffers;
+ const int mShaderVersion;
+};
+
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
+{
+ TIntermSymbol *symbol =
+ ReferenceBuiltInVariable(TString("gl_FragData"), *mSymbolTable, mShaderVersion);
+ TIntermTyped *indexNode = CreateIndexNode(index);
+
+ TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
+ return binary;
+}
+
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const
+{
+ TIntermTyped *fragDataIndex = constructGLFragDataNode(index);
+ TIntermTyped *fragDataZero = constructGLFragDataNode(0);
+
+ return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
+}
+
+void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
+{
+ if (node->getSymbol() == "gl_FragColor")
+ {
+ queueReplacement(constructGLFragDataNode(0), OriginalNode::IS_DROPPED);
+ mGLFragColorUsed = true;
+ }
+}
+
+void GLFragColorBroadcastTraverser::broadcastGLFragColor(TIntermBlock *root)
+{
+ ASSERT(mMaxDrawBuffers > 1);
+ if (!mGLFragColorUsed)
+ {
+ return;
+ }
+
+ TIntermBlock *broadcastBlock = new TIntermBlock();
+ // Now insert statements
+ // gl_FragData[1] = gl_FragData[0];
+ // ...
+ // gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
+ for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
+ {
+ broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex));
+ }
+ RunAtTheEndOfShader(root, broadcastBlock, mSymbolTable);
+}
+
+} // namespace anonymous
+
+void EmulateGLFragColorBroadcast(TIntermBlock *root,
+ int maxDrawBuffers,
+ std::vector<sh::OutputVariable> *outputVariables,
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+{
+ ASSERT(maxDrawBuffers > 1);
+ GLFragColorBroadcastTraverser traverser(maxDrawBuffers, symbolTable, shaderVersion);
+ root->traverse(&traverser);
+ if (traverser.isGLFragColorUsed())
+ {
+ traverser.updateTree();
+ traverser.broadcastGLFragColor(root);
+ for (auto &var : *outputVariables)
+ {
+ if (var.name == "gl_FragColor")
+ {
+ // TODO(zmo): Find a way to keep the original variable information.
+ var.name = "gl_FragData";
+ var.mappedName = "gl_FragData";
+ var.arraySizes.push_back(maxDrawBuffers);
+ ASSERT(var.arraySizes.size() == 1u);
+ }
+ }
+ }
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h b/src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h
new file mode 100644
index 0000000000..e652b7e07e
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h
@@ -0,0 +1,31 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Emulate gl_FragColor broadcast behaviors in ES2 where
+// GL_EXT_draw_buffers is explicitly enabled in a fragment shader.
+//
+
+#ifndef COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
+#define COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
+
+#include <vector>
+
+namespace sh
+{
+struct OutputVariable;
+class TIntermBlock;
+class TSymbolTable;
+
+// Replace all gl_FragColor with gl_FragData[0], and in the end of main() function,
+// assign gl_FragData[1] ... gl_FragData[maxDrawBuffers - 1] with gl_FragData[0].
+// If gl_FragColor is in outputVariables, it is replaced by gl_FragData.
+void EmulateGLFragColorBroadcast(TIntermBlock *root,
+ int maxDrawBuffers,
+ std::vector<OutputVariable> *outputVariables,
+ TSymbolTable *symbolTable,
+ int shaderVersion);
+}
+
+#endif // COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp
index 4a7fa54155..ba09fd77df 100644
--- a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp
@@ -6,88 +6,206 @@
#include "compiler/translator/EmulatePrecision.h"
-namespace
-{
+#include <memory>
-static void writeVectorPrecisionEmulationHelpers(
- TInfoSinkBase& sink, ShShaderOutput outputLanguage, unsigned int size)
+namespace sh
{
- std::stringstream vecTypeStrStr;
- if (outputLanguage == SH_ESSL_OUTPUT)
- vecTypeStrStr << "highp ";
- vecTypeStrStr << "vec" << size;
- std::string vecType = vecTypeStrStr.str();
- sink <<
- vecType << " angle_frm(in " << vecType << " v) {\n"
- " v = clamp(v, -65504.0, 65504.0);\n"
- " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
- " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
- " v = v * exp2(-exponent);\n"
- " v = sign(v) * floor(abs(v));\n"
- " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
- "}\n";
+namespace
+{
- sink <<
- vecType << " angle_frl(in " << vecType << " v) {\n"
- " v = clamp(v, -2.0, 2.0);\n"
- " v = v * 256.0;\n"
- " v = sign(v) * floor(abs(v));\n"
- " return v * 0.00390625;\n"
- "}\n";
-}
+class RoundingHelperWriter : angle::NonCopyable
+{
+ public:
+ static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
+
+ void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
+ void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
+ const char *lType,
+ const char *rType,
+ const char *opStr,
+ const char *opNameStr);
+
+ virtual ~RoundingHelperWriter() {}
+
+ protected:
+ RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
+ RoundingHelperWriter() = delete;
+
+ const ShShaderOutput mOutputLanguage;
+
+ private:
+ virtual std::string getTypeString(const char *glslType) = 0;
+ virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
+ virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
+ virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) = 0;
+};
+
+class RoundingHelperWriterGLSL : public RoundingHelperWriter
+{
+ public:
+ RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriter(outputLanguage)
+ {
+ }
-static void writeMatrixPrecisionEmulationHelper(
- TInfoSinkBase& sink, ShShaderOutput outputLanguage, unsigned int size, const char *functionName)
+ private:
+ std::string getTypeString(const char *glslType) override;
+ void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
+ void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
+ void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) override;
+};
+
+class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
{
- std::stringstream matTypeStrStr;
- if (outputLanguage == SH_ESSL_OUTPUT)
- matTypeStrStr << "highp ";
- matTypeStrStr << "mat" << size;
- std::string matType = matTypeStrStr.str();
+ public:
+ RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriterGLSL(outputLanguage)
+ {
+ }
- sink << matType << " " << functionName << "(in " << matType << " m) {\n"
- " " << matType << " rounded;\n";
+ private:
+ std::string getTypeString(const char *glslType) override;
+};
- for (unsigned int i = 0; i < size; ++i)
+class RoundingHelperWriterHLSL : public RoundingHelperWriter
+{
+ public:
+ RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriter(outputLanguage)
{
- sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
}
- sink << " return rounded;\n"
- "}\n";
+ private:
+ std::string getTypeString(const char *glslType) override;
+ void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
+ void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
+ void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) override;
+};
+
+RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
+{
+ ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
+ switch (outputLanguage)
+ {
+ case SH_HLSL_4_1_OUTPUT:
+ return new RoundingHelperWriterHLSL(outputLanguage);
+ case SH_ESSL_OUTPUT:
+ return new RoundingHelperWriterESSL(outputLanguage);
+ default:
+ return new RoundingHelperWriterGLSL(outputLanguage);
+ }
}
-static void writeCommonPrecisionEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage)
+void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
{
// Write the angle_frm functions that round floating point numbers to
// half precision, and angle_frl functions that round them to minimum lowp
// precision.
+ writeFloatRoundingHelpers(sink);
+ writeVectorRoundingHelpers(sink, 2);
+ writeVectorRoundingHelpers(sink, 3);
+ writeVectorRoundingHelpers(sink, 4);
+ if (shaderVersion > 100)
+ {
+ for (unsigned int columns = 2; columns <= 4; ++columns)
+ {
+ for (unsigned int rows = 2; rows <= 4; ++rows)
+ {
+ writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
+ writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
+ }
+ }
+ }
+ else
+ {
+ for (unsigned int size = 2; size <= 4; ++size)
+ {
+ writeMatrixRoundingHelper(sink, size, size, "angle_frm");
+ writeMatrixRoundingHelper(sink, size, size, "angle_frl");
+ }
+ }
+}
+
+void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
+ const char *lType,
+ const char *rType,
+ const char *opStr,
+ const char *opNameStr)
+{
+ std::string lTypeStr = getTypeString(lType);
+ std::string rTypeStr = getTypeString(rType);
+
+ // Note that y should be passed through angle_frm at the function call site,
+ // but x can't be passed through angle_frm there since it is an inout parameter.
+ // So only pass x and the result through angle_frm here.
+ // clang-format off
+ sink <<
+ lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
+ " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
+ " return x;\n"
+ "}\n";
+ sink <<
+ lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
+ " x = angle_frl(angle_frl(x) " << opStr << " y);\n"
+ " return x;\n"
+ "}\n";
+ // clang-format on
+}
+
+std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
+{
+ return glslType;
+}
+
+std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
+{
+ std::stringstream typeStrStr;
+ typeStrStr << "highp " << glslType;
+ return typeStrStr.str();
+}
+
+void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
+{
// Unoptimized version of angle_frm for single floats:
//
- // int webgl_maxNormalExponent(in int exponentBits) {
+ // int webgl_maxNormalExponent(in int exponentBits)
+ // {
// int possibleExponents = int(exp2(float(exponentBits)));
// int exponentBias = possibleExponents / 2 - 1;
// int allExponentBitsOne = possibleExponents - 1;
// return (allExponentBitsOne - 1) - exponentBias;
// }
//
- // float angle_frm(in float x) {
+ // float angle_frm(in float x)
+ // {
// int mantissaBits = 10;
// int exponentBits = 5;
// float possibleMantissas = exp2(float(mantissaBits));
// float mantissaMax = 2.0 - 1.0 / possibleMantissas;
// int maxNE = webgl_maxNormalExponent(exponentBits);
// float max = exp2(float(maxNE)) * mantissaMax;
- // if (x > max) {
+ // if (x > max)
+ // {
// return max;
// }
- // if (x < -max) {
+ // if (x < -max)
+ // {
// return -max;
// }
// float exponent = floor(log2(abs(x)));
- // if (abs(x) == 0.0 || exponent < -float(maxNE)) {
+ // if (abs(x) == 0.0 || exponent < -float(maxNE))
+ // {
// return 0.0 * sign(x)
// }
// x = x * exp2(-(exponent - float(mantissaBits)));
@@ -109,140 +227,215 @@ static void writeCommonPrecisionEmulationHelpers(TInfoSinkBase& sink, ShShaderOu
// numbers will be flushed to zero either way (2^-15 is the smallest
// normal positive number), this does not introduce any error.
- std::string floatType = "float";
- if (outputLanguage == SH_ESSL_OUTPUT)
- floatType = "highp float";
+ std::string floatType = getTypeString("float");
+
+ // clang-format off
+ sink <<
+ floatType << " angle_frm(in " << floatType << " x) {\n"
+ " x = clamp(x, -65504.0, 65504.0);\n"
+ " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
+ " bool isNonZero = (exponent >= -25.0);\n"
+ " x = x * exp2(-exponent);\n"
+ " x = sign(x) * floor(abs(x));\n"
+ " return x * exp2(exponent) * float(isNonZero);\n"
+ "}\n";
+
+ sink <<
+ floatType << " angle_frl(in " << floatType << " x) {\n"
+ " x = clamp(x, -2.0, 2.0);\n"
+ " x = x * 256.0;\n"
+ " x = sign(x) * floor(abs(x));\n"
+ " return x * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
+
+void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
+ const unsigned int size)
+{
+ std::stringstream vecTypeStrStr;
+ vecTypeStrStr << "vec" << size;
+ std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
+ // clang-format off
sink <<
- floatType << " angle_frm(in " << floatType << " x) {\n"
- " x = clamp(x, -65504.0, 65504.0);\n"
- " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
- " bool isNonZero = (exponent >= -25.0);\n"
- " x = x * exp2(-exponent);\n"
- " x = sign(x) * floor(abs(x));\n"
- " return x * exp2(exponent) * float(isNonZero);\n"
- "}\n";
+ vecType << " angle_frm(in " << vecType << " v) {\n"
+ " v = clamp(v, -65504.0, 65504.0);\n"
+ " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
+ " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
+ " v = v * exp2(-exponent);\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
+ "}\n";
sink <<
- floatType << " angle_frl(in " << floatType << " x) {\n"
- " x = clamp(x, -2.0, 2.0);\n"
- " x = x * 256.0;\n"
- " x = sign(x) * floor(abs(x));\n"
- " return x * 0.00390625;\n"
- "}\n";
+ vecType << " angle_frl(in " << vecType << " v) {\n"
+ " v = clamp(v, -2.0, 2.0);\n"
+ " v = v * 256.0;\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
- writeVectorPrecisionEmulationHelpers(sink, outputLanguage, 2);
- writeVectorPrecisionEmulationHelpers(sink, outputLanguage, 3);
- writeVectorPrecisionEmulationHelpers(sink, outputLanguage, 4);
- for (unsigned int size = 2; size <= 4; ++size)
+void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName)
+{
+ std::stringstream matTypeStrStr;
+ matTypeStrStr << "mat" << columns;
+ if (rows != columns)
{
- writeMatrixPrecisionEmulationHelper(sink, outputLanguage, size, "angle_frm");
- writeMatrixPrecisionEmulationHelper(sink, outputLanguage, size, "angle_frl");
+ matTypeStrStr << "x" << rows;
+ }
+ std::string matType = getTypeString(matTypeStrStr.str().c_str());
+
+ sink << matType << " " << functionName << "(in " << matType << " m) {\n"
+ << " " << matType << " rounded;\n";
+
+ for (unsigned int i = 0; i < columns; ++i)
+ {
+ sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
}
+
+ sink << " return rounded;\n"
+ "}\n";
}
-static void writeCompoundAssignmentPrecisionEmulation(
- TInfoSinkBase& sink, ShShaderOutput outputLanguage,
- const char *lType, const char *rType, const char *opStr, const char *opNameStr)
+static const char *GetHLSLTypeStr(const char *floatTypeStr)
{
- std::string lTypeStr = lType;
- std::string rTypeStr = rType;
- if (outputLanguage == SH_ESSL_OUTPUT)
+ if (strcmp(floatTypeStr, "float") == 0)
+ {
+ return "float";
+ }
+ if (strcmp(floatTypeStr, "vec2") == 0)
+ {
+ return "float2";
+ }
+ if (strcmp(floatTypeStr, "vec3") == 0)
+ {
+ return "float3";
+ }
+ if (strcmp(floatTypeStr, "vec4") == 0)
+ {
+ return "float4";
+ }
+ if (strcmp(floatTypeStr, "mat2") == 0)
+ {
+ return "float2x2";
+ }
+ if (strcmp(floatTypeStr, "mat3") == 0)
{
- std::stringstream lTypeStrStr;
- lTypeStrStr << "highp " << lType;
- lTypeStr = lTypeStrStr.str();
- std::stringstream rTypeStrStr;
- rTypeStrStr << "highp " << rType;
- rTypeStr = rTypeStrStr.str();
+ return "float3x3";
}
+ if (strcmp(floatTypeStr, "mat4") == 0)
+ {
+ return "float4x4";
+ }
+ if (strcmp(floatTypeStr, "mat2x3") == 0)
+ {
+ return "float2x3";
+ }
+ if (strcmp(floatTypeStr, "mat2x4") == 0)
+ {
+ return "float2x4";
+ }
+ if (strcmp(floatTypeStr, "mat3x2") == 0)
+ {
+ return "float3x2";
+ }
+ if (strcmp(floatTypeStr, "mat3x4") == 0)
+ {
+ return "float3x4";
+ }
+ if (strcmp(floatTypeStr, "mat4x2") == 0)
+ {
+ return "float4x2";
+ }
+ if (strcmp(floatTypeStr, "mat4x3") == 0)
+ {
+ return "float4x3";
+ }
+ UNREACHABLE();
+ return nullptr;
+}
- // Note that y should be passed through angle_frm at the function call site,
- // but x can't be passed through angle_frm there since it is an inout parameter.
- // So only pass x and the result through angle_frm here.
+std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
+{
+ return GetHLSLTypeStr(glslType);
+}
+
+void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
+{
+ // In HLSL scalars are the same as 1-vectors.
+ writeVectorRoundingHelpers(sink, 1);
+}
+
+void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
+ const unsigned int size)
+{
+ std::stringstream vecTypeStrStr;
+ vecTypeStrStr << "float" << size;
+ std::string vecType = vecTypeStrStr.str();
+
+ // clang-format off
sink <<
- lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
- " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
- " return x;\n"
- "}\n";
+ vecType << " angle_frm(" << vecType << " v) {\n"
+ " v = clamp(v, -65504.0, 65504.0);\n"
+ " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
+ " bool" << size << " isNonZero = exponent < -25.0;\n"
+ " v = v * exp2(-exponent);\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
+ "}\n";
+
sink <<
- lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
- " x = angle_frl(angle_frm(x) " << opStr << " y);\n"
- " return x;\n"
- "}\n";
+ vecType << " angle_frl(" << vecType << " v) {\n"
+ " v = clamp(v, -2.0, 2.0);\n"
+ " v = v * 256.0;\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * 0.00390625;\n"
+ "}\n";
+ // clang-format on
}
-const char *getFloatTypeStr(const TType& type)
+void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName)
{
- switch (type.getNominalSize())
+ std::stringstream matTypeStrStr;
+ matTypeStrStr << "float" << columns << "x" << rows;
+ std::string matType = matTypeStrStr.str();
+
+ sink << matType << " " << functionName << "(" << matType << " m) {\n"
+ << " " << matType << " rounded;\n";
+
+ for (unsigned int i = 0; i < columns; ++i)
{
- case 1:
- return "float";
- case 2:
- switch(type.getSecondarySize())
- {
- case 1:
- return "vec2";
- case 2:
- return "mat2";
- case 3:
- return "mat2x3";
- case 4:
- return "mat2x4";
- default:
- UNREACHABLE();
- return NULL;
- }
- case 3:
- switch(type.getSecondarySize())
- {
- case 1:
- return "vec3";
- case 2:
- return "mat3x2";
- case 3:
- return "mat3";
- case 4:
- return "mat3x4";
- default:
- UNREACHABLE();
- return NULL;
- }
- case 4:
- switch(type.getSecondarySize())
- {
- case 1:
- return "vec4";
- case 2:
- return "mat4x2";
- case 3:
- return "mat4x3";
- case 4:
- return "mat4";
- default:
- UNREACHABLE();
- return NULL;
- }
- default:
- UNREACHABLE();
- return NULL;
+ sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
}
+
+ sink << " return rounded;\n"
+ "}\n";
}
bool canRoundFloat(const TType &type)
{
- return type.getBasicType() == EbtFloat && !type.isNonSquareMatrix() && !type.isArray() &&
- (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
+ return type.getBasicType() == EbtFloat && !type.isArray() &&
+ (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
}
-TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child)
+TIntermAggregate *createInternalFunctionCallNode(const TType &type,
+ TString name,
+ TIntermSequence *arguments)
{
- TIntermAggregate *callNode = new TIntermAggregate();
- callNode->setOp(EOpFunctionCall);
- TName nameObj(TFunction::mangleName(name));
+ TName nameObj(name);
nameObj.setInternal(true);
- callNode->setNameObj(nameObj);
- callNode->getSequence()->push_back(child);
+ TIntermAggregate *callNode =
+ TIntermAggregate::Create(type, EOpCallInternalRawFunction, arguments);
+ callNode->getFunctionSymbolInfo()->setNameObj(nameObj);
return callNode;
}
@@ -252,63 +445,92 @@ TIntermAggregate *createRoundingFunctionCallNode(TIntermTyped *roundedChild)
if (roundedChild->getPrecision() == EbpMedium)
roundFunctionName = "angle_frm";
else
- roundFunctionName = "angle_frl";
- return createInternalFunctionCallNode(roundFunctionName, roundedChild);
+ roundFunctionName = "angle_frl";
+ TIntermSequence *arguments = new TIntermSequence();
+ arguments->push_back(roundedChild);
+ TIntermAggregate *callNode =
+ createInternalFunctionCallNode(roundedChild->getType(), roundFunctionName, arguments);
+ callNode->getFunctionSymbolInfo()->setKnownToNotHaveSideEffects(true);
+ return callNode;
}
-TIntermAggregate *createCompoundAssignmentFunctionCallNode(TIntermTyped *left, TIntermTyped *right, const char *opNameStr)
+TIntermAggregate *createCompoundAssignmentFunctionCallNode(TIntermTyped *left,
+ TIntermTyped *right,
+ const char *opNameStr)
{
std::stringstream strstr;
if (left->getPrecision() == EbpMedium)
strstr << "angle_compound_" << opNameStr << "_frm";
else
strstr << "angle_compound_" << opNameStr << "_frl";
- TString functionName = strstr.str().c_str();
- TIntermAggregate *callNode = createInternalFunctionCallNode(functionName, left);
- callNode->getSequence()->push_back(right);
- return callNode;
+ TString functionName = strstr.str().c_str();
+ TIntermSequence *arguments = new TIntermSequence();
+ arguments->push_back(left);
+ arguments->push_back(right);
+ return createInternalFunctionCallNode(left->getType(), functionName, arguments);
}
-bool parentUsesResult(TIntermNode* parent, TIntermNode* node)
+bool ParentUsesResult(TIntermNode *parent, TIntermTyped *node)
{
if (!parent)
{
return false;
}
- TIntermAggregate *aggParent = parent->getAsAggregate();
- // If the parent's op is EOpSequence, the result is not assigned anywhere,
+ TIntermBlock *blockParent = parent->getAsBlock();
+ // If the parent is a block, the result is not assigned anywhere,
// so rounding it is not needed. In particular, this can avoid a lot of
// unnecessary rounding of unused return values of assignment.
- if (aggParent && aggParent->getOp() == EOpSequence)
+ if (blockParent)
{
return false;
}
- if (aggParent && aggParent->getOp() == EOpComma && (aggParent->getSequence()->back() != node))
+ TIntermBinary *binaryParent = parent->getAsBinaryNode();
+ if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
{
return false;
}
return true;
}
+bool ParentConstructorTakesCareOfRounding(TIntermNode *parent, TIntermTyped *node)
+{
+ if (!parent)
+ {
+ return false;
+ }
+ TIntermAggregate *parentConstructor = parent->getAsAggregate();
+ if (!parentConstructor || parentConstructor->getOp() != EOpConstruct)
+ {
+ return false;
+ }
+ if (parentConstructor->getPrecision() != node->getPrecision())
+ {
+ return false;
+ }
+ return canRoundFloat(parentConstructor->getType());
+}
+
} // namespace anonymous
-EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion)
+EmulatePrecision::EmulatePrecision(TSymbolTable *symbolTable, int shaderVersion)
: TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion),
mDeclaringVariables(false)
-{}
+{
+}
void EmulatePrecision::visitSymbol(TIntermSymbol *node)
{
- if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere())
+ TIntermNode *parent = getParentNode();
+ if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
+ !ParentConstructorTakesCareOfRounding(parent, node) && !mDeclaringVariables &&
+ !isLValueRequiredHere())
{
- TIntermNode *parent = getParentNode();
TIntermNode *replacement = createRoundingFunctionCallNode(node);
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
+ queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
}
}
-
bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
{
bool visitChildren = true;
@@ -319,189 +541,211 @@ bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
if (op == EOpInitialize && visit == InVisit)
mDeclaringVariables = false;
- if ((op == EOpIndexDirectStruct || op == EOpVectorSwizzle) && visit == InVisit)
+ if ((op == EOpIndexDirectStruct) && visit == InVisit)
visitChildren = false;
if (visit != PreVisit)
return visitChildren;
- const TType& type = node->getType();
- bool roundFloat = canRoundFloat(type);
-
- if (roundFloat) {
- switch (op) {
- // Math operators that can result in a float may need to apply rounding to the return
- // value. Note that in the case of assignment, the rounding is applied to its return
- // value here, not the value being assigned.
- case EOpAssign:
- case EOpAdd:
- case EOpSub:
- case EOpMul:
- case EOpDiv:
- case EOpVectorTimesScalar:
- case EOpVectorTimesMatrix:
- case EOpMatrixTimesVector:
- case EOpMatrixTimesScalar:
- case EOpMatrixTimesMatrix:
- {
- TIntermNode *parent = getParentNode();
- if (!parentUsesResult(parent, node))
+ const TType &type = node->getType();
+ bool roundFloat = canRoundFloat(type);
+
+ if (roundFloat)
+ {
+ switch (op)
+ {
+ // Math operators that can result in a float may need to apply rounding to the return
+ // value. Note that in the case of assignment, the rounding is applied to its return
+ // value here, not the value being assigned.
+ case EOpAssign:
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpDiv:
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
{
+ TIntermNode *parent = getParentNode();
+ if (!ParentUsesResult(parent, node) ||
+ ParentConstructorTakesCareOfRounding(parent, node))
+ {
+ break;
+ }
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
break;
}
- TIntermNode *replacement = createRoundingFunctionCallNode(node);
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
- break;
- }
- // Compound assignment cases need to replace the operator with a function call.
- case EOpAddAssign:
- {
- mEmulateCompoundAdd.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
- TIntermNode *parent = getParentNode();
- TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "add");
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
- break;
- }
- case EOpSubAssign:
- {
- mEmulateCompoundSub.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
- TIntermNode *parent = getParentNode();
- TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "sub");
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
- break;
- }
- case EOpMulAssign:
- case EOpVectorTimesMatrixAssign:
- case EOpVectorTimesScalarAssign:
- case EOpMatrixTimesScalarAssign:
- case EOpMatrixTimesMatrixAssign:
- {
- mEmulateCompoundMul.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
- TIntermNode *parent = getParentNode();
- TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "mul");
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
- break;
- }
- case EOpDivAssign:
- {
- mEmulateCompoundDiv.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
- TIntermNode *parent = getParentNode();
- TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "div");
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
- break;
- }
- default:
- // The rest of the binary operations should not need precision emulation.
- break;
+ // Compound assignment cases need to replace the operator with a function call.
+ case EOpAddAssign:
+ {
+ mEmulateCompoundAdd.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "add");
+ queueReplacement(replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpSubAssign:
+ {
+ mEmulateCompoundSub.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "sub");
+ queueReplacement(replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ {
+ mEmulateCompoundMul.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "mul");
+ queueReplacement(replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpDivAssign:
+ {
+ mEmulateCompoundDiv.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "div");
+ queueReplacement(replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ default:
+ // The rest of the binary operations should not need precision emulation.
+ break;
}
}
return visitChildren;
}
+bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ // Variable or interface block declaration.
+ if (visit == PreVisit)
+ {
+ mDeclaringVariables = true;
+ }
+ else if (visit == InVisit)
+ {
+ mDeclaringVariables = true;
+ }
+ else
+ {
+ mDeclaringVariables = false;
+ }
+ return true;
+}
+
+bool EmulatePrecision::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
+{
+ return false;
+}
+
+bool EmulatePrecision::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
+{
+ return false;
+}
+
bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
{
- bool visitChildren = true;
+ if (visit != PreVisit)
+ return true;
switch (node->getOp())
{
- case EOpSequence:
- case EOpConstructStruct:
- case EOpFunction:
- break;
- case EOpPrototype:
- visitChildren = false;
- break;
- case EOpParameters:
- visitChildren = false;
- break;
- case EOpInvariantDeclaration:
- visitChildren = false;
- break;
- case EOpDeclaration:
- // Variable declaration.
- if (visit == PreVisit)
- {
- mDeclaringVariables = true;
- }
- else if (visit == InVisit)
- {
- mDeclaringVariables = true;
- }
- else
- {
- mDeclaringVariables = false;
- }
- break;
- case EOpFunctionCall:
- {
- // Function call.
- if (visit == PreVisit)
- {
- // User-defined function return values are not rounded, this relies on that
- // calculations producing the value were rounded.
+ case EOpCallInternalRawFunction:
+ case EOpCallFunctionInAST:
+ // User-defined function return values are not rounded. The calculations that produced
+ // the value inside the function definition should have been rounded.
+ break;
+ case EOpConstruct:
+ if (node->getBasicType() == EbtStruct)
+ {
+ break;
+ }
+ default:
TIntermNode *parent = getParentNode();
- if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
- parentUsesResult(parent, node))
+ if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
+ !ParentConstructorTakesCareOfRounding(parent, node))
{
TIntermNode *replacement = createRoundingFunctionCallNode(node);
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
+ queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
}
- }
- break;
- }
- default:
- TIntermNode *parent = getParentNode();
- if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node))
- {
- TIntermNode *replacement = createRoundingFunctionCallNode(node);
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
- }
- break;
+ break;
}
- return visitChildren;
+ return true;
}
bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
{
switch (node->getOp())
{
- case EOpNegative:
- case EOpVectorLogicalNot:
- case EOpLogicalNot:
- case EOpPostIncrement:
- case EOpPostDecrement:
- case EOpPreIncrement:
- case EOpPreDecrement:
- break;
- default:
- if (canRoundFloat(node->getType()) && visit == PreVisit)
- {
- TIntermNode *parent = getParentNode();
- TIntermNode *replacement = createRoundingFunctionCallNode(node);
- mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
- }
- break;
+ case EOpNegative:
+ case EOpLogicalNot:
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ case EOpLogicalNotComponentWise:
+ break;
+ default:
+ if (canRoundFloat(node->getType()) && visit == PreVisit)
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
+ }
+ break;
}
return true;
}
-void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage)
+void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
+ const int shaderVersion,
+ const ShShaderOutput outputLanguage)
{
- // Other languages not yet supported
- ASSERT(outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
- IsGLSL130OrNewer(outputLanguage) ||
- outputLanguage == SH_ESSL_OUTPUT);
- writeCommonPrecisionEmulationHelpers(sink, outputLanguage);
+ std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
+ RoundingHelperWriter::createHelperWriter(outputLanguage));
+
+ roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
EmulationSet::const_iterator it;
for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
- writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "+", "add");
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
- writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "-", "sub");
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
- writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "/", "div");
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
- writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "*", "mul");
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
+}
+
+// static
+bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
+{
+ switch (outputLanguage)
+ {
+ case SH_HLSL_4_1_OUTPUT:
+ case SH_ESSL_OUTPUT:
+ return true;
+ default:
+ // Other languages not yet supported
+ return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
+ sh::IsGLSL130OrNewer(outputLanguage));
+ }
}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h
index 08177b3414..8044465410 100644
--- a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h
+++ b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h
@@ -7,34 +7,43 @@
#ifndef COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
#define COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
+#include "GLSLANG/ShaderLang.h"
#include "common/angleutils.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/IntermNode.h"
-#include "GLSLANG/ShaderLang.h"
+#include "compiler/translator/IntermTraverse.h"
// This class gathers all compound assignments from the AST and can then write
// the functions required for their precision emulation. This way there is no
// need to write a huge number of variations of the emulated compound assignment
// to every translated shader with emulation enabled.
+namespace sh
+{
+
class EmulatePrecision : public TLValueTrackingTraverser
{
public:
- EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion);
+ EmulatePrecision(TSymbolTable *symbolTable, int shaderVersion);
void visitSymbol(TIntermSymbol *node) override;
bool visitBinary(Visit visit, TIntermBinary *node) override;
bool visitUnary(Visit visit, TIntermUnary *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override;
+
+ void writeEmulationHelpers(TInfoSinkBase &sink,
+ const int shaderVersion,
+ const ShShaderOutput outputLanguage);
- void writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage);
+ static bool SupportedInLanguage(const ShShaderOutput outputLanguage);
private:
struct TypePair
{
- TypePair(const char *l, const char *r)
- : lType(l), rType(r) { }
+ TypePair(const char *l, const char *r) : lType(l), rType(r) {}
const char *lType;
const char *rType;
@@ -42,7 +51,7 @@ class EmulatePrecision : public TLValueTrackingTraverser
struct TypePairComparator
{
- bool operator() (const TypePair& l, const TypePair& r) const
+ bool operator()(const TypePair &l, const TypePair &r) const
{
if (l.lType == r.lType)
return l.rType < r.rType;
@@ -59,4 +68,6 @@ class EmulatePrecision : public TLValueTrackingTraverser
bool mDeclaringVariables;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp b/src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
new file mode 100644
index 0000000000..f17dd73657
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
@@ -0,0 +1,153 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of the integer pow expressions HLSL bug workaround.
+// See header for more info.
+
+#include "compiler/translator/ExpandIntegerPowExpressions.h"
+
+#include <cmath>
+#include <cstdlib>
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root, TSymbolTable *symbolTable);
+
+ private:
+ Traverser(TSymbolTable *symbolTable);
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ void nextIteration();
+
+ bool mFound = false;
+};
+
+// static
+void Traverser::Apply(TIntermNode *root, TSymbolTable *symbolTable)
+{
+ Traverser traverser(symbolTable);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ traverser.updateTree();
+ }
+ } while (traverser.mFound);
+}
+
+Traverser::Traverser(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
+{
+}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+ nextTemporaryId();
+}
+
+bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Test 0: skip non-pow operators.
+ if (node->getOp() != EOpPow)
+ {
+ return true;
+ }
+
+ const TIntermSequence *sequence = node->getSequence();
+ ASSERT(sequence->size() == 2u);
+ const TIntermConstantUnion *constantNode = sequence->at(1)->getAsConstantUnion();
+
+ // Test 1: check for a single constant.
+ if (!constantNode || constantNode->getNominalSize() != 1)
+ {
+ return true;
+ }
+
+ const TConstantUnion *constant = constantNode->getUnionArrayPointer();
+
+ TConstantUnion asFloat;
+ asFloat.cast(EbtFloat, *constant);
+
+ float value = asFloat.getFConst();
+
+ // Test 2: value is in the problematic range.
+ if (value < -5.0f || value > 9.0f)
+ {
+ return true;
+ }
+
+ // Test 3: value is integer or pretty close to an integer.
+ float absval = std::abs(value);
+ float frac = absval - std::round(absval);
+ if (frac > 0.0001f)
+ {
+ return true;
+ }
+
+ // Test 4: skip -1, 0, and 1
+ int exponent = static_cast<int>(value);
+ int n = std::abs(exponent);
+ if (n < 2)
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround.
+ nextTemporaryId();
+
+ TIntermTyped *lhs = sequence->at(0)->getAsTyped();
+ ASSERT(lhs);
+
+ TIntermDeclaration *init = createTempInitDeclaration(lhs);
+ TIntermTyped *current = createTempSymbol(lhs->getType());
+
+ insertStatementInParentBlock(init);
+
+ // Create a chain of n-1 multiples.
+ for (int i = 1; i < n; ++i)
+ {
+ TIntermBinary *mul = new TIntermBinary(EOpMul, current, createTempSymbol(lhs->getType()));
+ mul->setLine(node->getLine());
+ current = mul;
+ }
+
+ // For negative pow, compute the reciprocal of the positive pow.
+ if (exponent < 0)
+ {
+ TConstantUnion *oneVal = new TConstantUnion();
+ oneVal->setFConst(1.0f);
+ TIntermConstantUnion *oneNode = new TIntermConstantUnion(oneVal, node->getType());
+ TIntermBinary *div = new TIntermBinary(EOpDiv, oneNode, current);
+ current = div;
+ }
+
+ queueReplacement(current, OriginalNode::IS_DROPPED);
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+void ExpandIntegerPowExpressions(TIntermNode *root, TSymbolTable *symbolTable)
+{
+ Traverser::Apply(root, symbolTable);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.h b/src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.h
new file mode 100644
index 0000000000..0074e5d663
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around a bug in the HLSL compiler optimizer with "pow" that
+// manifests under the following conditions:
+//
+// - If pow() has a literal exponent value
+// - ... and this value is integer or within 10e-6 of an integer
+// - ... and it is in {-4, -3, -2, 2, 3, 4, 5, 6, 7, 8}
+//
+// The workaround is to replace the pow with a series of multiplies.
+// See http://anglebug.com/851
+
+#ifndef COMPILER_TRANSLATOR_EXPANDINTEGERPOWEXPRESSIONS_H_
+#define COMPILER_TRANSLATOR_EXPANDINTEGERPOWEXPRESSIONS_H_
+
+namespace sh
+{
+
+class TIntermNode;
+class TSymbolTable;
+
+void ExpandIntegerPowExpressions(TIntermNode *root, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_EXPANDINTEGERPOWEXPRESSIONS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.cpp b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.cpp
new file mode 100644
index 0000000000..3f910b9050
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.cpp
@@ -0,0 +1,96 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ExtensionBehavior.cpp: Extension name enumeration and data structures for storing extension
+// behavior.
+
+#include "compiler/translator/ExtensionBehavior.h"
+
+#include "common/debug.h"
+
+#include <string.h>
+
+#define LIST_EXTENSIONS(OP) \
+ OP(ARB_texture_rectangle) \
+ OP(ARM_shader_framebuffer_fetch) \
+ OP(EXT_blend_func_extended) \
+ OP(EXT_draw_buffers) \
+ OP(EXT_frag_depth) \
+ OP(EXT_geometry_shader) \
+ OP(EXT_shader_framebuffer_fetch) \
+ OP(EXT_shader_texture_lod) \
+ OP(EXT_YUV_target) \
+ OP(NV_EGL_stream_consumer_external) \
+ OP(NV_shader_framebuffer_fetch) \
+ OP(OES_EGL_image_external) \
+ OP(OES_EGL_image_external_essl3) \
+ OP(OES_geometry_shader) \
+ OP(OES_standard_derivatives) \
+ OP(OVR_multiview)
+
+namespace sh
+{
+
+#define RETURN_EXTENSION_NAME_CASE(ext) \
+ case TExtension::ext: \
+ return "GL_" #ext;
+
+const char *GetExtensionNameString(TExtension extension)
+{
+ switch (extension)
+ {
+ LIST_EXTENSIONS(RETURN_EXTENSION_NAME_CASE)
+ default:
+ UNREACHABLE();
+ return "";
+ }
+}
+
+#define RETURN_EXTENSION_IF_NAME_MATCHES(ext) \
+ if (strcmp(extWithoutGLPrefix, #ext) == 0) \
+ { \
+ return TExtension::ext; \
+ }
+
+TExtension GetExtensionByName(const char *extension)
+{
+ // If first characters of the extension don't equal "GL_", early out.
+ if (strncmp(extension, "GL_", 3) != 0)
+ {
+ return TExtension::UNDEFINED;
+ }
+ const char *extWithoutGLPrefix = extension + 3;
+
+ LIST_EXTENSIONS(RETURN_EXTENSION_IF_NAME_MATCHES)
+
+ return TExtension::UNDEFINED;
+}
+
+const char *GetBehaviorString(TBehavior b)
+{
+ switch (b)
+ {
+ case EBhRequire:
+ return "require";
+ case EBhEnable:
+ return "enable";
+ case EBhWarn:
+ return "warn";
+ case EBhDisable:
+ return "disable";
+ default:
+ return nullptr;
+ }
+}
+
+bool IsExtensionEnabled(const TExtensionBehavior &extBehavior, TExtension extension)
+{
+ ASSERT(extension != TExtension::UNDEFINED);
+ auto iter = extBehavior.find(extension);
+ return iter != extBehavior.end() &&
+ (iter->second == EBhEnable || iter->second == EBhRequire || iter->second == EBhWarn);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h
index 782c1c9217..09cc03f10f 100644
--- a/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h
+++ b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h
@@ -3,41 +3,58 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// ExtensionBehavior.h: Extension name enumeration and data structures for storing extension
+// behavior.
#ifndef COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
#define COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
#include <map>
-#include <string>
-typedef enum
+namespace sh
+{
+
+enum class TExtension
+{
+ UNDEFINED, // Special value used to indicate no extension.
+
+ ARB_texture_rectangle,
+ ARM_shader_framebuffer_fetch,
+ EXT_blend_func_extended,
+ EXT_draw_buffers,
+ EXT_frag_depth,
+ EXT_geometry_shader,
+ EXT_shader_framebuffer_fetch,
+ EXT_shader_texture_lod,
+ EXT_YUV_target,
+ NV_EGL_stream_consumer_external,
+ NV_shader_framebuffer_fetch,
+ OES_EGL_image_external,
+ OES_EGL_image_external_essl3,
+ OES_geometry_shader,
+ OES_standard_derivatives,
+ OVR_multiview
+};
+
+enum TBehavior
{
EBhRequire,
EBhEnable,
EBhWarn,
EBhDisable,
EBhUndefined
-} TBehavior;
+};
-inline const char* getBehaviorString(TBehavior b)
-{
- switch(b)
- {
- case EBhRequire: return "require";
- case EBhEnable: return "enable";
- case EBhWarn: return "warn";
- case EBhDisable: return "disable";
- default: return NULL;
- }
-}
-
-// Mapping between extension name and behavior.
-typedef std::map<std::string, TBehavior> TExtensionBehavior;
-
-inline bool IsExtensionEnabled(const TExtensionBehavior &extBehavior, const char *extension)
-{
- auto iter = extBehavior.find(extension);
- return iter != extBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire);
-}
+const char *GetExtensionNameString(TExtension extension);
+TExtension GetExtensionByName(const char *extension);
+
+const char *GetBehaviorString(TBehavior b);
+
+// Mapping between extension id and behavior.
+typedef std::map<TExtension, TBehavior> TExtensionBehavior;
+
+bool IsExtensionEnabled(const TExtensionBehavior &extBehavior, TExtension extension);
+
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
+#endif // COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp
index d7f45f7eef..5b5dc580e9 100644
--- a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp
@@ -10,6 +10,9 @@
#include "compiler/translator/VersionGLSL.h"
+namespace sh
+{
+
TExtensionGLSL::TExtensionGLSL(ShShaderOutput output)
: TIntermTraverser(true, false, false), mTargetVersion(ShaderOutputTypeToGLSLVersion(output))
{
@@ -98,3 +101,5 @@ void TExtensionGLSL::checkOperator(TIntermOperator *node)
break;
}
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h
index 6bb84d612d..a1b9cb2225 100644
--- a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h
@@ -12,7 +12,10 @@
#include <set>
#include <string>
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
// Traverses the intermediate tree to determine which GLSL extensions are required
// to support the shader.
@@ -36,4 +39,6 @@ class TExtensionGLSL : public TIntermTraverser
std::set<std::string> mRequiredExtensions;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_EXTENSIONGLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/FindMain.cpp b/src/3rdparty/angle/src/compiler/translator/FindMain.cpp
new file mode 100644
index 0000000000..7417fbac8a
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/FindMain.cpp
@@ -0,0 +1,38 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// FindMain.cpp: Find the main() function definition in a given AST.
+
+#include "compiler/translator/FindMain.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+TIntermFunctionDefinition *FindMain(TIntermBlock *root)
+{
+ for (TIntermNode *node : *root->getSequence())
+ {
+ TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
+ if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
+ {
+ return nodeFunction;
+ }
+ }
+ return nullptr;
+}
+
+TIntermBlock *FindMainBody(TIntermBlock *root)
+{
+ TIntermFunctionDefinition *main = FindMain(root);
+ ASSERT(main != nullptr);
+ TIntermBlock *mainBody = main->getBody();
+ ASSERT(mainBody != nullptr);
+ return mainBody;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/FindMain.h b/src/3rdparty/angle/src/compiler/translator/FindMain.h
new file mode 100644
index 0000000000..bf2c45d871
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/FindMain.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// FindMain.h: Adds functions to get the main function definition and its body.
+
+#ifndef COMPILER_TRANSLATOR_FINDMAIN_H_
+#define COMPILER_TRANSLATOR_FINDMAIN_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TIntermFunctionDefinition;
+
+TIntermFunctionDefinition *FindMain(TIntermBlock *root);
+TIntermBlock *FindMainBody(TIntermBlock *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_FINDMAIN_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/FindSymbolNode.cpp b/src/3rdparty/angle/src/compiler/translator/FindSymbolNode.cpp
new file mode 100644
index 0000000000..a2a10f128d
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/FindSymbolNode.cpp
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// FindSymbol.cpp:
+// Utility for finding a symbol node inside an AST tree.
+
+#include "compiler/translator/FindSymbolNode.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class SymbolFinder : public TIntermTraverser
+{
+ public:
+ SymbolFinder(const TString &symbolName, TBasicType basicType)
+ : TIntermTraverser(true, false, false),
+ mSymbolName(symbolName),
+ mNodeFound(nullptr),
+ mBasicType(basicType)
+ {
+ }
+
+ void visitSymbol(TIntermSymbol *node)
+ {
+ if (node->getBasicType() == mBasicType && node->getSymbol() == mSymbolName)
+ {
+ mNodeFound = node;
+ }
+ }
+
+ bool isFound() const { return mNodeFound != nullptr; }
+ const TIntermSymbol *getNode() const { return mNodeFound; }
+
+ private:
+ TString mSymbolName;
+ TIntermSymbol *mNodeFound;
+ TBasicType mBasicType;
+};
+
+} // anonymous namespace
+
+const TIntermSymbol *FindSymbolNode(TIntermNode *root,
+ const TString &symbolName,
+ TBasicType basicType)
+{
+ SymbolFinder finder(symbolName, basicType);
+ root->traverse(&finder);
+ return finder.getNode();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/FindSymbolNode.h b/src/3rdparty/angle/src/compiler/translator/FindSymbolNode.h
new file mode 100644
index 0000000000..08dfb9a222
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/FindSymbolNode.h
@@ -0,0 +1,27 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// FindSymbolNode.h:
+// Utility for finding a symbol node inside an AST tree.
+
+#ifndef COMPILER_TRANSLATOR_FIND_SYMBOL_H_
+#define COMPILER_TRANSLATOR_FIND_SYMBOL_H_
+
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Common.h"
+
+namespace sh
+{
+
+class TIntermNode;
+class TIntermSymbol;
+
+const TIntermSymbol *FindSymbolNode(TIntermNode *root,
+ const TString &symbolName,
+ TBasicType basicType);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_FIND_SYMBOL_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.cpp b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.cpp
index a751b768b7..fba837f55c 100644
--- a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.cpp
@@ -3,75 +3,73 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// FlagStd140Structs.cpp: Find structs in std140 blocks, where the padding added in the translator
+// conflicts with the "natural" unpadded type.
#include "compiler/translator/FlagStd140Structs.h"
+#include "compiler/translator/IntermTraverse.h"
+
namespace sh
{
-bool FlagStd140Structs::visitBinary(Visit visit, TIntermBinary *binaryNode)
+namespace
{
- if (binaryNode->getRight()->getBasicType() == EbtStruct)
- {
- switch (binaryNode->getOp())
- {
- case EOpIndexDirectInterfaceBlock:
- case EOpIndexDirectStruct:
- if (isInStd140InterfaceBlock(binaryNode->getLeft()))
- {
- mFlaggedNodes.push_back(binaryNode);
- }
- break;
-
- default: break;
- }
- return false;
- }
- if (binaryNode->getOp() == EOpIndexDirectStruct)
- {
- return false;
- }
+class FlagStd140StructsTraverser : public TIntermTraverser
+{
+ public:
+ FlagStd140StructsTraverser() : TIntermTraverser(true, false, false) {}
- return visit == PreVisit;
-}
+ const std::vector<MappedStruct> getMappedStructs() const { return mMappedStructs; }
+
+ protected:
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+
+ private:
+ void mapBlockStructMembers(TIntermSymbol *blockDeclarator, TInterfaceBlock *block);
+
+ std::vector<MappedStruct> mMappedStructs;
+};
-void FlagStd140Structs::visitSymbol(TIntermSymbol *symbol)
+void FlagStd140StructsTraverser::mapBlockStructMembers(TIntermSymbol *blockDeclarator,
+ TInterfaceBlock *block)
{
- if (isInStd140InterfaceBlock(symbol) && symbol->getBasicType() == EbtStruct)
+ for (auto *field : block->fields())
{
- mFlaggedNodes.push_back(symbol);
+ if (field->type()->getBasicType() == EbtStruct)
+ {
+ MappedStruct mappedStruct;
+ mappedStruct.blockDeclarator = blockDeclarator;
+ mappedStruct.field = field;
+ mMappedStructs.push_back(mappedStruct);
+ }
}
}
-bool FlagStd140Structs::isInStd140InterfaceBlock(TIntermTyped *node) const
+bool FlagStd140StructsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
{
- TIntermBinary *binaryNode = node->getAsBinaryNode();
-
- if (binaryNode)
- {
- return isInStd140InterfaceBlock(binaryNode->getLeft());
- }
-
- const TType &type = node->getType();
-
- // determine if we are in the standard layout
- const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
- if (interfaceBlock)
+ TIntermTyped *declarator = node->getSequence()->back()->getAsTyped();
+ if (declarator->getBasicType() == EbtInterfaceBlock)
{
- return (interfaceBlock->blockStorage() == EbsStd140);
+ TInterfaceBlock *block = declarator->getType().getInterfaceBlock();
+ if (block->blockStorage() == EbsStd140)
+ {
+ mapBlockStructMembers(declarator->getAsSymbolNode(), block);
+ }
}
-
return false;
}
-std::vector<TIntermTyped *> FlagStd140ValueStructs(TIntermNode *node)
+} // anonymous namespace
+
+std::vector<MappedStruct> FlagStd140Structs(TIntermNode *node)
{
- FlagStd140Structs flaggingTraversal;
+ FlagStd140StructsTraverser flaggingTraversal;
node->traverse(&flaggingTraversal);
- return flaggingTraversal.getFlaggedNodes();
+ return flaggingTraversal.getMappedStructs();
}
-}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h
index cfcd775af7..f548d8b6ed 100644
--- a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h
+++ b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h
@@ -3,41 +3,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// FlagStd140Structs.h: Find structs in std140 blocks, where the padding added in the translator
+// conflicts with the "natural" unpadded type.
#ifndef COMPILER_TRANSLATOR_FLAGSTD140STRUCTS_H_
#define COMPILER_TRANSLATOR_FLAGSTD140STRUCTS_H_
-#include "compiler/translator/IntermNode.h"
+#include <vector>
namespace sh
{
-// This class finds references to nested structs of std140 blocks that access
-// the nested struct "by value", where the padding added in the translator
-// conflicts with the "natural" unpadded type.
-class FlagStd140Structs : public TIntermTraverser
-{
- public:
-
- FlagStd140Structs()
- : TIntermTraverser(true, false, false)
- {
- }
-
- const std::vector<TIntermTyped *> getFlaggedNodes() const { return mFlaggedNodes; }
+class TField;
+class TIntermNode;
+class TIntermSymbol;
- protected:
- bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
- void visitSymbol(TIntermSymbol *symbol) override;
-
- private:
- bool isInStd140InterfaceBlock(TIntermTyped *node) const;
-
- std::vector<TIntermTyped *> mFlaggedNodes;
+struct MappedStruct
+{
+ TIntermSymbol *blockDeclarator;
+ TField *field;
};
-std::vector<TIntermTyped *> FlagStd140ValueStructs(TIntermNode *node);
-
+std::vector<MappedStruct> FlagStd140Structs(TIntermNode *node);
}
-#endif // COMPILER_TRANSLATOR_FLAGSTD140STRUCTS_H_
+#endif // COMPILER_TRANSLATOR_FLAGSTD140STRUCTS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp
deleted file mode 100644
index 4cc1c26a13..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/ForLoopUnroll.h"
-
-#include "compiler/translator/ValidateLimitations.h"
-#include "angle_gl.h"
-
-bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node)
-{
- if (mUnrollCondition != kSamplerArrayIndex)
- return true;
-
- // If a sampler array index is also the loop index,
- // 1) if the index type is integer, mark the loop for unrolling;
- // 2) if the index type if float, set a flag to later fail compile.
- switch (node->getOp())
- {
- case EOpIndexIndirect:
- if (node->getLeft() != NULL && node->getRight() != NULL && node->getLeft()->getAsSymbolNode())
- {
- TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();
- if (IsSampler(symbol->getBasicType()) && symbol->isArray() && !mLoopStack.empty())
- {
- mVisitSamplerArrayIndexNodeInsideLoop = true;
- node->getRight()->traverse(this);
- mVisitSamplerArrayIndexNodeInsideLoop = false;
- // We have already visited all the children.
- return false;
- }
- }
- break;
- default:
- break;
- }
- return true;
-}
-
-bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node)
-{
- bool canBeUnrolled = mHasRunLoopValidation;
- if (!mHasRunLoopValidation)
- {
- canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node);
- }
- if (mUnrollCondition == kIntegerIndex && canBeUnrolled)
- {
- // Check if loop index type is integer.
- // This is called after ValidateLimitations pass, so the loop has the limited form specified
- // in ESSL 1.00 appendix A.
- TIntermSequence *declSeq = node->getInit()->getAsAggregate()->getSequence();
- TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
- if (symbol->getBasicType() == EbtInt)
- node->setUnrollFlag(true);
- }
-
- TIntermNode *body = node->getBody();
- if (body != nullptr)
- {
- if (canBeUnrolled)
- {
- mLoopStack.push(node);
- body->traverse(this);
- mLoopStack.pop();
- }
- else
- {
- body->traverse(this);
- }
- }
- // The loop is fully processed - no need to visit children.
- return false;
-}
-
-void ForLoopUnrollMarker::visitSymbol(TIntermSymbol* symbol)
-{
- if (!mVisitSamplerArrayIndexNodeInsideLoop)
- return;
- TIntermLoop *loop = mLoopStack.findLoop(symbol);
- if (loop)
- {
- switch (symbol->getBasicType())
- {
- case EbtFloat:
- mSamplerArrayIndexIsFloatLoopIndex = true;
- break;
- case EbtInt:
- loop->setUnrollFlag(true);
- break;
- default:
- UNREACHABLE();
- }
- }
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h
deleted file mode 100644
index 9c49ecad33..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
-#define COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
-
-#include "compiler/translator/LoopInfo.h"
-
-// This class detects for-loops that needs to be unrolled.
-// Currently we support two unroll conditions:
-// 1) kForLoopWithIntegerIndex: unroll if the index type is integer.
-// 2) kForLoopWithSamplerArrayIndex: unroll where a sampler array index
-// is also the loop integer index, and reject and fail a compile
-// where a sampler array index is also the loop float index.
-class ForLoopUnrollMarker : public TIntermTraverser
-{
- public:
- enum UnrollCondition
- {
- kIntegerIndex,
- kSamplerArrayIndex
- };
-
- ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation)
- : TIntermTraverser(true, false, false),
- mUnrollCondition(condition),
- mSamplerArrayIndexIsFloatLoopIndex(false),
- mVisitSamplerArrayIndexNodeInsideLoop(false),
- mHasRunLoopValidation(hasRunLoopValidation)
- {
- }
-
- bool visitBinary(Visit, TIntermBinary *node) override;
- bool visitLoop(Visit, TIntermLoop *node) override;
- void visitSymbol(TIntermSymbol *node) override;
-
- bool samplerArrayIndexIsFloatLoopIndex() const
- {
- return mSamplerArrayIndexIsFloatLoopIndex;
- }
-
- private:
- UnrollCondition mUnrollCondition;
- TLoopStack mLoopStack;
- bool mSamplerArrayIndexIsFloatLoopIndex;
- bool mVisitSamplerArrayIndexNodeInsideLoop;
- bool mHasRunLoopValidation;
-};
-
-#endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/HashNames.cpp b/src/3rdparty/angle/src/compiler/translator/HashNames.cpp
new file mode 100644
index 0000000000..6bc90faf94
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/HashNames.cpp
@@ -0,0 +1,72 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/HashNames.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// GLSL ES 3.00.6 section 3.9: the maximum length of an identifier is 1024 characters.
+static const unsigned int kESSLMaxIdentifierLength = 1024u;
+
+static const char *kHashedNamePrefix = "webgl_";
+
+// Can't prefix with just _ because then we might introduce a double underscore, which is not safe
+// in GLSL (ESSL 3.00.6 section 3.8: All identifiers containing a double underscore are reserved for
+// use by the underlying implementation). u is short for user-defined.
+static const char *kUnhashedNamePrefix = "_u";
+static const unsigned int kUnhashedNamePrefixLength = 2u;
+
+TString HashName(const TString &name, ShHashFunction64 hashFunction)
+{
+ ASSERT(!name.empty());
+ ASSERT(hashFunction);
+ khronos_uint64_t number = (*hashFunction)(name.c_str(), name.length());
+ TStringStream stream;
+ stream << kHashedNamePrefix << std::hex << number;
+ TString hashedName = stream.str();
+ return hashedName;
+}
+
+} // anonymous namespace
+
+TString HashName(const TName &name, ShHashFunction64 hashFunction, NameMap *nameMap)
+{
+ if (name.getString().empty() || name.isInternal())
+ {
+ return name.getString();
+ }
+ if (hashFunction == nullptr)
+ {
+ if (name.getString().length() + kUnhashedNamePrefixLength > kESSLMaxIdentifierLength)
+ {
+ // If the identifier length is already close to the limit, we can't prefix it. This is
+ // not a problem since there are no builtins or ANGLE's internal variables that would
+ // have as long names and could conflict.
+ return name.getString();
+ }
+ return kUnhashedNamePrefix + name.getString();
+ }
+ if (nameMap)
+ {
+ NameMap::const_iterator it = nameMap->find(name.getString().c_str());
+ if (it != nameMap->end())
+ return it->second.c_str();
+ }
+ TString hashedName = HashName(name.getString(), hashFunction);
+ if (nameMap)
+ {
+ (*nameMap)[name.getString().c_str()] = hashedName.c_str();
+ }
+ return hashedName;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/HashNames.h b/src/3rdparty/angle/src/compiler/translator/HashNames.h
index 09c959f9da..28e861b309 100644
--- a/src/3rdparty/angle/src/compiler/translator/HashNames.h
+++ b/src/3rdparty/angle/src/compiler/translator/HashNames.h
@@ -9,10 +9,20 @@
#include <map>
-#include "compiler/translator/IntermNode.h"
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/translator/Common.h"
-#define HASHED_NAME_PREFIX "webgl_"
+namespace sh
+{
typedef std::map<TPersistString, TPersistString> NameMap;
+class TName;
+
+// Hash user-defined name for GLSL output, with special handling for internal names.
+// The nameMap parameter is optional and is used to cache hashed names if set.
+TString HashName(const TName &name, ShHashFunction64 hashFunction, NameMap *nameMap);
+
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_HASHNAMES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.cpp
new file mode 100644
index 0000000000..40b5e1f1e2
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.cpp
@@ -0,0 +1,304 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ImageFunctionHLSL: Class for writing implementations of ESSL image functions into HLSL output.
+//
+
+#include "compiler/translator/ImageFunctionHLSL.h"
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+
+// static
+void ImageFunctionHLSL::OutputImageFunctionArgumentList(
+ TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction)
+{
+ if (imageFunction.readonly)
+ {
+ out << TextureString(imageFunction.image, imageFunction.imageInternalFormat) << " tex";
+ }
+ else
+ {
+ out << RWTextureString(imageFunction.image, imageFunction.imageInternalFormat) << " tex";
+ }
+
+ if (imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::LOAD ||
+ imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::STORE)
+ {
+ switch (imageFunction.image)
+ {
+ case EbtImage2D:
+ case EbtIImage2D:
+ case EbtUImage2D:
+ out << ", int2 p";
+ break;
+ case EbtImage3D:
+ case EbtIImage3D:
+ case EbtUImage3D:
+ case EbtImageCube:
+ case EbtIImageCube:
+ case EbtUImageCube:
+ case EbtImage2DArray:
+ case EbtIImage2DArray:
+ case EbtUImage2DArray:
+ out << ", int3 p";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::STORE)
+ {
+ switch (imageFunction.image)
+ {
+ case EbtImage2D:
+ case EbtImage3D:
+ case EbtImageCube:
+ case EbtImage2DArray:
+ out << ", float4 data";
+ break;
+ case EbtIImage2D:
+ case EbtIImage3D:
+ case EbtIImageCube:
+ case EbtIImage2DArray:
+ out << ", int4 data";
+ break;
+ case EbtUImage2D:
+ case EbtUImage3D:
+ case EbtUImageCube:
+ case EbtUImage2DArray:
+ out << ", uint4 data";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+}
+
+// static
+void ImageFunctionHLSL::OutputImageSizeFunctionBody(
+ TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction,
+ const TString &imageReference)
+{
+ if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+ IsImageCube(imageFunction.image))
+ {
+ // "depth" stores either the number of layers in an array texture or 3D depth
+ out << " uint width; uint height; uint depth;\n"
+ << " " << imageReference << ".GetDimensions(width, height, depth);\n";
+ }
+ else if (IsImage2D(imageFunction.image))
+ {
+ out << " uint width; uint height;\n"
+ << " " << imageReference << ".GetDimensions(width, height);\n";
+ }
+ else
+ UNREACHABLE();
+
+ if (strcmp(imageFunction.getReturnType(), "int3") == 0)
+ {
+ out << " return int3(width, height, depth);\n";
+ }
+ else
+ {
+ out << " return int2(width, height);\n";
+ }
+}
+
+// static
+void ImageFunctionHLSL::OutputImageLoadFunctionBody(
+ TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction,
+ const TString &imageReference)
+{
+ if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+ IsImageCube(imageFunction.image))
+ {
+ out << " return " << imageReference << "[uint3(p.x, p.y, p.z)];\n";
+ }
+ else if (IsImage2D(imageFunction.image))
+ {
+ out << " return " << imageReference << "[uint2(p.x, p.y)];\n";
+ }
+ else
+ UNREACHABLE();
+}
+
+// static
+void ImageFunctionHLSL::OutputImageStoreFunctionBody(
+ TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction,
+ const TString &imageReference)
+{
+ if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+ IsImage2D(imageFunction.image) || IsImageCube(imageFunction.image))
+ {
+ out << " " << imageReference << "[p] = data;\n";
+ }
+ else
+ UNREACHABLE();
+}
+
+TString ImageFunctionHLSL::ImageFunction::name() const
+{
+ TString name = "gl_image";
+ if (readonly)
+ {
+ name += TextureTypeSuffix(image, imageInternalFormat);
+ }
+ else
+ {
+ name += RWTextureTypeSuffix(image, imageInternalFormat);
+ }
+
+ switch (method)
+ {
+ case Method::SIZE:
+ name += "Size";
+ break;
+ case Method::LOAD:
+ name += "Load";
+ break;
+ case Method::STORE:
+ name += "Store";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return name;
+}
+
+const char *ImageFunctionHLSL::ImageFunction::getReturnType() const
+{
+ if (method == ImageFunction::Method::SIZE)
+ {
+ switch (image)
+ {
+ case EbtImage2D:
+ case EbtIImage2D:
+ case EbtUImage2D:
+ case EbtImageCube:
+ case EbtIImageCube:
+ case EbtUImageCube:
+ return "int2";
+ case EbtImage3D:
+ case EbtIImage3D:
+ case EbtUImage3D:
+ case EbtImage2DArray:
+ case EbtIImage2DArray:
+ case EbtUImage2DArray:
+ return "int3";
+ default:
+ UNREACHABLE();
+ }
+ }
+ else if (method == ImageFunction::Method::LOAD)
+ {
+ switch (image)
+ {
+ case EbtImage2D:
+ case EbtImage3D:
+ case EbtImageCube:
+ case EbtImage2DArray:
+ return "float4";
+ case EbtIImage2D:
+ case EbtIImage3D:
+ case EbtIImageCube:
+ case EbtIImage2DArray:
+ return "int4";
+ case EbtUImage2D:
+ case EbtUImage3D:
+ case EbtUImageCube:
+ case EbtUImage2DArray:
+ return "uint4";
+ default:
+ UNREACHABLE();
+ }
+ }
+ else if (method == ImageFunction::Method::STORE)
+ {
+ return "void";
+ }
+ else
+ {
+ UNREACHABLE();
+ }
+ return "";
+}
+
+bool ImageFunctionHLSL::ImageFunction::operator<(const ImageFunction &rhs) const
+{
+ return std::tie(image, imageInternalFormat, readonly, method) <
+ std::tie(rhs.image, rhs.imageInternalFormat, rhs.readonly, rhs.method);
+}
+
+TString ImageFunctionHLSL::useImageFunction(const TString &name,
+ const TBasicType &type,
+ TLayoutImageInternalFormat imageInternalFormat,
+ bool readonly)
+{
+ ASSERT(IsImage(type));
+ ImageFunction imageFunction;
+ imageFunction.image = type;
+ imageFunction.imageInternalFormat = imageInternalFormat;
+ imageFunction.readonly = readonly;
+
+ if (name == "imageSize")
+ {
+ imageFunction.method = ImageFunction::Method::SIZE;
+ }
+ else if (name == "imageLoad")
+ {
+ imageFunction.method = ImageFunction::Method::LOAD;
+ }
+ else if (name == "imageStore")
+ {
+ imageFunction.method = ImageFunction::Method::STORE;
+ }
+ else
+ UNREACHABLE();
+
+ mUsesImage.insert(imageFunction);
+ return imageFunction.name();
+}
+
+void ImageFunctionHLSL::imageFunctionHeader(TInfoSinkBase &out)
+{
+ for (const ImageFunction &imageFunction : mUsesImage)
+ {
+ // Function header
+ out << imageFunction.getReturnType() << " " << imageFunction.name() << "(";
+
+ OutputImageFunctionArgumentList(out, imageFunction);
+
+ out << ")\n"
+ "{\n";
+
+ TString imageReference("tex");
+
+ if (imageFunction.method == ImageFunction::Method::SIZE)
+ {
+ OutputImageSizeFunctionBody(out, imageFunction, imageReference);
+ }
+ else if (imageFunction.method == ImageFunction::Method::LOAD)
+ {
+ OutputImageLoadFunctionBody(out, imageFunction, imageReference);
+ }
+ else
+ {
+ OutputImageStoreFunctionBody(out, imageFunction, imageReference);
+ }
+
+ out << "}\n"
+ "\n";
+ }
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.h b/src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.h
new file mode 100644
index 0000000000..9db17a6bbf
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.h
@@ -0,0 +1,76 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ImageFunctionHLSL: Class for writing implementations of ESSL image functions into HLSL output.
+//
+
+#ifndef COMPILER_TRANSLATOR_IMAGEFUNCTIONHLSL_H_
+#define COMPILER_TRANSLATOR_IMAGEFUNCTIONHLSL_H_
+
+#include <set>
+
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Common.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/Types.h"
+
+namespace sh
+{
+
+class ImageFunctionHLSL final : angle::NonCopyable
+{
+ public:
+ // Returns the name of the image function implementation to caller.
+ // The name that's passed in is the name of the GLSL image function that it should implement.
+ TString useImageFunction(const TString &name,
+ const TBasicType &type,
+ TLayoutImageInternalFormat imageInternalFormat,
+ bool readonly);
+
+ void imageFunctionHeader(TInfoSinkBase &out);
+
+ private:
+ struct ImageFunction
+ {
+ // See ESSL 3.10.4 section 8.12 for reference about what the different methods below do.
+ enum class Method
+ {
+ SIZE,
+ LOAD,
+ STORE
+ };
+
+ TString name() const;
+
+ bool operator<(const ImageFunction &rhs) const;
+
+ const char *getReturnType() const;
+
+ TBasicType image;
+ TLayoutImageInternalFormat imageInternalFormat;
+ bool readonly;
+ Method method;
+ };
+
+ static void OutputImageFunctionArgumentList(
+ TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction);
+ static void OutputImageSizeFunctionBody(TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction,
+ const TString &imageReference);
+ static void OutputImageLoadFunctionBody(TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction,
+ const TString &imageReference);
+ static void OutputImageStoreFunctionBody(TInfoSinkBase &out,
+ const ImageFunctionHLSL::ImageFunction &imageFunction,
+ const TString &imageReference);
+ using ImageFunctionSet = std::set<ImageFunction>;
+ ImageFunctionSet mUsesImage;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_IMAGEFUNCTIONHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/InfoSink.cpp b/src/3rdparty/angle/src/compiler/translator/InfoSink.cpp
index cd59658ff7..db26aa67e8 100644
--- a/src/3rdparty/angle/src/compiler/translator/InfoSink.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/InfoSink.cpp
@@ -6,32 +6,27 @@
#include "compiler/translator/InfoSink.h"
-void TInfoSinkBase::prefix(TPrefixType p) {
- switch(p) {
- case EPrefixNone:
- break;
- case EPrefixWarning:
+namespace sh
+{
+
+void TInfoSinkBase::prefix(Severity severity)
+{
+ switch (severity)
+ {
+ case SH_WARNING:
sink.append("WARNING: ");
break;
- case EPrefixError:
+ case SH_ERROR:
sink.append("ERROR: ");
break;
- case EPrefixInternalError:
- sink.append("INTERNAL ERROR: ");
- break;
- case EPrefixUnimplemented:
- sink.append("UNIMPLEMENTED: ");
- break;
- case EPrefixNote:
- sink.append("NOTE: ");
- break;
default:
sink.append("UNKOWN ERROR: ");
break;
}
}
-void TInfoSinkBase::location(int file, int line) {
+void TInfoSinkBase::location(int file, int line)
+{
TPersistStringStream stream;
if (line)
stream << file << ":" << line;
@@ -42,13 +37,4 @@ void TInfoSinkBase::location(int file, int line) {
sink.append(stream.str());
}
-void TInfoSinkBase::location(const TSourceLoc& loc) {
- location(loc.first_file, loc.first_line);
-}
-
-void TInfoSinkBase::message(TPrefixType p, const TSourceLoc& loc, const char* m) {
- prefix(p);
- location(loc);
- sink.append(m);
- sink.append("\n");
-}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/InfoSink.h b/src/3rdparty/angle/src/compiler/translator/InfoSink.h
index f47fafa8ee..2705f48d59 100644
--- a/src/3rdparty/angle/src/compiler/translator/InfoSink.h
+++ b/src/3rdparty/angle/src/compiler/translator/InfoSink.h
@@ -10,38 +10,32 @@
#include <math.h>
#include <stdlib.h>
#include "compiler/translator/Common.h"
+#include "compiler/translator/Severity.h"
+
+namespace sh
+{
// Returns the fractional part of the given floating-point number.
-inline float fractionalPart(float f) {
- float intPart = 0.0f;
- return modff(f, &intPart);
+inline float fractionalPart(float f)
+{
+ float intPart = 0.0f;
+ return modff(f, &intPart);
}
//
-// TPrefixType is used to centralize how info log messages start.
-// See below.
-//
-enum TPrefixType {
- EPrefixNone,
- EPrefixWarning,
- EPrefixError,
- EPrefixInternalError,
- EPrefixUnimplemented,
- EPrefixNote
-};
-
-//
// Encapsulate info logs for all objects that have them.
//
// The methods are a general set of tools for getting a variety of
// messages and types inserted into the log.
//
-class TInfoSinkBase {
-public:
+class TInfoSinkBase
+{
+ public:
TInfoSinkBase() {}
template <typename T>
- TInfoSinkBase& operator<<(const T& t) {
+ TInfoSinkBase &operator<<(const T &t)
+ {
TPersistStringStream stream;
stream << t;
sink.append(stream.str());
@@ -49,33 +43,41 @@ public:
}
// Override << operator for specific types. It is faster to append strings
// and characters directly to the sink.
- TInfoSinkBase& operator<<(char c) {
+ TInfoSinkBase &operator<<(char c)
+ {
sink.append(1, c);
return *this;
}
- TInfoSinkBase& operator<<(const char* str) {
+ TInfoSinkBase &operator<<(const char *str)
+ {
sink.append(str);
return *this;
}
- TInfoSinkBase& operator<<(const TPersistString& str) {
+ TInfoSinkBase &operator<<(const TPersistString &str)
+ {
sink.append(str);
return *this;
}
- TInfoSinkBase& operator<<(const TString& str) {
+ TInfoSinkBase &operator<<(const TString &str)
+ {
sink.append(str.c_str());
return *this;
}
// Make sure floats are written with correct precision.
- TInfoSinkBase& operator<<(float f) {
+ TInfoSinkBase &operator<<(float f)
+ {
// Make sure that at least one decimal point is written. If a number
// does not have a fractional part, the default precision format does
// not write the decimal portion which gets interpreted as integer by
// the compiler.
TPersistStringStream stream;
- if (fractionalPart(f) == 0.0f) {
+ if (fractionalPart(f) == 0.0f)
+ {
stream.precision(1);
stream << std::showpoint << std::fixed << f;
- } else {
+ }
+ else
+ {
stream.unsetf(std::ios::fixed);
stream.unsetf(std::ios::scientific);
stream.precision(8);
@@ -85,8 +87,9 @@ public:
return *this;
}
// Write boolean values as their names instead of integral value.
- TInfoSinkBase& operator<<(bool b) {
- const char* str = b ? "true" : "false";
+ TInfoSinkBase &operator<<(bool b)
+ {
+ const char *str = b ? "true" : "false";
sink.append(str);
return *this;
}
@@ -94,23 +97,24 @@ public:
void erase() { sink.clear(); }
int size() { return static_cast<int>(sink.size()); }
- const TPersistString& str() const { return sink; }
- const char* c_str() const { return sink.c_str(); }
+ const TPersistString &str() const { return sink; }
+ const char *c_str() const { return sink.c_str(); }
- void prefix(TPrefixType p);
+ void prefix(Severity severity);
void location(int file, int line);
- void location(const TSourceLoc& loc);
- void message(TPrefixType p, const TSourceLoc& loc, const char* m);
-private:
+ private:
TPersistString sink;
};
-class TInfoSink {
-public:
+class TInfoSink
+{
+ public:
TInfoSinkBase info;
TInfoSinkBase debug;
TInfoSinkBase obj;
};
-#endif // COMPILER_TRANSLATOR_INFOSINK_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INFOSINK_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Initialize.cpp b/src/3rdparty/angle/src/compiler/translator/Initialize.cpp
index 2f51aada7f..6f8baee96b 100644
--- a/src/3rdparty/angle/src/compiler/translator/Initialize.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/Initialize.cpp
@@ -6,7 +6,7 @@
//
// Create symbols that declare built-in definitions, add built-ins that
-// cannot be expressed in the files, and establish mappings between
+// cannot be expressed in the files, and establish mappings between
// built-in functions and operators.
//
@@ -16,18 +16,25 @@
#include "compiler/translator/IntermNode.h"
#include "angle_gl.h"
-void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &symbolTable)
+namespace sh
{
- const TType *float1 = TCache::getType(EbtFloat);
- const TType *float2 = TCache::getType(EbtFloat, 2);
- const TType *float3 = TCache::getType(EbtFloat, 3);
- const TType *float4 = TCache::getType(EbtFloat, 4);
- const TType *int1 = TCache::getType(EbtInt);
- const TType *int2 = TCache::getType(EbtInt, 2);
- const TType *int3 = TCache::getType(EbtInt, 3);
- const TType *uint1 = TCache::getType(EbtUInt);
- const TType *bool1 = TCache::getType(EbtBool);
- const TType *genType = TCache::getType(EbtGenType);
+
+void InsertBuiltInFunctions(sh::GLenum type,
+ ShShaderSpec spec,
+ const ShBuiltInResources &resources,
+ TSymbolTable &symbolTable)
+{
+ const TType *voidType = TCache::getType(EbtVoid);
+ const TType *float1 = TCache::getType(EbtFloat);
+ const TType *float2 = TCache::getType(EbtFloat, 2);
+ const TType *float3 = TCache::getType(EbtFloat, 3);
+ const TType *float4 = TCache::getType(EbtFloat, 4);
+ const TType *int1 = TCache::getType(EbtInt);
+ const TType *int2 = TCache::getType(EbtInt, 2);
+ const TType *int3 = TCache::getType(EbtInt, 3);
+ const TType *uint1 = TCache::getType(EbtUInt);
+ const TType *bool1 = TCache::getType(EbtBool);
+ const TType *genType = TCache::getType(EbtGenType);
const TType *genIType = TCache::getType(EbtGenIType);
const TType *genUType = TCache::getType(EbtGenUType);
const TType *genBType = TCache::getType(EbtGenBType);
@@ -35,113 +42,117 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
//
// Angle and Trigonometric Functions.
//
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpRadians, genType, "radians", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpDegrees, genType, "degrees", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSin, genType, "sin", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpCos, genType, "cos", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpTan, genType, "tan", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAsin, genType, "asin", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAcos, genType, "acos", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAtan, genType, "atan", genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAtan, genType, "atan", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpSinh, genType, "sinh", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpCosh, genType, "cosh", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTanh, genType, "tanh", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAsinh, genType, "asinh", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAcosh, genType, "acosh", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAtanh, genType, "atanh", genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpRadians, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpDegrees, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSin, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpCos, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpTan, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAsin, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAcos, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAtan, genType, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAtan, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpSinh, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpCosh, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTanh, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpAsinh, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpAcosh, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpAtanh, genType, genType);
//
// Exponential Functions.
//
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpPow, genType, "pow", genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpExp, genType, "exp", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLog, genType, "log", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpExp2, genType, "exp2", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLog2, genType, "log2", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSqrt, genType, "sqrt", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpInverseSqrt, genType, "inversesqrt", genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpPow, genType, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpExp, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLog, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpExp2, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLog2, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSqrt, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpInverseSqrt, genType, genType);
//
// Common Functions.
//
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAbs, genType, "abs", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAbs, genIType, "abs", genIType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSign, genType, "sign", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpSign, genIType, "sign", genIType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpFloor, genType, "floor", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTrunc, genType, "trunc", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpRound, genType, "round", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpRoundEven, genType, "roundEven", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpCeil, genType, "ceil", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpFract, genType, "fract", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMod, genType, "mod", genType, float1);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMod, genType, "mod", genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMin, genType, "min", genType, float1);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMin, genType, "min", genType, genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genIType, "min", genIType, genIType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genIType, "min", genIType, int1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genUType, "min", genUType, genUType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genUType, "min", genUType, uint1);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMax, genType, "max", genType, float1);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMax, genType, "max", genType, genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genIType, "max", genIType, genIType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genIType, "max", genIType, int1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genUType, "max", genUType, genUType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genUType, "max", genUType, uint1);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpClamp, genType, "clamp", genType, float1, float1);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpClamp, genType, "clamp", genType, genType, genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genIType, "clamp", genIType, int1, int1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genIType, "clamp", genIType, genIType, genIType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genUType, "clamp", genUType, uint1, uint1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genUType, "clamp", genUType, genUType, genUType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, float1);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMix, genType, "mix", genType, genType, genBType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", float1, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", genType, genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", float1, float1, genType);
-
- const TType *outFloat1 = TCache::getType(EbtFloat, EvqOut);
- const TType *outFloat2 = TCache::getType(EbtFloat, EvqOut, 2);
- const TType *outFloat3 = TCache::getType(EbtFloat, EvqOut, 3);
- const TType *outFloat4 = TCache::getType(EbtFloat, EvqOut, 4);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float1, "modf", float1, outFloat1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float2, "modf", float2, outFloat2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float3, "modf", float3, outFloat3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float4, "modf", float4, outFloat4);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpIsNan, genBType, "isnan", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpIsInf, genBType, "isinf", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpFloatBitsToInt, genIType, "floatBitsToInt", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpFloatBitsToUint, genUType, "floatBitsToUint", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpIntBitsToFloat, genType, "intBitsToFloat", genIType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUintBitsToFloat, genType, "uintBitsToFloat", genUType);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpPackSnorm2x16, uint1, "packSnorm2x16", float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpPackUnorm2x16, uint1, "packUnorm2x16", float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpPackHalf2x16, uint1, "packHalf2x16", float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUnpackSnorm2x16, float2, "unpackSnorm2x16", uint1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUnpackUnorm2x16, float2, "unpackUnorm2x16", uint1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUnpackHalf2x16, float2, "unpackHalf2x16", uint1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAbs, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpAbs, genIType, genIType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSign, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpSign, genIType, genIType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpFloor, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTrunc, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpRound, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpRoundEven, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpCeil, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpFract, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMod, genType, genType, float1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMod, genType, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMin, genType, genType, float1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMin, genType, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMin, genIType, genIType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMin, genIType, genIType, int1);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMin, genUType, genUType, genUType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMin, genUType, genUType, uint1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMax, genType, genType, float1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMax, genType, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMax, genIType, genIType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMax, genIType, genIType, int1);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMax, genUType, genUType, genUType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMax, genUType, genUType, uint1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpClamp, genType, genType, float1, float1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpClamp, genType, genType, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpClamp, genIType, genIType, int1, int1);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpClamp, genIType, genIType, genIType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpClamp, genUType, genUType, uint1, uint1);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpClamp, genUType, genUType, genUType, genUType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMix, genType, genType, genType, float1);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMix, genType, genType, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMix, genType, genType, genType, genBType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpStep, genType, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpStep, genType, float1, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSmoothStep, genType, genType, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSmoothStep, genType, float1, float1, genType);
+
+ const TType *outGenType = TCache::getType(EbtGenType, EvqOut);
+ const TType *outGenIType = TCache::getType(EbtGenIType, EvqOut);
+
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpModf, genType, genType, outGenType);
+
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpIsNan, genBType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpIsInf, genBType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpFloatBitsToInt, genIType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpFloatBitsToUint, genUType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpIntBitsToFloat, genType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpUintBitsToFloat, genType, genUType);
+
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFrexp, genType, genType, outGenIType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpLdexp, genType, genType, genIType);
+
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpPackSnorm2x16, uint1, float2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpPackUnorm2x16, uint1, float2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpPackHalf2x16, uint1, float2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpUnpackSnorm2x16, float2, uint1);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpUnpackUnorm2x16, float2, uint1);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpUnpackHalf2x16, float2, uint1);
+
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpPackUnorm4x8, uint1, float4);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpPackSnorm4x8, uint1, float4);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUnpackUnorm4x8, float4, uint1);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUnpackSnorm4x8, float4, uint1);
//
// Geometric Functions.
//
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLength, float1, "length", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpDistance, float1, "distance", genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpDot, float1, "dot", genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpCross, float3, "cross", float3, float3);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpNormalize, genType, "normalize", genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpFaceForward, genType, "faceforward", genType, genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpReflect, genType, "reflect", genType, genType);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpRefract, genType, "refract", genType, genType, float1);
-
- const TType *mat2 = TCache::getType(EbtFloat, 2, 2);
- const TType *mat3 = TCache::getType(EbtFloat, 3, 3);
- const TType *mat4 = TCache::getType(EbtFloat, 4, 4);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLength, float1, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpDistance, float1, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpDot, float1, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpCross, float3, float3, float3);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpNormalize, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpFaceforward, genType, genType, genType,
+ genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpReflect, genType, genType, genType);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpRefract, genType, genType, genType, float1);
+
+ const TType *mat2 = TCache::getType(EbtFloat, 2, 2);
+ const TType *mat3 = TCache::getType(EbtFloat, 3, 3);
+ const TType *mat4 = TCache::getType(EbtFloat, 4, 4);
const TType *mat2x3 = TCache::getType(EbtFloat, 2, 3);
const TType *mat3x2 = TCache::getType(EbtFloat, 3, 2);
const TType *mat2x4 = TCache::getType(EbtFloat, 2, 4);
@@ -152,45 +163,45 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
//
// Matrix Functions.
//
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMul, mat2, "matrixCompMult", mat2, mat2);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMul, mat3, "matrixCompMult", mat3, mat3);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMul, mat4, "matrixCompMult", mat4, mat4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat2x3, "matrixCompMult", mat2x3, mat2x3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat3x2, "matrixCompMult", mat3x2, mat3x2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat2x4, "matrixCompMult", mat2x4, mat2x4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat4x2, "matrixCompMult", mat4x2, mat4x2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat3x4, "matrixCompMult", mat3x4, mat3x4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat4x3, "matrixCompMult", mat4x3, mat4x3);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat2, "outerProduct", float2, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat3, "outerProduct", float3, float3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat4, "outerProduct", float4, float4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat2x3, "outerProduct", float3, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat3x2, "outerProduct", float2, float3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat2x4, "outerProduct", float4, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat4x2, "outerProduct", float2, float4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat3x4, "outerProduct", float4, float3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat4x3, "outerProduct", float3, float4);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat2, "transpose", mat2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat3, "transpose", mat3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat4, "transpose", mat4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat2x3, "transpose", mat3x2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat3x2, "transpose", mat2x3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat2x4, "transpose", mat4x2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat4x2, "transpose", mat2x4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat3x4, "transpose", mat4x3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat4x3, "transpose", mat3x4);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDeterminant, float1, "determinant", mat2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDeterminant, float1, "determinant", mat3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDeterminant, float1, "determinant", mat4);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat2, "inverse", mat2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat3, "inverse", mat3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat4, "inverse", mat4);
-
- const TType *vec = TCache::getType(EbtVec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMulMatrixComponentWise, mat2, mat2, mat2);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMulMatrixComponentWise, mat3, mat3, mat3);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpMulMatrixComponentWise, mat4, mat4, mat4);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMulMatrixComponentWise, mat2x3, mat2x3, mat2x3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMulMatrixComponentWise, mat3x2, mat3x2, mat3x2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMulMatrixComponentWise, mat2x4, mat2x4, mat2x4);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMulMatrixComponentWise, mat4x2, mat4x2, mat4x2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMulMatrixComponentWise, mat3x4, mat3x4, mat3x4);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpMulMatrixComponentWise, mat4x3, mat4x3, mat4x3);
+
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat2, float2, float2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat3, float3, float3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat4, float4, float4);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat2x3, float3, float2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat3x2, float2, float3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat2x4, float4, float2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat4x2, float2, float4);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat3x4, float4, float3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpOuterProduct, mat4x3, float3, float4);
+
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat2, mat2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat3, mat3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat4, mat4);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat2x3, mat3x2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat3x2, mat2x3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat2x4, mat4x2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat4x2, mat2x4);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat3x4, mat4x3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpTranspose, mat4x3, mat3x4);
+
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpDeterminant, float1, mat2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpDeterminant, float1, mat3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpDeterminant, float1, mat4);
+
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpInverse, mat2, mat2);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpInverse, mat3, mat3);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpInverse, mat4, mat4);
+
+ const TType *vec = TCache::getType(EbtVec);
const TType *ivec = TCache::getType(EbtIVec);
const TType *uvec = TCache::getType(EbtUVec);
const TType *bvec = TCache::getType(EbtBVec);
@@ -198,31 +209,62 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
//
// Vector relational functions.
//
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThan, bvec, "lessThan", vec, vec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThan, bvec, "lessThan", ivec, ivec);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpLessThan, bvec, "lessThan", uvec, uvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThanEqual, bvec, "lessThanEqual", vec, vec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThanEqual, bvec, "lessThanEqual", ivec, ivec);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpLessThanEqual, bvec, "lessThanEqual", uvec, uvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThan, bvec, "greaterThan", vec, vec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThan, bvec, "greaterThan", ivec, ivec);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpGreaterThan, bvec, "greaterThan", uvec, uvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThanEqual, bvec, "greaterThanEqual", vec, vec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThanEqual, bvec, "greaterThanEqual", ivec, ivec);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpGreaterThanEqual, bvec, "greaterThanEqual", uvec, uvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorEqual, bvec, "equal", vec, vec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorEqual, bvec, "equal", ivec, ivec);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpVectorEqual, bvec, "equal", uvec, uvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorEqual, bvec, "equal", bvec, bvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", vec, vec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", ivec, ivec);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", uvec, uvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", bvec, bvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAny, bool1, "any", bvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAll, bool1, "all", bvec);
- symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorLogicalNot, bvec, "not", bvec);
-
- const TType *sampler2D = TCache::getType(EbtSampler2D);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLessThanComponentWise, bvec, vec, vec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLessThanComponentWise, bvec, ivec, ivec);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpLessThanComponentWise, bvec, uvec, uvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLessThanEqualComponentWise, bvec, vec, vec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLessThanEqualComponentWise, bvec, ivec, ivec);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpLessThanEqualComponentWise, bvec, uvec, uvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpGreaterThanComponentWise, bvec, vec, vec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpGreaterThanComponentWise, bvec, ivec, ivec);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpGreaterThanComponentWise, bvec, uvec, uvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpGreaterThanEqualComponentWise, bvec, vec, vec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpGreaterThanEqualComponentWise, bvec, ivec,
+ ivec);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpGreaterThanEqualComponentWise, bvec, uvec, uvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpEqualComponentWise, bvec, vec, vec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpEqualComponentWise, bvec, ivec, ivec);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpEqualComponentWise, bvec, uvec, uvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpEqualComponentWise, bvec, bvec, bvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpNotEqualComponentWise, bvec, vec, vec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpNotEqualComponentWise, bvec, ivec, ivec);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpNotEqualComponentWise, bvec, uvec, uvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpNotEqualComponentWise, bvec, bvec, bvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAny, bool1, bvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAll, bool1, bvec);
+ symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLogicalNotComponentWise, bvec, bvec);
+
+ //
+ // Integer functions
+ //
+ const TType *outGenUType = TCache::getType(EbtGenUType, EvqOut);
+
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldExtract, genIType, genIType, int1,
+ int1);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldExtract, genUType, genUType, int1,
+ int1);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldInsert, genIType, genIType, genIType,
+ int1, int1);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldInsert, genUType, genUType, genUType,
+ int1, int1);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldReverse, genIType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldReverse, genUType, genUType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitCount, genIType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitCount, genIType, genUType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindLSB, genIType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindLSB, genIType, genUType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindMSB, genIType, genIType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindMSB, genIType, genUType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUaddCarry, genUType, genUType, genUType,
+ outGenUType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUsubBorrow, genUType, genUType, genUType,
+ outGenUType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUmulExtended, voidType, genUType, genUType,
+ outGenUType, outGenUType);
+ symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpImulExtended, voidType, genIType, genIType,
+ outGenIType, outGenIType);
+
+ const TType *sampler2D = TCache::getType(EbtSampler2D);
const TType *samplerCube = TCache::getType(EbtSamplerCube);
//
@@ -233,13 +275,15 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float4);
symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCube", samplerCube, float3);
- if (resources.OES_EGL_image_external)
+ if (resources.OES_EGL_image_external || resources.NV_EGL_stream_consumer_external)
{
const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2D", samplerExternalOES, float2);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES, float3);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES, float4);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES,
+ float3);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES,
+ float4);
}
if (resources.ARB_texture_rectangle)
@@ -247,8 +291,10 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
const TType *sampler2DRect = TCache::getType(EbtSampler2DRect);
symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRect", sampler2DRect, float2);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect, float3);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect, float4);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect,
+ float3);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect,
+ float4);
}
if (resources.EXT_shader_texture_lod)
@@ -256,49 +302,68 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
/* The *Grad* variants are new to both vertex and fragment shaders; the fragment
* shader specific pieces are added separately below.
*/
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DGradEXT", sampler2D, float2, float2, float2);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjGradEXT", sampler2D, float3, float2, float2);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjGradEXT", sampler2D, float4, float2, float2);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "textureCubeGradEXT", samplerCube, float3, float3, float3);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "texture2DGradEXT", sampler2D, float2, float2, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "texture2DProjGradEXT", sampler2D, float3, float2, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "texture2DProjGradEXT", sampler2D, float4, float2, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "textureCubeGradEXT", samplerCube, float3, float3, float3);
}
if (type == GL_FRAGMENT_SHADER)
{
symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2D", sampler2D, float2, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float3, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float4, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCube", samplerCube, float3, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float3,
+ float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float4,
+ float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCube", samplerCube, float3,
+ float1);
if (resources.OES_standard_derivatives)
{
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, EOpDFdx, "GL_OES_standard_derivatives", genType, "dFdx", genType);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, EOpDFdy, "GL_OES_standard_derivatives", genType, "dFdy", genType);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, EOpFwidth, "GL_OES_standard_derivatives", genType, "fwidth", genType);
+ symbolTable.insertBuiltInOp(ESSL1_BUILTINS, EOpDFdx,
+ TExtension::OES_standard_derivatives, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL1_BUILTINS, EOpDFdy,
+ TExtension::OES_standard_derivatives, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL1_BUILTINS, EOpFwidth,
+ TExtension::OES_standard_derivatives, genType, genType);
}
if (resources.EXT_shader_texture_lod)
{
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DLodEXT", sampler2D, float2, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjLodEXT", sampler2D, float3, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjLodEXT", sampler2D, float4, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "textureCubeLodEXT", samplerCube, float3, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "texture2DLodEXT", sampler2D, float2, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "texture2DProjLodEXT", sampler2D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "texture2DProjLodEXT", sampler2D, float4, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, TExtension::EXT_shader_texture_lod, float4,
+ "textureCubeLodEXT", samplerCube, float3, float1);
}
}
if (type == GL_VERTEX_SHADER)
{
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DLod", sampler2D, float2, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProjLod", sampler2D, float3, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProjLod", sampler2D, float4, float1);
- symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCubeLod", samplerCube, float3, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DLod", sampler2D, float2,
+ float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProjLod", sampler2D, float3,
+ float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProjLod", sampler2D, float4,
+ float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCubeLod", samplerCube, float3,
+ float1);
}
const TType *gvec4 = TCache::getType(EbtGVec4);
- const TType *gsampler2D = TCache::getType(EbtGSampler2D);
- const TType *gsamplerCube = TCache::getType(EbtGSamplerCube);
- const TType *gsampler3D = TCache::getType(EbtGSampler3D);
+ const TType *gsampler2D = TCache::getType(EbtGSampler2D);
+ const TType *gsamplerCube = TCache::getType(EbtGSamplerCube);
+ const TType *gsampler3D = TCache::getType(EbtGSampler3D);
const TType *gsampler2DArray = TCache::getType(EbtGSampler2DArray);
+ const TType *gsampler2DMS = TCache::getType(EbtGSampler2DMS);
//
// Texture Functions for GLSL ES 3.0
@@ -315,32 +380,91 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLod", gsamplerCube, float3, float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLod", gsampler2DArray, float3, float1);
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "texture", samplerExternalOES, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float4);
+ }
+
+ if (resources.EXT_YUV_target)
+ {
+ const TType *samplerExternal2DY2YEXT = TCache::getType(EbtSamplerExternal2DY2YEXT);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float4, "texture",
+ samplerExternal2DY2YEXT, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float4, "textureProj",
+ samplerExternal2DY2YEXT, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float4, "textureProj",
+ samplerExternal2DY2YEXT, float4);
+
+ const TType *yuvCscStandardEXT = TCache::getType(EbtYuvCscStandardEXT);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float3, "rgb_2_yuv",
+ float3, yuvCscStandardEXT);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float3, "yuv_2_rgb",
+ float3, yuvCscStandardEXT);
+ }
+
if (type == GL_FRAGMENT_SHADER)
{
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler2D, float2, float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler3D, float3, float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsamplerCube, float3, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler2DArray, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler2DArray, float3,
+ float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler2D, float3, float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler2D, float4, float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler3D, float4, float1);
+
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "texture", samplerExternalOES, float2,
+ float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float4, float1);
+ }
+
+ if (resources.EXT_YUV_target)
+ {
+ const TType *samplerExternal2DY2YEXT = TCache::getType(EbtSamplerExternal2DY2YEXT);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float4, "texture",
+ samplerExternal2DY2YEXT, float2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float4,
+ "textureProj", samplerExternal2DY2YEXT, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float4,
+ "textureProj", samplerExternal2DY2YEXT, float4, float1);
+ }
}
- const TType *sampler2DShadow = TCache::getType(EbtSampler2DShadow);
- const TType *samplerCubeShadow = TCache::getType(EbtSamplerCubeShadow);
+ const TType *sampler2DShadow = TCache::getType(EbtSampler2DShadow);
+ const TType *samplerCubeShadow = TCache::getType(EbtSamplerCubeShadow);
const TType *sampler2DArrayShadow = TCache::getType(EbtSampler2DArrayShadow);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DShadow, float3);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", samplerCubeShadow, float4);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DArrayShadow, float4);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProj", sampler2DShadow, float4);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureLod", sampler2DShadow, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureLod", sampler2DShadow, float3,
+ float1);
if (type == GL_FRAGMENT_SHADER)
{
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DShadow, float3, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", samplerCubeShadow, float4, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProj", sampler2DShadow, float4, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DShadow, float3,
+ float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", samplerCubeShadow, float4,
+ float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProj", sampler2DShadow, float4,
+ float1);
}
symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", gsampler2D, int1);
@@ -350,135 +474,398 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", sampler2DShadow, int1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", samplerCubeShadow, int1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, int3, "textureSize", sampler2DArrayShadow, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", gsampler2DMS);
+
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", samplerExternalOES, int1);
+ }
+
+ if (resources.EXT_YUV_target)
+ {
+ const TType *samplerExternal2DY2YEXT = TCache::getType(EbtSamplerExternal2DY2YEXT);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, int2, "textureSize",
+ samplerExternal2DY2YEXT, int1);
+ }
if (type == GL_FRAGMENT_SHADER)
{
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDFdx, genType, "dFdx", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDFdy, genType, "dFdy", genType);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpFwidth, genType, "fwidth", genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpDFdx, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpDFdy, genType, genType);
+ symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpFwidth, genType, genType);
}
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2D, float2, int2);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler3D, float3, int3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureOffset", sampler2DShadow, float3, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2DArray, float3, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureOffset", sampler2DShadow, float3,
+ int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2DArray, float3,
+ int2);
if (type == GL_FRAGMENT_SHADER)
{
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2D, float2, int2, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler3D, float3, int3, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureOffset", sampler2DShadow, float3, int2, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2DArray, float3, int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2D, float2, int2,
+ float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler3D, float3, int3,
+ float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureOffset", sampler2DShadow, float3,
+ int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2DArray, float3,
+ int2, float1);
}
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float3, int2);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float4, int2);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler3D, float4, int3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjOffset", sampler2DShadow, float4, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjOffset", sampler2DShadow, float4,
+ int2);
if (type == GL_FRAGMENT_SHADER)
{
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float3, int2, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float4, int2, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler3D, float4, int3, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjOffset", sampler2DShadow, float4, int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float3,
+ int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float4,
+ int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler3D, float4,
+ int3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjOffset", sampler2DShadow,
+ float4, int2, float1);
}
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler2D, float2, float1, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler3D, float3, float1, int3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureLodOffset", sampler2DShadow, float3, float1, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler2DArray, float3, float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler2D, float2, float1,
+ int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler3D, float3, float1,
+ int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureLodOffset", sampler2DShadow, float3,
+ float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler2DArray, float3,
+ float1, int2);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLod", gsampler2D, float3, float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLod", gsampler2D, float4, float1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLod", gsampler3D, float4, float1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjLod", sampler2DShadow, float4, float1);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler2D, float3, float1, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler2D, float4, float1, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler3D, float4, float1, int3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjLodOffset", sampler2DShadow, float4, float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjLod", sampler2DShadow, float4,
+ float1);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler2D, float3,
+ float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler2D, float4,
+ float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler3D, float4,
+ float1, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjLodOffset", sampler2DShadow,
+ float4, float1, int2);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetch", gsampler2D, int2, int1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetch", gsampler3D, int3, int1);
symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetch", gsampler2DArray, int3, int1);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler2D, int2, int1, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler3D, int3, int1, int3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler2DArray, int3, int1, int2);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2D, float2, float2, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler3D, float3, float3, float3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsamplerCube, float3, float3, float3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DShadow, float3, float2, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", samplerCubeShadow, float4, float3, float3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2DArray, float3, float2, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DArrayShadow, float4, float2, float2);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler2D, float2, float2, float2, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler3D, float3, float3, float3, int3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGradOffset", sampler2DShadow, float3, float2, float2, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler2DArray, float3, float2, float2, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGradOffset", sampler2DArrayShadow, float4, float2, float2, int2);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler2D, float3, float2, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler2D, float4, float2, float2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler3D, float4, float3, float3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjGrad", sampler2DShadow, float4, float2, float2);
-
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler2D, float3, float2, float2, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler2D, float4, float2, float2, int2);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler3D, float4, float3, float3, int3);
- symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjGradOffset", sampler2DShadow, float4, float2, float2, int2);
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "texelFetch", samplerExternalOES, int2,
+ int1);
+ }
+
+ if (resources.EXT_YUV_target)
+ {
+ const TType *samplerExternal2DY2YEXT = TCache::getType(EbtSamplerExternal2DY2YEXT);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, TExtension::EXT_YUV_target, float4, "texelFetch",
+ samplerExternal2DY2YEXT, int2, int1);
+ }
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler2D, int2, int1,
+ int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler3D, int3, int1,
+ int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler2DArray, int3,
+ int1, int2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2D, float2, float2,
+ float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler3D, float3, float3,
+ float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsamplerCube, float3, float3,
+ float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DShadow, float3,
+ float2, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", samplerCubeShadow, float4,
+ float3, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2DArray, float3, float2,
+ float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DArrayShadow, float4,
+ float2, float2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler2D, float2,
+ float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler3D, float3,
+ float3, float3, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGradOffset", sampler2DShadow, float3,
+ float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler2DArray, float3,
+ float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGradOffset", sampler2DArrayShadow,
+ float4, float2, float2, int2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler2D, float3, float2,
+ float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler2D, float4, float2,
+ float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler3D, float4, float3,
+ float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjGrad", sampler2DShadow, float4,
+ float2, float2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler2D, float3,
+ float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler2D, float4,
+ float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler3D, float4,
+ float3, float3, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjGradOffset", sampler2DShadow,
+ float4, float2, float2, int2);
+
+ const TType *atomicCounter = TCache::getType(EbtAtomicCounter);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounter", atomicCounter);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterIncrement", atomicCounter);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterDecrement", atomicCounter);
+
+ // Insert all atomic memory functions
+ const TType *int1InOut = TCache::getType(EbtInt, EvqInOut);
+ const TType *uint1InOut = TCache::getType(EbtUInt, EvqInOut);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicAdd", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicAdd", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicMin", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicMin", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicMax", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicMax", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicAnd", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicAnd", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicOr", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicOr", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicXor", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicXor", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicExchange", uint1InOut, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicExchange", int1InOut, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCompSwap", uint1InOut, uint1, uint1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicCompSwap", int1InOut, int1, int1);
+
+ const TType *gimage2D = TCache::getType(EbtGImage2D);
+ const TType *gimage3D = TCache::getType(EbtGImage3D);
+ const TType *gimage2DArray = TCache::getType(EbtGImage2DArray);
+ const TType *gimageCube = TCache::getType(EbtGImageCube);
+
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimage2D, int2, gvec4);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimage3D, int3, gvec4);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimage2DArray, int3, gvec4);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimageCube, int3, gvec4);
+
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimage2D, int2);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimage3D, int3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimage2DArray, int3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimageCube, int3);
+
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int2, "imageSize", gimage2D);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int3, "imageSize", gimage3D);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int3, "imageSize", gimage2DArray);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int2, "imageSize", gimageCube);
+
+ symbolTable.insertBuiltInFunctionNoParameters(ESSL3_1_BUILTINS, EOpMemoryBarrier, voidType,
+ "memoryBarrier");
+ symbolTable.insertBuiltInFunctionNoParameters(ESSL3_1_BUILTINS, EOpMemoryBarrierAtomicCounter,
+ voidType, "memoryBarrierAtomicCounter");
+ symbolTable.insertBuiltInFunctionNoParameters(ESSL3_1_BUILTINS, EOpMemoryBarrierBuffer,
+ voidType, "memoryBarrierBuffer");
+ symbolTable.insertBuiltInFunctionNoParameters(ESSL3_1_BUILTINS, EOpMemoryBarrierImage, voidType,
+ "memoryBarrierImage");
+
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "texelFetch", gsampler2DMS, int2, int1);
+
+ // Insert all variations of textureGather.
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGather", gsampler2D, float2);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGather", gsampler2D, float2, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGather", gsampler2DArray, float3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGather", gsampler2DArray, float3,
+ int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGather", gsamplerCube, float3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGather", gsamplerCube, float3, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGather", sampler2DShadow, float2);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGather", sampler2DShadow, float2,
+ float1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGather", sampler2DArrayShadow,
+ float3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGather", sampler2DArrayShadow,
+ float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGather", samplerCubeShadow, float3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGather", samplerCubeShadow, float3,
+ float1);
+
+ // Insert all variations of textureGatherOffset.
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGatherOffset", gsampler2D, float2,
+ int2);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGatherOffset", gsampler2D, float2,
+ int2, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGatherOffset", gsampler2DArray,
+ float3, int2);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "textureGatherOffset", gsampler2DArray,
+ float3, int2, int1);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGatherOffset", sampler2DShadow,
+ float2, float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, float4, "textureGatherOffset", sampler2DArrayShadow,
+ float3, float1, int2);
+
+ if (type == GL_COMPUTE_SHADER)
+ {
+ symbolTable.insertBuiltInFunctionNoParameters(ESSL3_1_BUILTINS, EOpBarrier, voidType,
+ "barrier");
+ symbolTable.insertBuiltInFunctionNoParameters(ESSL3_1_BUILTINS, EOpMemoryBarrierShared,
+ voidType, "memoryBarrierShared");
+ symbolTable.insertBuiltInFunctionNoParameters(ESSL3_1_BUILTINS, EOpGroupMemoryBarrier,
+ voidType, "groupMemoryBarrier");
+ }
+
+ if (type == GL_GEOMETRY_SHADER_OES)
+ {
+ TExtension extension = TExtension::OES_geometry_shader;
+ symbolTable.insertBuiltInFunctionNoParametersExt(ESSL3_1_BUILTINS, extension, EOpEmitVertex,
+ voidType, "EmitVertex");
+ symbolTable.insertBuiltInFunctionNoParametersExt(ESSL3_1_BUILTINS, extension,
+ EOpEndPrimitive, voidType, "EndPrimitive");
+ }
//
// Depth range in window coordinates
//
- TFieldList *fields = NewPoolTFieldList();
+ TFieldList *fields = NewPoolTFieldList();
TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
- TField *near = new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1), NewPoolTString("near"), zeroSourceLoc);
- TField *far = new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1), NewPoolTString("far"), zeroSourceLoc);
- TField *diff = new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1), NewPoolTString("diff"), zeroSourceLoc);
+ auto highpFloat1 = new TType(EbtFloat, EbpHigh, EvqGlobal, 1);
+ TField *near = new TField(highpFloat1, NewPoolTString("near"), zeroSourceLoc);
+ TField *far = new TField(highpFloat1, NewPoolTString("far"), zeroSourceLoc);
+ TField *diff = new TField(highpFloat1, NewPoolTString("diff"), zeroSourceLoc);
fields->push_back(near);
fields->push_back(far);
fields->push_back(diff);
- TStructure *depthRangeStruct = new TStructure(NewPoolTString("gl_DepthRangeParameters"), fields);
- TVariable *depthRangeParameters = new TVariable(&depthRangeStruct->name(), depthRangeStruct, true);
- symbolTable.insert(COMMON_BUILTINS, depthRangeParameters);
- TVariable *depthRange = new TVariable(NewPoolTString("gl_DepthRange"), TType(depthRangeStruct));
- depthRange->setQualifier(EvqUniform);
- symbolTable.insert(COMMON_BUILTINS, depthRange);
+ TStructure *depthRangeStruct =
+ new TStructure(&symbolTable, NewPoolTString("gl_DepthRangeParameters"), fields);
+ symbolTable.insertStructType(COMMON_BUILTINS, depthRangeStruct);
+ TType depthRangeType(depthRangeStruct);
+ depthRangeType.setQualifier(EvqUniform);
+ symbolTable.insertVariable(COMMON_BUILTINS, "gl_DepthRange", depthRangeType);
//
// Implementation dependent built-in constants.
//
- symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexAttribs", resources.MaxVertexAttribs);
- symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexUniformVectors", resources.MaxVertexUniformVectors);
- symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexTextureImageUnits", resources.MaxVertexTextureImageUnits);
- symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxCombinedTextureImageUnits", resources.MaxCombinedTextureImageUnits);
- symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxTextureImageUnits", resources.MaxTextureImageUnits);
- symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxFragmentUniformVectors", resources.MaxFragmentUniformVectors);
-
- symbolTable.insertConstInt(ESSL1_BUILTINS, "gl_MaxVaryingVectors", resources.MaxVaryingVectors);
-
- if (spec != SH_CSS_SHADERS_SPEC)
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexAttribs", resources.MaxVertexAttribs,
+ EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexUniformVectors",
+ resources.MaxVertexUniformVectors, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexTextureImageUnits",
+ resources.MaxVertexTextureImageUnits, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxCombinedTextureImageUnits",
+ resources.MaxCombinedTextureImageUnits, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxTextureImageUnits",
+ resources.MaxTextureImageUnits, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxFragmentUniformVectors",
+ resources.MaxFragmentUniformVectors, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL1_BUILTINS, "gl_MaxVaryingVectors", resources.MaxVaryingVectors,
+ EbpMedium);
+
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers,
+ EbpMedium);
+ if (resources.EXT_blend_func_extended)
{
- symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers);
- if (resources.EXT_blend_func_extended)
- {
- symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended",
- "gl_MaxDualSourceDrawBuffersEXT",
- resources.MaxDualSourceDrawBuffers);
- }
+ symbolTable.insertConstIntExt(COMMON_BUILTINS, TExtension::EXT_blend_func_extended,
+ "gl_MaxDualSourceDrawBuffersEXT",
+ resources.MaxDualSourceDrawBuffers, EbpMedium);
}
- symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors", resources.MaxVertexOutputVectors);
- symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxFragmentInputVectors", resources.MaxFragmentInputVectors);
- symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MinProgramTexelOffset", resources.MinProgramTexelOffset);
- symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxProgramTexelOffset", resources.MaxProgramTexelOffset);
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors",
+ resources.MaxVertexOutputVectors, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxFragmentInputVectors",
+ resources.MaxFragmentInputVectors, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MinProgramTexelOffset",
+ resources.MinProgramTexelOffset, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxProgramTexelOffset",
+ resources.MaxProgramTexelOffset, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxImageUnits", resources.MaxImageUnits,
+ EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxVertexImageUniforms",
+ resources.MaxVertexImageUniforms, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxFragmentImageUniforms",
+ resources.MaxFragmentImageUniforms, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeImageUniforms",
+ resources.MaxComputeImageUniforms, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedImageUniforms",
+ resources.MaxCombinedImageUniforms, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedShaderOutputResources",
+ resources.MaxCombinedShaderOutputResources, EbpMedium);
+
+ symbolTable.insertConstIvec3(ESSL3_1_BUILTINS, "gl_MaxComputeWorkGroupCount",
+ resources.MaxComputeWorkGroupCount, EbpHigh);
+ symbolTable.insertConstIvec3(ESSL3_1_BUILTINS, "gl_MaxComputeWorkGroupSize",
+ resources.MaxComputeWorkGroupSize, EbpHigh);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeUniformComponents",
+ resources.MaxComputeUniformComponents, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeTextureImageUnits",
+ resources.MaxComputeTextureImageUnits, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeAtomicCounters",
+ resources.MaxComputeAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeAtomicCounterBuffers",
+ resources.MaxComputeAtomicCounterBuffers, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxVertexAtomicCounters",
+ resources.MaxVertexAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxFragmentAtomicCounters",
+ resources.MaxFragmentAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedAtomicCounters",
+ resources.MaxCombinedAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxAtomicCounterBindings",
+ resources.MaxAtomicCounterBindings, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxVertexAtomicCounterBuffers",
+ resources.MaxVertexAtomicCounterBuffers, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxFragmentAtomicCounterBuffers",
+ resources.MaxFragmentAtomicCounterBuffers, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedAtomicCounterBuffers",
+ resources.MaxCombinedAtomicCounterBuffers, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxAtomicCounterBufferSize",
+ resources.MaxAtomicCounterBufferSize, EbpMedium);
+
+ if (resources.OES_geometry_shader)
+ {
+ TExtension ext = TExtension::OES_geometry_shader;
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryInputComponents",
+ resources.MaxGeometryInputComponents, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryOutputComponents",
+ resources.MaxGeometryOutputComponents, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryImageUniforms",
+ resources.MaxGeometryImageUniforms, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryTextureImageUnits",
+ resources.MaxGeometryTextureImageUnits, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryOutputVertices",
+ resources.MaxGeometryOutputVertices, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryTotalOutputComponents",
+ resources.MaxGeometryTotalOutputComponents, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryUniformComponents",
+ resources.MaxGeometryUniformComponents, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryAtomicCounters",
+ resources.MaxGeometryAtomicCounters, EbpMedium);
+ symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryAtomicCounterBuffers",
+ resources.MaxGeometryAtomicCounterBuffers, EbpMedium);
+ }
}
-void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec,
+void IdentifyBuiltIns(sh::GLenum type,
+ ShShaderSpec spec,
const ShBuiltInResources &resources,
TSymbolTable &symbolTable)
{
@@ -486,136 +873,263 @@ void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec,
// Insert some special built-in variables that are not in
// the built-in header files.
//
+
+ if (resources.OVR_multiview && type != GL_COMPUTE_SHADER)
+ {
+ symbolTable.insertVariableExt(ESSL3_BUILTINS, TExtension::OVR_multiview, "gl_ViewID_OVR",
+ TType(EbtUInt, EbpHigh, EvqViewIDOVR, 1));
+
+ // ESSL 1.00 doesn't have unsigned integers, so gl_ViewID_OVR is a signed integer in ESSL
+ // 1.00. This is specified in the WEBGL_multiview spec.
+ symbolTable.insertVariableExt(ESSL1_BUILTINS, TExtension::OVR_multiview, "gl_ViewID_OVR",
+ TType(EbtInt, EbpHigh, EvqViewIDOVR, 1));
+ }
+
switch (type)
{
- case GL_FRAGMENT_SHADER:
- symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FragCoord"),
- TType(EbtFloat, EbpMedium, EvqFragCoord, 4)));
- symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FrontFacing"),
- TType(EbtBool, EbpUndefined, EvqFrontFacing, 1)));
- symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointCoord"),
- TType(EbtFloat, EbpMedium, EvqPointCoord, 2)));
-
- //
- // In CSS Shaders, gl_FragColor, gl_FragData, and gl_MaxDrawBuffers are not available.
- // Instead, css_MixColor and css_ColorMatrix are available.
- //
- if (spec != SH_CSS_SHADERS_SPEC)
+ case GL_FRAGMENT_SHADER:
{
- symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragColor"),
- TType(EbtFloat, EbpMedium, EvqFragColor, 4)));
- TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, 1, true);
- fragData.setArraySize(resources.MaxDrawBuffers);
- symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData));
+ symbolTable.insertVariable(COMMON_BUILTINS, "gl_FragCoord",
+ TType(EbtFloat, EbpMedium, EvqFragCoord, 4));
+ symbolTable.insertVariable(COMMON_BUILTINS, "gl_FrontFacing",
+ TType(EbtBool, EbpUndefined, EvqFrontFacing, 1));
+ symbolTable.insertVariable(COMMON_BUILTINS, "gl_PointCoord",
+ TType(EbtFloat, EbpMedium, EvqPointCoord, 2));
+
+ symbolTable.insertVariable(ESSL1_BUILTINS, "gl_FragColor",
+ TType(EbtFloat, EbpMedium, EvqFragColor, 4));
+ TType fragData(EbtFloat, EbpMedium, EvqFragData, 4);
+ if (spec != SH_WEBGL2_SPEC && spec != SH_WEBGL3_SPEC)
+ {
+ fragData.makeArray(resources.MaxDrawBuffers);
+ }
+ else
+ {
+ fragData.makeArray(1u);
+ }
+ symbolTable.insertVariable(ESSL1_BUILTINS, "gl_FragData", fragData);
if (resources.EXT_blend_func_extended)
{
- symbolTable.insert(
- ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
- new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"),
- TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4)));
- TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true);
- secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers);
- symbolTable.insert(
- ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
- new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData));
+ symbolTable.insertVariableExt(
+ ESSL1_BUILTINS, TExtension::EXT_blend_func_extended, "gl_SecondaryFragColorEXT",
+ TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4));
+ TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1);
+ secondaryFragData.makeArray(resources.MaxDualSourceDrawBuffers);
+ symbolTable.insertVariableExt(ESSL1_BUILTINS, TExtension::EXT_blend_func_extended,
+ "gl_SecondaryFragDataEXT", secondaryFragData);
}
if (resources.EXT_frag_depth)
{
- symbolTable.insert(
- ESSL1_BUILTINS, "GL_EXT_frag_depth",
- new TVariable(
- NewPoolTString("gl_FragDepthEXT"),
- TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium,
- EvqFragDepthEXT, 1)));
+ symbolTable.insertVariableExt(
+ ESSL1_BUILTINS, TExtension::EXT_frag_depth, "gl_FragDepthEXT",
+ TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium,
+ EvqFragDepthEXT, 1));
}
- symbolTable.insert(ESSL3_BUILTINS,
- new TVariable(NewPoolTString("gl_FragDepth"),
- TType(EbtFloat, EbpHigh, EvqFragDepth, 1)));
+ symbolTable.insertVariable(ESSL3_BUILTINS, "gl_FragDepth",
+ TType(EbtFloat, EbpHigh, EvqFragDepth, 1));
if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch)
{
- TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true);
- lastFragData.setArraySize(resources.MaxDrawBuffers);
+ TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1);
+ lastFragData.makeArray(resources.MaxDrawBuffers);
if (resources.EXT_shader_framebuffer_fetch)
{
- symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_shader_framebuffer_fetch",
- new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
+ symbolTable.insertVariableExt(ESSL1_BUILTINS,
+ TExtension::EXT_shader_framebuffer_fetch,
+ "gl_LastFragData", lastFragData);
}
else if (resources.NV_shader_framebuffer_fetch)
{
- symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
- new TVariable(NewPoolTString("gl_LastFragColor"),
- TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
- symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
- new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
+ symbolTable.insertVariableExt(
+ ESSL1_BUILTINS, TExtension::NV_shader_framebuffer_fetch, "gl_LastFragColor",
+ TType(EbtFloat, EbpMedium, EvqLastFragColor, 4));
+ symbolTable.insertVariableExt(ESSL1_BUILTINS,
+ TExtension::NV_shader_framebuffer_fetch,
+ "gl_LastFragData", lastFragData);
}
}
else if (resources.ARM_shader_framebuffer_fetch)
{
- symbolTable.insert(ESSL1_BUILTINS, "GL_ARM_shader_framebuffer_fetch",
- new TVariable(NewPoolTString("gl_LastFragColorARM"),
- TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
+ symbolTable.insertVariableExt(
+ ESSL1_BUILTINS, TExtension::ARM_shader_framebuffer_fetch, "gl_LastFragColorARM",
+ TType(EbtFloat, EbpMedium, EvqLastFragColor, 4));
+ }
+
+ if (resources.OES_geometry_shader)
+ {
+ TExtension extension = TExtension::OES_geometry_shader;
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveID",
+ TType(EbtInt, EbpHigh, EvqPrimitiveID, 1));
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Layer",
+ TType(EbtInt, EbpHigh, EvqLayer, 1));
}
+
+ break;
}
- else
+ case GL_VERTEX_SHADER:
{
- symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_MixColor"),
- TType(EbtFloat, EbpMedium, EvqGlobal, 4)));
- symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_ColorMatrix"),
- TType(EbtFloat, EbpMedium, EvqGlobal, 4, 4)));
+ symbolTable.insertVariable(COMMON_BUILTINS, "gl_Position",
+ TType(EbtFloat, EbpHigh, EvqPosition, 4));
+ symbolTable.insertVariable(COMMON_BUILTINS, "gl_PointSize",
+ TType(EbtFloat, EbpMedium, EvqPointSize, 1));
+ symbolTable.insertVariable(ESSL3_BUILTINS, "gl_InstanceID",
+ TType(EbtInt, EbpHigh, EvqInstanceID, 1));
+ symbolTable.insertVariable(ESSL3_BUILTINS, "gl_VertexID",
+ TType(EbtInt, EbpHigh, EvqVertexID, 1));
+
+ // For internal use by ANGLE - not exposed to the parser.
+ symbolTable.insertVariable(GLSL_BUILTINS, "gl_ViewportIndex",
+ TType(EbtInt, EbpHigh, EvqViewportIndex));
+ // gl_Layer exists in other shader stages in ESSL, but not in vertex shader so far.
+ symbolTable.insertVariable(GLSL_BUILTINS, "gl_Layer", TType(EbtInt, EbpHigh, EvqLayer));
+ break;
+ }
+ case GL_COMPUTE_SHADER:
+ {
+ symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_NumWorkGroups",
+ TType(EbtUInt, EbpUndefined, EvqNumWorkGroups, 3));
+ symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_WorkGroupSize",
+ TType(EbtUInt, EbpUndefined, EvqWorkGroupSize, 3));
+ symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_WorkGroupID",
+ TType(EbtUInt, EbpUndefined, EvqWorkGroupID, 3));
+ symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_LocalInvocationID",
+ TType(EbtUInt, EbpUndefined, EvqLocalInvocationID, 3));
+ symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_GlobalInvocationID",
+ TType(EbtUInt, EbpUndefined, EvqGlobalInvocationID, 3));
+ symbolTable.insertVariable(ESSL3_1_BUILTINS, "gl_LocalInvocationIndex",
+ TType(EbtUInt, EbpUndefined, EvqLocalInvocationIndex, 1));
+ break;
}
- break;
-
- case GL_VERTEX_SHADER:
- symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_Position"),
- TType(EbtFloat, EbpHigh, EvqPosition, 4)));
- symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointSize"),
- TType(EbtFloat, EbpMedium, EvqPointSize, 1)));
- symbolTable.insert(ESSL3_BUILTINS, new TVariable(NewPoolTString("gl_InstanceID"),
- TType(EbtInt, EbpHigh, EvqInstanceID, 1)));
- break;
-
- default:
- assert(false && "Language not supported");
+ case GL_GEOMETRY_SHADER_OES:
+ {
+ TExtension extension = TExtension::OES_geometry_shader;
+
+ // Add built-in interface block gl_PerVertex and the built-in array gl_in.
+ // TODO(jiawei.shao@intel.com): implement GL_OES_geometry_point_size.
+ const TString *glPerVertexString = NewPoolTString("gl_PerVertex");
+ symbolTable.insertInterfaceBlockNameExt(ESSL3_1_BUILTINS, extension, glPerVertexString);
+
+ TFieldList *fieldList = NewPoolTFieldList();
+ TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
+ TField *glPositionField = new TField(new TType(EbtFloat, EbpHigh, EvqPosition, 4),
+ NewPoolTString("gl_Position"), zeroSourceLoc);
+ fieldList->push_back(glPositionField);
+
+ TInterfaceBlock *glInBlock = new TInterfaceBlock(
+ glPerVertexString, fieldList, NewPoolTString("gl_in"), TLayoutQualifier::Create());
+
+ // The array size of gl_in is undefined until we get a valid input primitive
+ // declaration.
+ TType glInType(glInBlock, EvqPerVertexIn, TLayoutQualifier::Create());
+ glInType.makeArray(0u);
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_in", glInType);
+
+ TType glPositionType(EbtFloat, EbpHigh, EvqPosition, 4);
+ glPositionType.setInterfaceBlock(new TInterfaceBlock(
+ glPerVertexString, fieldList, nullptr, TLayoutQualifier::Create()));
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Position",
+ glPositionType);
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveIDIn",
+ TType(EbtInt, EbpHigh, EvqPrimitiveIDIn, 1));
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_InvocationID",
+ TType(EbtInt, EbpHigh, EvqInvocationID, 1));
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveID",
+ TType(EbtInt, EbpHigh, EvqPrimitiveID, 1));
+ symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Layer",
+ TType(EbtInt, EbpHigh, EvqLayer, 1));
+
+ break;
+ }
+ default:
+ UNREACHABLE();
}
}
-void InitExtensionBehavior(const ShBuiltInResources& resources,
- TExtensionBehavior& extBehavior)
+void InitExtensionBehavior(const ShBuiltInResources &resources, TExtensionBehavior &extBehavior)
{
if (resources.OES_standard_derivatives)
- extBehavior["GL_OES_standard_derivatives"] = EBhUndefined;
+ {
+ extBehavior[TExtension::OES_standard_derivatives] = EBhUndefined;
+ }
if (resources.OES_EGL_image_external)
- extBehavior["GL_OES_EGL_image_external"] = EBhUndefined;
+ {
+ extBehavior[TExtension::OES_EGL_image_external] = EBhUndefined;
+ }
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ extBehavior[TExtension::OES_EGL_image_external_essl3] = EBhUndefined;
+ }
+ if (resources.NV_EGL_stream_consumer_external)
+ {
+ extBehavior[TExtension::NV_EGL_stream_consumer_external] = EBhUndefined;
+ }
if (resources.ARB_texture_rectangle)
- extBehavior["GL_ARB_texture_rectangle"] = EBhUndefined;
+ {
+ // Special: ARB_texture_rectangle extension does not follow the standard for #extension
+ // directives - it is enabled by default. An extension directive may still disable it.
+ extBehavior[TExtension::ARB_texture_rectangle] = EBhEnable;
+ }
if (resources.EXT_blend_func_extended)
- extBehavior["GL_EXT_blend_func_extended"] = EBhUndefined;
+ {
+ extBehavior[TExtension::EXT_blend_func_extended] = EBhUndefined;
+ }
if (resources.EXT_draw_buffers)
- extBehavior["GL_EXT_draw_buffers"] = EBhUndefined;
+ {
+ extBehavior[TExtension::EXT_draw_buffers] = EBhUndefined;
+ }
if (resources.EXT_frag_depth)
- extBehavior["GL_EXT_frag_depth"] = EBhUndefined;
+ {
+ extBehavior[TExtension::EXT_frag_depth] = EBhUndefined;
+ }
if (resources.EXT_shader_texture_lod)
- extBehavior["GL_EXT_shader_texture_lod"] = EBhUndefined;
+ {
+ extBehavior[TExtension::EXT_shader_texture_lod] = EBhUndefined;
+ }
if (resources.EXT_shader_framebuffer_fetch)
- extBehavior["GL_EXT_shader_framebuffer_fetch"] = EBhUndefined;
+ {
+ extBehavior[TExtension::EXT_shader_framebuffer_fetch] = EBhUndefined;
+ }
if (resources.NV_shader_framebuffer_fetch)
- extBehavior["GL_NV_shader_framebuffer_fetch"] = EBhUndefined;
+ {
+ extBehavior[TExtension::NV_shader_framebuffer_fetch] = EBhUndefined;
+ }
if (resources.ARM_shader_framebuffer_fetch)
- extBehavior["GL_ARM_shader_framebuffer_fetch"] = EBhUndefined;
+ {
+ extBehavior[TExtension::ARM_shader_framebuffer_fetch] = EBhUndefined;
+ }
+ if (resources.OVR_multiview)
+ {
+ extBehavior[TExtension::OVR_multiview] = EBhUndefined;
+ }
+ if (resources.EXT_YUV_target)
+ {
+ extBehavior[TExtension::EXT_YUV_target] = EBhUndefined;
+ }
+ if (resources.OES_geometry_shader)
+ {
+ extBehavior[TExtension::OES_geometry_shader] = EBhUndefined;
+ extBehavior[TExtension::EXT_geometry_shader] = EBhUndefined;
+ }
}
void ResetExtensionBehavior(TExtensionBehavior &extBehavior)
{
- for (auto ext_iter = extBehavior.begin();
- ext_iter != extBehavior.end();
- ++ext_iter)
+ for (auto &ext : extBehavior)
{
- ext_iter->second = EBhUndefined;
+ if (ext.first == TExtension::ARB_texture_rectangle)
+ {
+ ext.second = EBhEnable;
+ }
+ else
+ {
+ ext.second = EBhUndefined;
+ }
}
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/Initialize.h b/src/3rdparty/angle/src/compiler/translator/Initialize.h
index c43ce3417a..2e61218a0f 100644
--- a/src/3rdparty/angle/src/compiler/translator/Initialize.h
+++ b/src/3rdparty/angle/src/compiler/translator/Initialize.h
@@ -11,14 +11,21 @@
#include "compiler/translator/Compiler.h"
#include "compiler/translator/SymbolTable.h"
-void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &table);
+namespace sh
+{
-void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec,
- const ShBuiltInResources& resources,
- TSymbolTable& symbolTable);
+void InsertBuiltInFunctions(sh::GLenum type,
+ ShShaderSpec spec,
+ const ShBuiltInResources &resources,
+ TSymbolTable &table);
-void InitExtensionBehavior(const ShBuiltInResources& resources,
- TExtensionBehavior& extensionBehavior);
+void IdentifyBuiltIns(sh::GLenum type,
+ ShShaderSpec spec,
+ const ShBuiltInResources &resources,
+ TSymbolTable &symbolTable);
+
+void InitExtensionBehavior(const ShBuiltInResources &resources,
+ TExtensionBehavior &extensionBehavior);
// Resets the behavior of the extensions listed in |extensionBehavior| to the
// undefined state. These extensions will only be those initially supported in
@@ -26,4 +33,6 @@ void InitExtensionBehavior(const ShBuiltInResources& resources,
// extensions will remain unsupported.
void ResetExtensionBehavior(TExtensionBehavior &extensionBehavior);
-#endif // COMPILER_TRANSLATOR_INITIALIZE_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INITIALIZE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp b/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp
index 713965389f..43c215f52b 100644
--- a/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp
@@ -7,24 +7,22 @@
#include "compiler/translator/Cache.h"
#include "compiler/translator/InitializeDll.h"
#include "compiler/translator/InitializeGlobals.h"
-#include "compiler/translator/InitializeParseContext.h"
#include "common/platform.h"
#include <assert.h>
+namespace sh
+{
+
bool InitProcess()
{
- if (!InitializePoolIndex()) {
+ if (!InitializePoolIndex())
+ {
assert(0 && "InitProcess(): Failed to initalize global pool");
return false;
}
- if (!InitializeParseContextIndex()) {
- assert(0 && "InitProcess(): Failed to initalize parse context");
- return false;
- }
-
TCache::initialize();
return true;
@@ -32,7 +30,8 @@ bool InitProcess()
void DetachProcess()
{
- FreeParseContextIndex();
FreePoolIndex();
TCache::destroy();
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeDll.h b/src/3rdparty/angle/src/compiler/translator/InitializeDll.h
index 4c400760f6..87f7251470 100644
--- a/src/3rdparty/angle/src/compiler/translator/InitializeDll.h
+++ b/src/3rdparty/angle/src/compiler/translator/InitializeDll.h
@@ -6,8 +6,10 @@
#ifndef COMPILER_TRANSLATOR_INITIALIZEDLL_H_
#define COMPILER_TRANSLATOR_INITIALIZEDLL_H_
+namespace sh
+{
bool InitProcess();
void DetachProcess();
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_INITIALIZEDLL_H_
-
+#endif // COMPILER_TRANSLATOR_INITIALIZEDLL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeGlobals.h b/src/3rdparty/angle/src/compiler/translator/InitializeGlobals.h
index 8c65cb28da..9a67ed0e04 100644
--- a/src/3rdparty/angle/src/compiler/translator/InitializeGlobals.h
+++ b/src/3rdparty/angle/src/compiler/translator/InitializeGlobals.h
@@ -10,4 +10,4 @@
bool InitializePoolIndex();
void FreePoolIndex();
-#endif // COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_
+#endif // COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.cpp b/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.cpp
deleted file mode 100644
index c35cedb348..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/InitializeParseContext.h"
-
-#include "common/tls.h"
-
-#include <assert.h>
-
-TLSIndex GlobalParseContextIndex = TLS_INVALID_INDEX;
-
-bool InitializeParseContextIndex()
-{
- assert(GlobalParseContextIndex == TLS_INVALID_INDEX);
-
- GlobalParseContextIndex = CreateTLSIndex();
- return GlobalParseContextIndex != TLS_INVALID_INDEX;
-}
-
-void FreeParseContextIndex()
-{
- assert(GlobalParseContextIndex != TLS_INVALID_INDEX);
-
- DestroyTLSIndex(GlobalParseContextIndex);
- GlobalParseContextIndex = TLS_INVALID_INDEX;
-}
-
-void SetGlobalParseContext(TParseContext* context)
-{
- assert(GlobalParseContextIndex != TLS_INVALID_INDEX);
- SetTLSValue(GlobalParseContextIndex, context);
-}
-
-TParseContext* GetGlobalParseContext()
-{
- assert(GlobalParseContextIndex != TLS_INVALID_INDEX);
- return static_cast<TParseContext*>(GetTLSValue(GlobalParseContextIndex));
-}
-
diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h b/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h
deleted file mode 100644
index 70dac702e2..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_
-#define COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_
-
-bool InitializeParseContextIndex();
-void FreeParseContextIndex();
-
-class TParseContext;
-extern void SetGlobalParseContext(TParseContext* context);
-extern TParseContext* GetGlobalParseContext();
-
-#endif // COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp
index 86d3e6bc83..aa1a042fac 100644
--- a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp
@@ -6,112 +6,285 @@
#include "compiler/translator/InitializeVariables.h"
+#include "angle_gl.h"
#include "common/debug.h"
+#include "compiler/translator/FindMain.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
namespace
{
-TIntermConstantUnion *constructFloatConstUnionNode(const TType &type)
+void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
+ bool canUseLoopsToInitialize,
+ TIntermSequence *initSequenceOut,
+ TSymbolTable *symbolTable);
+
+void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
+ bool canUseLoopsToInitialize,
+ TIntermSequence *initSequenceOut,
+ TSymbolTable *symbolTable);
+
+TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
{
- TType myType = type;
- unsigned char size = static_cast<unsigned char>(myType.getNominalSize());
- if (myType.isMatrix())
- size *= size;
- TConstantUnion *u = new TConstantUnion[size];
- for (int ii = 0; ii < size; ++ii)
- u[ii].setFConst(0.0f);
-
- myType.clearArrayness();
- myType.setQualifier(EvqConst);
- TIntermConstantUnion *node = new TIntermConstantUnion(u, myType);
- return node;
+ TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
+ return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
}
-TIntermConstantUnion *constructIndexNode(int index)
+void AddZeroInitSequence(const TIntermTyped *initializedNode,
+ bool canUseLoopsToInitialize,
+ TIntermSequence *initSequenceOut,
+ TSymbolTable *symbolTable)
{
- TConstantUnion *u = new TConstantUnion[1];
- u[0].setIConst(index);
+ if (initializedNode->isArray())
+ {
+ AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, initSequenceOut,
+ symbolTable);
+ }
+ else if (initializedNode->getType().isStructureContainingArrays() ||
+ initializedNode->getType().isNamelessStruct())
+ {
+ AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, initSequenceOut,
+ symbolTable);
+ }
+ else
+ {
+ initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode));
+ }
+}
- TType type(EbtInt, EbpUndefined, EvqConst, 1);
- TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
- return node;
+void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
+ bool canUseLoopsToInitialize,
+ TIntermSequence *initSequenceOut,
+ TSymbolTable *symbolTable)
+{
+ ASSERT(initializedNode->getBasicType() == EbtStruct);
+ const TStructure *structType = initializedNode->getType().getStruct();
+ for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
+ {
+ TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
+ initializedNode->deepCopy(), CreateIndexNode(i));
+ // Structs can't be defined inside structs, so the type of a struct field can't be a
+ // nameless struct.
+ ASSERT(!element->getType().isNamelessStruct());
+ AddZeroInitSequence(element, canUseLoopsToInitialize, initSequenceOut, symbolTable);
+ }
}
-} // namespace anonymous
+void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
+ bool canUseLoopsToInitialize,
+ TIntermSequence *initSequenceOut,
+ TSymbolTable *symbolTable)
+{
+ for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
+ {
+ TIntermBinary *element =
+ new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
+ AddZeroInitSequence(element, canUseLoopsToInitialize, initSequenceOut, symbolTable);
+ }
+}
-bool InitializeVariables::visitAggregate(Visit visit, TIntermAggregate *node)
+void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode,
+ TIntermSequence *initSequenceOut,
+ TSymbolTable *symbolTable)
{
- bool visitChildren = !mCodeInserted;
- switch (node->getOp())
+ ASSERT(initializedNode->isArray());
+ TSymbolUniqueId indexSymbol(symbolTable);
+
+ TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexSymbol, TType(EbtInt), EvqTemporary);
+ TIntermDeclaration *indexInit =
+ CreateTempInitDeclarationNode(indexSymbol, CreateZeroNode(TType(EbtInt)), EvqTemporary);
+ TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize());
+ TIntermBinary *indexSmallerThanSize =
+ new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
+ TIntermUnary *indexIncrement = new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy());
+
+ TIntermBlock *forLoopBody = new TIntermBlock();
+ TIntermSequence *forLoopBodySeq = forLoopBody->getSequence();
+
+ TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(),
+ indexSymbolNode->deepCopy());
+ AddZeroInitSequence(element, true, forLoopBodySeq, symbolTable);
+
+ TIntermLoop *forLoop =
+ new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody);
+ initSequenceOut->push_back(forLoop);
+}
+
+void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
+ bool canUseLoopsToInitialize,
+ TIntermSequence *initSequenceOut,
+ TSymbolTable *symbolTable)
+{
+ // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which
+ // doesn't have array assignment. We'll do this either with a for loop or just a list of
+ // statements assigning to each array index. Note that it is important to have the array init in
+ // the right order to workaround http://crbug.com/709317
+ bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u ||
+ (initializedNode->getBasicType() != EbtStruct &&
+ !initializedNode->getType().isArrayOfArrays() &&
+ initializedNode->getOutermostArraySize() <= 3u);
+ if (initializedNode->getQualifier() == EvqFragData ||
+ initializedNode->getQualifier() == EvqFragmentOut || isSmallArray ||
+ !canUseLoopsToInitialize)
+ {
+ // Fragment outputs should not be indexed by non-constant indices.
+ // Also it doesn't make sense to use loops to initialize very small arrays.
+ AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize, initSequenceOut,
+ symbolTable);
+ }
+ else
{
- case EOpSequence:
- break;
- case EOpFunction:
- {
- // Function definition.
- ASSERT(visit == PreVisit);
- if (node->getName() == "main(")
+ AddArrayZeroInitForLoop(initializedNode, initSequenceOut, symbolTable);
+ }
+}
+
+void InsertInitCode(TIntermSequence *mainBody,
+ const InitVariableList &variables,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ const TExtensionBehavior &extensionBehavior,
+ bool canUseLoopsToInitialize)
+{
+ for (const auto &var : variables)
+ {
+ TString name = TString(var.name.c_str());
+ size_t pos = name.find_last_of('[');
+ if (pos != TString::npos)
{
- TIntermSequence *sequence = node->getSequence();
- ASSERT((sequence->size() == 1) || (sequence->size() == 2));
- TIntermAggregate *body = NULL;
- if (sequence->size() == 1)
- {
- body = new TIntermAggregate(EOpSequence);
- sequence->push_back(body);
- }
- else
+ name = name.substr(0, pos);
+ }
+
+ TIntermTyped *initializedSymbol = nullptr;
+ if (var.isBuiltIn())
+ {
+ initializedSymbol = ReferenceBuiltInVariable(name, *symbolTable, shaderVersion);
+ if (initializedSymbol->getQualifier() == EvqFragData &&
+ !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
{
- body = (*sequence)[1]->getAsAggregate();
+ // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be
+ // written to.
+ // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up
+ // with a good way to do it. Right now "gl_FragData" in symbol table is initialized
+ // to have the array size of MaxDrawBuffers, and the initialization happens before
+ // the shader sets the extensions it is using.
+ initializedSymbol =
+ new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0));
}
- ASSERT(body);
- insertInitCode(body->getSequence());
- mCodeInserted = true;
}
- break;
- }
- default:
- visitChildren = false;
- break;
+ else
+ {
+ initializedSymbol = ReferenceGlobalVariable(name, *symbolTable);
+ }
+ ASSERT(initializedSymbol != nullptr);
+
+ TIntermSequence *initCode =
+ CreateInitCode(initializedSymbol, canUseLoopsToInitialize, symbolTable);
+ mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end());
}
- return visitChildren;
}
-void InitializeVariables::insertInitCode(TIntermSequence *sequence)
+class InitializeLocalsTraverser : public TIntermTraverser
{
- for (size_t ii = 0; ii < mVariables.size(); ++ii)
+ public:
+ InitializeLocalsTraverser(int shaderVersion,
+ TSymbolTable *symbolTable,
+ bool canUseLoopsToInitialize)
+ : TIntermTraverser(true, false, false, symbolTable),
+ mShaderVersion(shaderVersion),
+ mCanUseLoopsToInitialize(canUseLoopsToInitialize)
{
- const InitVariableInfo &varInfo = mVariables[ii];
+ }
- if (varInfo.type.isArray())
+ protected:
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
+ {
+ for (TIntermNode *declarator : *node->getSequence())
{
- for (int index = varInfo.type.getArraySize() - 1; index >= 0; --index)
+ if (!mInGlobalScope && !declarator->getAsBinaryNode())
{
- TIntermBinary *assign = new TIntermBinary(EOpAssign);
- sequence->insert(sequence->begin(), assign);
-
- TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
- TIntermSymbol *symbol = new TIntermSymbol(0, varInfo.name, varInfo.type);
- indexDirect->setLeft(symbol);
- TIntermConstantUnion *indexNode = constructIndexNode(index);
- indexDirect->setRight(indexNode);
-
- assign->setLeft(indexDirect);
+ TIntermSymbol *symbol = declarator->getAsSymbolNode();
+ ASSERT(symbol);
+ if (symbol->getSymbol() == "")
+ {
+ continue;
+ }
- TIntermConstantUnion *zeroConst = constructFloatConstUnionNode(varInfo.type);
- assign->setRight(zeroConst);
+ // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
+ // support array constructors or assigning arrays.
+ bool arrayConstructorUnavailable =
+ (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
+ mShaderVersion == 100;
+ // Nameless struct constructors can't be referred to, so they also need to be
+ // initialized one element at a time.
+ // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
+ // could use an initializer. It could at least reduce code size for very large
+ // arrays, but could hurt runtime performance.
+ if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
+ {
+ // SimplifyLoopConditions should have been run so the parent node of this node
+ // should not be a loop.
+ ASSERT(getParentNode()->getAsLoopNode() == nullptr);
+ // SeparateDeclarations should have already been run, so we don't need to worry
+ // about further declarators in this declaration depending on the effects of
+ // this declarator.
+ ASSERT(node->getSequence()->size() == 1);
+ insertStatementsInParentBlock(
+ TIntermSequence(),
+ *CreateInitCode(symbol, mCanUseLoopsToInitialize, mSymbolTable));
+ }
+ else
+ {
+ TIntermBinary *init =
+ new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
+ queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
+ }
}
}
- else
- {
- TIntermBinary *assign = new TIntermBinary(EOpAssign);
- sequence->insert(sequence->begin(), assign);
- TIntermSymbol *symbol = new TIntermSymbol(0, varInfo.name, varInfo.type);
- assign->setLeft(symbol);
- TIntermConstantUnion *zeroConst = constructFloatConstUnionNode(varInfo.type);
- assign->setRight(zeroConst);
- }
-
+ return false;
}
+
+ private:
+ int mShaderVersion;
+ bool mCanUseLoopsToInitialize;
+};
+
+} // namespace anonymous
+
+TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol,
+ bool canUseLoopsToInitialize,
+ TSymbolTable *symbolTable)
+{
+ TIntermSequence *initCode = new TIntermSequence();
+ AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, initCode, symbolTable);
+ return initCode;
+}
+
+void InitializeUninitializedLocals(TIntermBlock *root,
+ int shaderVersion,
+ bool canUseLoopsToInitialize,
+ TSymbolTable *symbolTable)
+{
+ InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize);
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+void InitializeVariables(TIntermBlock *root,
+ const InitVariableList &vars,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ const TExtensionBehavior &extensionBehavior,
+ bool canUseLoopsToInitialize)
+{
+ TIntermBlock *body = FindMainBody(root);
+ InsertInitCode(body->getSequence(), vars, symbolTable, shaderVersion, extensionBehavior,
+ canUseLoopsToInitialize);
}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h
index 2a141ec91c..1a7b3c0f24 100644
--- a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h
+++ b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h
@@ -7,45 +7,47 @@
#ifndef COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
#define COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
+#include <GLSLANG/ShaderLang.h>
+
+#include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/IntermNode.h"
-class InitializeVariables : public TIntermTraverser
+namespace sh
{
- public:
- struct InitVariableInfo
- {
- TString name;
- TType type;
-
- InitVariableInfo(const TString &_name, const TType &_type)
- : name(_name),
- type(_type)
- {
- }
- };
- typedef TVector<InitVariableInfo> InitVariableInfoList;
-
- InitializeVariables(const InitVariableInfoList &vars)
- : TIntermTraverser(true, false, false),
- mVariables(vars),
- mCodeInserted(false)
- {
- }
-
- protected:
- bool visitBinary(Visit, TIntermBinary *node) override { return false; }
- bool visitUnary(Visit, TIntermUnary *node) override { return false; }
- bool visitSelection(Visit, TIntermSelection *node) override { return false; }
- bool visitLoop(Visit, TIntermLoop *node) override { return false; }
- bool visitBranch(Visit, TIntermBranch *node) override { return false; }
-
- bool visitAggregate(Visit visit, TIntermAggregate *node) override;
-
- private:
- void insertInitCode(TIntermSequence *sequence);
-
- InitVariableInfoList mVariables;
- bool mCodeInserted;
-};
+class TSymbolTable;
+
+typedef std::vector<sh::ShaderVariable> InitVariableList;
+
+// For all of the functions below: If canUseLoopsToInitialize is set, for loops are used instead of
+// a large number of initializers where it can make sense, such as for initializing large arrays.
+
+// Return a sequence of assignment operations to initialize "initializedSymbol". initializedSymbol
+// may be an array, struct or any combination of these, as long as it contains only basic types.
+TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol,
+ bool canUseLoopsToInitialize,
+ TSymbolTable *symbolTable);
+
+// Initialize all uninitialized local variables, so that undefined behavior is avoided.
+void InitializeUninitializedLocals(TIntermBlock *root,
+ int shaderVersion,
+ bool canUseLoopsToInitialize,
+ TSymbolTable *symbolTable);
+
+// This function can initialize all the types that CreateInitCode is able to initialize. All
+// variables must be globals which can be found in the symbol table. For now it is used for the
+// following two scenarios:
+// 1. Initializing gl_Position;
+// 2. Initializing output variables referred to in the shader source.
+// Note: The type of each lvalue in an initializer is retrieved from the symbol table. gl_FragData
+// requires special handling because the number of indices which can be initialized is determined by
+// enabled extensions.
+void InitializeVariables(TIntermBlock *root,
+ const InitVariableList &vars,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ const TExtensionBehavior &extensionBehavior,
+ bool canUseLoopsToInitialize);
+
+} // namespace sh
#endif // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp b/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp
index 2a92860088..a57ffcb4bc 100644
--- a/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp
@@ -17,14 +17,18 @@
#include "common/mathutil.h"
#include "common/matrix_utils.h"
-#include "compiler/translator/HashNames.h"
+#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
namespace
{
-const float kPi = 3.14159265358979323846f;
+const float kPi = 3.14159265358979323846f;
const float kDegreesToRadiansMultiplier = kPi / 180.0f;
const float kRadiansToDegreesMultiplier = 180.0f / kPi;
@@ -33,72 +37,40 @@ TPrecision GetHigherPrecision(TPrecision left, TPrecision right)
return left > right ? left : right;
}
-bool ValidateMultiplication(TOperator op, const TType &left, const TType &right)
-{
- switch (op)
- {
- case EOpMul:
- case EOpMulAssign:
- return left.getNominalSize() == right.getNominalSize() &&
- left.getSecondarySize() == right.getSecondarySize();
- case EOpVectorTimesScalar:
- case EOpVectorTimesScalarAssign:
- return true;
- case EOpVectorTimesMatrix:
- return left.getNominalSize() == right.getRows();
- case EOpVectorTimesMatrixAssign:
- return left.getNominalSize() == right.getRows() &&
- left.getNominalSize() == right.getCols();
- case EOpMatrixTimesVector:
- return left.getCols() == right.getNominalSize();
- case EOpMatrixTimesScalar:
- case EOpMatrixTimesScalarAssign:
- return true;
- case EOpMatrixTimesMatrix:
- return left.getCols() == right.getRows();
- case EOpMatrixTimesMatrixAssign:
- return left.getCols() == right.getCols() &&
- left.getRows() == right.getRows();
-
- default:
- UNREACHABLE();
- return false;
- }
-}
-
TConstantUnion *Vectorize(const TConstantUnion &constant, size_t size)
{
TConstantUnion *constUnion = new TConstantUnion[size];
for (unsigned int i = 0; i < size; ++i)
- constUnion[i] = constant;
+ constUnion[i] = constant;
return constUnion;
}
-void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType,
- TInfoSink &infoSink, TConstantUnion *result)
+void UndefinedConstantFoldingError(const TSourceLoc &loc,
+ TOperator op,
+ TBasicType basicType,
+ TDiagnostics *diagnostics,
+ TConstantUnion *result)
{
- std::stringstream constantFoldingErrorStream;
- constantFoldingErrorStream << "'" << GetOperatorString(op)
- << "' operation result is undefined for the values passed in";
- infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str());
+ diagnostics->warning(loc, "operation result is undefined for the values passed in",
+ GetOperatorString(op));
switch (basicType)
{
- case EbtFloat :
- result->setFConst(0.0f);
- break;
- case EbtInt:
- result->setIConst(0);
- break;
- case EbtUInt:
- result->setUConst(0u);
- break;
- case EbtBool:
- result->setBConst(false);
- break;
- default:
- break;
+ case EbtFloat:
+ result->setFConst(0.0f);
+ break;
+ case EbtInt:
+ result->setIConst(0);
+ break;
+ case EbtUInt:
+ result->setUConst(0u);
+ break;
+ case EbtBool:
+ result->setBConst(false);
+ break;
+ default:
+ break;
}
}
@@ -123,7 +95,7 @@ float VectorDotProduct(const TConstantUnion *paramArray1,
return result;
}
-TIntermTyped *CreateFoldedNode(TConstantUnion *constArray,
+TIntermTyped *CreateFoldedNode(const TConstantUnion *constArray,
const TIntermTyped *originalNode,
TQualifier qualifier)
{
@@ -145,8 +117,9 @@ angle::Matrix<float> GetMatrix(const TConstantUnion *paramArray,
for (size_t i = 0; i < rows * cols; i++)
elements.push_back(paramArray[i].getFConst());
// Transpose is used since the Matrix constructor expects arguments in row-major order,
- // whereas the paramArray is in column-major order.
- return angle::Matrix<float>(elements, rows, cols).transpose();
+ // whereas the paramArray is in column-major order. Rows/cols parameters are also flipped below
+ // so that the created matrix will have the expected dimensions after the transpose.
+ return angle::Matrix<float>(elements, cols, rows).transpose();
}
angle::Matrix<float> GetMatrix(const TConstantUnion *paramArray, const unsigned int &size)
@@ -163,7 +136,7 @@ void SetUnionArrayFromMatrix(const angle::Matrix<float> &m, TConstantUnion *resu
{
// Transpose is used since the input Matrix is in row-major order,
// whereas the actual result should be in column-major order.
- angle::Matrix<float> result = m.transpose();
+ angle::Matrix<float> result = m.transpose();
std::vector<float> resultElements = result.elements();
for (size_t i = 0; i < resultElements.size(); i++)
resultArray[i].setFConst(resultElements[i]);
@@ -171,7 +144,6 @@ void SetUnionArrayFromMatrix(const angle::Matrix<float> &m, TConstantUnion *resu
} // namespace anonymous
-
////////////////////////////////////////////////////////////////
//
// Member functions of the nodes used for building the tree.
@@ -181,90 +153,214 @@ void SetUnionArrayFromMatrix(const angle::Matrix<float> &m, TConstantUnion *resu
void TIntermTyped::setTypePreservePrecision(const TType &t)
{
TPrecision precision = getPrecision();
- mType = t;
+ mType = t;
ASSERT(mType.getBasicType() != EbtBool || precision == EbpUndefined);
mType.setPrecision(precision);
}
#define REPLACE_IF_IS(node, type, original, replacement) \
- if (node == original) { \
- node = static_cast<type *>(replacement); \
- return true; \
+ if (node == original) \
+ { \
+ node = static_cast<type *>(replacement); \
+ return true; \
}
-bool TIntermLoop::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+bool TIntermLoop::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
+ ASSERT(original != nullptr); // This risks replacing multiple children.
REPLACE_IF_IS(mInit, TIntermNode, original, replacement);
REPLACE_IF_IS(mCond, TIntermTyped, original, replacement);
REPLACE_IF_IS(mExpr, TIntermTyped, original, replacement);
- REPLACE_IF_IS(mBody, TIntermAggregate, original, replacement);
+ REPLACE_IF_IS(mBody, TIntermBlock, original, replacement);
return false;
}
-bool TIntermBranch::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+bool TIntermBranch::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(mExpression, TIntermTyped, original, replacement);
return false;
}
-bool TIntermBinary::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+bool TIntermSwizzle::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ ASSERT(original->getAsTyped()->getType() == replacement->getAsTyped()->getType());
+ REPLACE_IF_IS(mOperand, TIntermTyped, original, replacement);
+ return false;
+}
+
+bool TIntermBinary::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(mLeft, TIntermTyped, original, replacement);
REPLACE_IF_IS(mRight, TIntermTyped, original, replacement);
return false;
}
-bool TIntermUnary::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+bool TIntermUnary::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
+ ASSERT(original->getAsTyped()->getType() == replacement->getAsTyped()->getType());
REPLACE_IF_IS(mOperand, TIntermTyped, original, replacement);
return false;
}
-bool TIntermAggregate::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+bool TIntermInvariantDeclaration::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mSymbol, TIntermSymbol, original, replacement);
+ return false;
+}
+
+bool TIntermFunctionDefinition::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mPrototype, TIntermFunctionPrototype, original, replacement);
+ REPLACE_IF_IS(mBody, TIntermBlock, original, replacement);
+ return false;
+}
+
+bool TIntermAggregate::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ return replaceChildNodeInternal(original, replacement);
+}
+
+bool TIntermBlock::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
- for (size_t ii = 0; ii < mSequence.size(); ++ii)
+ return replaceChildNodeInternal(original, replacement);
+}
+
+bool TIntermFunctionPrototype::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ return replaceChildNodeInternal(original, replacement);
+}
+
+bool TIntermDeclaration::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ return replaceChildNodeInternal(original, replacement);
+}
+
+bool TIntermAggregateBase::replaceChildNodeInternal(TIntermNode *original, TIntermNode *replacement)
+{
+ for (size_t ii = 0; ii < getSequence()->size(); ++ii)
{
- REPLACE_IF_IS(mSequence[ii], TIntermNode, original, replacement);
+ REPLACE_IF_IS((*getSequence())[ii], TIntermNode, original, replacement);
}
return false;
}
-bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements)
+bool TIntermAggregateBase::replaceChildNodeWithMultiple(TIntermNode *original,
+ const TIntermSequence &replacements)
{
- for (auto it = mSequence.begin(); it < mSequence.end(); ++it)
+ for (auto it = getSequence()->begin(); it < getSequence()->end(); ++it)
{
if (*it == original)
{
- it = mSequence.erase(it);
- mSequence.insert(it, replacements.begin(), replacements.end());
+ it = getSequence()->erase(it);
+ getSequence()->insert(it, replacements.begin(), replacements.end());
return true;
}
}
return false;
}
-bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions)
+bool TIntermAggregateBase::insertChildNodes(TIntermSequence::size_type position,
+ const TIntermSequence &insertions)
{
- if (position > mSequence.size())
+ if (position > getSequence()->size())
{
return false;
}
- auto it = mSequence.begin() + position;
- mSequence.insert(it, insertions.begin(), insertions.end());
+ auto it = getSequence()->begin() + position;
+ getSequence()->insert(it, insertions.begin(), insertions.end());
return true;
}
+TIntermAggregate *TIntermAggregate::CreateFunctionCall(const TFunction &func,
+ TIntermSequence *arguments)
+{
+ TIntermAggregate *callNode =
+ new TIntermAggregate(func.getReturnType(), EOpCallFunctionInAST, arguments);
+ callNode->getFunctionSymbolInfo()->setFromFunction(func);
+ return callNode;
+}
+
+TIntermAggregate *TIntermAggregate::CreateFunctionCall(const TType &type,
+ const TSymbolUniqueId &id,
+ const TName &name,
+ TIntermSequence *arguments)
+{
+ TIntermAggregate *callNode = new TIntermAggregate(type, EOpCallFunctionInAST, arguments);
+ callNode->getFunctionSymbolInfo()->setId(id);
+ callNode->getFunctionSymbolInfo()->setNameObj(name);
+ return callNode;
+}
+
+TIntermAggregate *TIntermAggregate::CreateBuiltInFunctionCall(const TFunction &func,
+ TIntermSequence *arguments)
+{
+ TIntermAggregate *callNode =
+ new TIntermAggregate(func.getReturnType(), EOpCallBuiltInFunction, arguments);
+ callNode->getFunctionSymbolInfo()->setFromFunction(func);
+ // Note that name needs to be set before texture function type is determined.
+ callNode->setBuiltInFunctionPrecision();
+ return callNode;
+}
+
+TIntermAggregate *TIntermAggregate::CreateConstructor(const TType &type,
+ TIntermSequence *arguments)
+{
+ return new TIntermAggregate(type, EOpConstruct, arguments);
+}
+
+TIntermAggregate *TIntermAggregate::Create(const TType &type,
+ TOperator op,
+ TIntermSequence *arguments)
+{
+ TIntermAggregate *node = new TIntermAggregate(type, op, arguments);
+ ASSERT(op != EOpCallFunctionInAST); // Should use CreateFunctionCall
+ ASSERT(op != EOpCallBuiltInFunction); // Should use CreateBuiltInFunctionCall
+ ASSERT(!node->isConstructor()); // Should use CreateConstructor
+ return node;
+}
+
+TIntermAggregate::TIntermAggregate(const TType &type, TOperator op, TIntermSequence *arguments)
+ : TIntermOperator(op), mUseEmulatedFunction(false), mGotPrecisionFromChildren(false)
+{
+ if (arguments != nullptr)
+ {
+ mArguments.swap(*arguments);
+ }
+ setTypePrecisionAndQualifier(type);
+}
+
+void TIntermAggregate::setTypePrecisionAndQualifier(const TType &type)
+{
+ setType(type);
+ mType.setQualifier(EvqTemporary);
+ if (!isFunctionCall())
+ {
+ if (isConstructor())
+ {
+ // Structs should not be precision qualified, the individual members may be.
+ // Built-in types on the other hand should be precision qualified.
+ if (getBasicType() != EbtStruct)
+ {
+ setPrecisionFromChildren();
+ }
+ }
+ else
+ {
+ setPrecisionForBuiltInOp();
+ }
+ if (areChildrenConstQualified())
+ {
+ mType.setQualifier(EvqConst);
+ }
+ }
+}
+
bool TIntermAggregate::areChildrenConstQualified()
{
- for (TIntermNode *&child : mSequence)
+ for (TIntermNode *&arg : mArguments)
{
- TIntermTyped *typed = child->getAsTyped();
- if (typed && typed->getQualifier() != EvqConst)
+ TIntermTyped *typedArg = arg->getAsTyped();
+ if (typedArg && typedArg->getQualifier() != EvqConst)
{
return false;
}
@@ -281,9 +377,9 @@ void TIntermAggregate::setPrecisionFromChildren()
return;
}
- TPrecision precision = EbpUndefined;
- TIntermSequence::iterator childIter = mSequence.begin();
- while (childIter != mSequence.end())
+ TPrecision precision = EbpUndefined;
+ TIntermSequence::iterator childIter = mArguments.begin();
+ while (childIter != mArguments.end())
{
TIntermTyped *typed = (*childIter)->getAsTyped();
if (typed)
@@ -293,51 +389,149 @@ void TIntermAggregate::setPrecisionFromChildren()
mType.setPrecision(precision);
}
+void TIntermAggregate::setPrecisionForBuiltInOp()
+{
+ ASSERT(!isConstructor());
+ ASSERT(!isFunctionCall());
+ if (!setPrecisionForSpecialBuiltInOp())
+ {
+ setPrecisionFromChildren();
+ }
+}
+
+bool TIntermAggregate::setPrecisionForSpecialBuiltInOp()
+{
+ switch (mOp)
+ {
+ case EOpBitfieldExtract:
+ mType.setPrecision(mArguments[0]->getAsTyped()->getPrecision());
+ mGotPrecisionFromChildren = true;
+ return true;
+ case EOpBitfieldInsert:
+ mType.setPrecision(GetHigherPrecision(mArguments[0]->getAsTyped()->getPrecision(),
+ mArguments[1]->getAsTyped()->getPrecision()));
+ mGotPrecisionFromChildren = true;
+ return true;
+ case EOpUaddCarry:
+ case EOpUsubBorrow:
+ mType.setPrecision(EbpHigh);
+ return true;
+ default:
+ return false;
+ }
+}
+
void TIntermAggregate::setBuiltInFunctionPrecision()
{
// All built-ins returning bool should be handled as ops, not functions.
ASSERT(getBasicType() != EbtBool);
+ ASSERT(mOp == EOpCallBuiltInFunction);
TPrecision precision = EbpUndefined;
- TIntermSequence::iterator childIter = mSequence.begin();
- while (childIter != mSequence.end())
+ for (TIntermNode *arg : mArguments)
{
- TIntermTyped *typed = (*childIter)->getAsTyped();
+ TIntermTyped *typed = arg->getAsTyped();
// ESSL spec section 8: texture functions get their precision from the sampler.
if (typed && IsSampler(typed->getBasicType()))
{
precision = typed->getPrecision();
break;
}
- ++childIter;
}
// ESSL 3.0 spec section 8: textureSize always gets highp precision.
// All other functions that take a sampler are assumed to be texture functions.
- if (mName.getString().find("textureSize") == 0)
+ if (mFunctionInfo.getName().find("textureSize") == 0)
mType.setPrecision(EbpHigh);
else
mType.setPrecision(precision);
}
-bool TIntermSelection::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+TString TIntermAggregate::getSymbolTableMangledName() const
+{
+ ASSERT(!isConstructor());
+ switch (mOp)
+ {
+ case EOpCallInternalRawFunction:
+ case EOpCallBuiltInFunction:
+ case EOpCallFunctionInAST:
+ return TFunction::GetMangledNameFromCall(mFunctionInfo.getName(), mArguments);
+ default:
+ TString opString = GetOperatorString(mOp);
+ return TFunction::GetMangledNameFromCall(opString, mArguments);
+ }
+}
+
+bool TIntermAggregate::hasSideEffects() const
+{
+ if (isFunctionCall() && mFunctionInfo.isKnownToNotHaveSideEffects())
+ {
+ for (TIntermNode *arg : mArguments)
+ {
+ if (arg->getAsTyped()->hasSideEffects())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ // Conservatively assume most aggregate operators have side-effects
+ return true;
+}
+
+void TIntermBlock::appendStatement(TIntermNode *statement)
+{
+ // Declaration nodes with no children can appear if it was an empty declaration or if all the
+ // declarators just added constants to the symbol table instead of generating code. We still
+ // need to add the declaration to the AST in that case because it might be relevant to the
+ // validity of switch/case.
+ if (statement != nullptr)
+ {
+ mStatements.push_back(statement);
+ }
+}
+
+void TIntermFunctionPrototype::appendParameter(TIntermSymbol *parameter)
+{
+ ASSERT(parameter != nullptr);
+ mParameters.push_back(parameter);
+}
+
+void TIntermDeclaration::appendDeclarator(TIntermTyped *declarator)
+{
+ ASSERT(declarator != nullptr);
+ ASSERT(declarator->getAsSymbolNode() != nullptr ||
+ (declarator->getAsBinaryNode() != nullptr &&
+ declarator->getAsBinaryNode()->getOp() == EOpInitialize));
+ ASSERT(mDeclarators.empty() ||
+ declarator->getType().sameNonArrayType(mDeclarators.back()->getAsTyped()->getType()));
+ mDeclarators.push_back(declarator);
+}
+
+bool TIntermTernary::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
- REPLACE_IF_IS(mTrueBlock, TIntermNode, original, replacement);
- REPLACE_IF_IS(mFalseBlock, TIntermNode, original, replacement);
+ REPLACE_IF_IS(mTrueExpression, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mFalseExpression, TIntermTyped, original, replacement);
return false;
}
-bool TIntermSwitch::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+bool TIntermIfElse::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mTrueBlock, TIntermBlock, original, replacement);
+ REPLACE_IF_IS(mFalseBlock, TIntermBlock, original, replacement);
+ return false;
+}
+
+bool TIntermSwitch::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(mInit, TIntermTyped, original, replacement);
- REPLACE_IF_IS(mStatementList, TIntermAggregate, original, replacement);
+ REPLACE_IF_IS(mStatementList, TIntermBlock, original, replacement);
+ ASSERT(mStatementList);
return false;
}
-bool TIntermCase::replaceChildNode(
- TIntermNode *original, TIntermNode *replacement)
+bool TIntermCase::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
{
REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
return false;
@@ -351,28 +545,104 @@ TIntermTyped::TIntermTyped(const TIntermTyped &node) : TIntermNode(), mType(node
mLine = node.mLine;
}
+bool TIntermTyped::isConstructorWithOnlyConstantUnionParameters()
+{
+ TIntermAggregate *constructor = getAsAggregate();
+ if (!constructor || !constructor->isConstructor())
+ {
+ return false;
+ }
+ for (TIntermNode *&node : *constructor->getSequence())
+ {
+ if (!node->getAsConstantUnion())
+ return false;
+ }
+ return true;
+}
+
TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node)
{
mUnionArrayPointer = node.mUnionArrayPointer;
}
+void TFunctionSymbolInfo::setFromFunction(const TFunction &function)
+{
+ setName(function.getName());
+ setId(TSymbolUniqueId(function));
+}
+
+TFunctionSymbolInfo::TFunctionSymbolInfo(const TSymbolUniqueId &id)
+ : mId(new TSymbolUniqueId(id)), mKnownToNotHaveSideEffects(false)
+{
+}
+
+TFunctionSymbolInfo::TFunctionSymbolInfo(const TFunctionSymbolInfo &info)
+ : mName(info.mName), mId(nullptr), mKnownToNotHaveSideEffects(info.mKnownToNotHaveSideEffects)
+{
+ if (info.mId)
+ {
+ mId = new TSymbolUniqueId(*info.mId);
+ }
+}
+
+TFunctionSymbolInfo &TFunctionSymbolInfo::operator=(const TFunctionSymbolInfo &info)
+{
+ mName = info.mName;
+ if (info.mId)
+ {
+ mId = new TSymbolUniqueId(*info.mId);
+ }
+ else
+ {
+ mId = nullptr;
+ }
+ return *this;
+}
+
+void TFunctionSymbolInfo::setId(const TSymbolUniqueId &id)
+{
+ mId = new TSymbolUniqueId(id);
+}
+
+const TSymbolUniqueId &TFunctionSymbolInfo::getId() const
+{
+ ASSERT(mId);
+ return *mId;
+}
+
TIntermAggregate::TIntermAggregate(const TIntermAggregate &node)
: TIntermOperator(node),
- mName(node.mName),
- mUserDefined(node.mUserDefined),
- mFunctionId(node.mFunctionId),
mUseEmulatedFunction(node.mUseEmulatedFunction),
- mGotPrecisionFromChildren(node.mGotPrecisionFromChildren)
+ mGotPrecisionFromChildren(node.mGotPrecisionFromChildren),
+ mFunctionInfo(node.mFunctionInfo)
{
- for (TIntermNode *child : node.mSequence)
+ for (TIntermNode *arg : node.mArguments)
{
- TIntermTyped *typedChild = child->getAsTyped();
- ASSERT(typedChild != nullptr);
- TIntermTyped *childCopy = typedChild->deepCopy();
- mSequence.push_back(childCopy);
+ TIntermTyped *typedArg = arg->getAsTyped();
+ ASSERT(typedArg != nullptr);
+ TIntermTyped *argCopy = typedArg->deepCopy();
+ mArguments.push_back(argCopy);
}
}
+TIntermAggregate *TIntermAggregate::shallowCopy() const
+{
+ TIntermSequence *copySeq = new TIntermSequence();
+ copySeq->insert(copySeq->begin(), getSequence()->begin(), getSequence()->end());
+ TIntermAggregate *copyNode = new TIntermAggregate(mType, mOp, copySeq);
+ *copyNode->getFunctionSymbolInfo() = mFunctionInfo;
+ copyNode->setLine(mLine);
+ return copyNode;
+}
+
+TIntermSwizzle::TIntermSwizzle(const TIntermSwizzle &node) : TIntermTyped(node)
+{
+ TIntermTyped *operandCopy = node.mOperand->deepCopy();
+ ASSERT(operandCopy != nullptr);
+ mOperand = operandCopy;
+ mSwizzleOffsets = node.mSwizzleOffsets;
+}
+
TIntermBinary::TIntermBinary(const TIntermBinary &node)
: TIntermOperator(node), mAddIndexClamp(node.mAddIndexClamp)
{
@@ -391,106 +661,141 @@ TIntermUnary::TIntermUnary(const TIntermUnary &node)
mOperand = operandCopy;
}
-TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node)
+TIntermTernary::TIntermTernary(const TIntermTernary &node) : TIntermTyped(node)
{
- // Only supported for ternary nodes, not if statements.
- TIntermTyped *trueTyped = node.mTrueBlock->getAsTyped();
- TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped();
- ASSERT(trueTyped != nullptr);
- ASSERT(falseTyped != nullptr);
TIntermTyped *conditionCopy = node.mCondition->deepCopy();
- TIntermTyped *trueCopy = trueTyped->deepCopy();
- TIntermTyped *falseCopy = falseTyped->deepCopy();
+ TIntermTyped *trueCopy = node.mTrueExpression->deepCopy();
+ TIntermTyped *falseCopy = node.mFalseExpression->deepCopy();
ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr);
- mCondition = conditionCopy;
- mTrueBlock = trueCopy;
- mFalseBlock = falseCopy;
+ mCondition = conditionCopy;
+ mTrueExpression = trueCopy;
+ mFalseExpression = falseCopy;
}
-//
-// Say whether or not an operation node changes the value of a variable.
-//
bool TIntermOperator::isAssignment() const
{
- switch (mOp)
- {
- case EOpPostIncrement:
- case EOpPostDecrement:
- case EOpPreIncrement:
- case EOpPreDecrement:
- case EOpAssign:
- case EOpAddAssign:
- case EOpSubAssign:
- case EOpMulAssign:
- case EOpVectorTimesMatrixAssign:
- case EOpVectorTimesScalarAssign:
- case EOpMatrixTimesScalarAssign:
- case EOpMatrixTimesMatrixAssign:
- case EOpDivAssign:
- case EOpIModAssign:
- case EOpBitShiftLeftAssign:
- case EOpBitShiftRightAssign:
- case EOpBitwiseAndAssign:
- case EOpBitwiseXorAssign:
- case EOpBitwiseOrAssign:
- return true;
- default:
- return false;
- }
+ return IsAssignment(mOp);
}
bool TIntermOperator::isMultiplication() const
{
switch (mOp)
{
- case EOpMul:
- case EOpMatrixTimesMatrix:
- case EOpMatrixTimesVector:
- case EOpMatrixTimesScalar:
- case EOpVectorTimesMatrix:
- case EOpVectorTimesScalar:
- return true;
- default:
- return false;
+ case EOpMul:
+ case EOpMatrixTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpVectorTimesScalar:
+ return true;
+ default:
+ return false;
}
}
-//
-// returns true if the operator is for one of the constructors
-//
bool TIntermOperator::isConstructor() const
{
+ return (mOp == EOpConstruct);
+}
+
+bool TIntermOperator::isFunctionCall() const
+{
switch (mOp)
{
- case EOpConstructVec2:
- case EOpConstructVec3:
- case EOpConstructVec4:
- case EOpConstructMat2:
- case EOpConstructMat2x3:
- case EOpConstructMat2x4:
- case EOpConstructMat3x2:
- case EOpConstructMat3:
- case EOpConstructMat3x4:
- case EOpConstructMat4x2:
- case EOpConstructMat4x3:
- case EOpConstructMat4:
- case EOpConstructFloat:
- case EOpConstructIVec2:
- case EOpConstructIVec3:
- case EOpConstructIVec4:
- case EOpConstructInt:
- case EOpConstructUVec2:
- case EOpConstructUVec3:
- case EOpConstructUVec4:
- case EOpConstructUInt:
- case EOpConstructBVec2:
- case EOpConstructBVec3:
- case EOpConstructBVec4:
- case EOpConstructBool:
- case EOpConstructStruct:
- return true;
- default:
- return false;
+ case EOpCallFunctionInAST:
+ case EOpCallBuiltInFunction:
+ case EOpCallInternalRawFunction:
+ return true;
+ default:
+ return false;
+ }
+}
+
+TOperator TIntermBinary::GetMulOpBasedOnOperands(const TType &left, const TType &right)
+{
+ if (left.isMatrix())
+ {
+ if (right.isMatrix())
+ {
+ return EOpMatrixTimesMatrix;
+ }
+ else
+ {
+ if (right.isVector())
+ {
+ return EOpMatrixTimesVector;
+ }
+ else
+ {
+ return EOpMatrixTimesScalar;
+ }
+ }
+ }
+ else
+ {
+ if (right.isMatrix())
+ {
+ if (left.isVector())
+ {
+ return EOpVectorTimesMatrix;
+ }
+ else
+ {
+ return EOpMatrixTimesScalar;
+ }
+ }
+ else
+ {
+ // Neither operand is a matrix.
+ if (left.isVector() == right.isVector())
+ {
+ // Leave as component product.
+ return EOpMul;
+ }
+ else
+ {
+ return EOpVectorTimesScalar;
+ }
+ }
+ }
+}
+
+TOperator TIntermBinary::GetMulAssignOpBasedOnOperands(const TType &left, const TType &right)
+{
+ if (left.isMatrix())
+ {
+ if (right.isMatrix())
+ {
+ return EOpMatrixTimesMatrixAssign;
+ }
+ else
+ {
+ // right should be scalar, but this may not be validated yet.
+ return EOpMatrixTimesScalarAssign;
+ }
+ }
+ else
+ {
+ if (right.isMatrix())
+ {
+ // Left should be a vector, but this may not be validated yet.
+ return EOpVectorTimesMatrixAssign;
+ }
+ else
+ {
+ // Neither operand is a matrix.
+ if (left.isVector() == right.isVector())
+ {
+ // Leave as component product.
+ return EOpMulAssign;
+ }
+ else
+ {
+ // left should be vector and right should be scalar, but this may not be validated
+ // yet.
+ return EOpVectorTimesScalarAssign;
+ }
+ }
}
}
@@ -498,70 +803,281 @@ bool TIntermOperator::isConstructor() const
// Make sure the type of a unary operator is appropriate for its
// combination of operation and operand type.
//
-void TIntermUnary::promote(const TType *funcReturnType)
+void TIntermUnary::promote()
{
+ if (mOp == EOpArrayLength)
+ {
+ // Special case: the qualifier of .length() doesn't depend on the operand qualifier.
+ setType(TType(EbtInt, EbpUndefined, EvqConst));
+ return;
+ }
+
+ TQualifier resultQualifier = EvqTemporary;
+ if (mOperand->getQualifier() == EvqConst)
+ resultQualifier = EvqConst;
+
+ unsigned char operandPrimarySize =
+ static_cast<unsigned char>(mOperand->getType().getNominalSize());
switch (mOp)
{
- case EOpFloatBitsToInt:
- case EOpFloatBitsToUint:
- case EOpIntBitsToFloat:
- case EOpUintBitsToFloat:
- case EOpPackSnorm2x16:
- case EOpPackUnorm2x16:
- case EOpPackHalf2x16:
- case EOpUnpackSnorm2x16:
- case EOpUnpackUnorm2x16:
- mType.setPrecision(EbpHigh);
- break;
- case EOpUnpackHalf2x16:
- mType.setPrecision(EbpMedium);
- break;
- default:
- setType(mOperand->getType());
+ case EOpFloatBitsToInt:
+ setType(TType(EbtInt, EbpHigh, resultQualifier, operandPrimarySize));
+ break;
+ case EOpFloatBitsToUint:
+ setType(TType(EbtUInt, EbpHigh, resultQualifier, operandPrimarySize));
+ break;
+ case EOpIntBitsToFloat:
+ case EOpUintBitsToFloat:
+ setType(TType(EbtFloat, EbpHigh, resultQualifier, operandPrimarySize));
+ break;
+ case EOpPackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpPackUnorm4x8:
+ case EOpPackSnorm4x8:
+ setType(TType(EbtUInt, EbpHigh, resultQualifier));
+ break;
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackUnorm2x16:
+ setType(TType(EbtFloat, EbpHigh, resultQualifier, 2));
+ break;
+ case EOpUnpackHalf2x16:
+ setType(TType(EbtFloat, EbpMedium, resultQualifier, 2));
+ break;
+ case EOpUnpackUnorm4x8:
+ case EOpUnpackSnorm4x8:
+ setType(TType(EbtFloat, EbpMedium, resultQualifier, 4));
+ break;
+ case EOpAny:
+ case EOpAll:
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
+ case EOpLength:
+ case EOpDeterminant:
+ setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier));
+ break;
+ case EOpTranspose:
+ setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier,
+ static_cast<unsigned char>(mOperand->getType().getRows()),
+ static_cast<unsigned char>(mOperand->getType().getCols())));
+ break;
+ case EOpIsInf:
+ case EOpIsNan:
+ setType(TType(EbtBool, EbpUndefined, resultQualifier, operandPrimarySize));
+ break;
+ case EOpBitfieldReverse:
+ setType(TType(mOperand->getBasicType(), EbpHigh, resultQualifier, operandPrimarySize));
+ break;
+ case EOpBitCount:
+ setType(TType(EbtInt, EbpLow, resultQualifier, operandPrimarySize));
+ break;
+ case EOpFindLSB:
+ setType(TType(EbtInt, EbpLow, resultQualifier, operandPrimarySize));
+ break;
+ case EOpFindMSB:
+ setType(TType(EbtInt, EbpLow, resultQualifier, operandPrimarySize));
+ break;
+ default:
+ setType(mOperand->getType());
+ mType.setQualifier(resultQualifier);
+ break;
+ }
+}
+
+TIntermSwizzle::TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets)
+ : TIntermTyped(TType(EbtFloat, EbpUndefined)),
+ mOperand(operand),
+ mSwizzleOffsets(swizzleOffsets)
+{
+ ASSERT(mSwizzleOffsets.size() <= 4);
+ promote();
+}
+
+TIntermUnary::TIntermUnary(TOperator op, TIntermTyped *operand)
+ : TIntermOperator(op), mOperand(operand), mUseEmulatedFunction(false)
+{
+ promote();
+}
+
+TIntermBinary::TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right)
+ : TIntermOperator(op), mLeft(left), mRight(right), mAddIndexClamp(false)
+{
+ promote();
+}
+
+TIntermInvariantDeclaration::TIntermInvariantDeclaration(TIntermSymbol *symbol, const TSourceLoc &line)
+ : TIntermNode(), mSymbol(symbol)
+{
+ ASSERT(symbol);
+ setLine(line);
+}
+
+TIntermTernary::TIntermTernary(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression)
+ : TIntermTyped(trueExpression->getType()),
+ mCondition(cond),
+ mTrueExpression(trueExpression),
+ mFalseExpression(falseExpression)
+{
+ getTypePointer()->setQualifier(
+ TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression));
+}
+
+TIntermLoop::TIntermLoop(TLoopType type,
+ TIntermNode *init,
+ TIntermTyped *cond,
+ TIntermTyped *expr,
+ TIntermBlock *body)
+ : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body)
+{
+ // Declaration nodes with no children can appear if all the declarators just added constants to
+ // the symbol table instead of generating code. They're no-ops so don't add them to the tree.
+ if (mInit && mInit->getAsDeclarationNode() &&
+ mInit->getAsDeclarationNode()->getSequence()->empty())
+ {
+ mInit = nullptr;
+ }
+}
+
+TIntermIfElse::TIntermIfElse(TIntermTyped *cond, TIntermBlock *trueB, TIntermBlock *falseB)
+ : TIntermNode(), mCondition(cond), mTrueBlock(trueB), mFalseBlock(falseB)
+{
+ // Prune empty false blocks so that there won't be unnecessary operations done on it.
+ if (mFalseBlock && mFalseBlock->getSequence()->empty())
+ {
+ mFalseBlock = nullptr;
+ }
+}
+
+TIntermSwitch::TIntermSwitch(TIntermTyped *init, TIntermBlock *statementList)
+ : TIntermNode(), mInit(init), mStatementList(statementList)
+{
+ ASSERT(mStatementList);
+}
+
+void TIntermSwitch::setStatementList(TIntermBlock *statementList)
+{
+ ASSERT(statementList);
+ mStatementList = statementList;
+}
+
+// static
+TQualifier TIntermTernary::DetermineQualifier(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression)
+{
+ if (cond->getQualifier() == EvqConst && trueExpression->getQualifier() == EvqConst &&
+ falseExpression->getQualifier() == EvqConst)
+ {
+ return EvqConst;
}
+ return EvqTemporary;
+}
- if (funcReturnType != nullptr)
+TIntermTyped *TIntermTernary::fold()
+{
+ if (mCondition->getAsConstantUnion())
{
- if (funcReturnType->getBasicType() == EbtBool)
+ if (mCondition->getAsConstantUnion()->getBConst(0))
{
- // Bool types should not have precision.
- setType(*funcReturnType);
+ mTrueExpression->getTypePointer()->setQualifier(mType.getQualifier());
+ return mTrueExpression;
}
else
{
- // Precision of the node has been set based on the operand.
- setTypePreservePrecision(*funcReturnType);
+ mFalseExpression->getTypePointer()->setQualifier(mType.getQualifier());
+ return mFalseExpression;
}
}
+ return this;
+}
+void TIntermSwizzle::promote()
+{
+ TQualifier resultQualifier = EvqTemporary;
if (mOperand->getQualifier() == EvqConst)
- mType.setQualifier(EvqConst);
- else
- mType.setQualifier(EvqTemporary);
+ resultQualifier = EvqConst;
+
+ auto numFields = mSwizzleOffsets.size();
+ setType(TType(mOperand->getBasicType(), mOperand->getPrecision(), resultQualifier,
+ static_cast<unsigned char>(numFields)));
}
-//
-// Establishes the type of the resultant operation, as well as
-// makes the operator the correct one for the operands.
-//
-// For lots of operations it should already be established that the operand
-// combination is valid, but returns false if operator can't work on operands.
-//
-bool TIntermBinary::promote(TInfoSink &infoSink)
+bool TIntermSwizzle::hasDuplicateOffsets() const
{
- ASSERT(mLeft->isArray() == mRight->isArray());
+ int offsetCount[4] = {0u, 0u, 0u, 0u};
+ for (const auto offset : mSwizzleOffsets)
+ {
+ offsetCount[offset]++;
+ if (offsetCount[offset] > 1)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TIntermSwizzle::offsetsMatch(int offset) const
+{
+ return mSwizzleOffsets.size() == 1 && mSwizzleOffsets[0] == offset;
+}
+
+void TIntermSwizzle::writeOffsetsAsXYZW(TInfoSinkBase *out) const
+{
+ for (const int offset : mSwizzleOffsets)
+ {
+ switch (offset)
+ {
+ case 0:
+ *out << "x";
+ break;
+ case 1:
+ *out << "y";
+ break;
+ case 2:
+ *out << "z";
+ break;
+ case 3:
+ *out << "w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+TQualifier TIntermBinary::GetCommaQualifier(int shaderVersion,
+ const TIntermTyped *left,
+ const TIntermTyped *right)
+{
+ // ESSL3.00 section 12.43: The result of a sequence operator is not a constant-expression.
+ if (shaderVersion >= 300 || left->getQualifier() != EvqConst ||
+ right->getQualifier() != EvqConst)
+ {
+ return EvqTemporary;
+ }
+ return EvqConst;
+}
+
+// Establishes the type of the result of the binary operation.
+void TIntermBinary::promote()
+{
+ ASSERT(!isMultiplication() ||
+ mOp == GetMulOpBasedOnOperands(mLeft->getType(), mRight->getType()));
+
+ // Comma is handled as a special case. Note that the comma node qualifier depends on the shader
+ // version and so is not being set here.
+ if (mOp == EOpComma)
+ {
+ setType(mRight->getType());
+ return;
+ }
- //
// Base assumption: just make the type the same as the left
// operand. Then only deviations from this need be coded.
- //
setType(mLeft->getType());
- // The result gets promoted to the highest precision.
- TPrecision higherPrecision = GetHigherPrecision(
- mLeft->getPrecision(), mRight->getPrecision());
- getTypePointer()->setPrecision(higherPrecision);
-
TQualifier resultQualifier = EvqConst;
// Binary operations results in temporary variables unless both
// operands are const.
@@ -571,8 +1087,56 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
getTypePointer()->setQualifier(EvqTemporary);
}
- const int nominalSize =
- std::max(mLeft->getNominalSize(), mRight->getNominalSize());
+ // Handle indexing ops.
+ switch (mOp)
+ {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ if (mLeft->isArray())
+ {
+ mType.toArrayElementType();
+ }
+ else if (mLeft->isMatrix())
+ {
+ setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier,
+ static_cast<unsigned char>(mLeft->getRows())));
+ }
+ else if (mLeft->isVector())
+ {
+ setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier));
+ }
+ else
+ {
+ UNREACHABLE();
+ }
+ return;
+ case EOpIndexDirectStruct:
+ {
+ const TFieldList &fields = mLeft->getType().getStruct()->fields();
+ const int i = mRight->getAsConstantUnion()->getIConst(0);
+ setType(*fields[i]->type());
+ getTypePointer()->setQualifier(resultQualifier);
+ return;
+ }
+ case EOpIndexDirectInterfaceBlock:
+ {
+ const TFieldList &fields = mLeft->getType().getInterfaceBlock()->fields();
+ const int i = mRight->getAsConstantUnion()->getIConst(0);
+ setType(*fields[i]->type());
+ getTypePointer()->setQualifier(resultQualifier);
+ return;
+ }
+ default:
+ break;
+ }
+
+ ASSERT(mLeft->isArray() == mRight->isArray());
+
+ // The result gets promoted to the highest precision.
+ TPrecision higherPrecision = GetHigherPrecision(mLeft->getPrecision(), mRight->getPrecision());
+ getTypePointer()->setPrecision(higherPrecision);
+
+ const int nominalSize = std::max(mLeft->getNominalSize(), mRight->getNominalSize());
//
// All scalars or structs. Code after this test assumes this case is removed!
@@ -581,316 +1145,319 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
{
switch (mOp)
{
- //
- // Promote to conditional
- //
- case EOpEqual:
- case EOpNotEqual:
- case EOpLessThan:
- case EOpGreaterThan:
- case EOpLessThanEqual:
- case EOpGreaterThanEqual:
- setType(TType(EbtBool, EbpUndefined));
- break;
+ //
+ // Promote to conditional
+ //
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
- //
- // And and Or operate on conditionals
- //
- case EOpLogicalAnd:
- case EOpLogicalXor:
- case EOpLogicalOr:
- ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
- setType(TType(EbtBool, EbpUndefined));
- break;
+ //
+ // And and Or operate on conditionals
+ //
+ case EOpLogicalAnd:
+ case EOpLogicalXor:
+ case EOpLogicalOr:
+ ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
- default:
- break;
+ default:
+ break;
}
- return true;
+ return;
}
// If we reach here, at least one of the operands is vector or matrix.
// The other operand could be a scalar, vector, or matrix.
- // Can these two operands be combined?
- //
TBasicType basicType = mLeft->getBasicType();
+
switch (mOp)
{
- case EOpMul:
- if (!mLeft->isMatrix() && mRight->isMatrix())
- {
- if (mLeft->isVector())
- {
- mOp = EOpVectorTimesMatrix;
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mRight->getCols()), 1));
- }
- else
+ case EOpMul:
+ break;
+ case EOpMatrixTimesScalar:
+ if (mRight->isMatrix())
{
- mOp = EOpMatrixTimesScalar;
setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mRight->getCols()),
static_cast<unsigned char>(mRight->getRows())));
}
- }
- else if (mLeft->isMatrix() && !mRight->isMatrix())
- {
- if (mRight->isVector())
- {
- mOp = EOpMatrixTimesVector;
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mLeft->getRows()), 1));
- }
- else
- {
- mOp = EOpMatrixTimesScalar;
- }
- }
- else if (mLeft->isMatrix() && mRight->isMatrix())
- {
- mOp = EOpMatrixTimesMatrix;
+ break;
+ case EOpMatrixTimesVector:
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(mLeft->getRows()), 1));
+ break;
+ case EOpMatrixTimesMatrix:
setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mRight->getCols()),
static_cast<unsigned char>(mLeft->getRows())));
+ break;
+ case EOpVectorTimesScalar:
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(nominalSize), 1));
+ break;
+ case EOpVectorTimesMatrix:
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(mRight->getCols()), 1));
+ break;
+ case EOpMulAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ ASSERT(mOp == GetMulAssignOpBasedOnOperands(mLeft->getType(), mRight->getType()));
+ break;
+ case EOpAssign:
+ case EOpInitialize:
+ ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
+ (mLeft->getSecondarySize() == mRight->getSecondarySize()));
+ break;
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpIMod:
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ {
+ const int secondarySize =
+ std::max(mLeft->getSecondarySize(), mRight->getSecondarySize());
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(nominalSize),
+ static_cast<unsigned char>(secondarySize)));
+ ASSERT(!mLeft->isArray() && !mRight->isArray());
+ break;
}
- else if (!mLeft->isMatrix() && !mRight->isMatrix())
- {
- if (mLeft->isVector() && mRight->isVector())
- {
- // leave as component product
- }
- else if (mLeft->isVector() || mRight->isVector())
- {
- mOp = EOpVectorTimesScalar;
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(nominalSize), 1));
- }
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(),
- "Missing elses");
- return false;
- }
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
+ (mLeft->getSecondarySize() == mRight->getSecondarySize()));
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
- if (!ValidateMultiplication(mOp, mLeft->getType(), mRight->getType()))
- {
- return false;
- }
- break;
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectInterfaceBlock:
+ case EOpIndexDirectStruct:
+ // These ops should be already fully handled.
+ UNREACHABLE();
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+const TConstantUnion *TIntermConstantUnion::foldIndexing(int index)
+{
+ if (isArray())
+ {
+ ASSERT(index < static_cast<int>(getType().getOutermostArraySize()));
+ TType arrayElementType = getType();
+ arrayElementType.toArrayElementType();
+ size_t arrayElementSize = arrayElementType.getObjectSize();
+ return &mUnionArrayPointer[arrayElementSize * index];
+ }
+ else if (isMatrix())
+ {
+ ASSERT(index < getType().getCols());
+ int size = getType().getRows();
+ return &mUnionArrayPointer[size * index];
+ }
+ else if (isVector())
+ {
+ ASSERT(index < getType().getNominalSize());
+ return &mUnionArrayPointer[index];
+ }
+ else
+ {
+ UNREACHABLE();
+ return nullptr;
+ }
+}
- case EOpMulAssign:
- if (!mLeft->isMatrix() && mRight->isMatrix())
+TIntermTyped *TIntermSwizzle::fold()
+{
+ TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
+ if (operandConstant == nullptr)
+ {
+ return this;
+ }
+
+ TConstantUnion *constArray = new TConstantUnion[mSwizzleOffsets.size()];
+ for (size_t i = 0; i < mSwizzleOffsets.size(); ++i)
+ {
+ constArray[i] = *operandConstant->foldIndexing(mSwizzleOffsets.at(i));
+ }
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
+}
+
+TIntermTyped *TIntermBinary::fold(TDiagnostics *diagnostics)
+{
+ TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion();
+ TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion();
+ switch (mOp)
+ {
+ case EOpComma:
{
- if (mLeft->isVector())
+ if (mLeft->hasSideEffects())
{
- mOp = EOpVectorTimesMatrixAssign;
- }
- else
- {
- return false;
+ return this;
}
+ mRight->getTypePointer()->setQualifier(mType.getQualifier());
+ return mRight;
}
- else if (mLeft->isMatrix() && !mRight->isMatrix())
+ case EOpIndexDirect:
{
- if (mRight->isVector())
+ if (leftConstant == nullptr || rightConstant == nullptr)
{
- return false;
+ return this;
}
- else
+ int index = rightConstant->getIConst(0);
+
+ const TConstantUnion *constArray = leftConstant->foldIndexing(index);
+ if (!constArray)
{
- mOp = EOpMatrixTimesScalarAssign;
+ return this;
}
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
}
- else if (mLeft->isMatrix() && mRight->isMatrix())
+ case EOpIndexDirectStruct:
{
- mOp = EOpMatrixTimesMatrixAssign;
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mRight->getCols()),
- static_cast<unsigned char>(mLeft->getRows())));
- }
- else if (!mLeft->isMatrix() && !mRight->isMatrix())
- {
- if (mLeft->isVector() && mRight->isVector())
+ if (leftConstant == nullptr || rightConstant == nullptr)
{
- // leave as component product
+ return this;
}
- else if (mLeft->isVector() || mRight->isVector())
+ const TFieldList &fields = mLeft->getType().getStruct()->fields();
+ size_t index = static_cast<size_t>(rightConstant->getIConst(0));
+
+ size_t previousFieldsSize = 0;
+ for (size_t i = 0; i < index; ++i)
{
- if (!mLeft->isVector())
- return false;
- mOp = EOpVectorTimesScalarAssign;
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mLeft->getNominalSize()), 1));
+ previousFieldsSize += fields[i]->type()->getObjectSize();
}
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(),
- "Missing elses");
- return false;
- }
-
- if (!ValidateMultiplication(mOp, mLeft->getType(), mRight->getType()))
- {
- return false;
- }
- break;
-
- case EOpAssign:
- case EOpInitialize:
- // No more additional checks are needed.
- ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
- (mLeft->getSecondarySize() == mRight->getSecondarySize()));
- break;
- case EOpAdd:
- case EOpSub:
- case EOpDiv:
- case EOpIMod:
- case EOpBitShiftLeft:
- case EOpBitShiftRight:
- case EOpBitwiseAnd:
- case EOpBitwiseXor:
- case EOpBitwiseOr:
- case EOpAddAssign:
- case EOpSubAssign:
- case EOpDivAssign:
- case EOpIModAssign:
- case EOpBitShiftLeftAssign:
- case EOpBitShiftRightAssign:
- case EOpBitwiseAndAssign:
- case EOpBitwiseXorAssign:
- case EOpBitwiseOrAssign:
- if ((mLeft->isMatrix() && mRight->isVector()) ||
- (mLeft->isVector() && mRight->isMatrix()))
- {
- return false;
- }
-
- // Are the sizes compatible?
- if (mLeft->getNominalSize() != mRight->getNominalSize() ||
- mLeft->getSecondarySize() != mRight->getSecondarySize())
- {
- // If the nominal sizes of operands do not match:
- // One of them must be a scalar.
- if (!mLeft->isScalar() && !mRight->isScalar())
- return false;
- // In the case of compound assignment other than multiply-assign,
- // the right side needs to be a scalar. Otherwise a vector/matrix
- // would be assigned to a scalar. A scalar can't be shifted by a
- // vector either.
- if (!mRight->isScalar() &&
- (isAssignment() ||
- mOp == EOpBitShiftLeft ||
- mOp == EOpBitShiftRight))
- return false;
+ const TConstantUnion *constArray = leftConstant->getUnionArrayPointer();
+ return CreateFoldedNode(constArray + previousFieldsSize, this, mType.getQualifier());
}
-
+ case EOpIndexIndirect:
+ case EOpIndexDirectInterfaceBlock:
+ // Can never be constant folded.
+ return this;
+ default:
{
- const int secondarySize = std::max(
- mLeft->getSecondarySize(), mRight->getSecondarySize());
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(nominalSize),
- static_cast<unsigned char>(secondarySize)));
- if (mLeft->isArray())
+ if (leftConstant == nullptr || rightConstant == nullptr)
{
- ASSERT(mLeft->getArraySize() == mRight->getArraySize());
- mType.setArraySize(mLeft->getArraySize());
+ return this;
+ }
+ TConstantUnion *constArray =
+ leftConstant->foldBinary(mOp, rightConstant, diagnostics, mLeft->getLine());
+ if (!constArray)
+ {
+ return this;
}
- }
- break;
-
- case EOpEqual:
- case EOpNotEqual:
- case EOpLessThan:
- case EOpGreaterThan:
- case EOpLessThanEqual:
- case EOpGreaterThanEqual:
- ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
- (mLeft->getSecondarySize() == mRight->getSecondarySize()));
- setType(TType(EbtBool, EbpUndefined));
- break;
- default:
- return false;
+ // Nodes may be constant folded without being qualified as constant.
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
+ }
}
- return true;
}
-TIntermTyped *TIntermBinary::fold(TInfoSink &infoSink)
+TIntermTyped *TIntermUnary::fold(TDiagnostics *diagnostics)
{
- TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion();
- TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion();
- if (leftConstant == nullptr || rightConstant == nullptr)
- {
- return nullptr;
- }
- TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, infoSink);
+ TConstantUnion *constArray = nullptr;
- // Nodes may be constant folded without being qualified as constant.
- TQualifier resultQualifier = EvqConst;
- if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst)
+ if (mOp == EOpArrayLength)
{
- resultQualifier = EvqTemporary;
+ // The size of runtime-sized arrays may only be determined at runtime.
+ if (mOperand->hasSideEffects() || mOperand->getType().isUnsizedArray())
+ {
+ return this;
+ }
+ constArray = new TConstantUnion[1];
+ constArray->setIConst(mOperand->getOutermostArraySize());
}
- return CreateFoldedNode(constArray, this, resultQualifier);
-}
-
-TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink)
-{
- TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
- if (operandConstant == nullptr)
+ else
{
- return nullptr;
- }
+ TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
+ if (operandConstant == nullptr)
+ {
+ return this;
+ }
- TConstantUnion *constArray = nullptr;
- switch (mOp)
+ switch (mOp)
+ {
+ case EOpAny:
+ case EOpAll:
+ case EOpLength:
+ case EOpTranspose:
+ case EOpDeterminant:
+ case EOpInverse:
+ case EOpPackSnorm2x16:
+ case EOpUnpackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpUnpackUnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpUnpackHalf2x16:
+ case EOpPackUnorm4x8:
+ case EOpPackSnorm4x8:
+ case EOpUnpackUnorm4x8:
+ case EOpUnpackSnorm4x8:
+ constArray = operandConstant->foldUnaryNonComponentWise(mOp);
+ break;
+ default:
+ constArray = operandConstant->foldUnaryComponentWise(mOp, diagnostics);
+ break;
+ }
+ }
+ if (constArray == nullptr)
{
- case EOpAny:
- case EOpAll:
- case EOpLength:
- case EOpTranspose:
- case EOpDeterminant:
- case EOpInverse:
- case EOpPackSnorm2x16:
- case EOpUnpackSnorm2x16:
- case EOpPackUnorm2x16:
- case EOpUnpackUnorm2x16:
- case EOpPackHalf2x16:
- case EOpUnpackHalf2x16:
- constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink);
- break;
- default:
- constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink);
- break;
+ return this;
}
// Nodes may be constant folded without being qualified as constant.
- TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary;
- return CreateFoldedNode(constArray, this, resultQualifier);
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
}
-TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
+TIntermTyped *TIntermAggregate::fold(TDiagnostics *diagnostics)
{
// Make sure that all params are constant before actual constant folding.
for (auto *param : *getSequence())
{
if (param->getAsConstantUnion() == nullptr)
{
- return nullptr;
+ return this;
}
}
TConstantUnion *constArray = nullptr;
if (isConstructor())
- constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink);
+ constArray = TIntermConstantUnion::FoldAggregateConstructor(this);
else
- constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink);
+ constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, diagnostics);
// Nodes may be constant folded without being qualified as constant.
- TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary;
- return CreateFoldedNode(constArray, this, resultQualifier);
+ return CreateFoldedNode(constArray, this, getQualifier());
}
//
@@ -899,15 +1466,15 @@ TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
//
// Returns the constant value to keep using or nullptr.
//
-TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op,
+ TIntermConstantUnion *rightNode,
+ TDiagnostics *diagnostics,
+ const TSourceLoc &line)
{
const TConstantUnion *leftArray = getUnionArrayPointer();
const TConstantUnion *rightArray = rightNode->getUnionArrayPointer();
- if (!leftArray)
- return nullptr;
- if (!rightArray)
- return nullptr;
+ ASSERT(leftArray && rightArray);
size_t objectSize = getType().getObjectSize();
@@ -919,48 +1486,45 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn
else if (rightNode->getType().getObjectSize() > 1 && objectSize == 1)
{
// for a case like float f = 1.2 + vec4(2, 3, 4, 5);
- leftArray = Vectorize(*getUnionArrayPointer(), rightNode->getType().getObjectSize());
+ leftArray = Vectorize(*getUnionArrayPointer(), rightNode->getType().getObjectSize());
objectSize = rightNode->getType().getObjectSize();
}
TConstantUnion *resultArray = nullptr;
- switch(op)
+ switch (op)
{
- case EOpAdd:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] + rightArray[i];
- break;
- case EOpSub:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] - rightArray[i];
- break;
+ case EOpAdd:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] =
+ TConstantUnion::add(leftArray[i], rightArray[i], diagnostics, line);
+ break;
+ case EOpSub:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] =
+ TConstantUnion::sub(leftArray[i], rightArray[i], diagnostics, line);
+ break;
- case EOpMul:
- case EOpVectorTimesScalar:
- case EOpMatrixTimesScalar:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] * rightArray[i];
- break;
+ case EOpMul:
+ case EOpVectorTimesScalar:
+ case EOpMatrixTimesScalar:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] =
+ TConstantUnion::mul(leftArray[i], rightArray[i], diagnostics, line);
+ break;
- case EOpMatrixTimesMatrix:
+ case EOpMatrixTimesMatrix:
{
- if (getType().getBasicType() != EbtFloat ||
- rightNode->getBasicType() != EbtFloat)
- {
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Constant Folding cannot be done for matrix multiply");
- return nullptr;
- }
+ // TODO(jmadll): This code should check for overflows.
+ ASSERT(getType().getBasicType() == EbtFloat && rightNode->getBasicType() == EbtFloat);
- const int leftCols = getCols();
- const int leftRows = getRows();
- const int rightCols = rightNode->getType().getCols();
- const int rightRows = rightNode->getType().getRows();
+ const int leftCols = getCols();
+ const int leftRows = getRows();
+ const int rightCols = rightNode->getType().getCols();
+ const int rightRows = rightNode->getType().getRows();
const int resultCols = rightCols;
const int resultRows = leftRows;
@@ -975,94 +1539,155 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn
resultArray[resultRows * column + row].setFConst(
resultArray[resultRows * column + row].getFConst() +
leftArray[i * leftRows + row].getFConst() *
- rightArray[column * rightRows + i].getFConst());
+ rightArray[column * rightRows + i].getFConst());
}
}
}
}
break;
- case EOpDiv:
- case EOpIMod:
+ case EOpDiv:
+ case EOpIMod:
{
resultArray = new TConstantUnion[objectSize];
for (size_t i = 0; i < objectSize; i++)
{
switch (getType().getBasicType())
{
- case EbtFloat:
- if (rightArray[i] == 0.0f)
- {
- infoSink.info.message(EPrefixWarning, getLine(),
- "Divide by zero error during constant folding");
- resultArray[i].setFConst(leftArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX);
- }
- else
+ case EbtFloat:
{
ASSERT(op == EOpDiv);
- resultArray[i].setFConst(leftArray[i].getFConst() / rightArray[i].getFConst());
- }
- break;
-
- case EbtInt:
- if (rightArray[i] == 0)
- {
- infoSink.info.message(EPrefixWarning, getLine(),
- "Divide by zero error during constant folding");
- resultArray[i].setIConst(INT_MAX);
- }
- else
- {
- if (op == EOpDiv)
+ float dividend = leftArray[i].getFConst();
+ float divisor = rightArray[i].getFConst();
+ if (divisor == 0.0f)
+ {
+ if (dividend == 0.0f)
+ {
+ diagnostics->warning(
+ getLine(),
+ "Zero divided by zero during constant folding generated NaN",
+ "/");
+ resultArray[i].setFConst(std::numeric_limits<float>::quiet_NaN());
+ }
+ else
+ {
+ diagnostics->warning(getLine(),
+ "Divide by zero during constant folding", "/");
+ bool negativeResult =
+ std::signbit(dividend) != std::signbit(divisor);
+ resultArray[i].setFConst(
+ negativeResult ? -std::numeric_limits<float>::infinity()
+ : std::numeric_limits<float>::infinity());
+ }
+ }
+ else if (gl::isInf(dividend) && gl::isInf(divisor))
{
- resultArray[i].setIConst(leftArray[i].getIConst() / rightArray[i].getIConst());
+ diagnostics->warning(getLine(),
+ "Infinity divided by infinity during constant "
+ "folding generated NaN",
+ "/");
+ resultArray[i].setFConst(std::numeric_limits<float>::quiet_NaN());
}
else
{
- ASSERT(op == EOpIMod);
- resultArray[i].setIConst(leftArray[i].getIConst() % rightArray[i].getIConst());
+ float result = dividend / divisor;
+ if (!gl::isInf(dividend) && gl::isInf(result))
+ {
+ diagnostics->warning(
+ getLine(), "Constant folded division overflowed to infinity",
+ "/");
+ }
+ resultArray[i].setFConst(result);
}
+ break;
}
- break;
+ case EbtInt:
+ if (rightArray[i] == 0)
+ {
+ diagnostics->warning(
+ getLine(), "Divide by zero error during constant folding", "/");
+ resultArray[i].setIConst(INT_MAX);
+ }
+ else
+ {
+ int lhs = leftArray[i].getIConst();
+ int divisor = rightArray[i].getIConst();
+ if (op == EOpDiv)
+ {
+ // Check for the special case where the minimum representable number
+ // is
+ // divided by -1. If left alone this leads to integer overflow in
+ // C++.
+ // ESSL 3.00.6 section 4.1.3 Integers:
+ // "However, for the case where the minimum representable value is
+ // divided by -1, it is allowed to return either the minimum
+ // representable value or the maximum representable value."
+ if (lhs == -0x7fffffff - 1 && divisor == -1)
+ {
+ resultArray[i].setIConst(0x7fffffff);
+ }
+ else
+ {
+ resultArray[i].setIConst(lhs / divisor);
+ }
+ }
+ else
+ {
+ ASSERT(op == EOpIMod);
+ if (lhs < 0 || divisor < 0)
+ {
+ // ESSL 3.00.6 section 5.9: Results of modulus are undefined
+ // when
+ // either one of the operands is negative.
+ diagnostics->warning(getLine(),
+ "Negative modulus operator operand "
+ "encountered during constant folding",
+ "%");
+ resultArray[i].setIConst(0);
+ }
+ else
+ {
+ resultArray[i].setIConst(lhs % divisor);
+ }
+ }
+ }
+ break;
- case EbtUInt:
- if (rightArray[i] == 0)
- {
- infoSink.info.message(EPrefixWarning, getLine(),
- "Divide by zero error during constant folding");
- resultArray[i].setUConst(UINT_MAX);
- }
- else
- {
- if (op == EOpDiv)
+ case EbtUInt:
+ if (rightArray[i] == 0)
{
- resultArray[i].setUConst(leftArray[i].getUConst() / rightArray[i].getUConst());
+ diagnostics->warning(
+ getLine(), "Divide by zero error during constant folding", "/");
+ resultArray[i].setUConst(UINT_MAX);
}
else
{
- ASSERT(op == EOpIMod);
- resultArray[i].setUConst(leftArray[i].getUConst() % rightArray[i].getUConst());
+ if (op == EOpDiv)
+ {
+ resultArray[i].setUConst(leftArray[i].getUConst() /
+ rightArray[i].getUConst());
+ }
+ else
+ {
+ ASSERT(op == EOpIMod);
+ resultArray[i].setUConst(leftArray[i].getUConst() %
+ rightArray[i].getUConst());
+ }
}
- }
- break;
+ break;
- default:
- infoSink.info.message(EPrefixInternalError, getLine(),
- "Constant folding cannot be done for \"/\"");
- return nullptr;
+ default:
+ UNREACHABLE();
+ return nullptr;
}
}
}
break;
- case EOpMatrixTimesVector:
+ case EOpMatrixTimesVector:
{
- if (rightNode->getBasicType() != EbtFloat)
- {
- infoSink.info.message(EPrefixInternalError, getLine(),
- "Constant Folding cannot be done for matrix times vector");
- return nullptr;
- }
+ // TODO(jmadll): This code should check for overflows.
+ ASSERT(rightNode->getBasicType() == EbtFloat);
const int matrixCols = getCols();
const int matrixRows = getRows();
@@ -1074,22 +1699,19 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn
resultArray[matrixRow].setFConst(0.0f);
for (int col = 0; col < matrixCols; col++)
{
- resultArray[matrixRow].setFConst(resultArray[matrixRow].getFConst() +
- leftArray[col * matrixRows + matrixRow].getFConst() *
- rightArray[col].getFConst());
+ resultArray[matrixRow].setFConst(
+ resultArray[matrixRow].getFConst() +
+ leftArray[col * matrixRows + matrixRow].getFConst() *
+ rightArray[col].getFConst());
}
}
}
break;
- case EOpVectorTimesMatrix:
+ case EOpVectorTimesMatrix:
{
- if (getType().getBasicType() != EbtFloat)
- {
- infoSink.info.message(EPrefixInternalError, getLine(),
- "Constant Folding cannot be done for vector times matrix");
- return nullptr;
- }
+ // TODO(jmadll): This code should check for overflows.
+ ASSERT(getType().getBasicType() == EbtFloat);
const int matrixCols = rightNode->getType().getCols();
const int matrixRows = rightNode->getType().getRows();
@@ -1101,15 +1723,16 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn
resultArray[matrixCol].setFConst(0.0f);
for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++)
{
- resultArray[matrixCol].setFConst(resultArray[matrixCol].getFConst() +
- leftArray[matrixRow].getFConst() *
- rightArray[matrixCol * matrixRows + matrixRow].getFConst());
+ resultArray[matrixCol].setFConst(
+ resultArray[matrixCol].getFConst() +
+ leftArray[matrixRow].getFConst() *
+ rightArray[matrixCol * matrixRows + matrixRow].getFConst());
}
}
}
break;
- case EOpLogicalAnd:
+ case EOpLogicalAnd:
{
resultArray = new TConstantUnion[objectSize];
for (size_t i = 0; i < objectSize; i++)
@@ -1119,7 +1742,7 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn
}
break;
- case EOpLogicalOr:
+ case EOpLogicalOr:
{
resultArray = new TConstantUnion[objectSize];
for (size_t i = 0; i < objectSize; i++)
@@ -1129,79 +1752,74 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn
}
break;
- case EOpLogicalXor:
+ case EOpLogicalXor:
{
+ ASSERT(getType().getBasicType() == EbtBool);
resultArray = new TConstantUnion[objectSize];
for (size_t i = 0; i < objectSize; i++)
{
- switch (getType().getBasicType())
- {
- case EbtBool:
- resultArray[i].setBConst(leftArray[i] != rightArray[i]);
- break;
- default:
- UNREACHABLE();
- break;
- }
+ resultArray[i].setBConst(leftArray[i] != rightArray[i]);
}
}
break;
- case EOpBitwiseAnd:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] & rightArray[i];
- break;
- case EOpBitwiseXor:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] ^ rightArray[i];
- break;
- case EOpBitwiseOr:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] | rightArray[i];
- break;
- case EOpBitShiftLeft:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] << rightArray[i];
- break;
- case EOpBitShiftRight:
- resultArray = new TConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- resultArray[i] = leftArray[i] >> rightArray[i];
- break;
+ case EOpBitwiseAnd:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = leftArray[i] & rightArray[i];
+ break;
+ case EOpBitwiseXor:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = leftArray[i] ^ rightArray[i];
+ break;
+ case EOpBitwiseOr:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = leftArray[i] | rightArray[i];
+ break;
+ case EOpBitShiftLeft:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] =
+ TConstantUnion::lshift(leftArray[i], rightArray[i], diagnostics, line);
+ break;
+ case EOpBitShiftRight:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] =
+ TConstantUnion::rshift(leftArray[i], rightArray[i], diagnostics, line);
+ break;
- case EOpLessThan:
- ASSERT(objectSize == 1);
- resultArray = new TConstantUnion[1];
- resultArray->setBConst(*leftArray < *rightArray);
- break;
+ case EOpLessThan:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(*leftArray < *rightArray);
+ break;
- case EOpGreaterThan:
- ASSERT(objectSize == 1);
- resultArray = new TConstantUnion[1];
- resultArray->setBConst(*leftArray > *rightArray);
- break;
+ case EOpGreaterThan:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(*leftArray > *rightArray);
+ break;
- case EOpLessThanEqual:
- ASSERT(objectSize == 1);
- resultArray = new TConstantUnion[1];
- resultArray->setBConst(!(*leftArray > *rightArray));
- break;
+ case EOpLessThanEqual:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(!(*leftArray > *rightArray));
+ break;
- case EOpGreaterThanEqual:
- ASSERT(objectSize == 1);
- resultArray = new TConstantUnion[1];
- resultArray->setBConst(!(*leftArray < *rightArray));
- break;
+ case EOpGreaterThanEqual:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(!(*leftArray < *rightArray));
+ break;
- case EOpEqual:
- case EOpNotEqual:
+ case EOpEqual:
+ case EOpNotEqual:
{
resultArray = new TConstantUnion[1];
- bool equal = true;
+ bool equal = true;
for (size_t i = 0; i < objectSize; i++)
{
if (leftArray[i] != rightArray[i])
@@ -1221,38 +1839,29 @@ TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUn
}
break;
- default:
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Invalid operator for constant folding");
- return nullptr;
+ default:
+ UNREACHABLE();
+ return nullptr;
}
return resultArray;
}
-//
-// The fold functions see if an operation on a constant can be done in place,
-// without generating run-time code.
-//
-// Returns the constant value to keep using or nullptr.
-//
-TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink)
+// The fold functions do operations on a constant at GLSL compile time, without generating run-time
+// code. Returns the constant value to keep using. Nullptr should not be returned.
+TConstantUnion *TIntermConstantUnion::foldUnaryNonComponentWise(TOperator op)
{
- //
- // Do operations where the return type has a different number of components compared to the operand type.
- //
+ // Do operations where the return type may have a different number of components compared to the
+ // operand type.
const TConstantUnion *operandArray = getUnionArrayPointer();
- if (!operandArray)
- return nullptr;
+ ASSERT(operandArray);
- size_t objectSize = getType().getObjectSize();
+ size_t objectSize = getType().getObjectSize();
TConstantUnion *resultArray = nullptr;
switch (op)
{
- case EOpAny:
- if (getType().getBasicType() == EbtBool)
- {
+ case EOpAny:
+ ASSERT(getType().getBasicType() == EbtBool);
resultArray = new TConstantUnion();
resultArray->setBConst(false);
for (size_t i = 0; i < objectSize; i++)
@@ -1264,16 +1873,9 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator
}
}
break;
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpAll:
- if (getType().getBasicType() == EbtBool)
- {
+ case EOpAll:
+ ASSERT(getType().getBasicType() == EbtBool);
resultArray = new TConstantUnion();
resultArray->setBConst(true);
for (size_t i = 0; i < objectSize; i++)
@@ -1285,89 +1887,55 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator
}
}
break;
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpLength:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpLength:
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray = new TConstantUnion();
resultArray->setFConst(VectorLength(operandArray, objectSize));
break;
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpTranspose:
- if (getType().getBasicType() == EbtFloat)
+ case EOpTranspose:
{
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray = new TConstantUnion[objectSize];
angle::Matrix<float> result =
- GetMatrix(operandArray, getType().getNominalSize(), getType().getSecondarySize()).transpose();
+ GetMatrix(operandArray, getType().getRows(), getType().getCols()).transpose();
SetUnionArrayFromMatrix(result, resultArray);
break;
}
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpDeterminant:
- if (getType().getBasicType() == EbtFloat)
+ case EOpDeterminant:
{
+ ASSERT(getType().getBasicType() == EbtFloat);
unsigned int size = getType().getNominalSize();
ASSERT(size >= 2 && size <= 4);
resultArray = new TConstantUnion();
resultArray->setFConst(GetMatrix(operandArray, size).determinant());
break;
}
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpInverse:
- if (getType().getBasicType() == EbtFloat)
+ case EOpInverse:
{
+ ASSERT(getType().getBasicType() == EbtFloat);
unsigned int size = getType().getNominalSize();
ASSERT(size >= 2 && size <= 4);
- resultArray = new TConstantUnion[objectSize];
+ resultArray = new TConstantUnion[objectSize];
angle::Matrix<float> result = GetMatrix(operandArray, size).inverse();
SetUnionArrayFromMatrix(result, resultArray);
break;
}
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpPackSnorm2x16:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpPackSnorm2x16:
+ ASSERT(getType().getBasicType() == EbtFloat);
ASSERT(getType().getNominalSize() == 2);
resultArray = new TConstantUnion();
- resultArray->setUConst(gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+ resultArray->setUConst(
+ gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
break;
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpUnpackSnorm2x16:
- if (getType().getBasicType() == EbtUInt)
+ case EOpUnpackSnorm2x16:
{
+ ASSERT(getType().getBasicType() == EbtUInt);
resultArray = new TConstantUnion[2];
float f1, f2;
gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2);
@@ -1375,29 +1943,18 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator
resultArray[1].setFConst(f2);
break;
}
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpPackUnorm2x16:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpPackUnorm2x16:
+ ASSERT(getType().getBasicType() == EbtFloat);
ASSERT(getType().getNominalSize() == 2);
resultArray = new TConstantUnion();
- resultArray->setUConst(gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+ resultArray->setUConst(
+ gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
break;
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpUnpackUnorm2x16:
- if (getType().getBasicType() == EbtUInt)
+ case EOpUnpackUnorm2x16:
{
+ ASSERT(getType().getBasicType() == EbtUInt);
resultArray = new TConstantUnion[2];
float f1, f2;
gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2);
@@ -1405,29 +1962,18 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator
resultArray[1].setFConst(f2);
break;
}
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpPackHalf2x16:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpPackHalf2x16:
+ ASSERT(getType().getBasicType() == EbtFloat);
ASSERT(getType().getNominalSize() == 2);
resultArray = new TConstantUnion();
- resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+ resultArray->setUConst(
+ gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
break;
- }
- else
- {
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- }
- case EOpUnpackHalf2x16:
- if (getType().getBasicType() == EbtUInt)
+ case EOpUnpackHalf2x16:
{
+ ASSERT(getType().getBasicType() == EbtUInt);
resultArray = new TConstantUnion[2];
float f1, f2;
gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2);
@@ -1435,274 +1981,301 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator
resultArray[1].setFConst(f2);
break;
}
- else
+
+ case EOpPackUnorm4x8:
{
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray = new TConstantUnion();
+ resultArray->setUConst(
+ gl::PackUnorm4x8(operandArray[0].getFConst(), operandArray[1].getFConst(),
+ operandArray[2].getFConst(), operandArray[3].getFConst()));
+ break;
+ }
+ case EOpPackSnorm4x8:
+ {
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray = new TConstantUnion();
+ resultArray->setUConst(
+ gl::PackSnorm4x8(operandArray[0].getFConst(), operandArray[1].getFConst(),
+ operandArray[2].getFConst(), operandArray[3].getFConst()));
+ break;
+ }
+ case EOpUnpackUnorm4x8:
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ resultArray = new TConstantUnion[4];
+ float f[4];
+ gl::UnpackUnorm4x8(operandArray[0].getUConst(), f);
+ for (size_t i = 0; i < 4; ++i)
+ {
+ resultArray[i].setFConst(f[i]);
+ }
+ break;
+ }
+ case EOpUnpackSnorm4x8:
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ resultArray = new TConstantUnion[4];
+ float f[4];
+ gl::UnpackSnorm4x8(operandArray[0].getUConst(), f);
+ for (size_t i = 0; i < 4; ++i)
+ {
+ resultArray[i].setFConst(f[i]);
+ }
+ break;
}
- break;
- default:
- break;
+ default:
+ UNREACHABLE();
+ break;
}
return resultArray;
}
-TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::foldUnaryComponentWise(TOperator op,
+ TDiagnostics *diagnostics)
{
- //
- // Do unary operations where the return type is the same as operand type.
- //
+ // Do unary operations where each component of the result is computed based on the corresponding
+ // component of the operand. Also folds normalize, though the divisor in that case takes all
+ // components into account.
const TConstantUnion *operandArray = getUnionArrayPointer();
- if (!operandArray)
- return nullptr;
+ ASSERT(operandArray);
size_t objectSize = getType().getObjectSize();
TConstantUnion *resultArray = new TConstantUnion[objectSize];
for (size_t i = 0; i < objectSize; i++)
{
- switch(op)
+ switch (op)
{
- case EOpNegative:
- switch (getType().getBasicType())
- {
- case EbtFloat:
- resultArray[i].setFConst(-operandArray[i].getFConst());
- break;
- case EbtInt:
- resultArray[i].setIConst(-operandArray[i].getIConst());
- break;
- case EbtUInt:
- resultArray[i].setUConst(static_cast<unsigned int>(
- -static_cast<int>(operandArray[i].getUConst())));
+ case EOpNegative:
+ switch (getType().getBasicType())
+ {
+ case EbtFloat:
+ resultArray[i].setFConst(-operandArray[i].getFConst());
+ break;
+ case EbtInt:
+ if (operandArray[i] == std::numeric_limits<int>::min())
+ {
+ // The minimum representable integer doesn't have a positive
+ // counterpart, rather the negation overflows and in ESSL is supposed to
+ // wrap back to the minimum representable integer. Make sure that we
+ // don't actually let the negation overflow, which has undefined
+ // behavior in C++.
+ resultArray[i].setIConst(std::numeric_limits<int>::min());
+ }
+ else
+ {
+ resultArray[i].setIConst(-operandArray[i].getIConst());
+ }
+ break;
+ case EbtUInt:
+ if (operandArray[i] == 0x80000000u)
+ {
+ resultArray[i].setUConst(0x80000000u);
+ }
+ else
+ {
+ resultArray[i].setUConst(static_cast<unsigned int>(
+ -static_cast<int>(operandArray[i].getUConst())));
+ }
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
break;
- default:
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- }
- break;
- case EOpPositive:
- switch (getType().getBasicType())
- {
- case EbtFloat:
- resultArray[i].setFConst(operandArray[i].getFConst());
- break;
- case EbtInt:
- resultArray[i].setIConst(operandArray[i].getIConst());
- break;
- case EbtUInt:
- resultArray[i].setUConst(static_cast<unsigned int>(
- static_cast<int>(operandArray[i].getUConst())));
+ case EOpPositive:
+ switch (getType().getBasicType())
+ {
+ case EbtFloat:
+ resultArray[i].setFConst(operandArray[i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setIConst(operandArray[i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setUConst(static_cast<unsigned int>(
+ static_cast<int>(operandArray[i].getUConst())));
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
break;
- default:
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- }
- break;
- case EOpLogicalNot:
- // this code is written for possible future use,
- // will not get executed currently
- switch (getType().getBasicType())
- {
- case EbtBool:
- resultArray[i].setBConst(!operandArray[i].getBConst());
+ case EOpLogicalNot:
+ switch (getType().getBasicType())
+ {
+ case EbtBool:
+ resultArray[i].setBConst(!operandArray[i].getBConst());
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
break;
- default:
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- }
- break;
- case EOpBitwiseNot:
- switch (getType().getBasicType())
- {
- case EbtInt:
- resultArray[i].setIConst(~operandArray[i].getIConst());
- break;
- case EbtUInt:
- resultArray[i].setUConst(~operandArray[i].getUConst());
+ case EOpBitwiseNot:
+ switch (getType().getBasicType())
+ {
+ case EbtInt:
+ resultArray[i].setIConst(~operandArray[i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setUConst(~operandArray[i].getUConst());
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
break;
- default:
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- }
- break;
- case EOpRadians:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpRadians:
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst());
break;
- }
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- case EOpDegrees:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpDegrees:
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst());
break;
- }
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
-
- case EOpSin:
- if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i]))
- return nullptr;
- break;
-
- case EOpCos:
- if (!foldFloatTypeUnary(operandArray[i], &cosf, infoSink, &resultArray[i]))
- return nullptr;
- break;
- case EOpTan:
- if (!foldFloatTypeUnary(operandArray[i], &tanf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpSin:
+ foldFloatTypeUnary(operandArray[i], &sinf, &resultArray[i]);
+ break;
- case EOpAsin:
- // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0.
- if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &asinf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpCos:
+ foldFloatTypeUnary(operandArray[i], &cosf, &resultArray[i]);
+ break;
- case EOpAcos:
- // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0.
- if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &acosf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpTan:
+ foldFloatTypeUnary(operandArray[i], &tanf, &resultArray[i]);
+ break;
- case EOpAtan:
- if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpAsin:
+ // For asin(x), results are undefined if |x| > 1, we are choosing to set result to
+ // 0.
+ if (fabsf(operandArray[i].getFConst()) > 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &asinf, &resultArray[i]);
+ break;
- case EOpSinh:
- if (!foldFloatTypeUnary(operandArray[i], &sinhf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpAcos:
+ // For acos(x), results are undefined if |x| > 1, we are choosing to set result to
+ // 0.
+ if (fabsf(operandArray[i].getFConst()) > 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &acosf, &resultArray[i]);
+ break;
- case EOpCosh:
- if (!foldFloatTypeUnary(operandArray[i], &coshf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpAtan:
+ foldFloatTypeUnary(operandArray[i], &atanf, &resultArray[i]);
+ break;
- case EOpTanh:
- if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpSinh:
+ foldFloatTypeUnary(operandArray[i], &sinhf, &resultArray[i]);
+ break;
- case EOpAsinh:
- if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpCosh:
+ foldFloatTypeUnary(operandArray[i], &coshf, &resultArray[i]);
+ break;
- case EOpAcosh:
- // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
- if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 1.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &acoshf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpTanh:
+ foldFloatTypeUnary(operandArray[i], &tanhf, &resultArray[i]);
+ break;
- case EOpAtanh:
- // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0.
- if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) >= 1.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &atanhf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpAsinh:
+ foldFloatTypeUnary(operandArray[i], &asinhf, &resultArray[i]);
+ break;
- case EOpAbs:
- switch (getType().getBasicType())
- {
- case EbtFloat:
- resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
+ case EOpAcosh:
+ // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
+ if (operandArray[i].getFConst() < 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &acoshf, &resultArray[i]);
break;
- case EbtInt:
- resultArray[i].setIConst(abs(operandArray[i].getIConst()));
+
+ case EOpAtanh:
+ // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to
+ // 0.
+ if (fabsf(operandArray[i].getFConst()) >= 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &atanhf, &resultArray[i]);
break;
- default:
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- }
- break;
- case EOpSign:
- switch (getType().getBasicType())
- {
- case EbtFloat:
+ case EOpAbs:
+ switch (getType().getBasicType())
{
- float fConst = operandArray[i].getFConst();
- float fResult = 0.0f;
- if (fConst > 0.0f)
- fResult = 1.0f;
- else if (fConst < 0.0f)
- fResult = -1.0f;
- resultArray[i].setFConst(fResult);
+ case EbtFloat:
+ resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
+ break;
+ case EbtInt:
+ resultArray[i].setIConst(abs(operandArray[i].getIConst()));
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
}
break;
- case EbtInt:
+
+ case EOpSign:
+ switch (getType().getBasicType())
{
- int iConst = operandArray[i].getIConst();
- int iResult = 0;
- if (iConst > 0)
- iResult = 1;
- else if (iConst < 0)
- iResult = -1;
- resultArray[i].setIConst(iResult);
+ case EbtFloat:
+ {
+ float fConst = operandArray[i].getFConst();
+ float fResult = 0.0f;
+ if (fConst > 0.0f)
+ fResult = 1.0f;
+ else if (fConst < 0.0f)
+ fResult = -1.0f;
+ resultArray[i].setFConst(fResult);
+ break;
+ }
+ case EbtInt:
+ {
+ int iConst = operandArray[i].getIConst();
+ int iResult = 0;
+ if (iConst > 0)
+ iResult = 1;
+ else if (iConst < 0)
+ iResult = -1;
+ resultArray[i].setIConst(iResult);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ return nullptr;
}
break;
- default:
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- }
- break;
- case EOpFloor:
- if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpFloor:
+ foldFloatTypeUnary(operandArray[i], &floorf, &resultArray[i]);
+ break;
- case EOpTrunc:
- if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpTrunc:
+ foldFloatTypeUnary(operandArray[i], &truncf, &resultArray[i]);
+ break;
- case EOpRound:
- if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpRound:
+ foldFloatTypeUnary(operandArray[i], &roundf, &resultArray[i]);
+ break;
- case EOpRoundEven:
- if (getType().getBasicType() == EbtFloat)
+ case EOpRoundEven:
{
+ ASSERT(getType().getBasicType() == EbtFloat);
float x = operandArray[i].getFConst();
float result;
float fractPart = modff(x, &result);
@@ -1713,197 +2286,226 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op,
resultArray[i].setFConst(result);
break;
}
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- case EOpCeil:
- if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpCeil:
+ foldFloatTypeUnary(operandArray[i], &ceilf, &resultArray[i]);
+ break;
- case EOpFract:
- if (getType().getBasicType() == EbtFloat)
+ case EOpFract:
{
+ ASSERT(getType().getBasicType() == EbtFloat);
float x = operandArray[i].getFConst();
resultArray[i].setFConst(x - floorf(x));
break;
}
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- case EOpIsNan:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpIsNan:
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst()));
break;
- }
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- case EOpIsInf:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpIsInf:
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst()));
break;
- }
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- case EOpFloatBitsToInt:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpFloatBitsToInt:
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray[i].setIConst(gl::bitCast<int32_t>(operandArray[0].getFConst()));
break;
- }
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- case EOpFloatBitsToUint:
- if (getType().getBasicType() == EbtFloat)
- {
+ case EOpFloatBitsToUint:
+ ASSERT(getType().getBasicType() == EbtFloat);
resultArray[i].setUConst(gl::bitCast<uint32_t>(operandArray[0].getFConst()));
break;
- }
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- case EOpIntBitsToFloat:
- if (getType().getBasicType() == EbtInt)
- {
+ case EOpIntBitsToFloat:
+ ASSERT(getType().getBasicType() == EbtInt);
resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getIConst()));
break;
- }
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- case EOpUintBitsToFloat:
- if (getType().getBasicType() == EbtUInt)
- {
+ case EOpUintBitsToFloat:
+ ASSERT(getType().getBasicType() == EbtUInt);
resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getUConst()));
break;
- }
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- case EOpExp:
- if (!foldFloatTypeUnary(operandArray[i], &expf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpExp:
+ foldFloatTypeUnary(operandArray[i], &expf, &resultArray[i]);
+ break;
- case EOpLog:
- // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
- if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpLog:
+ // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
+ if (operandArray[i].getFConst() <= 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+ break;
- case EOpExp2:
- if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpExp2:
+ foldFloatTypeUnary(operandArray[i], &exp2f, &resultArray[i]);
+ break;
- case EOpLog2:
- // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
- // And log2f is not available on some plarforms like old android, so just using log(x)/log(2) here.
- if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
- return nullptr;
- else
- resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
- break;
+ case EOpLog2:
+ // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
+ // And log2f is not available on some plarforms like old android, so just using
+ // log(x)/log(2) here.
+ if (operandArray[i].getFConst() <= 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ {
+ foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+ resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
+ }
+ break;
- case EOpSqrt:
- // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
- if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 0.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
- return nullptr;
- break;
+ case EOpSqrt:
+ // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
+ if (operandArray[i].getFConst() < 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+ break;
- case EOpInverseSqrt:
- // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
- // so getting the square root first using builtin function sqrt() and then taking its inverse.
- // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set result to 0.
- if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
- else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
- return nullptr;
- else
- resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
- break;
+ case EOpInverseSqrt:
+ // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
+ // so getting the square root first using builtin function sqrt() and then taking
+ // its inverse.
+ // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set
+ // result to 0.
+ if (operandArray[i].getFConst() <= 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ {
+ foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+ resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
+ }
+ break;
- case EOpVectorLogicalNot:
- if (getType().getBasicType() == EbtBool)
- {
+ case EOpLogicalNotComponentWise:
+ ASSERT(getType().getBasicType() == EbtBool);
resultArray[i].setBConst(!operandArray[i].getBConst());
break;
- }
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return nullptr;
- case EOpNormalize:
- if (getType().getBasicType() == EbtFloat)
+ case EOpNormalize:
{
- float x = operandArray[i].getFConst();
+ ASSERT(getType().getBasicType() == EbtFloat);
+ float x = operandArray[i].getFConst();
float length = VectorLength(operandArray, objectSize);
if (length)
resultArray[i].setFConst(x / length);
else
- UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink,
- &resultArray[i]);
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
break;
}
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
-
- case EOpDFdx:
- case EOpDFdy:
- case EOpFwidth:
- if (getType().getBasicType() == EbtFloat)
+ case EOpBitfieldReverse:
+ {
+ uint32_t value;
+ if (getType().getBasicType() == EbtInt)
+ {
+ value = static_cast<uint32_t>(operandArray[i].getIConst());
+ }
+ else
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ value = operandArray[i].getUConst();
+ }
+ uint32_t result = gl::BitfieldReverse(value);
+ if (getType().getBasicType() == EbtInt)
+ {
+ resultArray[i].setIConst(static_cast<int32_t>(result));
+ }
+ else
+ {
+ resultArray[i].setUConst(result);
+ }
+ break;
+ }
+ case EOpBitCount:
+ {
+ uint32_t value;
+ if (getType().getBasicType() == EbtInt)
+ {
+ value = static_cast<uint32_t>(operandArray[i].getIConst());
+ }
+ else
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ value = operandArray[i].getUConst();
+ }
+ int result = gl::BitCount(value);
+ resultArray[i].setIConst(result);
+ break;
+ }
+ case EOpFindLSB:
+ {
+ uint32_t value;
+ if (getType().getBasicType() == EbtInt)
+ {
+ value = static_cast<uint32_t>(operandArray[i].getIConst());
+ }
+ else
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ value = operandArray[i].getUConst();
+ }
+ resultArray[i].setIConst(gl::FindLSB(value));
+ break;
+ }
+ case EOpFindMSB:
{
+ uint32_t value;
+ if (getType().getBasicType() == EbtInt)
+ {
+ int intValue = operandArray[i].getIConst();
+ value = static_cast<uint32_t>(intValue);
+ if (intValue < 0)
+ {
+ // Look for zero instead of one in value. This also handles the intValue ==
+ // -1 special case, where the return value needs to be -1.
+ value = ~value;
+ }
+ }
+ else
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ value = operandArray[i].getUConst();
+ }
+ resultArray[i].setIConst(gl::FindMSB(value));
+ break;
+ }
+ case EOpDFdx:
+ case EOpDFdy:
+ case EOpFwidth:
+ ASSERT(getType().getBasicType() == EbtFloat);
// Derivatives of constant arguments should be 0.
resultArray[i].setFConst(0.0f);
break;
- }
- infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
- return nullptr;
- default:
- return nullptr;
+ default:
+ return nullptr;
}
}
return resultArray;
}
-bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc,
- TInfoSink &infoSink, TConstantUnion *result) const
+void TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter,
+ FloatTypeUnaryFunc builtinFunc,
+ TConstantUnion *result) const
{
ASSERT(builtinFunc);
- if (getType().getBasicType() == EbtFloat)
- {
- result->setFConst(builtinFunc(parameter.getFConst()));
- return true;
- }
-
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Unary operation not folded into constant");
- return false;
+ ASSERT(getType().getBasicType() == EbtFloat);
+ result->setFConst(builtinFunc(parameter.getFConst()));
}
// static
-TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate,
- TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate)
{
ASSERT(aggregate->getSequence()->size() > 0u);
size_t resultSize = aggregate->getType().getObjectSize();
@@ -1958,7 +2560,7 @@ TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate
int argumentCols = argumentConstant->getType().getCols();
int argumentRows = argumentConstant->getType().getRows();
int resultCols = aggregate->getType().getCols();
- int resultRows = aggregate->getType().getRows();
+ int resultRows = aggregate->getType().getRows();
for (int col = 0; col < resultCols; ++col)
{
for (int row = 0; row < resultRows; ++row)
@@ -2001,664 +2603,728 @@ TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate
return resultArray;
}
+bool TIntermAggregate::CanFoldAggregateBuiltInOp(TOperator op)
+{
+ switch (op)
+ {
+ case EOpAtan:
+ case EOpPow:
+ case EOpMod:
+ case EOpMin:
+ case EOpMax:
+ case EOpClamp:
+ case EOpMix:
+ case EOpStep:
+ case EOpSmoothStep:
+ case EOpLdexp:
+ case EOpMulMatrixComponentWise:
+ case EOpOuterProduct:
+ case EOpEqualComponentWise:
+ case EOpNotEqualComponentWise:
+ case EOpLessThanComponentWise:
+ case EOpLessThanEqualComponentWise:
+ case EOpGreaterThanComponentWise:
+ case EOpGreaterThanEqualComponentWise:
+ case EOpDistance:
+ case EOpDot:
+ case EOpCross:
+ case EOpFaceforward:
+ case EOpReflect:
+ case EOpRefract:
+ case EOpBitfieldExtract:
+ case EOpBitfieldInsert:
+ return true;
+ default:
+ return false;
+ }
+}
+
// static
-TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+ TDiagnostics *diagnostics)
{
- TOperator op = aggregate->getOp();
- TIntermSequence *sequence = aggregate->getSequence();
- unsigned int paramsCount = static_cast<unsigned int>(sequence->size());
- std::vector<const TConstantUnion *> unionArrays(paramsCount);
- std::vector<size_t> objectSizes(paramsCount);
+ TOperator op = aggregate->getOp();
+ TIntermSequence *arguments = aggregate->getSequence();
+ unsigned int argsCount = static_cast<unsigned int>(arguments->size());
+ std::vector<const TConstantUnion *> unionArrays(argsCount);
+ std::vector<size_t> objectSizes(argsCount);
size_t maxObjectSize = 0;
TBasicType basicType = EbtVoid;
TSourceLoc loc;
- for (unsigned int i = 0; i < paramsCount; i++)
+ for (unsigned int i = 0; i < argsCount; i++)
{
- TIntermConstantUnion *paramConstant = (*sequence)[i]->getAsConstantUnion();
- ASSERT(paramConstant != nullptr); // Should be checked already.
+ TIntermConstantUnion *argConstant = (*arguments)[i]->getAsConstantUnion();
+ ASSERT(argConstant != nullptr); // Should be checked already.
if (i == 0)
{
- basicType = paramConstant->getType().getBasicType();
- loc = paramConstant->getLine();
+ basicType = argConstant->getType().getBasicType();
+ loc = argConstant->getLine();
}
- unionArrays[i] = paramConstant->getUnionArrayPointer();
- objectSizes[i] = paramConstant->getType().getObjectSize();
+ unionArrays[i] = argConstant->getUnionArrayPointer();
+ objectSizes[i] = argConstant->getType().getObjectSize();
if (objectSizes[i] > maxObjectSize)
maxObjectSize = objectSizes[i];
}
- if (!(*sequence)[0]->getAsTyped()->isMatrix())
+ if (!(*arguments)[0]->getAsTyped()->isMatrix() && aggregate->getOp() != EOpOuterProduct)
{
- for (unsigned int i = 0; i < paramsCount; i++)
+ for (unsigned int i = 0; i < argsCount; i++)
if (objectSizes[i] != maxObjectSize)
unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize);
}
TConstantUnion *resultArray = nullptr;
- if (paramsCount == 2)
+
+ switch (op)
{
- //
- // Binary built-in
- //
- switch (op)
+ case EOpAtan:
{
- case EOpAtan:
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- if (basicType == EbtFloat)
- {
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
- {
- float y = unionArrays[0][i].getFConst();
- float x = unionArrays[1][i].getFConst();
- // Results are undefined if x and y are both 0.
- if (x == 0.0f && y == 0.0f)
- UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
- else
- resultArray[i].setFConst(atan2f(y, x));
- }
- }
+ float y = unionArrays[0][i].getFConst();
+ float x = unionArrays[1][i].getFConst();
+ // Results are undefined if x and y are both 0.
+ if (x == 0.0f && y == 0.0f)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics, &resultArray[i]);
else
- UNREACHABLE();
+ resultArray[i].setFConst(atan2f(y, x));
}
break;
+ }
- case EOpPow:
+ case EOpPow:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- if (basicType == EbtFloat)
- {
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
- {
- float x = unionArrays[0][i].getFConst();
- float y = unionArrays[1][i].getFConst();
- // Results are undefined if x < 0.
- // Results are undefined if x = 0 and y <= 0.
- if (x < 0.0f)
- UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
- else if (x == 0.0f && y <= 0.0f)
- UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
- else
- resultArray[i].setFConst(powf(x, y));
- }
- }
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ // Results are undefined if x < 0.
+ // Results are undefined if x = 0 and y <= 0.
+ if (x < 0.0f)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics, &resultArray[i]);
+ else if (x == 0.0f && y <= 0.0f)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics, &resultArray[i]);
else
- UNREACHABLE();
+ resultArray[i].setFConst(powf(x, y));
}
break;
+ }
- case EOpMod:
+ case EOpMod:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- if (basicType == EbtFloat)
- {
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
- {
- float x = unionArrays[0][i].getFConst();
- float y = unionArrays[1][i].getFConst();
- resultArray[i].setFConst(x - y * floorf(x / y));
- }
- }
- else
- UNREACHABLE();
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ resultArray[i].setFConst(x - y * floorf(x / y));
}
break;
+ }
- case EOpMin:
+ case EOpMin:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
+ case EbtFloat:
+ resultArray[i].setFConst(
+ std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
break;
- case EbtInt:
- resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
+ case EbtInt:
+ resultArray[i].setIConst(
+ std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
break;
- case EbtUInt:
- resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
+ case EbtUInt:
+ resultArray[i].setUConst(
+ std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
break;
+ }
- case EOpMax:
+ case EOpMax:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
+ case EbtFloat:
+ resultArray[i].setFConst(
+ std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
break;
- case EbtInt:
- resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
+ case EbtInt:
+ resultArray[i].setIConst(
+ std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
break;
- case EbtUInt:
- resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
+ case EbtUInt:
+ resultArray[i].setUConst(
+ std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
break;
+ }
- case EOpStep:
- {
- if (basicType == EbtFloat)
- {
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
- resultArray[i].setFConst(unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f);
- }
- else
- UNREACHABLE();
- }
+ case EOpStep:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ resultArray[i].setFConst(
+ unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f);
break;
+ }
- case EOpLessThan:
+ case EOpLessThanComponentWise:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setBConst(unionArrays[0][i].getFConst() < unionArrays[1][i].getFConst());
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() <
+ unionArrays[1][i].getFConst());
break;
- case EbtInt:
- resultArray[i].setBConst(unionArrays[0][i].getIConst() < unionArrays[1][i].getIConst());
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() <
+ unionArrays[1][i].getIConst());
break;
- case EbtUInt:
- resultArray[i].setBConst(unionArrays[0][i].getUConst() < unionArrays[1][i].getUConst());
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() <
+ unionArrays[1][i].getUConst());
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
break;
+ }
- case EOpLessThanEqual:
+ case EOpLessThanEqualComponentWise:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setBConst(unionArrays[0][i].getFConst() <= unionArrays[1][i].getFConst());
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() <=
+ unionArrays[1][i].getFConst());
break;
- case EbtInt:
- resultArray[i].setBConst(unionArrays[0][i].getIConst() <= unionArrays[1][i].getIConst());
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() <=
+ unionArrays[1][i].getIConst());
break;
- case EbtUInt:
- resultArray[i].setBConst(unionArrays[0][i].getUConst() <= unionArrays[1][i].getUConst());
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() <=
+ unionArrays[1][i].getUConst());
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
break;
+ }
- case EOpGreaterThan:
+ case EOpGreaterThanComponentWise:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setBConst(unionArrays[0][i].getFConst() > unionArrays[1][i].getFConst());
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() >
+ unionArrays[1][i].getFConst());
break;
- case EbtInt:
- resultArray[i].setBConst(unionArrays[0][i].getIConst() > unionArrays[1][i].getIConst());
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() >
+ unionArrays[1][i].getIConst());
break;
- case EbtUInt:
- resultArray[i].setBConst(unionArrays[0][i].getUConst() > unionArrays[1][i].getUConst());
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() >
+ unionArrays[1][i].getUConst());
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
break;
-
- case EOpGreaterThanEqual:
+ }
+ case EOpGreaterThanEqualComponentWise:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setBConst(unionArrays[0][i].getFConst() >= unionArrays[1][i].getFConst());
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() >=
+ unionArrays[1][i].getFConst());
break;
- case EbtInt:
- resultArray[i].setBConst(unionArrays[0][i].getIConst() >= unionArrays[1][i].getIConst());
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() >=
+ unionArrays[1][i].getIConst());
break;
- case EbtUInt:
- resultArray[i].setBConst(unionArrays[0][i].getUConst() >= unionArrays[1][i].getUConst());
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() >=
+ unionArrays[1][i].getUConst());
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
- break;
+ }
+ break;
- case EOpVectorEqual:
+ case EOpEqualComponentWise:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setBConst(unionArrays[0][i].getFConst() == unionArrays[1][i].getFConst());
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() ==
+ unionArrays[1][i].getFConst());
break;
- case EbtInt:
- resultArray[i].setBConst(unionArrays[0][i].getIConst() == unionArrays[1][i].getIConst());
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() ==
+ unionArrays[1][i].getIConst());
break;
- case EbtUInt:
- resultArray[i].setBConst(unionArrays[0][i].getUConst() == unionArrays[1][i].getUConst());
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() ==
+ unionArrays[1][i].getUConst());
break;
- case EbtBool:
- resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst());
+ case EbtBool:
+ resultArray[i].setBConst(unionArrays[0][i].getBConst() ==
+ unionArrays[1][i].getBConst());
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
break;
+ }
- case EOpVectorNotEqual:
+ case EOpNotEqualComponentWise:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
- {
- case EbtFloat:
- resultArray[i].setBConst(unionArrays[0][i].getFConst() != unionArrays[1][i].getFConst());
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() !=
+ unionArrays[1][i].getFConst());
break;
- case EbtInt:
- resultArray[i].setBConst(unionArrays[0][i].getIConst() != unionArrays[1][i].getIConst());
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() !=
+ unionArrays[1][i].getIConst());
break;
- case EbtUInt:
- resultArray[i].setBConst(unionArrays[0][i].getUConst() != unionArrays[1][i].getUConst());
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() !=
+ unionArrays[1][i].getUConst());
break;
- case EbtBool:
- resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst());
+ case EbtBool:
+ resultArray[i].setBConst(unionArrays[0][i].getBConst() !=
+ unionArrays[1][i].getBConst());
break;
- default:
+ default:
UNREACHABLE();
break;
- }
}
}
break;
+ }
- case EOpDistance:
- if (basicType == EbtFloat)
+ case EOpDistance:
+ {
+ ASSERT(basicType == EbtFloat);
+ TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize];
+ resultArray = new TConstantUnion();
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize];
- resultArray = new TConstantUnion();
- for (size_t i = 0; i < maxObjectSize; i++)
- {
- float x = unionArrays[0][i].getFConst();
- float y = unionArrays[1][i].getFConst();
- distanceArray[i].setFConst(x - y);
- }
- resultArray->setFConst(VectorLength(distanceArray, maxObjectSize));
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ distanceArray[i].setFConst(x - y);
}
- else
- UNREACHABLE();
+ resultArray->setFConst(VectorLength(distanceArray, maxObjectSize));
break;
+ }
- case EOpDot:
-
- if (basicType == EbtFloat)
- {
- resultArray = new TConstantUnion();
- resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
- }
- else
- UNREACHABLE();
+ case EOpDot:
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion();
+ resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
break;
- case EOpCross:
- if (basicType == EbtFloat && maxObjectSize == 3)
- {
- resultArray = new TConstantUnion[maxObjectSize];
- float x0 = unionArrays[0][0].getFConst();
- float x1 = unionArrays[0][1].getFConst();
- float x2 = unionArrays[0][2].getFConst();
- float y0 = unionArrays[1][0].getFConst();
- float y1 = unionArrays[1][1].getFConst();
- float y2 = unionArrays[1][2].getFConst();
- resultArray[0].setFConst(x1 * y2 - y1 * x2);
- resultArray[1].setFConst(x2 * y0 - y2 * x0);
- resultArray[2].setFConst(x0 * y1 - y0 * x1);
- }
- else
- UNREACHABLE();
+ case EOpCross:
+ {
+ ASSERT(basicType == EbtFloat && maxObjectSize == 3);
+ resultArray = new TConstantUnion[maxObjectSize];
+ float x0 = unionArrays[0][0].getFConst();
+ float x1 = unionArrays[0][1].getFConst();
+ float x2 = unionArrays[0][2].getFConst();
+ float y0 = unionArrays[1][0].getFConst();
+ float y1 = unionArrays[1][1].getFConst();
+ float y2 = unionArrays[1][2].getFConst();
+ resultArray[0].setFConst(x1 * y2 - y1 * x2);
+ resultArray[1].setFConst(x2 * y0 - y2 * x0);
+ resultArray[2].setFConst(x0 * y1 - y0 * x1);
break;
+ }
- case EOpReflect:
- if (basicType == EbtFloat)
+ case EOpReflect:
+ {
+ ASSERT(basicType == EbtFloat);
+ // genType reflect (genType I, genType N) :
+ // For the incident vector I and surface orientation N, returns the reflection
+ // direction:
+ // I - 2 * dot(N, I) * N.
+ resultArray = new TConstantUnion[maxObjectSize];
+ float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- // genType reflect (genType I, genType N) :
- // For the incident vector I and surface orientation N, returns the reflection direction:
- // I - 2 * dot(N, I) * N.
- resultArray = new TConstantUnion[maxObjectSize];
- float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
- for (size_t i = 0; i < maxObjectSize; i++)
- {
- float result = unionArrays[0][i].getFConst() -
- 2.0f * dotProduct * unionArrays[1][i].getFConst();
- resultArray[i].setFConst(result);
- }
+ float result = unionArrays[0][i].getFConst() -
+ 2.0f * dotProduct * unionArrays[1][i].getFConst();
+ resultArray[i].setFConst(result);
}
- else
- UNREACHABLE();
break;
+ }
- case EOpMul:
- if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
- (*sequence)[1]->getAsTyped()->isMatrix())
- {
- // Perform component-wise matrix multiplication.
- resultArray = new TConstantUnion[maxObjectSize];
- int size = (*sequence)[0]->getAsTyped()->getNominalSize();
- angle::Matrix<float> result =
- GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
- SetUnionArrayFromMatrix(result, resultArray);
- }
- else
- UNREACHABLE();
+ case EOpMulMatrixComponentWise:
+ {
+ ASSERT(basicType == EbtFloat && (*arguments)[0]->getAsTyped()->isMatrix() &&
+ (*arguments)[1]->getAsTyped()->isMatrix());
+ // Perform component-wise matrix multiplication.
+ resultArray = new TConstantUnion[maxObjectSize];
+ int size = (*arguments)[0]->getAsTyped()->getNominalSize();
+ angle::Matrix<float> result =
+ GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
+ SetUnionArrayFromMatrix(result, resultArray);
break;
+ }
- case EOpOuterProduct:
- if (basicType == EbtFloat)
- {
- size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize();
- size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize();
- resultArray = new TConstantUnion[numRows * numCols];
- angle::Matrix<float> result =
- GetMatrix(unionArrays[0], 1, static_cast<int>(numCols))
- .outerProduct(GetMatrix(unionArrays[1], static_cast<int>(numRows), 1));
- SetUnionArrayFromMatrix(result, resultArray);
- }
- else
- UNREACHABLE();
+ case EOpOuterProduct:
+ {
+ ASSERT(basicType == EbtFloat);
+ size_t numRows = (*arguments)[0]->getAsTyped()->getType().getObjectSize();
+ size_t numCols = (*arguments)[1]->getAsTyped()->getType().getObjectSize();
+ resultArray = new TConstantUnion[numRows * numCols];
+ angle::Matrix<float> result =
+ GetMatrix(unionArrays[0], static_cast<int>(numRows), 1)
+ .outerProduct(GetMatrix(unionArrays[1], 1, static_cast<int>(numCols)));
+ SetUnionArrayFromMatrix(result, resultArray);
break;
-
- default:
- UNREACHABLE();
- // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above.
- return nullptr;
}
- }
- else if (paramsCount == 3)
- {
- //
- // Ternary built-in
- //
- switch (op)
+
+ case EOpClamp:
{
- case EOpClamp:
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
+ switch (basicType)
{
- switch (basicType)
+ case EbtFloat:
{
- case EbtFloat:
- {
- float x = unionArrays[0][i].getFConst();
- float min = unionArrays[1][i].getFConst();
- float max = unionArrays[2][i].getFConst();
- // Results are undefined if min > max.
- if (min > max)
- UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
- else
- resultArray[i].setFConst(gl::clamp(x, min, max));
- }
+ float x = unionArrays[0][i].getFConst();
+ float min = unionArrays[1][i].getFConst();
+ float max = unionArrays[2][i].getFConst();
+ // Results are undefined if min > max.
+ if (min > max)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setFConst(gl::clamp(x, min, max));
break;
- case EbtInt:
- {
- int x = unionArrays[0][i].getIConst();
- int min = unionArrays[1][i].getIConst();
- int max = unionArrays[2][i].getIConst();
- // Results are undefined if min > max.
- if (min > max)
- UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
- else
- resultArray[i].setIConst(gl::clamp(x, min, max));
- }
+ }
+
+ case EbtInt:
+ {
+ int x = unionArrays[0][i].getIConst();
+ int min = unionArrays[1][i].getIConst();
+ int max = unionArrays[2][i].getIConst();
+ // Results are undefined if min > max.
+ if (min > max)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setIConst(gl::clamp(x, min, max));
break;
- case EbtUInt:
- {
- unsigned int x = unionArrays[0][i].getUConst();
- unsigned int min = unionArrays[1][i].getUConst();
- unsigned int max = unionArrays[2][i].getUConst();
- // Results are undefined if min > max.
- if (min > max)
- UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
- else
- resultArray[i].setUConst(gl::clamp(x, min, max));
- }
+ }
+ case EbtUInt:
+ {
+ unsigned int x = unionArrays[0][i].getUConst();
+ unsigned int min = unionArrays[1][i].getUConst();
+ unsigned int max = unionArrays[2][i].getUConst();
+ // Results are undefined if min > max.
+ if (min > max)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setUConst(gl::clamp(x, min, max));
break;
- default:
+ }
+ default:
UNREACHABLE();
break;
- }
}
}
break;
+ }
- case EOpMix:
+ case EOpMix:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- if (basicType == EbtFloat)
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ TBasicType type = (*arguments)[2]->getAsTyped()->getType().getBasicType();
+ if (type == EbtFloat)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
- {
- float x = unionArrays[0][i].getFConst();
- float y = unionArrays[1][i].getFConst();
- TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
- if (type == EbtFloat)
- {
- // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
- float a = unionArrays[2][i].getFConst();
- resultArray[i].setFConst(x * (1.0f - a) + y * a);
- }
- else // 3rd parameter is EbtBool
- {
- ASSERT(type == EbtBool);
- // Selects which vector each returned component comes from.
- // For a component of a that is false, the corresponding component of x is returned.
- // For a component of a that is true, the corresponding component of y is returned.
- bool a = unionArrays[2][i].getBConst();
- resultArray[i].setFConst(a ? y : x);
- }
- }
+ // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
+ float a = unionArrays[2][i].getFConst();
+ resultArray[i].setFConst(x * (1.0f - a) + y * a);
+ }
+ else // 3rd parameter is EbtBool
+ {
+ ASSERT(type == EbtBool);
+ // Selects which vector each returned component comes from.
+ // For a component of a that is false, the corresponding component of x is
+ // returned.
+ // For a component of a that is true, the corresponding component of y is
+ // returned.
+ bool a = unionArrays[2][i].getBConst();
+ resultArray[i].setFConst(a ? y : x);
}
- else
- UNREACHABLE();
}
break;
+ }
- case EOpSmoothStep:
+ case EOpSmoothStep:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- if (basicType == EbtFloat)
+ float edge0 = unionArrays[0][i].getFConst();
+ float edge1 = unionArrays[1][i].getFConst();
+ float x = unionArrays[2][i].getFConst();
+ // Results are undefined if edge0 >= edge1.
+ if (edge0 >= edge1)
{
- resultArray = new TConstantUnion[maxObjectSize];
- for (size_t i = 0; i < maxObjectSize; i++)
- {
- float edge0 = unionArrays[0][i].getFConst();
- float edge1 = unionArrays[1][i].getFConst();
- float x = unionArrays[2][i].getFConst();
- // Results are undefined if edge0 >= edge1.
- if (edge0 >= edge1)
- {
- UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
- }
- else
- {
- // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
- // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
- float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
- resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
- }
- }
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics, &resultArray[i]);
}
else
- UNREACHABLE();
+ {
+ // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
+ // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
+ float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+ resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
+ }
}
break;
+ }
- case EOpFaceForward:
- if (basicType == EbtFloat)
+ case EOpLdexp:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
{
- // genType faceforward(genType N, genType I, genType Nref) :
- // If dot(Nref, I) < 0 return N, otherwise return -N.
- resultArray = new TConstantUnion[maxObjectSize];
- float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize);
- for (size_t i = 0; i < maxObjectSize; i++)
+ float x = unionArrays[0][i].getFConst();
+ int exp = unionArrays[1][i].getIConst();
+ if (exp > 128)
{
- if (dotProduct < 0)
- resultArray[i].setFConst(unionArrays[0][i].getFConst());
- else
- resultArray[i].setFConst(-unionArrays[0][i].getFConst());
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics, &resultArray[i]);
}
- }
- else
- UNREACHABLE();
- break;
-
- case EOpRefract:
- if (basicType == EbtFloat)
- {
- // genType refract(genType I, genType N, float eta) :
- // For the incident vector I and surface normal N, and the ratio of indices of refraction eta,
- // return the refraction vector. The result is computed by
- // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
- // if (k < 0.0)
- // return genType(0.0)
- // else
- // return eta * I - (eta * dot(N, I) + sqrt(k)) * N
- resultArray = new TConstantUnion[maxObjectSize];
- float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
- for (size_t i = 0; i < maxObjectSize; i++)
+ else
{
- float eta = unionArrays[2][i].getFConst();
- float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
- if (k < 0.0f)
- resultArray[i].setFConst(0.0f);
- else
- resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() -
- (eta * dotProduct + sqrtf(k)) * unionArrays[1][i].getFConst());
+ resultArray[i].setFConst(gl::Ldexp(x, exp));
}
}
- else
- UNREACHABLE();
break;
-
- default:
- UNREACHABLE();
- // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above.
- return nullptr;
}
- }
- return resultArray;
-}
-// static
-TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunction)
-{
- if (hashFunction == NULL || name.empty())
- return name;
- khronos_uint64_t number = (*hashFunction)(name.c_str(), name.length());
- TStringStream stream;
- stream << HASHED_NAME_PREFIX << std::hex << number;
- TString hashedName = stream.str();
- return hashedName;
-}
+ case EOpFaceforward:
+ {
+ ASSERT(basicType == EbtFloat);
+ // genType faceforward(genType N, genType I, genType Nref) :
+ // If dot(Nref, I) < 0 return N, otherwise return -N.
+ resultArray = new TConstantUnion[maxObjectSize];
+ float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize);
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ if (dotProduct < 0)
+ resultArray[i].setFConst(unionArrays[0][i].getFConst());
+ else
+ resultArray[i].setFConst(-unionArrays[0][i].getFConst());
+ }
+ break;
+ }
-void TIntermTraverser::updateTree()
-{
- for (size_t ii = 0; ii < mInsertions.size(); ++ii)
- {
- const NodeInsertMultipleEntry &insertion = mInsertions[ii];
- ASSERT(insertion.parent);
- if (!insertion.insertionsAfter.empty())
+ case EOpRefract:
{
- bool inserted = insertion.parent->insertChildNodes(insertion.position + 1,
- insertion.insertionsAfter);
- ASSERT(inserted);
- UNUSED_ASSERTION_VARIABLE(inserted);
+ ASSERT(basicType == EbtFloat);
+ // genType refract(genType I, genType N, float eta) :
+ // For the incident vector I and surface normal N, and the ratio of indices of
+ // refraction eta,
+ // return the refraction vector. The result is computed by
+ // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
+ // if (k < 0.0)
+ // return genType(0.0)
+ // else
+ // return eta * I - (eta * dot(N, I) + sqrt(k)) * N
+ resultArray = new TConstantUnion[maxObjectSize];
+ float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float eta = unionArrays[2][i].getFConst();
+ float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
+ if (k < 0.0f)
+ resultArray[i].setFConst(0.0f);
+ else
+ resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() -
+ (eta * dotProduct + sqrtf(k)) *
+ unionArrays[1][i].getFConst());
+ }
+ break;
}
- if (!insertion.insertionsBefore.empty())
+ case EOpBitfieldExtract:
{
- bool inserted =
- insertion.parent->insertChildNodes(insertion.position, insertion.insertionsBefore);
- ASSERT(inserted);
- UNUSED_ASSERTION_VARIABLE(inserted);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; ++i)
+ {
+ int offset = unionArrays[1][0].getIConst();
+ int bits = unionArrays[2][0].getIConst();
+ if (bits == 0)
+ {
+ if (aggregate->getBasicType() == EbtInt)
+ {
+ resultArray[i].setIConst(0);
+ }
+ else
+ {
+ ASSERT(aggregate->getBasicType() == EbtUInt);
+ resultArray[i].setUConst(0);
+ }
+ }
+ else if (offset < 0 || bits < 0 || offset >= 32 || bits > 32 || offset + bits > 32)
+ {
+ UndefinedConstantFoldingError(loc, op, aggregate->getBasicType(), diagnostics,
+ &resultArray[i]);
+ }
+ else
+ {
+ // bits can be 32 here, so we need to avoid bit shift overflow.
+ uint32_t maskMsb = 1u << (bits - 1);
+ uint32_t mask = ((maskMsb - 1u) | maskMsb) << offset;
+ if (aggregate->getBasicType() == EbtInt)
+ {
+ uint32_t value = static_cast<uint32_t>(unionArrays[0][i].getIConst());
+ uint32_t resultUnsigned = (value & mask) >> offset;
+ if ((resultUnsigned & maskMsb) != 0)
+ {
+ // The most significant bits (from bits+1 to the most significant bit)
+ // should be set to 1.
+ uint32_t higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;
+ resultUnsigned |= higherBitsMask;
+ }
+ resultArray[i].setIConst(static_cast<int32_t>(resultUnsigned));
+ }
+ else
+ {
+ ASSERT(aggregate->getBasicType() == EbtUInt);
+ uint32_t value = unionArrays[0][i].getUConst();
+ resultArray[i].setUConst((value & mask) >> offset);
+ }
+ }
+ }
+ break;
}
- }
- for (size_t ii = 0; ii < mReplacements.size(); ++ii)
- {
- const NodeUpdateEntry &replacement = mReplacements[ii];
- ASSERT(replacement.parent);
- bool replaced = replacement.parent->replaceChildNode(
- replacement.original, replacement.replacement);
- ASSERT(replaced);
- UNUSED_ASSERTION_VARIABLE(replaced);
-
- if (!replacement.originalBecomesChildOfReplacement)
+ case EOpBitfieldInsert:
{
- // In AST traversing, a parent is visited before its children.
- // After we replace a node, if its immediate child is to
- // be replaced, we need to make sure we don't update the replaced
- // node; instead, we update the replacement node.
- for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj)
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; ++i)
{
- NodeUpdateEntry &replacement2 = mReplacements[jj];
- if (replacement2.parent == replacement.original)
- replacement2.parent = replacement.replacement;
+ int offset = unionArrays[2][0].getIConst();
+ int bits = unionArrays[3][0].getIConst();
+ if (bits == 0)
+ {
+ if (aggregate->getBasicType() == EbtInt)
+ {
+ int32_t base = unionArrays[0][i].getIConst();
+ resultArray[i].setIConst(base);
+ }
+ else
+ {
+ ASSERT(aggregate->getBasicType() == EbtUInt);
+ uint32_t base = unionArrays[0][i].getUConst();
+ resultArray[i].setUConst(base);
+ }
+ }
+ else if (offset < 0 || bits < 0 || offset >= 32 || bits > 32 || offset + bits > 32)
+ {
+ UndefinedConstantFoldingError(loc, op, aggregate->getBasicType(), diagnostics,
+ &resultArray[i]);
+ }
+ else
+ {
+ // bits can be 32 here, so we need to avoid bit shift overflow.
+ uint32_t maskMsb = 1u << (bits - 1);
+ uint32_t insertMask = ((maskMsb - 1u) | maskMsb) << offset;
+ uint32_t baseMask = ~insertMask;
+ if (aggregate->getBasicType() == EbtInt)
+ {
+ uint32_t base = static_cast<uint32_t>(unionArrays[0][i].getIConst());
+ uint32_t insert = static_cast<uint32_t>(unionArrays[1][i].getIConst());
+ uint32_t resultUnsigned =
+ (base & baseMask) | ((insert << offset) & insertMask);
+ resultArray[i].setIConst(static_cast<int32_t>(resultUnsigned));
+ }
+ else
+ {
+ ASSERT(aggregate->getBasicType() == EbtUInt);
+ uint32_t base = unionArrays[0][i].getUConst();
+ uint32_t insert = unionArrays[1][i].getUConst();
+ resultArray[i].setUConst((base & baseMask) |
+ ((insert << offset) & insertMask));
+ }
+ }
}
+ break;
}
- }
- for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii)
- {
- const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii];
- ASSERT(replacement.parent);
- bool replaced = replacement.parent->replaceChildNodeWithMultiple(
- replacement.original, replacement.replacements);
- ASSERT(replaced);
- UNUSED_ASSERTION_VARIABLE(replaced);
- }
- mInsertions.clear();
- mReplacements.clear();
- mMultiReplacements.clear();
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ return resultArray;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode.h b/src/3rdparty/angle/src/compiler/translator/IntermNode.h
index ad500e2b1f..2170916201 100644
--- a/src/3rdparty/angle/src/compiler/translator/IntermNode.h
+++ b/src/3rdparty/angle/src/compiler/translator/IntermNode.h
@@ -25,14 +25,27 @@
#include "compiler/translator/Common.h"
#include "compiler/translator/ConstantUnion.h"
#include "compiler/translator/Operator.h"
+#include "compiler/translator/SymbolUniqueId.h"
#include "compiler/translator/Types.h"
+namespace sh
+{
+
+class TDiagnostics;
+
class TIntermTraverser;
class TIntermAggregate;
+class TIntermBlock;
+class TIntermInvariantDeclaration;
+class TIntermDeclaration;
+class TIntermFunctionPrototype;
+class TIntermFunctionDefinition;
+class TIntermSwizzle;
class TIntermBinary;
class TIntermUnary;
class TIntermConstantUnion;
-class TIntermSelection;
+class TIntermTernary;
+class TIntermIfElse;
class TIntermSwitch;
class TIntermCase;
class TIntermTyped;
@@ -41,8 +54,10 @@ class TIntermLoop;
class TInfoSink;
class TInfoSinkBase;
class TIntermRaw;
+class TIntermBranch;
class TSymbolTable;
+class TFunction;
// Encapsulate an identifier string and track whether it is coming from the original shader code
// (not internal) or from ANGLE (internal). Usually internal names shouldn't be decorated or hashed.
@@ -79,7 +94,7 @@ class TIntermNode : angle::NonCopyable
mLine.first_file = mLine.last_file = 0;
mLine.first_line = mLine.last_line = 0;
}
- virtual ~TIntermNode() { }
+ virtual ~TIntermNode() {}
const TSourceLoc &getLine() const { return mLine; }
void setLine(const TSourceLoc &l) { mLine = l; }
@@ -87,20 +102,27 @@ class TIntermNode : angle::NonCopyable
virtual void traverse(TIntermTraverser *) = 0;
virtual TIntermTyped *getAsTyped() { return 0; }
virtual TIntermConstantUnion *getAsConstantUnion() { return 0; }
+ virtual TIntermFunctionDefinition *getAsFunctionDefinition() { return nullptr; }
virtual TIntermAggregate *getAsAggregate() { return 0; }
+ virtual TIntermBlock *getAsBlock() { return nullptr; }
+ virtual TIntermFunctionPrototype *getAsFunctionPrototypeNode() { return nullptr; }
+ virtual TIntermInvariantDeclaration *getAsInvariantDeclarationNode() { return nullptr; }
+ virtual TIntermDeclaration *getAsDeclarationNode() { return nullptr; }
+ virtual TIntermSwizzle *getAsSwizzleNode() { return nullptr; }
virtual TIntermBinary *getAsBinaryNode() { return 0; }
virtual TIntermUnary *getAsUnaryNode() { return 0; }
- virtual TIntermSelection *getAsSelectionNode() { return 0; }
+ virtual TIntermTernary *getAsTernaryNode() { return nullptr; }
+ virtual TIntermIfElse *getAsIfElseNode() { return nullptr; }
virtual TIntermSwitch *getAsSwitchNode() { return 0; }
virtual TIntermCase *getAsCaseNode() { return 0; }
virtual TIntermSymbol *getAsSymbolNode() { return 0; }
virtual TIntermLoop *getAsLoopNode() { return 0; }
virtual TIntermRaw *getAsRawNode() { return 0; }
+ virtual TIntermBranch *getAsBranchNode() { return 0; }
// Replace a child node. Return true if |original| is a child
// node and it is replaced; otherwise, return false.
- virtual bool replaceChildNode(
- TIntermNode *original, TIntermNode *replacement) = 0;
+ virtual bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) = 0;
protected:
TSourceLoc mLine;
@@ -121,12 +143,15 @@ struct TIntermNodePair
class TIntermTyped : public TIntermNode
{
public:
- TIntermTyped(const TType &t) : mType(t) { }
+ TIntermTyped(const TType &t) : mType(t) {}
virtual TIntermTyped *deepCopy() const = 0;
TIntermTyped *getAsTyped() override { return this; }
+ // True if executing the expression represented by this node affects state, like values of
+ // variables. False if the executing the expression only computes its return value without
+ // affecting state. May return true conservatively.
virtual bool hasSideEffects() const = 0;
void setType(const TType &t) { mType = t; }
@@ -137,6 +162,7 @@ class TIntermTyped : public TIntermNode
TBasicType getBasicType() const { return mType.getBasicType(); }
TQualifier getQualifier() const { return mType.getQualifier(); }
TPrecision getPrecision() const { return mType.getPrecision(); }
+ TMemoryQualifier getMemoryQualifier() const { return mType.getMemoryQualifier(); }
int getCols() const { return mType.getCols(); }
int getRows() const { return mType.getRows(); }
int getNominalSize() const { return mType.getNominalSize(); }
@@ -144,14 +170,16 @@ class TIntermTyped : public TIntermNode
bool isInterfaceBlock() const { return mType.isInterfaceBlock(); }
bool isMatrix() const { return mType.isMatrix(); }
- bool isArray() const { return mType.isArray(); }
+ bool isArray() const { return mType.isArray(); }
bool isVector() const { return mType.isVector(); }
bool isScalar() const { return mType.isScalar(); }
bool isScalarInt() const { return mType.isScalarInt(); }
const char *getBasicString() const { return mType.getBasicString(); }
TString getCompleteString() const { return mType.getCompleteString(); }
- int getArraySize() const { return mType.getArraySize(); }
+ unsigned int getOutermostArraySize() const { return mType.getOutermostArraySize(); }
+
+ bool isConstructorWithOnlyConstantUnionParameters();
protected:
TType mType;
@@ -176,10 +204,7 @@ class TIntermLoop : public TIntermNode
TIntermNode *init,
TIntermTyped *cond,
TIntermTyped *expr,
- TIntermAggregate *body)
- : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body), mUnrollFlag(false)
- {
- }
+ TIntermBlock *body);
TIntermLoop *getAsLoopNode() override { return this; }
void traverse(TIntermTraverser *it) override;
@@ -189,19 +214,19 @@ class TIntermLoop : public TIntermNode
TIntermNode *getInit() { return mInit; }
TIntermTyped *getCondition() { return mCond; }
TIntermTyped *getExpression() { return mExpr; }
- TIntermAggregate *getBody() { return mBody; }
+ TIntermBlock *getBody() { return mBody; }
- void setUnrollFlag(bool flag) { mUnrollFlag = flag; }
- bool getUnrollFlag() const { return mUnrollFlag; }
+ void setInit(TIntermNode *init) { mInit = init; }
+ void setCondition(TIntermTyped *condition) { mCond = condition; }
+ void setExpression(TIntermTyped *expression) { mExpr = expression; }
+ void setBody(TIntermBlock *body) { mBody = body; }
protected:
TLoopType mType;
- TIntermNode *mInit; // for-loop initialization
- TIntermTyped *mCond; // loop exit condition
- TIntermTyped *mExpr; // for-loop expression
- TIntermAggregate *mBody; // loop body
-
- bool mUnrollFlag; // Whether the loop should be unrolled or not.
+ TIntermNode *mInit; // for-loop initialization
+ TIntermTyped *mCond; // loop exit condition
+ TIntermTyped *mExpr; // for-loop expression
+ TIntermBlock *mBody; // loop body
};
//
@@ -210,17 +235,16 @@ class TIntermLoop : public TIntermNode
class TIntermBranch : public TIntermNode
{
public:
- TIntermBranch(TOperator op, TIntermTyped *e)
- : mFlowOp(op),
- mExpression(e) { }
+ TIntermBranch(TOperator op, TIntermTyped *e) : mFlowOp(op), mExpression(e) {}
void traverse(TIntermTraverser *it) override;
+ TIntermBranch *getAsBranchNode() override { return this; }
bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
TOperator getFlowOp() { return mFlowOp; }
- TIntermTyped* getExpression() { return mExpression; }
+ TIntermTyped *getExpression() { return mExpression; }
-protected:
+ protected:
TOperator mFlowOp;
TIntermTyped *mExpression; // non-zero except for "return exp;" statements
};
@@ -234,7 +258,7 @@ class TIntermSymbol : public TIntermTyped
// if symbol is initialized as symbol(sym), the memory comes from the poolallocator of sym.
// If sym comes from per process globalpoolallocator, then it causes increased memory usage
// per compile it is essential to use "symbol = sym" to assign to symbol
- TIntermSymbol(int id, const TString &symbol, const TType &type)
+ TIntermSymbol(const TSymbolUniqueId &id, const TString &symbol, const TType &type)
: TIntermTyped(type), mId(id), mSymbol(symbol)
{
}
@@ -243,11 +267,10 @@ class TIntermSymbol : public TIntermTyped
bool hasSideEffects() const override { return false; }
- int getId() const { return mId; }
+ int getId() const { return mId.get(); }
const TString &getSymbol() const { return mSymbol.getString(); }
const TName &getName() const { return mSymbol; }
-
- void setId(int newId) { mId = newId; }
+ TName &getName() { return mSymbol; }
void setInternal(bool internal) { mSymbol.setInternal(internal); }
@@ -256,7 +279,7 @@ class TIntermSymbol : public TIntermTyped
bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; }
protected:
- int mId;
+ const TSymbolUniqueId mId;
TName mSymbol;
private:
@@ -269,9 +292,7 @@ class TIntermSymbol : public TIntermTyped
class TIntermRaw : public TIntermTyped
{
public:
- TIntermRaw(const TType &type, const TString &rawText)
- : TIntermTyped(type),
- mRawText(rawText) { }
+ TIntermRaw(const TType &type, const TString &rawText) : TIntermTyped(type), mRawText(rawText) {}
TIntermRaw(const TIntermRaw &) = delete;
TIntermTyped *deepCopy() const override
@@ -305,6 +326,7 @@ class TIntermConstantUnion : public TIntermTyped
TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type)
: TIntermTyped(type), mUnionArrayPointer(unionPointer)
{
+ ASSERT(unionPointer);
}
TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); }
@@ -332,6 +354,7 @@ class TIntermConstantUnion : public TIntermTyped
void replaceConstantUnion(const TConstantUnion *safeConstantUnion)
{
+ ASSERT(safeConstantUnion);
// Previous union pointer freed on pool deallocation.
mUnionArrayPointer = safeConstantUnion;
}
@@ -340,21 +363,27 @@ class TIntermConstantUnion : public TIntermTyped
void traverse(TIntermTraverser *it) override;
bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; }
- TConstantUnion *foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink);
- TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink);
- TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink);
+ TConstantUnion *foldBinary(TOperator op,
+ TIntermConstantUnion *rightNode,
+ TDiagnostics *diagnostics,
+ const TSourceLoc &line);
+ const TConstantUnion *foldIndexing(int index);
+ TConstantUnion *foldUnaryNonComponentWise(TOperator op);
+ TConstantUnion *foldUnaryComponentWise(TOperator op, TDiagnostics *diagnostics);
- static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate,
- TInfoSink &infoSink);
- static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink);
+ static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate);
+ static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+ TDiagnostics *diagnostics);
protected:
// Same data may be shared between multiple constant unions, so it can't be modified.
const TConstantUnion *mUnionArrayPointer;
private:
- typedef float(*FloatTypeUnaryFunc) (float);
- bool foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const;
+ typedef float (*FloatTypeUnaryFunc)(float);
+ void foldFloatTypeUnary(const TConstantUnion &parameter,
+ FloatTypeUnaryFunc builtinFunc,
+ TConstantUnion *result) const;
TIntermConstantUnion(const TIntermConstantUnion &node); // Note: not deleted, just private!
};
@@ -366,25 +395,57 @@ class TIntermOperator : public TIntermTyped
{
public:
TOperator getOp() const { return mOp; }
- void setOp(TOperator op) { mOp = op; }
bool isAssignment() const;
bool isMultiplication() const;
bool isConstructor() const;
+ // Returns true for calls mapped to EOpCall*, false for built-ins that have their own specific
+ // ops.
+ bool isFunctionCall() const;
+
bool hasSideEffects() const override { return isAssignment(); }
protected:
- TIntermOperator(TOperator op)
- : TIntermTyped(TType(EbtFloat, EbpUndefined)),
- mOp(op) {}
- TIntermOperator(TOperator op, const TType &type)
- : TIntermTyped(type),
- mOp(op) {}
+ TIntermOperator(TOperator op) : TIntermTyped(TType(EbtFloat, EbpUndefined)), mOp(op) {}
+ TIntermOperator(TOperator op, const TType &type) : TIntermTyped(type), mOp(op) {}
TIntermOperator(const TIntermOperator &) = default;
- TOperator mOp;
+ const TOperator mOp;
+};
+
+// Node for vector swizzles.
+class TIntermSwizzle : public TIntermTyped
+{
+ public:
+ // This constructor determines the type of the node based on the operand.
+ TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets);
+
+ TIntermTyped *deepCopy() const override { return new TIntermSwizzle(*this); }
+
+ TIntermSwizzle *getAsSwizzleNode() override { return this; };
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ bool hasSideEffects() const override { return mOperand->hasSideEffects(); }
+
+ TIntermTyped *getOperand() { return mOperand; }
+ void writeOffsetsAsXYZW(TInfoSinkBase *out) const;
+
+ bool hasDuplicateOffsets() const;
+ bool offsetsMatch(int offset) const;
+
+ TIntermTyped *fold();
+
+ protected:
+ TIntermTyped *mOperand;
+ TVector<int> mSwizzleOffsets;
+
+ private:
+ void promote();
+
+ TIntermSwizzle(const TIntermSwizzle &node); // Note: not deleted, just private!
};
//
@@ -393,12 +454,17 @@ class TIntermOperator : public TIntermTyped
class TIntermBinary : public TIntermOperator
{
public:
- TIntermBinary(TOperator op)
- : TIntermOperator(op),
- mAddIndexClamp(false) {}
+ // This constructor determines the type of the binary node based on the operands and op.
+ TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right);
TIntermTyped *deepCopy() const override { return new TIntermBinary(*this); }
+ static TOperator GetMulOpBasedOnOperands(const TType &left, const TType &right);
+ static TOperator GetMulAssignOpBasedOnOperands(const TType &left, const TType &right);
+ static TQualifier GetCommaQualifier(int shaderVersion,
+ const TIntermTyped *left,
+ const TIntermTyped *right);
+
TIntermBinary *getAsBinaryNode() override { return this; };
void traverse(TIntermTraverser *it) override;
bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
@@ -408,24 +474,23 @@ class TIntermBinary : public TIntermOperator
return isAssignment() || mLeft->hasSideEffects() || mRight->hasSideEffects();
}
- void setLeft(TIntermTyped *node) { mLeft = node; }
- void setRight(TIntermTyped *node) { mRight = node; }
TIntermTyped *getLeft() const { return mLeft; }
TIntermTyped *getRight() const { return mRight; }
- bool promote(TInfoSink &);
- TIntermTyped *fold(TInfoSink &infoSink);
+ TIntermTyped *fold(TDiagnostics *diagnostics);
void setAddIndexClamp() { mAddIndexClamp = true; }
bool getAddIndexClamp() { return mAddIndexClamp; }
protected:
- TIntermTyped* mLeft;
- TIntermTyped* mRight;
+ TIntermTyped *mLeft;
+ TIntermTyped *mRight;
// If set to true, wrap any EOpIndexIndirect with a clamp to bounds.
bool mAddIndexClamp;
private:
+ void promote();
+
TIntermBinary(const TIntermBinary &node); // Note: not deleted, just private!
};
@@ -435,14 +500,7 @@ class TIntermBinary : public TIntermOperator
class TIntermUnary : public TIntermOperator
{
public:
- TIntermUnary(TOperator op, const TType &type)
- : TIntermOperator(op, type),
- mOperand(NULL),
- mUseEmulatedFunction(false) {}
- TIntermUnary(TOperator op)
- : TIntermOperator(op),
- mOperand(NULL),
- mUseEmulatedFunction(false) {}
+ TIntermUnary(TOperator op, TIntermTyped *operand);
TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); }
@@ -452,10 +510,8 @@ class TIntermUnary : public TIntermOperator
bool hasSideEffects() const override { return isAssignment() || mOperand->hasSideEffects(); }
- void setOperand(TIntermTyped *operand) { mOperand = operand; }
TIntermTyped *getOperand() { return mOperand; }
- void promote(const TType *funcReturnType);
- TIntermTyped *fold(TInfoSink &infoSink);
+ TIntermTyped *fold(TDiagnostics *diagnostics);
void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
@@ -468,495 +524,394 @@ class TIntermUnary : public TIntermOperator
bool mUseEmulatedFunction;
private:
+ void promote();
+
TIntermUnary(const TIntermUnary &node); // note: not deleted, just private!
};
+class TFunctionSymbolInfo
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TFunctionSymbolInfo(const TSymbolUniqueId &id);
+ TFunctionSymbolInfo() : mId(nullptr), mKnownToNotHaveSideEffects(false) {}
+
+ TFunctionSymbolInfo(const TFunctionSymbolInfo &info);
+ TFunctionSymbolInfo &operator=(const TFunctionSymbolInfo &info);
+
+ void setFromFunction(const TFunction &function);
+
+ void setNameObj(const TName &name) { mName = name; }
+ const TName &getNameObj() const { return mName; }
+
+ const TString &getName() const { return mName.getString(); }
+ void setName(const TString &name) { mName.setString(name); }
+ bool isMain() const { return mName.getString() == "main"; }
+
+ void setKnownToNotHaveSideEffects(bool knownToNotHaveSideEffects)
+ {
+ mKnownToNotHaveSideEffects = knownToNotHaveSideEffects;
+ }
+ bool isKnownToNotHaveSideEffects() const { return mKnownToNotHaveSideEffects; }
+
+ void setId(const TSymbolUniqueId &functionId);
+ const TSymbolUniqueId &getId() const;
+
+ bool isImageFunction() const
+ {
+ return getName() == "imageSize" || getName() == "imageLoad" || getName() == "imageStore";
+ }
+
+ private:
+ TName mName;
+ TSymbolUniqueId *mId;
+ bool mKnownToNotHaveSideEffects;
+};
+
typedef TVector<TIntermNode *> TIntermSequence;
typedef TVector<int> TQualifierList;
//
+// This is just to help yacc.
+//
+struct TIntermFunctionCallOrMethod
+{
+ TIntermSequence *arguments;
+ TIntermNode *thisNode;
+};
+
+// Interface for node classes that have an arbitrarily sized set of children.
+class TIntermAggregateBase
+{
+ public:
+ virtual ~TIntermAggregateBase() {}
+
+ virtual TIntermSequence *getSequence() = 0;
+ virtual const TIntermSequence *getSequence() const = 0;
+
+ bool replaceChildNodeWithMultiple(TIntermNode *original, const TIntermSequence &replacements);
+ bool insertChildNodes(TIntermSequence::size_type position, const TIntermSequence &insertions);
+
+ protected:
+ TIntermAggregateBase() {}
+
+ bool replaceChildNodeInternal(TIntermNode *original, TIntermNode *replacement);
+};
+
+//
// Nodes that operate on an arbitrary sized set of children.
//
-class TIntermAggregate : public TIntermOperator
+class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
{
public:
- TIntermAggregate()
- : TIntermOperator(EOpNull),
- mUserDefined(false),
- mUseEmulatedFunction(false),
- mGotPrecisionFromChildren(false)
- {
- }
- TIntermAggregate(TOperator op)
- : TIntermOperator(op),
- mUseEmulatedFunction(false),
- mGotPrecisionFromChildren(false)
- {
- }
- ~TIntermAggregate() { }
+ static TIntermAggregate *CreateFunctionCall(const TFunction &func, TIntermSequence *arguments);
+
+ // If using this, ensure that there's a consistent function definition with the same symbol id
+ // added to the AST.
+ static TIntermAggregate *CreateFunctionCall(const TType &type,
+ const TSymbolUniqueId &id,
+ const TName &name,
+ TIntermSequence *arguments);
+
+ static TIntermAggregate *CreateBuiltInFunctionCall(const TFunction &func,
+ TIntermSequence *arguments);
+ static TIntermAggregate *CreateConstructor(const TType &type,
+ TIntermSequence *arguments);
+ static TIntermAggregate *Create(const TType &type, TOperator op, TIntermSequence *arguments);
+ ~TIntermAggregate() {}
// Note: only supported for nodes that can be a part of an expression.
TIntermTyped *deepCopy() const override { return new TIntermAggregate(*this); }
+ TIntermAggregate *shallowCopy() const;
+
TIntermAggregate *getAsAggregate() override { return this; }
void traverse(TIntermTraverser *it) override;
bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements);
- bool insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions);
- // Conservatively assume function calls and other aggregate operators have side-effects
- bool hasSideEffects() const override { return true; }
- TIntermTyped *fold(TInfoSink &infoSink);
- TIntermSequence *getSequence() { return &mSequence; }
+ bool hasSideEffects() const override;
- void setNameObj(const TName &name) { mName = name; }
- const TName &getNameObj() const { return mName; }
-
- void setName(const TString &name) { mName.setString(name); }
- const TString &getName() const { return mName.getString(); }
+ static bool CanFoldAggregateBuiltInOp(TOperator op);
+ TIntermTyped *fold(TDiagnostics *diagnostics);
- void setUserDefined() { mUserDefined = true; }
- bool isUserDefined() const { return mUserDefined; }
+ TIntermSequence *getSequence() override { return &mArguments; }
+ const TIntermSequence *getSequence() const override { return &mArguments; }
- void setFunctionId(int functionId) { mFunctionId = functionId; }
- int getFunctionId() const { return mFunctionId; }
+ TString getSymbolTableMangledName() const;
void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
- bool areChildrenConstQualified();
- void setPrecisionFromChildren();
- void setBuiltInFunctionPrecision();
-
// Returns true if changing parameter precision may affect the return value.
bool gotPrecisionFromChildren() const { return mGotPrecisionFromChildren; }
+ TFunctionSymbolInfo *getFunctionSymbolInfo() { return &mFunctionInfo; }
+ const TFunctionSymbolInfo *getFunctionSymbolInfo() const { return &mFunctionInfo; }
+
protected:
- TIntermSequence mSequence;
- TName mName;
- bool mUserDefined; // used for user defined function names
- int mFunctionId;
+ TIntermSequence mArguments;
// If set to true, replace the built-in function call with an emulated one
- // to work around driver bugs.
+ // to work around driver bugs. Only for calls mapped to ops other than EOpCall*.
bool mUseEmulatedFunction;
bool mGotPrecisionFromChildren;
+ TFunctionSymbolInfo mFunctionInfo;
+
private:
+ TIntermAggregate(const TType &type, TOperator op, TIntermSequence *arguments);
+
TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private!
+
+ void setTypePrecisionAndQualifier(const TType &type);
+
+ bool areChildrenConstQualified();
+
+ void setPrecisionFromChildren();
+
+ void setPrecisionForBuiltInOp();
+
+ // Returns true if precision was set according to special rules for this built-in.
+ bool setPrecisionForSpecialBuiltInOp();
+
+ // Used for built-in functions under EOpCallBuiltInFunction. The function name in the symbol
+ // info needs to be set before calling this.
+ void setBuiltInFunctionPrecision();
};
-//
-// For if tests.
-//
-class TIntermSelection : public TIntermTyped
+// A list of statements. Either the root node which contains declarations and function definitions,
+// or a block that can be marked with curly braces {}.
+class TIntermBlock : public TIntermNode, public TIntermAggregateBase
{
public:
- TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB)
- : TIntermTyped(TType(EbtVoid, EbpUndefined)),
- mCondition(cond),
- mTrueBlock(trueB),
- mFalseBlock(falseB) {}
- TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB,
- const TType &type)
- : TIntermTyped(type),
- mCondition(cond),
- mTrueBlock(trueB),
- mFalseBlock(falseB) {}
-
- // Note: only supported for ternary operator nodes.
- TIntermTyped *deepCopy() const override { return new TIntermSelection(*this); }
+ TIntermBlock() : TIntermNode() {}
+ ~TIntermBlock() {}
+ TIntermBlock *getAsBlock() override { return this; }
void traverse(TIntermTraverser *it) override;
bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- // Conservatively assume selections have side-effects
- bool hasSideEffects() const override { return true; }
+ // Only intended for initially building the block.
+ void appendStatement(TIntermNode *statement);
- bool usesTernaryOperator() const { return getBasicType() != EbtVoid; }
- TIntermNode *getCondition() const { return mCondition; }
- TIntermNode *getTrueBlock() const { return mTrueBlock; }
- TIntermNode *getFalseBlock() const { return mFalseBlock; }
- TIntermSelection *getAsSelectionNode() override { return this; }
+ TIntermSequence *getSequence() override { return &mStatements; }
+ const TIntermSequence *getSequence() const override { return &mStatements; }
protected:
- TIntermTyped *mCondition;
- TIntermNode *mTrueBlock;
- TIntermNode *mFalseBlock;
-
- private:
- TIntermSelection(const TIntermSelection &node); // Note: not deleted, just private!
+ TIntermSequence mStatements;
};
-//
-// Switch statement.
-//
-class TIntermSwitch : public TIntermNode
+// Function prototype. May be in the AST either as a function prototype declaration or as a part of
+// a function definition. The type of the node is the function return type.
+class TIntermFunctionPrototype : public TIntermTyped, public TIntermAggregateBase
{
public:
- TIntermSwitch(TIntermTyped *init, TIntermAggregate *statementList)
- : TIntermNode(),
- mInit(init),
- mStatementList(statementList)
+ // TODO(oetuaho@nvidia.com): See if TFunctionSymbolInfo could be added to constructor
+ // parameters.
+ TIntermFunctionPrototype(const TType &type, const TSymbolUniqueId &id)
+ : TIntermTyped(type), mFunctionInfo(id)
{
}
+ ~TIntermFunctionPrototype() {}
+ TIntermFunctionPrototype *getAsFunctionPrototypeNode() override { return this; }
void traverse(TIntermTraverser *it) override;
- bool replaceChildNode(
- TIntermNode *original, TIntermNode *replacement) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- TIntermSwitch *getAsSwitchNode() override { return this; }
+ TIntermTyped *deepCopy() const override
+ {
+ UNREACHABLE();
+ return nullptr;
+ }
+ bool hasSideEffects() const override
+ {
+ UNREACHABLE();
+ return true;
+ }
- TIntermTyped *getInit() { return mInit; }
- TIntermAggregate *getStatementList() { return mStatementList; }
- void setStatementList(TIntermAggregate *statementList) { mStatementList = statementList; }
+ // Only intended for initially building the declaration.
+ void appendParameter(TIntermSymbol *parameter);
+
+ TIntermSequence *getSequence() override { return &mParameters; }
+ const TIntermSequence *getSequence() const override { return &mParameters; }
+
+ TFunctionSymbolInfo *getFunctionSymbolInfo() { return &mFunctionInfo; }
+ const TFunctionSymbolInfo *getFunctionSymbolInfo() const { return &mFunctionInfo; }
protected:
- TIntermTyped *mInit;
- TIntermAggregate *mStatementList;
+ TIntermSequence mParameters;
+
+ TFunctionSymbolInfo mFunctionInfo;
};
-//
-// Case label.
-//
-class TIntermCase : public TIntermNode
+// Node for function definitions. The prototype child node stores the function header including
+// parameters, and the body child node stores the function body.
+class TIntermFunctionDefinition : public TIntermNode
{
public:
- TIntermCase(TIntermTyped *condition)
- : TIntermNode(),
- mCondition(condition)
+ TIntermFunctionDefinition(TIntermFunctionPrototype *prototype, TIntermBlock *body)
+ : TIntermNode(), mPrototype(prototype), mBody(body)
{
+ ASSERT(prototype != nullptr);
+ ASSERT(body != nullptr);
}
+ TIntermFunctionDefinition *getAsFunctionDefinition() override { return this; }
void traverse(TIntermTraverser *it) override;
- bool replaceChildNode(
- TIntermNode *original, TIntermNode *replacement) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- TIntermCase *getAsCaseNode() override { return this; }
+ TIntermFunctionPrototype *getFunctionPrototype() const { return mPrototype; }
+ TIntermBlock *getBody() const { return mBody; }
- bool hasCondition() const { return mCondition != nullptr; }
- TIntermTyped *getCondition() const { return mCondition; }
+ const TFunctionSymbolInfo *getFunctionSymbolInfo() const
+ {
+ return mPrototype->getFunctionSymbolInfo();
+ }
- protected:
- TIntermTyped *mCondition;
+ private:
+ TIntermFunctionPrototype *mPrototype;
+ TIntermBlock *mBody;
};
-enum Visit
+// Struct, interface block or variable declaration. Can contain multiple variable declarators.
+class TIntermDeclaration : public TIntermNode, public TIntermAggregateBase
{
- PreVisit,
- InVisit,
- PostVisit
+ public:
+ TIntermDeclaration() : TIntermNode() {}
+ ~TIntermDeclaration() {}
+
+ TIntermDeclaration *getAsDeclarationNode() override { return this; }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ // Only intended for initially building the declaration.
+ // The declarator node should be either TIntermSymbol or TIntermBinary with op set to
+ // EOpInitialize.
+ void appendDeclarator(TIntermTyped *declarator);
+
+ TIntermSequence *getSequence() override { return &mDeclarators; }
+ const TIntermSequence *getSequence() const override { return &mDeclarators; }
+ protected:
+ TIntermSequence mDeclarators;
};
-//
-// For traversing the tree. User should derive from this class overriding the visit functions,
-// and then pass an object of the subclass to a traverse method of a node.
-//
-// The traverse*() functions may also be overridden do other bookkeeping on the tree to provide
-// contextual information to the visit functions, such as whether the node is the target of an
-// assignment.
-//
-// When using this, just fill in the methods for nodes you want visited.
-// Return false from a pre-visit to skip visiting that node's subtree.
-//
-class TIntermTraverser : angle::NonCopyable
+// Specialized declarations for attributing invariance.
+class TIntermInvariantDeclaration : public TIntermNode
{
public:
- POOL_ALLOCATOR_NEW_DELETE();
- TIntermTraverser(bool preVisit, bool inVisit, bool postVisit)
- : preVisit(preVisit),
- inVisit(inVisit),
- postVisit(postVisit),
- mDepth(0),
- mMaxDepth(0),
- mTemporaryIndex(nullptr)
- {
- }
- virtual ~TIntermTraverser() {}
-
- virtual void visitSymbol(TIntermSymbol *node) {}
- virtual void visitRaw(TIntermRaw *node) {}
- virtual void visitConstantUnion(TIntermConstantUnion *node) {}
- virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; }
- virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; }
- virtual bool visitSelection(Visit visit, TIntermSelection *node) { return true; }
- virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; }
- virtual bool visitCase(Visit visit, TIntermCase *node) { return true; }
- virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; }
- virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; }
- virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; }
-
- // The traverse functions contain logic for iterating over the children of the node
- // and calling the visit functions in the appropriate places. They also track some
- // context that may be used by the visit functions.
- virtual void traverseSymbol(TIntermSymbol *node);
- virtual void traverseRaw(TIntermRaw *node);
- virtual void traverseConstantUnion(TIntermConstantUnion *node);
- virtual void traverseBinary(TIntermBinary *node);
- virtual void traverseUnary(TIntermUnary *node);
- virtual void traverseSelection(TIntermSelection *node);
- virtual void traverseSwitch(TIntermSwitch *node);
- virtual void traverseCase(TIntermCase *node);
- virtual void traverseAggregate(TIntermAggregate *node);
- virtual void traverseLoop(TIntermLoop *node);
- virtual void traverseBranch(TIntermBranch *node);
-
- int getMaxDepth() const { return mMaxDepth; }
-
- // Return the original name if hash function pointer is NULL;
- // otherwise return the hashed name.
- static TString hash(const TString &name, ShHashFunction64 hashFunction);
-
- // If traversers need to replace nodes, they can add the replacements in
- // mReplacements/mMultiReplacements during traversal and the user of the traverser should call
- // this function after traversal to perform them.
- void updateTree();
-
- // Start creating temporary symbols from the given temporary symbol index + 1.
- void useTemporaryIndex(unsigned int *temporaryIndex);
+ TIntermInvariantDeclaration(TIntermSymbol *symbol, const TSourceLoc &line);
- protected:
- void incrementDepth(TIntermNode *current)
- {
- mDepth++;
- mMaxDepth = std::max(mMaxDepth, mDepth);
- mPath.push_back(current);
- }
+ virtual TIntermInvariantDeclaration *getAsInvariantDeclarationNode() override { return this; }
- void decrementDepth()
- {
- mDepth--;
- mPath.pop_back();
- }
+ TIntermSymbol *getSymbol() { return mSymbol; }
- TIntermNode *getParentNode()
- {
- return mPath.size() == 0 ? NULL : mPath.back();
- }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- void pushParentBlock(TIntermAggregate *node);
- void incrementParentBlockPos();
- void popParentBlock();
+ private:
+ TIntermSymbol *mSymbol;
+};
- bool parentNodeIsBlock()
- {
- return !mParentBlockStack.empty() && getParentNode() == mParentBlockStack.back().node;
- }
+// For ternary operators like a ? b : c.
+class TIntermTernary : public TIntermTyped
+{
+ public:
+ TIntermTernary(TIntermTyped *cond, TIntermTyped *trueExpression, TIntermTyped *falseExpression);
- const bool preVisit;
- const bool inVisit;
- const bool postVisit;
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- int mDepth;
- int mMaxDepth;
+ TIntermTyped *getCondition() const { return mCondition; }
+ TIntermTyped *getTrueExpression() const { return mTrueExpression; }
+ TIntermTyped *getFalseExpression() const { return mFalseExpression; }
+ TIntermTernary *getAsTernaryNode() override { return this; }
- // All the nodes from root to the current node's parent during traversing.
- TVector<TIntermNode *> mPath;
+ TIntermTyped *deepCopy() const override { return new TIntermTernary(*this); }
- // To replace a single node with another on the parent node
- struct NodeUpdateEntry
- {
- NodeUpdateEntry(TIntermNode *_parent,
- TIntermNode *_original,
- TIntermNode *_replacement,
- bool _originalBecomesChildOfReplacement)
- : parent(_parent),
- original(_original),
- replacement(_replacement),
- originalBecomesChildOfReplacement(_originalBecomesChildOfReplacement) {}
-
- TIntermNode *parent;
- TIntermNode *original;
- TIntermNode *replacement;
- bool originalBecomesChildOfReplacement;
- };
-
- // To replace a single node with multiple nodes on the parent aggregate node
- struct NodeReplaceWithMultipleEntry
- {
- NodeReplaceWithMultipleEntry(TIntermAggregate *_parent, TIntermNode *_original, TIntermSequence _replacements)
- : parent(_parent),
- original(_original),
- replacements(_replacements)
- {
- }
-
- TIntermAggregate *parent;
- TIntermNode *original;
- TIntermSequence replacements;
- };
-
- // To insert multiple nodes on the parent aggregate node
- struct NodeInsertMultipleEntry
+ bool hasSideEffects() const override
{
- NodeInsertMultipleEntry(TIntermAggregate *_parent,
- TIntermSequence::size_type _position,
- TIntermSequence _insertionsBefore,
- TIntermSequence _insertionsAfter)
- : parent(_parent),
- position(_position),
- insertionsBefore(_insertionsBefore),
- insertionsAfter(_insertionsAfter)
- {
- }
-
- TIntermAggregate *parent;
- TIntermSequence::size_type position;
- TIntermSequence insertionsBefore;
- TIntermSequence insertionsAfter;
- };
-
- // During traversing, save all the changes that need to happen into
- // mReplacements/mMultiReplacements, then do them by calling updateTree().
- // Multi replacements are processed after single replacements.
- std::vector<NodeUpdateEntry> mReplacements;
- std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements;
- std::vector<NodeInsertMultipleEntry> mInsertions;
-
- // Helper to insert statements in the parent block (sequence) of the node currently being traversed.
- // The statements will be inserted before the node being traversed once updateTree is called.
- // Should only be called during PreVisit or PostVisit from sequence nodes.
- // Note that inserting more than one set of nodes to the same parent node on a single updateTree call is not
- // supported.
- void insertStatementsInParentBlock(const TIntermSequence &insertions);
-
- // Same as above, but supports simultaneous insertion of statements before and after the node
- // currently being traversed.
- void insertStatementsInParentBlock(const TIntermSequence &insertionsBefore,
- const TIntermSequence &insertionsAfter);
-
- // Helper to create a temporary symbol node with the given qualifier.
- TIntermSymbol *createTempSymbol(const TType &type, TQualifier qualifier);
- // Helper to create a temporary symbol node.
- TIntermSymbol *createTempSymbol(const TType &type);
- // Create a node that declares but doesn't initialize a temporary symbol.
- TIntermAggregate *createTempDeclaration(const TType &type);
- // Create a node that initializes the current temporary symbol with initializer having the given qualifier.
- TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier);
- // Create a node that initializes the current temporary symbol with initializer.
- TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer);
- // Create a node that assigns rightNode to the current temporary symbol.
- TIntermBinary *createTempAssignment(TIntermTyped *rightNode);
- // Increment temporary symbol index.
- void nextTemporaryIndex();
+ return mCondition->hasSideEffects() || mTrueExpression->hasSideEffects() ||
+ mFalseExpression->hasSideEffects();
+ }
+
+ TIntermTyped *fold();
private:
- struct ParentBlock
- {
- ParentBlock(TIntermAggregate *nodeIn, TIntermSequence::size_type posIn)
- : node(nodeIn),
- pos(posIn)
- {
- }
-
- TIntermAggregate *node;
- TIntermSequence::size_type pos;
- };
- // All the code blocks from the root to the current node's parent during traversal.
- std::vector<ParentBlock> mParentBlockStack;
-
- unsigned int *mTemporaryIndex;
+ TIntermTernary(const TIntermTernary &node); // Note: not deleted, just private!
+
+ static TQualifier DetermineQualifier(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression);
+
+ TIntermTyped *mCondition;
+ TIntermTyped *mTrueExpression;
+ TIntermTyped *mFalseExpression;
};
-// Traverser parent class that tracks where a node is a destination of a write operation and so is
-// required to be an l-value.
-class TLValueTrackingTraverser : public TIntermTraverser
+class TIntermIfElse : public TIntermNode
{
public:
- TLValueTrackingTraverser(bool preVisit,
- bool inVisit,
- bool postVisit,
- const TSymbolTable &symbolTable,
- int shaderVersion)
- : TIntermTraverser(preVisit, inVisit, postVisit),
- mOperatorRequiresLValue(false),
- mInFunctionCallOutParameter(false),
- mSymbolTable(symbolTable),
- mShaderVersion(shaderVersion)
- {
- }
- virtual ~TLValueTrackingTraverser() {}
+ TIntermIfElse(TIntermTyped *cond, TIntermBlock *trueB, TIntermBlock *falseB);
- void traverseBinary(TIntermBinary *node) override;
- void traverseUnary(TIntermUnary *node) override;
- void traverseAggregate(TIntermAggregate *node) override;
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- protected:
- bool isLValueRequiredHere() const
- {
- return mOperatorRequiresLValue || mInFunctionCallOutParameter;
- }
+ TIntermTyped *getCondition() const { return mCondition; }
+ TIntermBlock *getTrueBlock() const { return mTrueBlock; }
+ TIntermBlock *getFalseBlock() const { return mFalseBlock; }
+ TIntermIfElse *getAsIfElseNode() override { return this; }
- // Return true if the prototype or definition of the function being called has been encountered
- // during traversal.
- bool isInFunctionMap(const TIntermAggregate *callNode) const;
+ protected:
+ TIntermTyped *mCondition;
+ TIntermBlock *mTrueBlock;
+ TIntermBlock *mFalseBlock;
+};
- private:
- // Track whether an l-value is required in the node that is currently being traversed by the
- // surrounding operator.
- // Use isLValueRequiredHere to check all conditions which require an l-value.
- void setOperatorRequiresLValue(bool lValueRequired)
- {
- mOperatorRequiresLValue = lValueRequired;
- }
- bool operatorRequiresLValue() const { return mOperatorRequiresLValue; }
+//
+// Switch statement.
+//
+class TIntermSwitch : public TIntermNode
+{
+ public:
+ TIntermSwitch(TIntermTyped *init, TIntermBlock *statementList);
- // Add a function encountered during traversal to the function map.
- void addToFunctionMap(const TName &name, TIntermSequence *paramSequence);
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- // Return the parameters sequence from the function definition or prototype.
- TIntermSequence *getFunctionParameters(const TIntermAggregate *callNode);
+ TIntermSwitch *getAsSwitchNode() override { return this; }
- // Track whether an l-value is required inside a function call.
- void setInFunctionCallOutParameter(bool inOutParameter);
- bool isInFunctionCallOutParameter() const;
+ TIntermTyped *getInit() { return mInit; }
+ TIntermBlock *getStatementList() { return mStatementList; }
- bool mOperatorRequiresLValue;
- bool mInFunctionCallOutParameter;
+ // Must be called with a non-null statementList.
+ void setStatementList(TIntermBlock *statementList);
- struct TNameComparator
- {
- bool operator()(const TName &a, const TName &b) const
- {
- int compareResult = a.getString().compare(b.getString());
- if (compareResult != 0)
- return compareResult < 0;
- // Internal functions may have same names as non-internal functions.
- return !a.isInternal() && b.isInternal();
- }
- };
-
- // Map from mangled function names to their parameter sequences
- TMap<TName, TIntermSequence *, TNameComparator> mFunctionMap;
-
- const TSymbolTable &mSymbolTable;
- const int mShaderVersion;
+ protected:
+ TIntermTyped *mInit;
+ TIntermBlock *mStatementList;
};
//
-// For traversing the tree, and computing max depth.
-// Takes a maximum depth limit to prevent stack overflow.
+// Case label.
//
-class TMaxDepthTraverser : public TIntermTraverser
+class TIntermCase : public TIntermNode
{
public:
- POOL_ALLOCATOR_NEW_DELETE();
- TMaxDepthTraverser(int depthLimit)
- : TIntermTraverser(true, true, false),
- mDepthLimit(depthLimit) { }
+ TIntermCase(TIntermTyped *condition) : TIntermNode(), mCondition(condition) {}
- bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); }
- bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); }
- bool visitSelection(Visit, TIntermSelection *) override { return depthCheck(); }
- bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); }
- bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); }
- bool visitBranch(Visit, TIntermBranch *) override { return depthCheck(); }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
- protected:
- bool depthCheck() const { return mMaxDepth < mDepthLimit; }
+ TIntermCase *getAsCaseNode() override { return this; }
- int mDepthLimit;
+ bool hasCondition() const { return mCondition != nullptr; }
+ TIntermTyped *getCondition() const { return mCondition; }
+
+ protected:
+ TIntermTyped *mCondition;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_INTERMNODE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.cpp b/src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
new file mode 100644
index 0000000000..567e8f7440
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
@@ -0,0 +1,157 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermNodePatternMatcher is a helper class for matching node trees to given patterns.
+// It can be used whenever the same checks for certain node structures are common to multiple AST
+// traversers.
+//
+
+#include "compiler/translator/IntermNodePatternMatcher.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+IntermNodePatternMatcher::IntermNodePatternMatcher(const unsigned int mask) : mMask(mask)
+{
+}
+
+// static
+bool IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node)
+{
+ return node->getOp() == EOpIndexIndirect && !node->getLeft()->isArray() &&
+ node->getLeft()->getBasicType() != EbtStruct;
+}
+
+bool IntermNodePatternMatcher::matchInternal(TIntermBinary *node, TIntermNode *parentNode)
+{
+ if ((mMask & kExpressionReturningArray) != 0)
+ {
+ if (node->isArray() && node->getOp() == EOpAssign && parentNode != nullptr &&
+ !parentNode->getAsBlock())
+ {
+ return true;
+ }
+ }
+
+ if ((mMask & kUnfoldedShortCircuitExpression) != 0)
+ {
+ if (node->getRight()->hasSideEffects() &&
+ (node->getOp() == EOpLogicalOr || node->getOp() == EOpLogicalAnd))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermUnary *node)
+{
+ if ((mMask & kArrayLengthMethod) != 0)
+ {
+ if (node->getOp() == EOpArrayLength)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermBinary *node, TIntermNode *parentNode)
+{
+ // L-value tracking information is needed to check for dynamic indexing in L-value.
+ // Traversers that don't track l-values can still use this class and match binary nodes with
+ // this variation of this method if they don't need to check for dynamic indexing in l-values.
+ ASSERT((mMask & kDynamicIndexingOfVectorOrMatrixInLValue) == 0);
+ return matchInternal(node, parentNode);
+}
+
+bool IntermNodePatternMatcher::match(TIntermBinary *node,
+ TIntermNode *parentNode,
+ bool isLValueRequiredHere)
+{
+ if (matchInternal(node, parentNode))
+ {
+ return true;
+ }
+ if ((mMask & kDynamicIndexingOfVectorOrMatrixInLValue) != 0)
+ {
+ if (isLValueRequiredHere && IsDynamicIndexingOfVectorOrMatrix(node))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parentNode)
+{
+ if ((mMask & kExpressionReturningArray) != 0)
+ {
+ if (parentNode != nullptr)
+ {
+ TIntermBinary *parentBinary = parentNode->getAsBinaryNode();
+ bool parentIsAssignment =
+ (parentBinary != nullptr &&
+ (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize));
+
+ if (node->getType().isArray() && !parentIsAssignment &&
+ (node->isConstructor() || node->isFunctionCall()) && !parentNode->getAsBlock())
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermTernary *node)
+{
+ if ((mMask & kUnfoldedShortCircuitExpression) != 0)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermDeclaration *node)
+{
+ if ((mMask & kMultiDeclaration) != 0)
+ {
+ if (node->getSequence()->size() > 1)
+ {
+ return true;
+ }
+ }
+ if ((mMask & kArrayDeclaration) != 0)
+ {
+ if (node->getSequence()->front()->getAsTyped()->getType().isStructureContainingArrays())
+ {
+ return true;
+ }
+ // Need to check from all declarators whether they are arrays since that may vary between
+ // declarators.
+ for (TIntermNode *declarator : *node->getSequence())
+ {
+ if (declarator->getAsTyped()->isArray())
+ {
+ return true;
+ }
+ }
+ }
+ if ((mMask & kNamelessStructDeclaration) != 0)
+ {
+ TIntermTyped *declarator = node->getSequence()->front()->getAsTyped();
+ if (declarator->getBasicType() == EbtStruct &&
+ declarator->getType().getStruct()->name() == "")
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.h b/src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.h
new file mode 100644
index 0000000000..997fc2ef10
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.h
@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermNodePatternMatcher is a helper class for matching node trees to given patterns.
+// It can be used whenever the same checks for certain node structures are common to multiple AST
+// traversers.
+//
+
+#ifndef COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_
+#define COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_
+
+namespace sh
+{
+
+class TIntermAggregate;
+class TIntermBinary;
+class TIntermDeclaration;
+class TIntermNode;
+class TIntermTernary;
+class TIntermUnary;
+
+class IntermNodePatternMatcher
+{
+ public:
+ static bool IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node);
+
+ enum PatternType
+ {
+ // Matches expressions that are unfolded to if statements by UnfoldShortCircuitToIf
+ kUnfoldedShortCircuitExpression = 0x0001,
+
+ // Matches expressions that return arrays with the exception of simple statements where a
+ // constructor or function call result is assigned.
+ kExpressionReturningArray = 0x0001 << 1,
+
+ // Matches dynamic indexing of vectors or matrices in l-values.
+ kDynamicIndexingOfVectorOrMatrixInLValue = 0x0001 << 2,
+
+ // Matches declarations with more than one declared variables.
+ kMultiDeclaration = 0x0001 << 3,
+
+ // Matches declarations of arrays.
+ kArrayDeclaration = 0x0001 << 4,
+
+ // Matches declarations of structs where the struct type does not have a name.
+ kNamelessStructDeclaration = 0x0001 << 5,
+
+ // Matches array length() method.
+ kArrayLengthMethod = 0x0001 << 6
+ };
+ IntermNodePatternMatcher(const unsigned int mask);
+
+ bool match(TIntermUnary *node);
+
+ bool match(TIntermBinary *node, TIntermNode *parentNode);
+
+ // Use this version for checking binary node matches in case you're using flag
+ // kDynamicIndexingOfVectorOrMatrixInLValue.
+ bool match(TIntermBinary *node, TIntermNode *parentNode, bool isLValueRequiredHere);
+
+ bool match(TIntermAggregate *node, TIntermNode *parentNode);
+ bool match(TIntermTernary *node);
+ bool match(TIntermDeclaration *node);
+
+ private:
+ const unsigned int mMask;
+
+ bool matchInternal(TIntermBinary *node, TIntermNode *parentNode);
+};
+
+} // namespace sh
+
+#endif
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode_util.cpp b/src/3rdparty/angle/src/compiler/translator/IntermNode_util.cpp
new file mode 100644
index 0000000000..9f1f596c43
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/IntermNode_util.cpp
@@ -0,0 +1,254 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermNode_util.cpp: High-level utilities for creating AST nodes and node hierarchies. Mostly
+// meant to be used in AST transforms.
+
+#include "compiler/translator/IntermNode_util.h"
+
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+TName GetInternalFunctionName(const char *name)
+{
+ TString nameStr(name);
+ TName nameObj(nameStr);
+ nameObj.setInternal(true);
+ return nameObj;
+}
+
+const TFunction *LookUpBuiltInFunction(const TString &name,
+ const TIntermSequence *arguments,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ TString mangledName = TFunction::GetMangledNameFromCall(name, *arguments);
+ TSymbol *symbol = symbolTable.findBuiltIn(mangledName, shaderVersion);
+ if (symbol)
+ {
+ ASSERT(symbol->isFunction());
+ return static_cast<const TFunction *>(symbol);
+ }
+ return nullptr;
+}
+
+} // anonymous namespace
+
+TIntermFunctionPrototype *CreateInternalFunctionPrototypeNode(const TType &returnType,
+ const char *name,
+ const TSymbolUniqueId &functionId)
+{
+ TIntermFunctionPrototype *functionNode = new TIntermFunctionPrototype(returnType, functionId);
+ functionNode->getFunctionSymbolInfo()->setNameObj(GetInternalFunctionName(name));
+ return functionNode;
+}
+
+TIntermFunctionDefinition *CreateInternalFunctionDefinitionNode(const TType &returnType,
+ const char *name,
+ TIntermBlock *functionBody,
+ const TSymbolUniqueId &functionId)
+{
+ TIntermFunctionPrototype *prototypeNode =
+ CreateInternalFunctionPrototypeNode(returnType, name, functionId);
+ return new TIntermFunctionDefinition(prototypeNode, functionBody);
+}
+
+TIntermAggregate *CreateInternalFunctionCallNode(const TType &returnType,
+ const char *name,
+ const TSymbolUniqueId &functionId,
+ TIntermSequence *arguments)
+{
+ TIntermAggregate *functionNode = TIntermAggregate::CreateFunctionCall(
+ returnType, functionId, GetInternalFunctionName(name), arguments);
+ return functionNode;
+}
+
+TIntermTyped *CreateZeroNode(const TType &type)
+{
+ TType constType(type);
+ constType.setQualifier(EvqConst);
+
+ if (!type.isArray() && type.getBasicType() != EbtStruct)
+ {
+ size_t size = constType.getObjectSize();
+ TConstantUnion *u = new TConstantUnion[size];
+ for (size_t i = 0; i < size; ++i)
+ {
+ switch (type.getBasicType())
+ {
+ case EbtFloat:
+ u[i].setFConst(0.0f);
+ break;
+ case EbtInt:
+ u[i].setIConst(0);
+ break;
+ case EbtUInt:
+ u[i].setUConst(0u);
+ break;
+ case EbtBool:
+ u[i].setBConst(false);
+ break;
+ default:
+ // CreateZeroNode is called by ParseContext that keeps parsing even when an
+ // error occurs, so it is possible for CreateZeroNode to be called with
+ // non-basic types. This happens only on error condition but CreateZeroNode
+ // needs to return a value with the correct type to continue the typecheck.
+ // That's why we handle non-basic type by setting whatever value, we just need
+ // the type to be right.
+ u[i].setIConst(42);
+ break;
+ }
+ }
+
+ TIntermConstantUnion *node = new TIntermConstantUnion(u, constType);
+ return node;
+ }
+
+ if (type.getBasicType() == EbtVoid)
+ {
+ // Void array. This happens only on error condition, similarly to the case above. We don't
+ // have a constructor operator for void, so this needs special handling. We'll end up with a
+ // value without the array type, but that should not be a problem.
+ while (constType.isArray())
+ {
+ constType.toArrayElementType();
+ }
+ return CreateZeroNode(constType);
+ }
+
+ TIntermSequence *arguments = new TIntermSequence();
+
+ if (type.isArray())
+ {
+ TType elementType(type);
+ elementType.toArrayElementType();
+
+ size_t arraySize = type.getOutermostArraySize();
+ for (size_t i = 0; i < arraySize; ++i)
+ {
+ arguments->push_back(CreateZeroNode(elementType));
+ }
+ }
+ else
+ {
+ ASSERT(type.getBasicType() == EbtStruct);
+
+ const TStructure *structure = type.getStruct();
+ for (const auto &field : structure->fields())
+ {
+ arguments->push_back(CreateZeroNode(*field->type()));
+ }
+ }
+
+ return TIntermAggregate::CreateConstructor(constType, arguments);
+}
+
+TIntermConstantUnion *CreateIndexNode(int index)
+{
+ TConstantUnion *u = new TConstantUnion[1];
+ u[0].setIConst(index);
+
+ TType type(EbtInt, EbpUndefined, EvqConst, 1);
+ TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
+ return node;
+}
+
+TIntermConstantUnion *CreateBoolNode(bool value)
+{
+ TConstantUnion *u = new TConstantUnion[1];
+ u[0].setBConst(value);
+
+ TType type(EbtBool, EbpUndefined, EvqConst, 1);
+ TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
+ return node;
+}
+
+TIntermSymbol *CreateTempSymbolNode(const TSymbolUniqueId &id,
+ const TType &type,
+ TQualifier qualifier)
+{
+ TInfoSinkBase symbolNameOut;
+ symbolNameOut << "s" << id.get();
+ TString symbolName = symbolNameOut.c_str();
+
+ TIntermSymbol *node = new TIntermSymbol(id, symbolName, type);
+ node->setInternal(true);
+
+ ASSERT(qualifier == EvqTemporary || qualifier == EvqConst || qualifier == EvqGlobal);
+ node->getTypePointer()->setQualifier(qualifier);
+
+ // TODO(oetuaho): Might be useful to sanitize layout qualifier etc. on the type of the created
+ // symbol. This might need to be done in other places as well.
+ return node;
+}
+
+TIntermDeclaration *CreateTempInitDeclarationNode(const TSymbolUniqueId &id,
+ TIntermTyped *initializer,
+ TQualifier qualifier)
+{
+ ASSERT(initializer != nullptr);
+ TIntermSymbol *tempSymbol = CreateTempSymbolNode(id, initializer->getType(), qualifier);
+ TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
+ TIntermBinary *tempInit = new TIntermBinary(EOpInitialize, tempSymbol, initializer);
+ tempDeclaration->appendDeclarator(tempInit);
+ return tempDeclaration;
+}
+
+TIntermBlock *EnsureBlock(TIntermNode *node)
+{
+ if (node == nullptr)
+ return nullptr;
+ TIntermBlock *blockNode = node->getAsBlock();
+ if (blockNode != nullptr)
+ return blockNode;
+
+ blockNode = new TIntermBlock();
+ blockNode->setLine(node->getLine());
+ blockNode->appendStatement(node);
+ return blockNode;
+}
+
+TIntermSymbol *ReferenceGlobalVariable(const TString &name, const TSymbolTable &symbolTable)
+{
+ TVariable *var = reinterpret_cast<TVariable *>(symbolTable.findGlobal(name));
+ ASSERT(var);
+ return new TIntermSymbol(var->getUniqueId(), name, var->getType());
+}
+
+TIntermSymbol *ReferenceBuiltInVariable(const TString &name,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ const TVariable *var =
+ reinterpret_cast<const TVariable *>(symbolTable.findBuiltIn(name, shaderVersion, true));
+ ASSERT(var);
+ return new TIntermSymbol(var->getUniqueId(), name, var->getType());
+}
+
+TIntermTyped *CreateBuiltInFunctionCallNode(const TString &name,
+ TIntermSequence *arguments,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ const TFunction *fn = LookUpBuiltInFunction(name, arguments, symbolTable, shaderVersion);
+ ASSERT(fn);
+ TOperator op = fn->getBuiltInOp();
+ if (op != EOpNull)
+ {
+ if (arguments->size() == 1)
+ {
+ return new TIntermUnary(op, arguments->at(0)->getAsTyped());
+ }
+ return TIntermAggregate::Create(fn->getReturnType(), op, arguments);
+ }
+ return TIntermAggregate::CreateBuiltInFunctionCall(*fn, arguments);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode_util.h b/src/3rdparty/angle/src/compiler/translator/IntermNode_util.h
new file mode 100644
index 0000000000..6f3b0674f0
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/IntermNode_util.h
@@ -0,0 +1,60 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermNode_util.h: High-level utilities for creating AST nodes and node hierarchies. Mostly meant
+// to be used in AST transforms.
+
+#ifndef COMPILER_TRANSLATOR_INTERMNODEUTIL_H_
+#define COMPILER_TRANSLATOR_INTERMNODEUTIL_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+TIntermFunctionPrototype *CreateInternalFunctionPrototypeNode(const TType &returnType,
+ const char *name,
+ const TSymbolUniqueId &functionId);
+TIntermFunctionDefinition *CreateInternalFunctionDefinitionNode(const TType &returnType,
+ const char *name,
+ TIntermBlock *functionBody,
+ const TSymbolUniqueId &functionId);
+TIntermAggregate *CreateInternalFunctionCallNode(const TType &returnType,
+ const char *name,
+ const TSymbolUniqueId &functionId,
+ TIntermSequence *arguments);
+
+TIntermTyped *CreateZeroNode(const TType &type);
+TIntermConstantUnion *CreateIndexNode(int index);
+TIntermConstantUnion *CreateBoolNode(bool value);
+
+TIntermSymbol *CreateTempSymbolNode(const TSymbolUniqueId &id,
+ const TType &type,
+ TQualifier qualifier);
+TIntermDeclaration *CreateTempInitDeclarationNode(const TSymbolUniqueId &id,
+ TIntermTyped *initializer,
+ TQualifier qualifier);
+
+// If the input node is nullptr, return nullptr.
+// If the input node is a block node, return it.
+// If the input node is not a block node, put it inside a block node and return that.
+TIntermBlock *EnsureBlock(TIntermNode *node);
+
+// Should be called from inside Compiler::compileTreeImpl() where the global level is in scope.
+TIntermSymbol *ReferenceGlobalVariable(const TString &name, const TSymbolTable &symbolTable);
+
+// Note: this can access desktop GLSL built-ins that are hidden from the parser.
+TIntermSymbol *ReferenceBuiltInVariable(const TString &name,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+TIntermTyped *CreateBuiltInFunctionCallNode(const TString &name,
+ TIntermSequence *arguments,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INTERMNODEUTIL_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp
index 7b588ca5a3..6c25c6c35a 100644
--- a/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp
@@ -4,10 +4,15 @@
// found in the LICENSE file.
//
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode_util.h"
#include "compiler/translator/SymbolTable.h"
+namespace sh
+{
+
void TIntermSymbol::traverse(TIntermTraverser *it)
{
it->traverseSymbol(this);
@@ -23,6 +28,11 @@ void TIntermConstantUnion::traverse(TIntermTraverser *it)
it->traverseConstantUnion(this);
}
+void TIntermSwizzle::traverse(TIntermTraverser *it)
+{
+ it->traverseSwizzle(this);
+}
+
void TIntermBinary::traverse(TIntermTraverser *it)
{
it->traverseBinary(this);
@@ -33,9 +43,14 @@ void TIntermUnary::traverse(TIntermTraverser *it)
it->traverseUnary(this);
}
-void TIntermSelection::traverse(TIntermTraverser *it)
+void TIntermTernary::traverse(TIntermTraverser *it)
+{
+ it->traverseTernary(this);
+}
+
+void TIntermIfElse::traverse(TIntermTraverser *it)
{
- it->traverseSelection(this);
+ it->traverseIfElse(this);
}
void TIntermSwitch::traverse(TIntermTraverser *it)
@@ -48,6 +63,31 @@ void TIntermCase::traverse(TIntermTraverser *it)
it->traverseCase(this);
}
+void TIntermFunctionDefinition::traverse(TIntermTraverser *it)
+{
+ it->traverseFunctionDefinition(this);
+}
+
+void TIntermBlock::traverse(TIntermTraverser *it)
+{
+ it->traverseBlock(this);
+}
+
+void TIntermInvariantDeclaration::traverse(TIntermTraverser *it)
+{
+ it->traverseInvariantDeclaration(this);
+}
+
+void TIntermDeclaration::traverse(TIntermTraverser *it)
+{
+ it->traverseDeclaration(this);
+}
+
+void TIntermFunctionPrototype::traverse(TIntermTraverser *it)
+{
+ it->traverseFunctionPrototype(this);
+}
+
void TIntermAggregate::traverse(TIntermTraverser *it)
{
it->traverseAggregate(this);
@@ -63,7 +103,35 @@ void TIntermBranch::traverse(TIntermTraverser *it)
it->traverseBranch(this);
}
-void TIntermTraverser::pushParentBlock(TIntermAggregate *node)
+TIntermTraverser::TIntermTraverser(bool preVisit,
+ bool inVisit,
+ bool postVisit,
+ TSymbolTable *symbolTable)
+ : preVisit(preVisit),
+ inVisit(inVisit),
+ postVisit(postVisit),
+ mDepth(-1),
+ mMaxDepth(0),
+ mInGlobalScope(true),
+ mSymbolTable(symbolTable),
+ mTemporaryId(nullptr)
+{
+}
+
+TIntermTraverser::~TIntermTraverser()
+{
+}
+
+const TIntermBlock *TIntermTraverser::getParentBlock() const
+{
+ if (!mParentBlockStack.empty())
+ {
+ return mParentBlockStack.back().node;
+ }
+ return nullptr;
+}
+
+void TIntermTraverser::pushParentBlock(TIntermBlock *node)
{
mParentBlockStack.push_back(ParentBlock(node, 0));
}
@@ -89,23 +157,32 @@ void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &inse
const TIntermSequence &insertionsAfter)
{
ASSERT(!mParentBlockStack.empty());
- NodeInsertMultipleEntry insert(mParentBlockStack.back().node, mParentBlockStack.back().pos,
- insertionsBefore, insertionsAfter);
+ ParentBlock &parentBlock = mParentBlockStack.back();
+ if (mPath.back() == parentBlock.node)
+ {
+ ASSERT(mParentBlockStack.size() >= 2u);
+ // The current node is a block node, so the parent block is not the topmost one in the block
+ // stack, but the one below that.
+ parentBlock = mParentBlockStack.at(mParentBlockStack.size() - 2u);
+ }
+ NodeInsertMultipleEntry insert(parentBlock.node, parentBlock.pos, insertionsBefore,
+ insertionsAfter);
mInsertions.push_back(insert);
}
-TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type, TQualifier qualifier)
+void TIntermTraverser::insertStatementInParentBlock(TIntermNode *statement)
{
- // Each traversal uses at most one temporary variable, so the index stays the same within a single traversal.
- TInfoSinkBase symbolNameOut;
- ASSERT(mTemporaryIndex != nullptr);
- symbolNameOut << "s" << (*mTemporaryIndex);
- TString symbolName = symbolNameOut.c_str();
+ TIntermSequence insertions;
+ insertions.push_back(statement);
+ insertStatementsInParentBlock(insertions);
+}
- TIntermSymbol *node = new TIntermSymbol(0, symbolName, type);
- node->setInternal(true);
- node->getTypePointer()->setQualifier(qualifier);
- return node;
+TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type, TQualifier qualifier)
+{
+ ASSERT(mTemporaryId != nullptr);
+ // nextTemporaryId() needs to be called when the code wants to start using another temporary
+ // symbol.
+ return CreateTempSymbolNode(*mTemporaryId, type, qualifier);
}
TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type)
@@ -113,27 +190,22 @@ TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type)
return createTempSymbol(type, EvqTemporary);
}
-TIntermAggregate *TIntermTraverser::createTempDeclaration(const TType &type)
+TIntermDeclaration *TIntermTraverser::createTempDeclaration(const TType &type)
{
- TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
- tempDeclaration->getSequence()->push_back(createTempSymbol(type));
+ ASSERT(mTemporaryId != nullptr);
+ TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
+ tempDeclaration->appendDeclarator(CreateTempSymbolNode(*mTemporaryId, type, EvqTemporary));
return tempDeclaration;
}
-TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier)
+TIntermDeclaration *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer,
+ TQualifier qualifier)
{
- ASSERT(initializer != nullptr);
- TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier);
- TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
- TIntermBinary *tempInit = new TIntermBinary(EOpInitialize);
- tempInit->setLeft(tempSymbol);
- tempInit->setRight(initializer);
- tempInit->setType(tempSymbol->getType());
- tempDeclaration->getSequence()->push_back(tempInit);
- return tempDeclaration;
+ ASSERT(mTemporaryId != nullptr);
+ return CreateTempInitDeclarationNode(*mTemporaryId, initializer, qualifier);
}
-TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer)
+TIntermDeclaration *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer)
{
return createTempInitDeclaration(initializer, EvqTemporary);
}
@@ -142,39 +214,38 @@ TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode)
{
ASSERT(rightNode != nullptr);
TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType());
- TIntermBinary *assignment = new TIntermBinary(EOpAssign);
- assignment->setLeft(tempSymbol);
- assignment->setRight(rightNode);
- assignment->setType(tempSymbol->getType());
+ TIntermBinary *assignment = new TIntermBinary(EOpAssign, tempSymbol, rightNode);
return assignment;
}
-void TIntermTraverser::useTemporaryIndex(unsigned int *temporaryIndex)
+void TIntermTraverser::nextTemporaryId()
{
- mTemporaryIndex = temporaryIndex;
-}
-
-void TIntermTraverser::nextTemporaryIndex()
-{
- ASSERT(mTemporaryIndex != nullptr);
- ++(*mTemporaryIndex);
+ ASSERT(mSymbolTable);
+ if (!mTemporaryId)
+ {
+ mTemporaryId = new TSymbolUniqueId(mSymbolTable);
+ return;
+ }
+ *mTemporaryId = TSymbolUniqueId(mSymbolTable);
}
-void TLValueTrackingTraverser::addToFunctionMap(const TName &name, TIntermSequence *paramSequence)
+void TLValueTrackingTraverser::addToFunctionMap(const TSymbolUniqueId &id,
+ TIntermSequence *paramSequence)
{
- mFunctionMap[name] = paramSequence;
+ mFunctionMap[id.get()] = paramSequence;
}
bool TLValueTrackingTraverser::isInFunctionMap(const TIntermAggregate *callNode) const
{
- ASSERT(callNode->getOp() == EOpFunctionCall);
- return (mFunctionMap.find(callNode->getNameObj()) != mFunctionMap.end());
+ ASSERT(callNode->getOp() == EOpCallFunctionInAST);
+ return (mFunctionMap.find(callNode->getFunctionSymbolInfo()->getId().get()) !=
+ mFunctionMap.end());
}
TIntermSequence *TLValueTrackingTraverser::getFunctionParameters(const TIntermAggregate *callNode)
{
ASSERT(isInFunctionMap(callNode));
- return mFunctionMap[callNode->getNameObj()];
+ return mFunctionMap[callNode->getFunctionSymbolInfo()->getId().get()];
}
void TLValueTrackingTraverser::setInFunctionCallOutParameter(bool inOutParameter)
@@ -203,19 +274,41 @@ bool TLValueTrackingTraverser::isInFunctionCallOutParameter() const
//
void TIntermTraverser::traverseSymbol(TIntermSymbol *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
visitSymbol(node);
}
void TIntermTraverser::traverseConstantUnion(TIntermConstantUnion *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
visitConstantUnion(node);
}
+void TIntermTraverser::traverseSwizzle(TIntermSwizzle *node)
+{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitSwizzle(PreVisit, node);
+
+ if (visit)
+ {
+ node->getOperand()->traverse(this);
+ }
+
+ if (visit && postVisit)
+ visitSwizzle(PostVisit, node);
+}
+
//
// Traverse a binary node.
//
void TIntermTraverser::traverseBinary(TIntermBinary *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
//
@@ -229,8 +322,6 @@ void TIntermTraverser::traverseBinary(TIntermBinary *node)
//
if (visit)
{
- incrementDepth(node);
-
if (node->getLeft())
node->getLeft()->traverse(this);
@@ -239,8 +330,6 @@ void TIntermTraverser::traverseBinary(TIntermBinary *node)
if (visit && node->getRight())
node->getRight()->traverse(this);
-
- decrementDepth();
}
//
@@ -253,6 +342,8 @@ void TIntermTraverser::traverseBinary(TIntermBinary *node)
void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
//
@@ -266,8 +357,6 @@ void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node)
//
if (visit)
{
- incrementDepth(node);
-
// Some binary operations like indexing can be inside an expression which must be an
// l-value.
bool parentOperatorRequiresLValue = operatorRequiresLValue();
@@ -302,8 +391,6 @@ void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node)
setOperatorRequiresLValue(parentOperatorRequiresLValue);
setInFunctionCallOutParameter(parentInFunctionCallOutParameter);
-
- decrementDepth();
}
//
@@ -319,6 +406,8 @@ void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node)
//
void TIntermTraverser::traverseUnary(TIntermUnary *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
if (preVisit)
@@ -326,11 +415,7 @@ void TIntermTraverser::traverseUnary(TIntermUnary *node)
if (visit)
{
- incrementDepth(node);
-
node->getOperand()->traverse(this);
-
- decrementDepth();
}
if (visit && postVisit)
@@ -339,6 +424,8 @@ void TIntermTraverser::traverseUnary(TIntermUnary *node)
void TLValueTrackingTraverser::traverseUnary(TIntermUnary *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
if (preVisit)
@@ -346,8 +433,6 @@ void TLValueTrackingTraverser::traverseUnary(TIntermUnary *node)
if (visit)
{
- incrementDepth(node);
-
ASSERT(!operatorRequiresLValue());
switch (node->getOp())
{
@@ -364,153 +449,362 @@ void TLValueTrackingTraverser::traverseUnary(TIntermUnary *node)
node->getOperand()->traverse(this);
setOperatorRequiresLValue(false);
-
- decrementDepth();
}
if (visit && postVisit)
visitUnary(PostVisit, node);
}
-//
-// Traverse an aggregate node. Same comments in binary node apply here.
-//
-void TIntermTraverser::traverseAggregate(TIntermAggregate *node)
+// Traverse a function definition node.
+void TIntermTraverser::traverseFunctionDefinition(TIntermFunctionDefinition *node)
{
- bool visit = true;
+ ScopedNodeInTraversalPath addToPath(this, node);
- TIntermSequence *sequence = node->getSequence();
+ bool visit = true;
if (preVisit)
- visit = visitAggregate(PreVisit, node);
+ visit = visitFunctionDefinition(PreVisit, node);
if (visit)
{
- incrementDepth(node);
+ mInGlobalScope = false;
+
+ node->getFunctionPrototype()->traverse(this);
+ if (inVisit)
+ visit = visitFunctionDefinition(InVisit, node);
+ node->getBody()->traverse(this);
+
+ mInGlobalScope = true;
+ }
- if (node->getOp() == EOpSequence)
- pushParentBlock(node);
+ if (visit && postVisit)
+ visitFunctionDefinition(PostVisit, node);
+}
+// Traverse a block node.
+void TIntermTraverser::traverseBlock(TIntermBlock *node)
+{
+ ScopedNodeInTraversalPath addToPath(this, node);
+ pushParentBlock(node);
+
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
+ if (preVisit)
+ visit = visitBlock(PreVisit, node);
+
+ if (visit)
+ {
for (auto *child : *sequence)
{
child->traverse(this);
if (visit && inVisit)
{
if (child != sequence->back())
- visit = visitAggregate(InVisit, node);
+ visit = visitBlock(InVisit, node);
}
- if (node->getOp() == EOpSequence)
- incrementParentBlockPos();
+ incrementParentBlockPos();
}
+ }
+
+ if (visit && postVisit)
+ visitBlock(PostVisit, node);
- if (node->getOp() == EOpSequence)
- popParentBlock();
+ popParentBlock();
+}
- decrementDepth();
+void TIntermTraverser::traverseInvariantDeclaration(TIntermInvariantDeclaration *node)
+{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ if (preVisit)
+ {
+ visit = visitInvariantDeclaration(PreVisit, node);
+ }
+
+ if (visit)
+ {
+ node->getSymbol()->traverse(this);
+ if (postVisit)
+ {
+ visitInvariantDeclaration(PostVisit, node);
+ }
+ }
+}
+
+// Traverse a declaration node.
+void TIntermTraverser::traverseDeclaration(TIntermDeclaration *node)
+{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
+ if (preVisit)
+ visit = visitDeclaration(PreVisit, node);
+
+ if (visit)
+ {
+ for (auto *child : *sequence)
+ {
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitDeclaration(InVisit, node);
+ }
+ }
}
if (visit && postVisit)
- visitAggregate(PostVisit, node);
+ visitDeclaration(PostVisit, node);
}
-void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
+void TIntermTraverser::traverseFunctionPrototype(TIntermFunctionPrototype *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
TIntermSequence *sequence = node->getSequence();
- switch (node->getOp())
+
+ if (preVisit)
+ visit = visitFunctionPrototype(PreVisit, node);
+
+ if (visit)
{
- case EOpFunction:
+ for (auto *child : *sequence)
{
- TIntermAggregate *params = sequence->front()->getAsAggregate();
- ASSERT(params != nullptr);
- ASSERT(params->getOp() == EOpParameters);
- addToFunctionMap(node->getNameObj(), params->getSequence());
- break;
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitFunctionPrototype(InVisit, node);
+ }
}
- case EOpPrototype:
- addToFunctionMap(node->getNameObj(), sequence);
- break;
- default:
- break;
}
+ if (visit && postVisit)
+ visitFunctionPrototype(PostVisit, node);
+}
+
+// Traverse an aggregate node. Same comments in binary node apply here.
+void TIntermTraverser::traverseAggregate(TIntermAggregate *node)
+{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
if (preVisit)
visit = visitAggregate(PreVisit, node);
if (visit)
{
- bool inFunctionMap = false;
- if (node->getOp() == EOpFunctionCall)
+ for (auto *child : *sequence)
{
- inFunctionMap = isInFunctionMap(node);
- if (!inFunctionMap)
+ child->traverse(this);
+ if (visit && inVisit)
{
- // The function is not user-defined - it is likely built-in texture function.
- // Assume that those do not have out parameters.
- setInFunctionCallOutParameter(false);
+ if (child != sequence->back())
+ visit = visitAggregate(InVisit, node);
}
}
+ }
- incrementDepth(node);
+ if (visit && postVisit)
+ visitAggregate(PostVisit, node);
+}
- if (inFunctionMap)
+bool TIntermTraverser::CompareInsertion(const NodeInsertMultipleEntry &a,
+ const NodeInsertMultipleEntry &b)
+{
+ if (a.parent != b.parent)
+ {
+ return a.parent > b.parent;
+ }
+ return a.position > b.position;
+}
+
+void TIntermTraverser::updateTree()
+{
+ // Sort the insertions so that insertion position is decreasing. This way multiple insertions to
+ // the same parent node are handled correctly.
+ std::sort(mInsertions.begin(), mInsertions.end(), CompareInsertion);
+ for (size_t ii = 0; ii < mInsertions.size(); ++ii)
+ {
+ // We can't know here what the intended ordering of two insertions to the same position is,
+ // so it is not supported.
+ ASSERT(ii == 0 || mInsertions[ii].position != mInsertions[ii - 1].position ||
+ mInsertions[ii].parent != mInsertions[ii - 1].parent);
+ const NodeInsertMultipleEntry &insertion = mInsertions[ii];
+ ASSERT(insertion.parent);
+ if (!insertion.insertionsAfter.empty())
{
- TIntermSequence *params = getFunctionParameters(node);
- TIntermSequence::iterator paramIter = params->begin();
- for (auto *child : *sequence)
+ bool inserted = insertion.parent->insertChildNodes(insertion.position + 1,
+ insertion.insertionsAfter);
+ ASSERT(inserted);
+ }
+ if (!insertion.insertionsBefore.empty())
+ {
+ bool inserted =
+ insertion.parent->insertChildNodes(insertion.position, insertion.insertionsBefore);
+ ASSERT(inserted);
+ }
+ }
+ for (size_t ii = 0; ii < mReplacements.size(); ++ii)
+ {
+ const NodeUpdateEntry &replacement = mReplacements[ii];
+ ASSERT(replacement.parent);
+ bool replaced =
+ replacement.parent->replaceChildNode(replacement.original, replacement.replacement);
+ ASSERT(replaced);
+
+ if (!replacement.originalBecomesChildOfReplacement)
+ {
+ // In AST traversing, a parent is visited before its children.
+ // After we replace a node, if its immediate child is to
+ // be replaced, we need to make sure we don't update the replaced
+ // node; instead, we update the replacement node.
+ for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj)
{
- ASSERT(paramIter != params->end());
- TQualifier qualifier = (*paramIter)->getAsTyped()->getQualifier();
- setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut);
+ NodeUpdateEntry &replacement2 = mReplacements[jj];
+ if (replacement2.parent == replacement.original)
+ replacement2.parent = replacement.replacement;
+ }
+ }
+ }
+ for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii)
+ {
+ const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii];
+ ASSERT(replacement.parent);
+ bool replaced = replacement.parent->replaceChildNodeWithMultiple(replacement.original,
+ replacement.replacements);
+ ASSERT(replaced);
+ }
- child->traverse(this);
- if (visit && inVisit)
+ clearReplacementQueue();
+}
+
+void TIntermTraverser::clearReplacementQueue()
+{
+ mReplacements.clear();
+ mMultiReplacements.clear();
+ mInsertions.clear();
+}
+
+void TIntermTraverser::queueReplacement(TIntermNode *replacement, OriginalNode originalStatus)
+{
+ queueReplacementWithParent(getParentNode(), mPath.back(), replacement, originalStatus);
+}
+
+void TIntermTraverser::queueReplacementWithParent(TIntermNode *parent,
+ TIntermNode *original,
+ TIntermNode *replacement,
+ OriginalNode originalStatus)
+{
+ bool originalBecomesChild = (originalStatus == OriginalNode::BECOMES_CHILD);
+ mReplacements.push_back(NodeUpdateEntry(parent, original, replacement, originalBecomesChild));
+}
+
+TLValueTrackingTraverser::TLValueTrackingTraverser(bool preVisit,
+ bool inVisit,
+ bool postVisit,
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+ : TIntermTraverser(preVisit, inVisit, postVisit, symbolTable),
+ mOperatorRequiresLValue(false),
+ mInFunctionCallOutParameter(false),
+ mShaderVersion(shaderVersion)
+{
+ ASSERT(symbolTable);
+}
+
+void TLValueTrackingTraverser::traverseFunctionPrototype(TIntermFunctionPrototype *node)
+{
+ TIntermSequence *sequence = node->getSequence();
+ addToFunctionMap(node->getFunctionSymbolInfo()->getId(), sequence);
+
+ TIntermTraverser::traverseFunctionPrototype(node);
+}
+
+void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
+{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
+ if (preVisit)
+ visit = visitAggregate(PreVisit, node);
+
+ if (visit)
+ {
+ if (node->getOp() == EOpCallFunctionInAST)
+ {
+ if (isInFunctionMap(node))
+ {
+ TIntermSequence *params = getFunctionParameters(node);
+ TIntermSequence::iterator paramIter = params->begin();
+ for (auto *child : *sequence)
{
- if (child != sequence->back())
- visit = visitAggregate(InVisit, node);
+ ASSERT(paramIter != params->end());
+ TQualifier qualifier = (*paramIter)->getAsTyped()->getQualifier();
+ setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut);
+
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitAggregate(InVisit, node);
+ }
+
+ ++paramIter;
+ }
+ }
+ else
+ {
+ // The node might not be in the function map in case we're in the middle of
+ // transforming the AST, and have inserted function call nodes without inserting the
+ // function definitions yet.
+ setInFunctionCallOutParameter(false);
+ for (auto *child : *sequence)
+ {
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitAggregate(InVisit, node);
+ }
}
-
- ++paramIter;
}
setInFunctionCallOutParameter(false);
}
else
{
- if (node->getOp() == EOpSequence)
- pushParentBlock(node);
-
// Find the built-in function corresponding to this op so that we can determine the
// in/out qualifiers of its parameters.
TFunction *builtInFunc = nullptr;
- TString opString = GetOperatorString(node->getOp());
- if (!node->isConstructor() && !opString.empty())
+ if (!node->isFunctionCall() && !node->isConstructor())
{
- // The return type doesn't affect the mangled name of the function, which is used
- // to look it up from the symbol table.
- TType dummyReturnType;
- TFunction call(&opString, &dummyReturnType, node->getOp());
- for (auto *child : *sequence)
- {
- TType *paramType = child->getAsTyped()->getTypePointer();
- TConstParameter p(paramType);
- call.addParameter(p);
- }
-
- TSymbol *sym = mSymbolTable.findBuiltIn(call.getMangledName(), mShaderVersion);
- if (sym != nullptr && sym->isFunction())
- {
- builtInFunc = static_cast<TFunction *>(sym);
- ASSERT(builtInFunc->getParamCount() == sequence->size());
- }
+ builtInFunc = static_cast<TFunction *>(
+ mSymbolTable->findBuiltIn(node->getSymbolTableMangledName(), mShaderVersion));
}
size_t paramIndex = 0;
for (auto *child : *sequence)
{
+ // This assumes that raw functions called with
+ // EOpCallInternalRawFunction don't have out parameters.
TQualifier qualifier = EvqIn;
if (builtInFunc != nullptr)
qualifier = builtInFunc->getParam(paramIndex).type->getQualifier();
@@ -523,19 +817,11 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
visit = visitAggregate(InVisit, node);
}
- if (node->getOp() == EOpSequence)
- incrementParentBlockPos();
-
++paramIndex;
}
setInFunctionCallOutParameter(false);
-
- if (node->getOp() == EOpSequence)
- popParentBlock();
}
-
- decrementDepth();
}
if (visit && postVisit)
@@ -543,28 +829,51 @@ void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
}
//
-// Traverse a selection node. Same comments in binary node apply here.
+// Traverse a ternary node. Same comments in binary node apply here.
//
-void TIntermTraverser::traverseSelection(TIntermSelection *node)
+void TIntermTraverser::traverseTernary(TIntermTernary *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
if (preVisit)
- visit = visitSelection(PreVisit, node);
+ visit = visitTernary(PreVisit, node);
+
+ if (visit)
+ {
+ node->getCondition()->traverse(this);
+ if (node->getTrueExpression())
+ node->getTrueExpression()->traverse(this);
+ if (node->getFalseExpression())
+ node->getFalseExpression()->traverse(this);
+ }
+
+ if (visit && postVisit)
+ visitTernary(PostVisit, node);
+}
+
+// Traverse an if-else node. Same comments in binary node apply here.
+void TIntermTraverser::traverseIfElse(TIntermIfElse *node)
+{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitIfElse(PreVisit, node);
if (visit)
{
- incrementDepth(node);
node->getCondition()->traverse(this);
if (node->getTrueBlock())
node->getTrueBlock()->traverse(this);
if (node->getFalseBlock())
node->getFalseBlock()->traverse(this);
- decrementDepth();
}
if (visit && postVisit)
- visitSelection(PostVisit, node);
+ visitIfElse(PostVisit, node);
}
//
@@ -572,6 +881,8 @@ void TIntermTraverser::traverseSelection(TIntermSelection *node)
//
void TIntermTraverser::traverseSwitch(TIntermSwitch *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
if (preVisit)
@@ -579,13 +890,11 @@ void TIntermTraverser::traverseSwitch(TIntermSwitch *node)
if (visit)
{
- incrementDepth(node);
node->getInit()->traverse(this);
if (inVisit)
visit = visitSwitch(InVisit, node);
if (visit && node->getStatementList())
node->getStatementList()->traverse(this);
- decrementDepth();
}
if (visit && postVisit)
@@ -597,13 +906,17 @@ void TIntermTraverser::traverseSwitch(TIntermSwitch *node)
//
void TIntermTraverser::traverseCase(TIntermCase *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
if (preVisit)
visit = visitCase(PreVisit, node);
if (visit && node->getCondition())
+ {
node->getCondition()->traverse(this);
+ }
if (visit && postVisit)
visitCase(PostVisit, node);
@@ -614,6 +927,8 @@ void TIntermTraverser::traverseCase(TIntermCase *node)
//
void TIntermTraverser::traverseLoop(TIntermLoop *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
if (preVisit)
@@ -621,8 +936,6 @@ void TIntermTraverser::traverseLoop(TIntermLoop *node)
if (visit)
{
- incrementDepth(node);
-
if (node->getInit())
node->getInit()->traverse(this);
@@ -634,8 +947,6 @@ void TIntermTraverser::traverseLoop(TIntermLoop *node)
if (node->getExpression())
node->getExpression()->traverse(this);
-
- decrementDepth();
}
if (visit && postVisit)
@@ -647,6 +958,8 @@ void TIntermTraverser::traverseLoop(TIntermLoop *node)
//
void TIntermTraverser::traverseBranch(TIntermBranch *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
+
bool visit = true;
if (preVisit)
@@ -654,9 +967,7 @@ void TIntermTraverser::traverseBranch(TIntermBranch *node)
if (visit && node->getExpression())
{
- incrementDepth(node);
node->getExpression()->traverse(this);
- decrementDepth();
}
if (visit && postVisit)
@@ -665,5 +976,8 @@ void TIntermTraverser::traverseBranch(TIntermBranch *node)
void TIntermTraverser::traverseRaw(TIntermRaw *node)
{
+ ScopedNodeInTraversalPath addToPath(this, node);
visitRaw(node);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/IntermTraverse.h b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.h
new file mode 100644
index 0000000000..f0300b586b
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.h
@@ -0,0 +1,355 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermTraverse.h : base classes for AST traversers that walk the AST and
+// also have the ability to transform it by replacing nodes.
+
+#ifndef COMPILER_TRANSLATOR_INTERMTRAVERSE_H_
+#define COMPILER_TRANSLATOR_INTERMTRAVERSE_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+class TSymbolTable;
+class TSymbolUniqueId;
+
+enum Visit
+{
+ PreVisit,
+ InVisit,
+ PostVisit
+};
+
+// For traversing the tree. User should derive from this class overriding the visit functions,
+// and then pass an object of the subclass to a traverse method of a node.
+//
+// The traverse*() functions may also be overridden to do other bookkeeping on the tree to provide
+// contextual information to the visit functions, such as whether the node is the target of an
+// assignment. This is complex to maintain and so should only be done in special cases.
+//
+// When using this, just fill in the methods for nodes you want visited.
+// Return false from a pre-visit to skip visiting that node's subtree.
+class TIntermTraverser : angle::NonCopyable
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TIntermTraverser(bool preVisit,
+ bool inVisit,
+ bool postVisit,
+ TSymbolTable *symbolTable = nullptr);
+ virtual ~TIntermTraverser();
+
+ virtual void visitSymbol(TIntermSymbol *node) {}
+ virtual void visitRaw(TIntermRaw *node) {}
+ virtual void visitConstantUnion(TIntermConstantUnion *node) {}
+ virtual bool visitSwizzle(Visit visit, TIntermSwizzle *node) { return true; }
+ virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; }
+ virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; }
+ virtual bool visitTernary(Visit visit, TIntermTernary *node) { return true; }
+ virtual bool visitIfElse(Visit visit, TIntermIfElse *node) { return true; }
+ virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; }
+ virtual bool visitCase(Visit visit, TIntermCase *node) { return true; }
+ virtual bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
+ {
+ return true;
+ }
+ virtual bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+ {
+ return true;
+ }
+ virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; }
+ virtual bool visitBlock(Visit visit, TIntermBlock *node) { return true; }
+ virtual bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
+ {
+ return true;
+ }
+ virtual bool visitDeclaration(Visit visit, TIntermDeclaration *node) { return true; }
+ virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; }
+ virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; }
+
+ // The traverse functions contain logic for iterating over the children of the node
+ // and calling the visit functions in the appropriate places. They also track some
+ // context that may be used by the visit functions.
+ virtual void traverseSymbol(TIntermSymbol *node);
+ virtual void traverseRaw(TIntermRaw *node);
+ virtual void traverseConstantUnion(TIntermConstantUnion *node);
+ virtual void traverseSwizzle(TIntermSwizzle *node);
+ virtual void traverseBinary(TIntermBinary *node);
+ virtual void traverseUnary(TIntermUnary *node);
+ virtual void traverseTernary(TIntermTernary *node);
+ virtual void traverseIfElse(TIntermIfElse *node);
+ virtual void traverseSwitch(TIntermSwitch *node);
+ virtual void traverseCase(TIntermCase *node);
+ virtual void traverseFunctionPrototype(TIntermFunctionPrototype *node);
+ virtual void traverseFunctionDefinition(TIntermFunctionDefinition *node);
+ virtual void traverseAggregate(TIntermAggregate *node);
+ virtual void traverseBlock(TIntermBlock *node);
+ virtual void traverseInvariantDeclaration(TIntermInvariantDeclaration *node);
+ virtual void traverseDeclaration(TIntermDeclaration *node);
+ virtual void traverseLoop(TIntermLoop *node);
+ virtual void traverseBranch(TIntermBranch *node);
+
+ int getMaxDepth() const { return mMaxDepth; }
+
+ // If traversers need to replace nodes, they can add the replacements in
+ // mReplacements/mMultiReplacements during traversal and the user of the traverser should call
+ // this function after traversal to perform them.
+ void updateTree();
+
+ protected:
+ // Should only be called from traverse*() functions
+ void incrementDepth(TIntermNode *current)
+ {
+ mDepth++;
+ mMaxDepth = std::max(mMaxDepth, mDepth);
+ mPath.push_back(current);
+ }
+
+ // Should only be called from traverse*() functions
+ void decrementDepth()
+ {
+ mDepth--;
+ mPath.pop_back();
+ }
+
+ // RAII helper for incrementDepth/decrementDepth
+ class ScopedNodeInTraversalPath
+ {
+ public:
+ ScopedNodeInTraversalPath(TIntermTraverser *traverser, TIntermNode *current)
+ : mTraverser(traverser)
+ {
+ mTraverser->incrementDepth(current);
+ }
+ ~ScopedNodeInTraversalPath() { mTraverser->decrementDepth(); }
+
+ private:
+ TIntermTraverser *mTraverser;
+ };
+
+ TIntermNode *getParentNode() { return mPath.size() <= 1 ? nullptr : mPath[mPath.size() - 2u]; }
+
+ // Return the nth ancestor of the node being traversed. getAncestorNode(0) == getParentNode()
+ TIntermNode *getAncestorNode(unsigned int n)
+ {
+ if (mPath.size() > n + 1u)
+ {
+ return mPath[mPath.size() - n - 2u];
+ }
+ return nullptr;
+ }
+
+ const TIntermBlock *getParentBlock() const;
+
+ void pushParentBlock(TIntermBlock *node);
+ void incrementParentBlockPos();
+ void popParentBlock();
+
+ // To replace a single node with multiple nodes in the parent aggregate. May be used with blocks
+ // but also with other nodes like declarations.
+ struct NodeReplaceWithMultipleEntry
+ {
+ NodeReplaceWithMultipleEntry(TIntermAggregateBase *_parent,
+ TIntermNode *_original,
+ TIntermSequence _replacements)
+ : parent(_parent), original(_original), replacements(_replacements)
+ {
+ }
+
+ TIntermAggregateBase *parent;
+ TIntermNode *original;
+ TIntermSequence replacements;
+ };
+
+ // Helper to insert statements in the parent block of the node currently being traversed.
+ // The statements will be inserted before the node being traversed once updateTree is called.
+ // Should only be called during PreVisit or PostVisit if called from block nodes.
+ // Note that two insertions to the same position in the same block are not supported.
+ void insertStatementsInParentBlock(const TIntermSequence &insertions);
+
+ // Same as above, but supports simultaneous insertion of statements before and after the node
+ // currently being traversed.
+ void insertStatementsInParentBlock(const TIntermSequence &insertionsBefore,
+ const TIntermSequence &insertionsAfter);
+
+ // Helper to insert a single statement.
+ void insertStatementInParentBlock(TIntermNode *statement);
+
+ // Helper to create a temporary symbol node with the given qualifier.
+ TIntermSymbol *createTempSymbol(const TType &type, TQualifier qualifier);
+ // Helper to create a temporary symbol node.
+ TIntermSymbol *createTempSymbol(const TType &type);
+ // Create a node that declares but doesn't initialize a temporary symbol.
+ TIntermDeclaration *createTempDeclaration(const TType &type);
+ // Create a node that initializes the current temporary symbol with initializer. The symbol will
+ // have the given qualifier.
+ TIntermDeclaration *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier);
+ // Create a node that initializes the current temporary symbol with initializer.
+ TIntermDeclaration *createTempInitDeclaration(TIntermTyped *initializer);
+ // Create a node that assigns rightNode to the current temporary symbol.
+ TIntermBinary *createTempAssignment(TIntermTyped *rightNode);
+ // Increment temporary symbol index.
+ void nextTemporaryId();
+
+ enum class OriginalNode
+ {
+ BECOMES_CHILD,
+ IS_DROPPED
+ };
+
+ void clearReplacementQueue();
+
+ // Replace the node currently being visited with replacement.
+ void queueReplacement(TIntermNode *replacement, OriginalNode originalStatus);
+ // Explicitly specify a node to replace with replacement.
+ void queueReplacementWithParent(TIntermNode *parent,
+ TIntermNode *original,
+ TIntermNode *replacement,
+ OriginalNode originalStatus);
+
+ const bool preVisit;
+ const bool inVisit;
+ const bool postVisit;
+
+ int mDepth;
+ int mMaxDepth;
+
+ bool mInGlobalScope;
+
+ // During traversing, save all the changes that need to happen into
+ // mReplacements/mMultiReplacements, then do them by calling updateTree().
+ // Multi replacements are processed after single replacements.
+ std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements;
+
+ TSymbolTable *mSymbolTable;
+
+ private:
+ // To insert multiple nodes into the parent block.
+ struct NodeInsertMultipleEntry
+ {
+ NodeInsertMultipleEntry(TIntermBlock *_parent,
+ TIntermSequence::size_type _position,
+ TIntermSequence _insertionsBefore,
+ TIntermSequence _insertionsAfter)
+ : parent(_parent),
+ position(_position),
+ insertionsBefore(_insertionsBefore),
+ insertionsAfter(_insertionsAfter)
+ {
+ }
+
+ TIntermBlock *parent;
+ TIntermSequence::size_type position;
+ TIntermSequence insertionsBefore;
+ TIntermSequence insertionsAfter;
+ };
+
+ static bool CompareInsertion(const NodeInsertMultipleEntry &a,
+ const NodeInsertMultipleEntry &b);
+
+ // To replace a single node with another on the parent node
+ struct NodeUpdateEntry
+ {
+ NodeUpdateEntry(TIntermNode *_parent,
+ TIntermNode *_original,
+ TIntermNode *_replacement,
+ bool _originalBecomesChildOfReplacement)
+ : parent(_parent),
+ original(_original),
+ replacement(_replacement),
+ originalBecomesChildOfReplacement(_originalBecomesChildOfReplacement)
+ {
+ }
+
+ TIntermNode *parent;
+ TIntermNode *original;
+ TIntermNode *replacement;
+ bool originalBecomesChildOfReplacement;
+ };
+
+ struct ParentBlock
+ {
+ ParentBlock(TIntermBlock *nodeIn, TIntermSequence::size_type posIn)
+ : node(nodeIn), pos(posIn)
+ {
+ }
+
+ TIntermBlock *node;
+ TIntermSequence::size_type pos;
+ };
+
+ std::vector<NodeInsertMultipleEntry> mInsertions;
+ std::vector<NodeUpdateEntry> mReplacements;
+
+ // All the nodes from root to the current node during traversing.
+ TVector<TIntermNode *> mPath;
+
+ // All the code blocks from the root to the current node's parent during traversal.
+ std::vector<ParentBlock> mParentBlockStack;
+
+ TSymbolUniqueId *mTemporaryId;
+};
+
+// Traverser parent class that tracks where a node is a destination of a write operation and so is
+// required to be an l-value.
+class TLValueTrackingTraverser : public TIntermTraverser
+{
+ public:
+ TLValueTrackingTraverser(bool preVisit,
+ bool inVisit,
+ bool postVisit,
+ TSymbolTable *symbolTable,
+ int shaderVersion);
+ virtual ~TLValueTrackingTraverser() {}
+
+ void traverseBinary(TIntermBinary *node) final;
+ void traverseUnary(TIntermUnary *node) final;
+ void traverseFunctionPrototype(TIntermFunctionPrototype *node) final;
+ void traverseAggregate(TIntermAggregate *node) final;
+
+ protected:
+ bool isLValueRequiredHere() const
+ {
+ return mOperatorRequiresLValue || mInFunctionCallOutParameter;
+ }
+
+ private:
+ // Track whether an l-value is required in the node that is currently being traversed by the
+ // surrounding operator.
+ // Use isLValueRequiredHere to check all conditions which require an l-value.
+ void setOperatorRequiresLValue(bool lValueRequired)
+ {
+ mOperatorRequiresLValue = lValueRequired;
+ }
+ bool operatorRequiresLValue() const { return mOperatorRequiresLValue; }
+
+ // Add a function encountered during traversal to the function map.
+ void addToFunctionMap(const TSymbolUniqueId &id, TIntermSequence *paramSequence);
+
+ // Return true if the prototype or definition of the function being called has been encountered
+ // during traversal.
+ bool isInFunctionMap(const TIntermAggregate *callNode) const;
+
+ // Return the parameters sequence from the function definition or prototype.
+ TIntermSequence *getFunctionParameters(const TIntermAggregate *callNode);
+
+ // Track whether an l-value is required inside a function call.
+ void setInFunctionCallOutParameter(bool inOutParameter);
+ bool isInFunctionCallOutParameter() const;
+
+ bool mOperatorRequiresLValue;
+ bool mInFunctionCallOutParameter;
+
+ // Map from function symbol id values to their parameter sequences
+ TMap<int, TIntermSequence *> mFunctionMap;
+
+ const int mShaderVersion;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INTERMTRAVERSE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp b/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp
deleted file mode 100644
index 0adb7212b7..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp
+++ /dev/null
@@ -1,508 +0,0 @@
-//
-// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-//
-// Build the intermediate representation.
-//
-
-#include <float.h>
-#include <limits.h>
-#include <algorithm>
-
-#include "compiler/translator/Intermediate.h"
-#include "compiler/translator/SymbolTable.h"
-
-////////////////////////////////////////////////////////////////////////////
-//
-// First set of functions are to help build the intermediate representation.
-// These functions are not member functions of the nodes.
-// They are called from parser productions.
-//
-/////////////////////////////////////////////////////////////////////////////
-
-//
-// Add a terminal node for an identifier in an expression.
-//
-// Returns the added node.
-//
-TIntermSymbol *TIntermediate::addSymbol(
- int id, const TString &name, const TType &type, const TSourceLoc &line)
-{
- TIntermSymbol *node = new TIntermSymbol(id, name, type);
- node->setLine(line);
-
- return node;
-}
-
-//
-// Connect two nodes with a new parent that does a binary operation on the nodes.
-//
-// Returns the added node.
-//
-TIntermTyped *TIntermediate::addBinaryMath(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line)
-{
- //
- // Need a new node holding things together then. Make
- // one and promote it to the right type.
- //
- TIntermBinary *node = new TIntermBinary(op);
- node->setLine(line);
-
- node->setLeft(left);
- node->setRight(right);
- if (!node->promote(mInfoSink))
- return NULL;
-
- // See if we can fold constants.
- TIntermTyped *foldedNode = node->fold(mInfoSink);
- if (foldedNode)
- return foldedNode;
-
- return node;
-}
-
-//
-// Connect two nodes through an assignment.
-//
-// Returns the added node.
-//
-TIntermTyped *TIntermediate::addAssign(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line)
-{
- if (left->getType().getStruct() || right->getType().getStruct())
- {
- if (left->getType() != right->getType())
- {
- return NULL;
- }
- }
-
- TIntermBinary *node = new TIntermBinary(op);
- node->setLine(line);
-
- node->setLeft(left);
- node->setRight(right);
- if (!node->promote(mInfoSink))
- return NULL;
-
- return node;
-}
-
-//
-// Connect two nodes through an index operator, where the left node is the base
-// of an array or struct, and the right node is a direct or indirect offset.
-//
-// Returns the added node.
-// The caller should set the type of the returned node.
-//
-TIntermTyped *TIntermediate::addIndex(
- TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &line)
-{
- TIntermBinary *node = new TIntermBinary(op);
- node->setLine(line);
- node->setLeft(base);
- node->setRight(index);
-
- // caller should set the type
-
- return node;
-}
-
-//
-// Add one node as the parent of another that it operates on.
-//
-// Returns the added node.
-//
-TIntermTyped *TIntermediate::addUnaryMath(
- TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType)
-{
- //
- // Make a new node for the operator.
- //
- TIntermUnary *node = new TIntermUnary(op);
- node->setLine(line);
- node->setOperand(child);
- node->promote(funcReturnType);
-
- TIntermTyped *foldedNode = node->fold(mInfoSink);
- if (foldedNode)
- return foldedNode;
-
- return node;
-}
-
-//
-// This is the safe way to change the operator on an aggregate, as it
-// does lots of error checking and fixing. Especially for establishing
-// a function call's operation on it's set of parameters. Sequences
-// of instructions are also aggregates, but they just direnctly set
-// their operator to EOpSequence.
-//
-// Returns an aggregate node, which could be the one passed in if
-// it was already an aggregate but no operator was set.
-//
-TIntermAggregate *TIntermediate::setAggregateOperator(
- TIntermNode *node, TOperator op, const TSourceLoc &line)
-{
- TIntermAggregate *aggNode;
-
- //
- // Make sure we have an aggregate. If not turn it into one.
- //
- if (node)
- {
- aggNode = node->getAsAggregate();
- if (aggNode == NULL || aggNode->getOp() != EOpNull)
- {
- //
- // Make an aggregate containing this node.
- //
- aggNode = new TIntermAggregate();
- aggNode->getSequence()->push_back(node);
- }
- }
- else
- {
- aggNode = new TIntermAggregate();
- }
-
- //
- // Set the operator.
- //
- aggNode->setOp(op);
- aggNode->setLine(line);
-
- return aggNode;
-}
-
-//
-// Safe way to combine two nodes into an aggregate. Works with null pointers,
-// a node that's not a aggregate yet, etc.
-//
-// Returns the resulting aggregate, unless 0 was passed in for
-// both existing nodes.
-//
-TIntermAggregate *TIntermediate::growAggregate(
- TIntermNode *left, TIntermNode *right, const TSourceLoc &line)
-{
- if (left == NULL && right == NULL)
- return NULL;
-
- TIntermAggregate *aggNode = NULL;
- if (left)
- aggNode = left->getAsAggregate();
- if (!aggNode || aggNode->getOp() != EOpNull)
- {
- aggNode = new TIntermAggregate;
- if (left)
- aggNode->getSequence()->push_back(left);
- }
-
- if (right)
- aggNode->getSequence()->push_back(right);
-
- aggNode->setLine(line);
-
- return aggNode;
-}
-
-//
-// Turn an existing node into an aggregate.
-//
-// Returns an aggregate, unless NULL was passed in for the existing node.
-//
-TIntermAggregate *TIntermediate::makeAggregate(
- TIntermNode *node, const TSourceLoc &line)
-{
- if (node == NULL)
- return NULL;
-
- TIntermAggregate *aggNode = new TIntermAggregate;
- aggNode->getSequence()->push_back(node);
-
- aggNode->setLine(line);
-
- return aggNode;
-}
-
-// If the input node is nullptr, return nullptr.
-// If the input node is a sequence (block) node, return it.
-// If the input node is not a sequence node, put it inside a sequence node and return that.
-TIntermAggregate *TIntermediate::ensureSequence(TIntermNode *node)
-{
- if (node == nullptr)
- return nullptr;
- TIntermAggregate *aggNode = node->getAsAggregate();
- if (aggNode != nullptr && aggNode->getOp() == EOpSequence)
- return aggNode;
-
- aggNode = makeAggregate(node, node->getLine());
- aggNode->setOp(EOpSequence);
- return aggNode;
-}
-
-//
-// For "if" test nodes. There are three children; a condition,
-// a true path, and a false path. The two paths are in the
-// nodePair.
-//
-// Returns the selection node created.
-//
-TIntermNode *TIntermediate::addSelection(
- TIntermTyped *cond, TIntermNodePair nodePair, const TSourceLoc &line)
-{
- //
- // For compile time constant selections, prune the code and
- // test now.
- //
-
- if (cond->getAsConstantUnion())
- {
- if (cond->getAsConstantUnion()->getBConst(0) == true)
- {
- return nodePair.node1 ? setAggregateOperator(
- nodePair.node1, EOpSequence, nodePair.node1->getLine()) : NULL;
- }
- else
- {
- return nodePair.node2 ? setAggregateOperator(
- nodePair.node2, EOpSequence, nodePair.node2->getLine()) : NULL;
- }
- }
-
- TIntermSelection *node = new TIntermSelection(
- cond, ensureSequence(nodePair.node1), ensureSequence(nodePair.node2));
- node->setLine(line);
-
- return node;
-}
-
-TIntermTyped *TIntermediate::addComma(TIntermTyped *left,
- TIntermTyped *right,
- const TSourceLoc &line,
- int shaderVersion)
-{
- TQualifier resultQualifier = EvqConst;
- // ESSL3.00 section 12.43: The result of a sequence operator is not a constant-expression.
- if (shaderVersion >= 300 || left->getQualifier() != EvqConst ||
- right->getQualifier() != EvqConst)
- {
- resultQualifier = EvqTemporary;
- }
-
- TIntermTyped *commaNode = nullptr;
- if (!left->hasSideEffects())
- {
- commaNode = right;
- }
- else
- {
- commaNode = growAggregate(left, right, line);
- commaNode->getAsAggregate()->setOp(EOpComma);
- commaNode->setType(right->getType());
- }
- commaNode->getTypePointer()->setQualifier(resultQualifier);
- return commaNode;
-}
-
-//
-// For "?:" test nodes. There are three children; a condition,
-// a true path, and a false path. The two paths are specified
-// as separate parameters.
-//
-// Returns the selection node created, or one of trueBlock and falseBlock if the expression could be folded.
-//
-TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
- const TSourceLoc &line)
-{
- TQualifier resultQualifier = EvqTemporary;
- if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst &&
- falseBlock->getQualifier() == EvqConst)
- {
- resultQualifier = EvqConst;
- }
- // Note that the node resulting from here can be a constant union without being qualified as
- // constant.
- if (cond->getAsConstantUnion())
- {
- if (cond->getAsConstantUnion()->getBConst(0))
- {
- trueBlock->getTypePointer()->setQualifier(resultQualifier);
- return trueBlock;
- }
- else
- {
- falseBlock->getTypePointer()->setQualifier(resultQualifier);
- return falseBlock;
- }
- }
-
- //
- // Make a selection node.
- //
- TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
- node->getTypePointer()->setQualifier(resultQualifier);
- node->setLine(line);
-
- return node;
-}
-
-TIntermSwitch *TIntermediate::addSwitch(
- TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line)
-{
- TIntermSwitch *node = new TIntermSwitch(init, statementList);
- node->setLine(line);
-
- return node;
-}
-
-TIntermCase *TIntermediate::addCase(
- TIntermTyped *condition, const TSourceLoc &line)
-{
- TIntermCase *node = new TIntermCase(condition);
- node->setLine(line);
-
- return node;
-}
-
-//
-// Constant terminal nodes. Has a union that contains bool, float or int constants
-//
-// Returns the constant union node created.
-//
-
-TIntermConstantUnion *TIntermediate::addConstantUnion(const TConstantUnion *constantUnion,
- const TType &type,
- const TSourceLoc &line)
-{
- TIntermConstantUnion *node = new TIntermConstantUnion(constantUnion, type);
- node->setLine(line);
-
- return node;
-}
-
-TIntermTyped *TIntermediate::addSwizzle(
- TVectorFields &fields, const TSourceLoc &line)
-{
-
- TIntermAggregate *node = new TIntermAggregate(EOpSequence);
-
- node->setLine(line);
- TIntermConstantUnion *constIntNode;
- TIntermSequence *sequenceVector = node->getSequence();
- TConstantUnion *unionArray;
-
- for (int i = 0; i < fields.num; i++)
- {
- unionArray = new TConstantUnion[1];
- unionArray->setIConst(fields.offsets[i]);
- constIntNode = addConstantUnion(
- unionArray, TType(EbtInt, EbpUndefined, EvqConst), line);
- sequenceVector->push_back(constIntNode);
- }
-
- return node;
-}
-
-//
-// Create loop nodes.
-//
-TIntermNode *TIntermediate::addLoop(
- TLoopType type, TIntermNode *init, TIntermTyped *cond, TIntermTyped *expr,
- TIntermNode *body, const TSourceLoc &line)
-{
- TIntermNode *node = new TIntermLoop(type, init, cond, expr, ensureSequence(body));
- node->setLine(line);
-
- return node;
-}
-
-//
-// Add branches.
-//
-TIntermBranch* TIntermediate::addBranch(
- TOperator branchOp, const TSourceLoc &line)
-{
- return addBranch(branchOp, 0, line);
-}
-
-TIntermBranch* TIntermediate::addBranch(
- TOperator branchOp, TIntermTyped *expression, const TSourceLoc &line)
-{
- TIntermBranch *node = new TIntermBranch(branchOp, expression);
- node->setLine(line);
-
- return node;
-}
-
-//
-// This is to be executed once the final root is put on top by the parsing
-// process.
-//
-TIntermAggregate *TIntermediate::postProcess(TIntermNode *root)
-{
- if (root == nullptr)
- return nullptr;
-
- //
- // Finish off the top level sequence, if any
- //
- TIntermAggregate *aggRoot = root->getAsAggregate();
- if (aggRoot != nullptr && aggRoot->getOp() == EOpNull)
- {
- aggRoot->setOp(EOpSequence);
- }
- else if (aggRoot == nullptr || aggRoot->getOp() != EOpSequence)
- {
- aggRoot = new TIntermAggregate(EOpSequence);
- aggRoot->setLine(root->getLine());
- aggRoot->getSequence()->push_back(root);
- }
-
- return aggRoot;
-}
-
-TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate)
-{
- switch (aggregate->getOp())
- {
- case EOpAtan:
- case EOpPow:
- case EOpMod:
- case EOpMin:
- case EOpMax:
- case EOpClamp:
- case EOpMix:
- case EOpStep:
- case EOpSmoothStep:
- case EOpMul:
- case EOpOuterProduct:
- case EOpLessThan:
- case EOpLessThanEqual:
- case EOpGreaterThan:
- case EOpGreaterThanEqual:
- case EOpVectorEqual:
- case EOpVectorNotEqual:
- case EOpDistance:
- case EOpDot:
- case EOpCross:
- case EOpFaceForward:
- case EOpReflect:
- case EOpRefract:
- return aggregate->fold(mInfoSink);
- default:
- // TODO: Add support for folding array constructors
- if (aggregate->isConstructor() && !aggregate->isArray())
- {
- return aggregate->fold(mInfoSink);
- }
- // Constant folding not supported for the built-in.
- return nullptr;
- }
-
- return nullptr;
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/Intermediate.h b/src/3rdparty/angle/src/compiler/translator/Intermediate.h
deleted file mode 100644
index f723fc7648..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/Intermediate.h
+++ /dev/null
@@ -1,75 +0,0 @@
-//
-// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_INTERMEDIATE_H_
-#define COMPILER_TRANSLATOR_INTERMEDIATE_H_
-
-#include "compiler/translator/IntermNode.h"
-
-struct TVectorFields
-{
- int offsets[4];
- int num;
-};
-
-//
-// Set of helper functions to help parse and build the tree.
-//
-class TInfoSink;
-class TIntermediate
-{
- public:
- POOL_ALLOCATOR_NEW_DELETE();
- TIntermediate(TInfoSink &i)
- : mInfoSink(i) { }
-
- TIntermSymbol *addSymbol(
- int id, const TString &, const TType &, const TSourceLoc &);
- TIntermTyped *addBinaryMath(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &);
- TIntermTyped *addAssign(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &);
- TIntermTyped *addIndex(
- TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &);
- TIntermTyped *addUnaryMath(
- TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType);
- TIntermAggregate *growAggregate(
- TIntermNode *left, TIntermNode *right, const TSourceLoc &);
- TIntermAggregate *makeAggregate(TIntermNode *node, const TSourceLoc &);
- TIntermAggregate *ensureSequence(TIntermNode *node);
- TIntermAggregate *setAggregateOperator(TIntermNode *, TOperator, const TSourceLoc &);
- TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &);
- TIntermTyped *addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
- const TSourceLoc &line);
- TIntermSwitch *addSwitch(
- TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line);
- TIntermCase *addCase(
- TIntermTyped *condition, const TSourceLoc &line);
- TIntermTyped *addComma(TIntermTyped *left,
- TIntermTyped *right,
- const TSourceLoc &line,
- int shaderVersion);
- TIntermConstantUnion *addConstantUnion(const TConstantUnion *constantUnion,
- const TType &type,
- const TSourceLoc &line);
- TIntermNode *addLoop(TLoopType, TIntermNode *, TIntermTyped *, TIntermTyped *,
- TIntermNode *, const TSourceLoc &);
- TIntermBranch *addBranch(TOperator, const TSourceLoc &);
- TIntermBranch *addBranch(TOperator, TIntermTyped *, const TSourceLoc &);
- TIntermTyped *addSwizzle(TVectorFields &, const TSourceLoc &);
- TIntermAggregate *postProcess(TIntermNode *root);
-
- static void outputTree(TIntermNode *, TInfoSinkBase &);
-
- TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate);
-
- private:
- void operator=(TIntermediate &); // prevent assignments
-
- TInfoSink & mInfoSink;
-};
-
-#endif // COMPILER_TRANSLATOR_INTERMEDIATE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.cpp b/src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.cpp
new file mode 100644
index 0000000000..aaad4f3c68
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.cpp
@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/IsASTDepthBelowLimit.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// Traverse the tree and compute max depth. Takes a maximum depth limit to prevent stack overflow.
+class MaxDepthTraverser : public TIntermTraverser
+{
+ public:
+ MaxDepthTraverser(int depthLimit) : TIntermTraverser(true, true, false), mDepthLimit(depthLimit)
+ {
+ }
+
+ bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); }
+ bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); }
+ bool visitTernary(Visit, TIntermTernary *) override { return depthCheck(); }
+ bool visitSwizzle(Visit, TIntermSwizzle *) override { return depthCheck(); }
+ bool visitIfElse(Visit, TIntermIfElse *) override { return depthCheck(); }
+ bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); }
+ bool visitBlock(Visit, TIntermBlock *) override { return depthCheck(); }
+ bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); }
+ bool visitBranch(Visit, TIntermBranch *) override { return depthCheck(); }
+
+ protected:
+ bool depthCheck() const { return mMaxDepth < mDepthLimit; }
+
+ int mDepthLimit;
+};
+
+} // anonymous namespace
+
+bool IsASTDepthBelowLimit(TIntermNode *root, int maxDepth)
+{
+ MaxDepthTraverser traverser(maxDepth + 1);
+ root->traverse(&traverser);
+
+ return traverser.getMaxDepth() <= maxDepth;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.h b/src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.h
new file mode 100644
index 0000000000..ef2f02c974
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.h
@@ -0,0 +1,20 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IsASTDepthBelowLimit: Check whether AST depth is below a specific limit.
+
+#ifndef COMPILER_TRANSLATOR_ISASTDEPTHBELOWLIMIT_H_
+#define COMPILER_TRANSLATOR_ISASTDEPTHBELOWLIMIT_H_
+
+namespace sh
+{
+
+class TIntermNode;
+
+bool IsASTDepthBelowLimit(TIntermNode *root, int maxDepth);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ISASTDEPTHBELOWLIMIT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/LoopInfo.cpp b/src/3rdparty/angle/src/compiler/translator/LoopInfo.cpp
deleted file mode 100644
index d931a18a23..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/LoopInfo.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-//
-// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/LoopInfo.h"
-
-namespace
-{
-
-int EvaluateIntConstant(TIntermConstantUnion *node)
-{
- ASSERT(node && node->getUnionArrayPointer());
- return node->getIConst(0);
-}
-
-int GetLoopIntIncrement(TIntermLoop *node)
-{
- TIntermNode *expr = node->getExpression();
- // for expression has one of the following forms:
- // loop_index++
- // loop_index--
- // loop_index += constant_expression
- // loop_index -= constant_expression
- // ++loop_index
- // --loop_index
- // The last two forms are not specified in the spec, but I am assuming
- // its an oversight.
- TIntermUnary *unOp = expr->getAsUnaryNode();
- TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
-
- TOperator op = EOpNull;
- TIntermConstantUnion *incrementNode = NULL;
- if (unOp)
- {
- op = unOp->getOp();
- }
- else if (binOp)
- {
- op = binOp->getOp();
- ASSERT(binOp->getRight());
- incrementNode = binOp->getRight()->getAsConstantUnion();
- ASSERT(incrementNode);
- }
-
- int increment = 0;
- // The operator is one of: ++ -- += -=.
- switch (op)
- {
- case EOpPostIncrement:
- case EOpPreIncrement:
- ASSERT(unOp && !binOp);
- increment = 1;
- break;
- case EOpPostDecrement:
- case EOpPreDecrement:
- ASSERT(unOp && !binOp);
- increment = -1;
- break;
- case EOpAddAssign:
- ASSERT(!unOp && binOp);
- increment = EvaluateIntConstant(incrementNode);
- break;
- case EOpSubAssign:
- ASSERT(!unOp && binOp);
- increment = - EvaluateIntConstant(incrementNode);
- break;
- default:
- UNREACHABLE();
- }
-
- return increment;
-}
-
-} // namespace anonymous
-
-TLoopIndexInfo::TLoopIndexInfo()
- : mId(-1),
- mType(EbtVoid),
- mInitValue(0),
- mStopValue(0),
- mIncrementValue(0),
- mOp(EOpNull),
- mCurrentValue(0)
-{
-}
-
-void TLoopIndexInfo::fillInfo(TIntermLoop *node)
-{
- if (node == NULL)
- return;
-
- // Here we assume all the operations are valid, because the loop node is
- // already validated in ValidateLimitations.
- TIntermSequence *declSeq =
- node->getInit()->getAsAggregate()->getSequence();
- TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
- TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
-
- mId = symbol->getId();
- mType = symbol->getBasicType();
-
- if (mType == EbtInt)
- {
- TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
- mInitValue = EvaluateIntConstant(initNode);
- mCurrentValue = mInitValue;
- mIncrementValue = GetLoopIntIncrement(node);
-
- TIntermBinary* binOp = node->getCondition()->getAsBinaryNode();
- mStopValue = EvaluateIntConstant(
- binOp->getRight()->getAsConstantUnion());
- mOp = binOp->getOp();
- }
-}
-
-bool TLoopIndexInfo::satisfiesLoopCondition() const
-{
- // Relational operator is one of: > >= < <= == or !=.
- switch (mOp)
- {
- case EOpEqual:
- return (mCurrentValue == mStopValue);
- case EOpNotEqual:
- return (mCurrentValue != mStopValue);
- case EOpLessThan:
- return (mCurrentValue < mStopValue);
- case EOpGreaterThan:
- return (mCurrentValue > mStopValue);
- case EOpLessThanEqual:
- return (mCurrentValue <= mStopValue);
- case EOpGreaterThanEqual:
- return (mCurrentValue >= mStopValue);
- default:
- UNREACHABLE();
- return false;
- }
-}
-
-TLoopInfo::TLoopInfo()
- : loop(NULL)
-{
-}
-
-TLoopInfo::TLoopInfo(TIntermLoop *node)
- : loop(node)
-{
- index.fillInfo(node);
-}
-
-TIntermLoop *TLoopStack::findLoop(TIntermSymbol *symbol)
-{
- if (!symbol)
- return NULL;
- for (iterator iter = begin(); iter != end(); ++iter)
- {
- if (iter->index.getId() == symbol->getId())
- return iter->loop;
- }
- return NULL;
-}
-
-TLoopIndexInfo *TLoopStack::getIndexInfo(TIntermSymbol *symbol)
-{
- if (!symbol)
- return NULL;
- for (iterator iter = begin(); iter != end(); ++iter)
- {
- if (iter->index.getId() == symbol->getId())
- return &(iter->index);
- }
- return NULL;
-}
-
-void TLoopStack::step()
-{
- ASSERT(!empty());
- rbegin()->index.step();
-}
-
-bool TLoopStack::satisfiesLoopCondition()
-{
- ASSERT(!empty());
- return rbegin()->index.satisfiesLoopCondition();
-}
-
-bool TLoopStack::needsToReplaceSymbolWithValue(TIntermSymbol *symbol)
-{
- TIntermLoop *loop = findLoop(symbol);
- return loop && loop->getUnrollFlag();
-}
-
-int TLoopStack::getLoopIndexValue(TIntermSymbol *symbol)
-{
- TLoopIndexInfo *info = getIndexInfo(symbol);
- ASSERT(info);
- return info->getCurrentValue();
-}
-
-void TLoopStack::push(TIntermLoop *loop)
-{
- TLoopInfo info(loop);
- push_back(info);
-}
-
-void TLoopStack::pop()
-{
- pop_back();
-}
-
diff --git a/src/3rdparty/angle/src/compiler/translator/LoopInfo.h b/src/3rdparty/angle/src/compiler/translator/LoopInfo.h
deleted file mode 100644
index ec73fd0fa5..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/LoopInfo.h
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_LOOPINFO_H_
-#define COMPILER_TRANSLATOR_LOOPINFO_H_
-
-#include "compiler/translator/IntermNode.h"
-
-class TLoopIndexInfo
-{
- public:
- TLoopIndexInfo();
-
- // If type is EbtInt, fill all fields of the structure with info
- // extracted from a loop node.
- // If type is not EbtInt, only fill id and type.
- void fillInfo(TIntermLoop *node);
-
- int getId() const { return mId; }
- void setId(int id) { mId = id; }
- TBasicType getType() const { return mType; }
- void setType(TBasicType type) { mType = type; }
- int getCurrentValue() const { return mCurrentValue; }
-
- void step() { mCurrentValue += mIncrementValue; }
-
- // Check if the current value satisfies the loop condition.
- bool satisfiesLoopCondition() const;
-
- private:
- int mId;
- TBasicType mType; // Either EbtInt or EbtFloat
-
- // Below fields are only valid if the index's type is int.
- int mInitValue;
- int mStopValue;
- int mIncrementValue;
- TOperator mOp;
- int mCurrentValue;
-};
-
-struct TLoopInfo
-{
- TLoopIndexInfo index;
- TIntermLoop *loop;
-
- TLoopInfo();
- TLoopInfo(TIntermLoop *node);
-};
-
-class TLoopStack : public TVector<TLoopInfo>
-{
- public:
- // Search loop stack for a loop whose index matches the input symbol.
- TIntermLoop *findLoop(TIntermSymbol *symbol);
-
- // Find the loop index info in the loop stack by the input symbol.
- TLoopIndexInfo *getIndexInfo(TIntermSymbol *symbol);
-
- // Update the currentValue for the next loop iteration.
- void step();
-
- // Return false if loop condition is no longer satisfied.
- bool satisfiesLoopCondition();
-
- // Check if the symbol is the index of a loop that's unrolled.
- bool needsToReplaceSymbolWithValue(TIntermSymbol *symbol);
-
- // Return the current value of a given loop index symbol.
- int getLoopIndexValue(TIntermSymbol *symbol);
-
- void push(TIntermLoop *info);
- void pop();
-};
-
-#endif // COMPILER_TRANSLATOR_LOOPINFO_H_
-
diff --git a/src/3rdparty/angle/src/compiler/translator/MMap.h b/src/3rdparty/angle/src/compiler/translator/MMap.h
deleted file mode 100644
index fca843992b..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/MMap.h
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_MMAP_H_
-#define COMPILER_TRANSLATOR_MMAP_H_
-
-//
-// Encapsulate memory mapped files
-//
-
-class TMMap {
-public:
- TMMap(const char* fileName) :
- fSize(-1), // -1 is the error value returned by GetFileSize()
- fp(NULL),
- fBuff(0) // 0 is the error value returned by MapViewOfFile()
- {
- if ((fp = fopen(fileName, "r")) == NULL)
- return;
- char c = getc(fp);
- fSize = 0;
- while (c != EOF) {
- fSize++;
- c = getc(fp);
- }
- if (c == EOF)
- fSize++;
- rewind(fp);
- fBuff = (char*)malloc(sizeof(char) * fSize);
- int count = 0;
- c = getc(fp);
- while (c != EOF) {
- fBuff[count++] = c;
- c = getc(fp);
- }
- fBuff[count++] = c;
- }
-
- char* getData() { return fBuff; }
- int getSize() { return fSize; }
-
- ~TMMap() {
- if (fp != NULL)
- fclose(fp);
- }
-
-private:
- int fSize; // size of file to map in
- FILE *fp;
- char* fBuff; // the actual data;
-};
-
-#endif // COMPILER_TRANSLATOR_MMAP_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/NodeSearch.h b/src/3rdparty/angle/src/compiler/translator/NodeSearch.h
index b13b1baabb..af86b8bde4 100644
--- a/src/3rdparty/angle/src/compiler/translator/NodeSearch.h
+++ b/src/3rdparty/angle/src/compiler/translator/NodeSearch.h
@@ -9,7 +9,7 @@
#ifndef COMPILER_TRANSLATOR_NODESEARCH_H_
#define COMPILER_TRANSLATOR_NODESEARCH_H_
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
namespace sh
{
@@ -18,10 +18,7 @@ template <class Parent>
class NodeSearchTraverser : public TIntermTraverser
{
public:
- NodeSearchTraverser()
- : TIntermTraverser(true, false, false),
- mFound(false)
- {}
+ NodeSearchTraverser() : TIntermTraverser(true, false, false), mFound(false) {}
bool found() const { return mFound; }
@@ -43,17 +40,17 @@ class FindDiscard : public NodeSearchTraverser<FindDiscard>
{
switch (node->getFlowOp())
{
- case EOpKill:
- mFound = true;
- break;
+ case EOpKill:
+ mFound = true;
+ break;
- default: break;
+ default:
+ break;
}
return !mFound;
}
};
-
}
-#endif // COMPILER_TRANSLATOR_NODESEARCH_H_
+#endif // COMPILER_TRANSLATOR_NODESEARCH_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Operator.cpp b/src/3rdparty/angle/src/compiler/translator/Operator.cpp
index 20e47f290e..7a2156611a 100644
--- a/src/3rdparty/angle/src/compiler/translator/Operator.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/Operator.cpp
@@ -10,192 +10,376 @@ const char *GetOperatorString(TOperator op)
{
switch (op)
{
- // Note: ops from EOpNull to EOpPrototype can't be handled here.
-
- case EOpNegative: return "-";
- case EOpPositive: return "+";
- case EOpLogicalNot: return "!";
- case EOpVectorLogicalNot: return "not";
- case EOpBitwiseNot: return "~";
-
- case EOpPostIncrement: return "++";
- case EOpPostDecrement: return "--";
- case EOpPreIncrement: return "++";
- case EOpPreDecrement: return "--";
-
- case EOpAdd: return "+";
- case EOpSub: return "-";
- case EOpMul: return "*";
- case EOpDiv: return "/";
- case EOpIMod: return "%";
- case EOpEqual: return "==";
- case EOpNotEqual: return "!=";
- case EOpVectorEqual: return "equal";
- case EOpVectorNotEqual: return "notEqual";
- case EOpLessThan: return "<";
- case EOpGreaterThan: return ">";
- case EOpLessThanEqual: return "<=";
- case EOpGreaterThanEqual: return ">=";
- case EOpComma: return ",";
-
- // Fall-through.
- case EOpVectorTimesScalar:
- case EOpVectorTimesMatrix:
- case EOpMatrixTimesVector:
- case EOpMatrixTimesScalar: return "*";
-
- case EOpLogicalOr: return "||";
- case EOpLogicalXor: return "^^";
- case EOpLogicalAnd: return "&&";
-
- case EOpBitShiftLeft: return "<<";
- case EOpBitShiftRight: return ">>";
-
- case EOpBitwiseAnd: return "&";
- case EOpBitwiseXor: return "^";
- case EOpBitwiseOr: return "|";
-
- // Fall-through.
- case EOpIndexDirect:
- case EOpIndexIndirect: return "[]";
-
- case EOpIndexDirectStruct:
- case EOpIndexDirectInterfaceBlock: return ".";
-
- case EOpVectorSwizzle: return ".";
-
- case EOpRadians: return "radians";
- case EOpDegrees: return "degrees";
- case EOpSin: return "sin";
- case EOpCos: return "cos";
- case EOpTan: return "tan";
- case EOpAsin: return "asin";
- case EOpAcos: return "acos";
- case EOpAtan: return "atan";
-
- case EOpSinh: return "sinh";
- case EOpCosh: return "cosh";
- case EOpTanh: return "tanh";
- case EOpAsinh: return "asinh";
- case EOpAcosh: return "acosh";
- case EOpAtanh: return "atanh";
-
- case EOpPow: return "pow";
- case EOpExp: return "exp";
- case EOpLog: return "log";
- case EOpExp2: return "exp2";
- case EOpLog2: return "log2";
- case EOpSqrt: return "sqrt";
- case EOpInverseSqrt: return "inversesqrt";
-
- case EOpAbs: return "abs";
- case EOpSign: return "sign";
- case EOpFloor: return "floor";
- case EOpTrunc: return "trunc";
- case EOpRound: return "round";
- case EOpRoundEven: return "roundEven";
- case EOpCeil: return "ceil";
- case EOpFract: return "fract";
- case EOpMod: return "mod";
- case EOpModf: return "modf";
- case EOpMin: return "min";
- case EOpMax: return "max";
- case EOpClamp: return "clamp";
- case EOpMix: return "mix";
- case EOpStep: return "step";
- case EOpSmoothStep: return "smoothstep";
- case EOpIsNan: return "isnan";
- case EOpIsInf: return "isinf";
-
- case EOpFloatBitsToInt: return "floatBitsToInt";
- case EOpFloatBitsToUint: return "floatBitsToUint";
- case EOpIntBitsToFloat: return "intBitsToFloat";
- case EOpUintBitsToFloat: return "uintBitsToFloat";
-
- case EOpPackSnorm2x16: return "packSnorm2x16";
- case EOpPackUnorm2x16: return "packUnorm2x16";
- case EOpPackHalf2x16: return "packHalf2x16";
- case EOpUnpackSnorm2x16: return "unpackSnorm2x16";
- case EOpUnpackUnorm2x16: return "unpackUnorm2x16";
- case EOpUnpackHalf2x16: return "unpackHalf2x16";
-
- case EOpLength: return "length";
- case EOpDistance: return "distance";
- case EOpDot: return "dot";
- case EOpCross: return "cross";
- case EOpNormalize: return "normalize";
- case EOpFaceForward: return "faceforward";
- case EOpReflect: return "reflect";
- case EOpRefract: return "refract";
-
- case EOpDFdx: return "dFdx";
- case EOpDFdy: return "dFdy";
- case EOpFwidth: return "fwidth";
-
- case EOpMatrixTimesMatrix: return "*";
-
- case EOpOuterProduct: return "outerProduct";
- case EOpTranspose: return "transpose";
- case EOpDeterminant: return "determinant";
- case EOpInverse: return "inverse";
-
- case EOpAny: return "any";
- case EOpAll: return "all";
-
- case EOpKill: return "kill";
- case EOpReturn: return "return";
- case EOpBreak: return "break";
- case EOpContinue: return "continue";
-
- case EOpConstructInt: return "int";
- case EOpConstructUInt: return "uint";
- case EOpConstructBool: return "bool";
- case EOpConstructFloat: return "float";
- case EOpConstructVec2: return "vec2";
- case EOpConstructVec3: return "vec3";
- case EOpConstructVec4: return "vec4";
- case EOpConstructBVec2: return "bvec2";
- case EOpConstructBVec3: return "bvec3";
- case EOpConstructBVec4: return "bvec4";
- case EOpConstructIVec2: return "ivec2";
- case EOpConstructIVec3: return "ivec3";
- case EOpConstructIVec4: return "ivec4";
- case EOpConstructUVec2: return "uvec2";
- case EOpConstructUVec3: return "uvec3";
- case EOpConstructUVec4: return "uvec4";
- case EOpConstructMat2: return "mat2";
- case EOpConstructMat2x3: return "mat2x3";
- case EOpConstructMat2x4: return "mat2x4";
- case EOpConstructMat3x2: return "mat3x2";
- case EOpConstructMat3: return "mat3";
- case EOpConstructMat3x4: return "mat3x4";
- case EOpConstructMat4x2: return "mat4x2";
- case EOpConstructMat4x3: return "mat4x3";
- case EOpConstructMat4: return "mat4";
- // Note: EOpConstructStruct can't be handled here
-
- case EOpAssign: return "=";
- case EOpInitialize: return "=";
- case EOpAddAssign: return "+=";
- case EOpSubAssign: return "-=";
-
- // Fall-through.
- case EOpMulAssign:
- case EOpVectorTimesMatrixAssign:
- case EOpVectorTimesScalarAssign:
- case EOpMatrixTimesScalarAssign:
- case EOpMatrixTimesMatrixAssign: return "*=";
-
- case EOpDivAssign: return "/=";
- case EOpIModAssign: return "%=";
- case EOpBitShiftLeftAssign: return "<<=";
- case EOpBitShiftRightAssign: return ">>=";
- case EOpBitwiseAndAssign: return "&=";
- case EOpBitwiseXorAssign: return "^=";
- case EOpBitwiseOrAssign: return "|=";
-
- default: break;
+ // Note: EOpNull and EOpCall* can't be handled here.
+
+ case EOpNegative:
+ return "-";
+ case EOpPositive:
+ return "+";
+ case EOpLogicalNot:
+ return "!";
+ case EOpBitwiseNot:
+ return "~";
+
+ case EOpPostIncrement:
+ return "++";
+ case EOpPostDecrement:
+ return "--";
+ case EOpPreIncrement:
+ return "++";
+ case EOpPreDecrement:
+ return "--";
+
+ case EOpArrayLength:
+ return ".length()";
+
+ case EOpAdd:
+ return "+";
+ case EOpSub:
+ return "-";
+ case EOpMul:
+ return "*";
+ case EOpDiv:
+ return "/";
+ case EOpIMod:
+ return "%";
+
+ case EOpEqual:
+ return "==";
+ case EOpNotEqual:
+ return "!=";
+ case EOpLessThan:
+ return "<";
+ case EOpGreaterThan:
+ return ">";
+ case EOpLessThanEqual:
+ return "<=";
+ case EOpGreaterThanEqual:
+ return ">=";
+
+ case EOpEqualComponentWise:
+ return "equal";
+ case EOpNotEqualComponentWise:
+ return "notEqual";
+ case EOpLessThanComponentWise:
+ return "lessThan";
+ case EOpGreaterThanComponentWise:
+ return "greaterThan";
+ case EOpLessThanEqualComponentWise:
+ return "lessThanEqual";
+ case EOpGreaterThanEqualComponentWise:
+ return "greaterThanEqual";
+
+ case EOpComma:
+ return ",";
+
+ // Fall-through.
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
+ return "*";
+
+ case EOpLogicalOr:
+ return "||";
+ case EOpLogicalXor:
+ return "^^";
+ case EOpLogicalAnd:
+ return "&&";
+
+ case EOpBitShiftLeft:
+ return "<<";
+ case EOpBitShiftRight:
+ return ">>";
+
+ case EOpBitwiseAnd:
+ return "&";
+ case EOpBitwiseXor:
+ return "^";
+ case EOpBitwiseOr:
+ return "|";
+
+ // Fall-through.
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ return "[]";
+
+ case EOpIndexDirectStruct:
+ case EOpIndexDirectInterfaceBlock:
+ return ".";
+
+ case EOpRadians:
+ return "radians";
+ case EOpDegrees:
+ return "degrees";
+ case EOpSin:
+ return "sin";
+ case EOpCos:
+ return "cos";
+ case EOpTan:
+ return "tan";
+ case EOpAsin:
+ return "asin";
+ case EOpAcos:
+ return "acos";
+ case EOpAtan:
+ return "atan";
+
+ case EOpSinh:
+ return "sinh";
+ case EOpCosh:
+ return "cosh";
+ case EOpTanh:
+ return "tanh";
+ case EOpAsinh:
+ return "asinh";
+ case EOpAcosh:
+ return "acosh";
+ case EOpAtanh:
+ return "atanh";
+
+ case EOpPow:
+ return "pow";
+ case EOpExp:
+ return "exp";
+ case EOpLog:
+ return "log";
+ case EOpExp2:
+ return "exp2";
+ case EOpLog2:
+ return "log2";
+ case EOpSqrt:
+ return "sqrt";
+ case EOpInverseSqrt:
+ return "inversesqrt";
+
+ case EOpAbs:
+ return "abs";
+ case EOpSign:
+ return "sign";
+ case EOpFloor:
+ return "floor";
+ case EOpTrunc:
+ return "trunc";
+ case EOpRound:
+ return "round";
+ case EOpRoundEven:
+ return "roundEven";
+ case EOpCeil:
+ return "ceil";
+ case EOpFract:
+ return "fract";
+ case EOpMod:
+ return "mod";
+ case EOpModf:
+ return "modf";
+ case EOpMin:
+ return "min";
+ case EOpMax:
+ return "max";
+ case EOpClamp:
+ return "clamp";
+ case EOpMix:
+ return "mix";
+ case EOpStep:
+ return "step";
+ case EOpSmoothStep:
+ return "smoothstep";
+ case EOpIsNan:
+ return "isnan";
+ case EOpIsInf:
+ return "isinf";
+
+ case EOpFloatBitsToInt:
+ return "floatBitsToInt";
+ case EOpFloatBitsToUint:
+ return "floatBitsToUint";
+ case EOpIntBitsToFloat:
+ return "intBitsToFloat";
+ case EOpUintBitsToFloat:
+ return "uintBitsToFloat";
+
+ case EOpFrexp:
+ return "frexp";
+ case EOpLdexp:
+ return "ldexp";
+
+ case EOpPackSnorm2x16:
+ return "packSnorm2x16";
+ case EOpPackUnorm2x16:
+ return "packUnorm2x16";
+ case EOpPackHalf2x16:
+ return "packHalf2x16";
+ case EOpUnpackSnorm2x16:
+ return "unpackSnorm2x16";
+ case EOpUnpackUnorm2x16:
+ return "unpackUnorm2x16";
+ case EOpUnpackHalf2x16:
+ return "unpackHalf2x16";
+
+ case EOpPackUnorm4x8:
+ return "packUnorm4x8";
+ case EOpPackSnorm4x8:
+ return "packSnorm4x8";
+ case EOpUnpackUnorm4x8:
+ return "unpackUnorm4x8";
+ case EOpUnpackSnorm4x8:
+ return "unpackSnorm4x8";
+
+ case EOpLength:
+ return "length";
+ case EOpDistance:
+ return "distance";
+ case EOpDot:
+ return "dot";
+ case EOpCross:
+ return "cross";
+ case EOpNormalize:
+ return "normalize";
+ case EOpFaceforward:
+ return "faceforward";
+ case EOpReflect:
+ return "reflect";
+ case EOpRefract:
+ return "refract";
+
+ case EOpDFdx:
+ return "dFdx";
+ case EOpDFdy:
+ return "dFdy";
+ case EOpFwidth:
+ return "fwidth";
+
+ case EOpMulMatrixComponentWise:
+ return "matrixCompMult";
+ case EOpOuterProduct:
+ return "outerProduct";
+ case EOpTranspose:
+ return "transpose";
+ case EOpDeterminant:
+ return "determinant";
+ case EOpInverse:
+ return "inverse";
+
+ case EOpAny:
+ return "any";
+ case EOpAll:
+ return "all";
+ case EOpLogicalNotComponentWise:
+ return "not";
+
+ case EOpBitfieldExtract:
+ return "bitfieldExtract";
+ case EOpBitfieldInsert:
+ return "bitfieldInsert";
+ case EOpBitfieldReverse:
+ return "bitfieldReverse";
+ case EOpBitCount:
+ return "bitCount";
+ case EOpFindLSB:
+ return "findLSB";
+ case EOpFindMSB:
+ return "findMSB";
+ case EOpUaddCarry:
+ return "uaddCarry";
+ case EOpUsubBorrow:
+ return "usubBorrow";
+ case EOpUmulExtended:
+ return "umulExtended";
+ case EOpImulExtended:
+ return "imulExtended";
+
+ case EOpKill:
+ return "kill";
+ case EOpReturn:
+ return "return";
+ case EOpBreak:
+ return "break";
+ case EOpContinue:
+ return "continue";
+
+ case EOpAssign:
+ return "=";
+ case EOpInitialize:
+ return "=";
+ case EOpAddAssign:
+ return "+=";
+ case EOpSubAssign:
+ return "-=";
+
+ // Fall-through.
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ return "*=";
+
+ case EOpDivAssign:
+ return "/=";
+ case EOpIModAssign:
+ return "%=";
+ case EOpBitShiftLeftAssign:
+ return "<<=";
+ case EOpBitShiftRightAssign:
+ return ">>=";
+ case EOpBitwiseAndAssign:
+ return "&=";
+ case EOpBitwiseXorAssign:
+ return "^=";
+ case EOpBitwiseOrAssign:
+ return "|=";
+ case EOpBarrier:
+ return "barrier";
+ case EOpMemoryBarrier:
+ return "memoryBarrier";
+ case EOpMemoryBarrierAtomicCounter:
+ return "memoryBarrierAtomicCounter";
+ case EOpMemoryBarrierBuffer:
+ return "memoryBarrierBuffer";
+ case EOpMemoryBarrierImage:
+ return "memoryBarrierImage";
+ case EOpMemoryBarrierShared:
+ return "memoryBarrierShared";
+ case EOpGroupMemoryBarrier:
+ return "groupMemoryBarrier";
+
+ case EOpEmitVertex:
+ return "EmitVertex";
+ case EOpEndPrimitive:
+ return "EndPrimitive";
+ default:
+ break;
}
return "";
}
+bool IsAssignment(TOperator op)
+{
+ switch (op)
+ {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ case EOpAssign:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ return true;
+ default:
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/Operator.h b/src/3rdparty/angle/src/compiler/translator/Operator.h
index b0efb8f48b..72f3dbf3f6 100644
--- a/src/3rdparty/angle/src/compiler/translator/Operator.h
+++ b/src/3rdparty/angle/src/compiler/translator/Operator.h
@@ -12,15 +12,21 @@
//
enum TOperator
{
- EOpNull, // if in a node, should only mean a node is still being built
- EOpSequence, // denotes a list of statements, or parameters, etc.
- EOpFunctionCall,
- EOpFunction, // For function definition
- EOpParameters, // an aggregate listing the parameters to a function
+ EOpNull, // if in a node, should only mean a node is still being built
- EOpDeclaration,
- EOpInvariantDeclaration, // Specialized declarations for attributing invariance
- EOpPrototype,
+ // Call a function defined in the AST. This might be a user-defined function or a function
+ // inserted by an AST transformation.
+ EOpCallFunctionInAST,
+
+ // Call an internal helper function with a raw implementation - the implementation can't be
+ // subject to AST transformations. Raw functions have a few constraints to keep them compatible
+ // with AST traversers:
+ // * They should not return arrays.
+ // * They should not have out parameters.
+ EOpCallInternalRawFunction,
+
+ // Call a built-in function like a texture or image function.
+ EOpCallBuiltInFunction,
//
// Unary operators
@@ -29,7 +35,6 @@ enum TOperator
EOpNegative,
EOpPositive,
EOpLogicalNot,
- EOpVectorLogicalNot,
EOpBitwiseNot,
EOpPostIncrement,
@@ -37,8 +42,11 @@ enum TOperator
EOpPreIncrement,
EOpPreDecrement,
+ EOpArrayLength,
+
//
- // binary operations
+ // binary operations (ones with special GLSL syntax are used in TIntermBinary nodes, others in
+ // TIntermAggregate nodes)
//
EOpAdd,
@@ -46,20 +54,28 @@ enum TOperator
EOpMul,
EOpDiv,
EOpIMod,
+
EOpEqual,
EOpNotEqual,
- EOpVectorEqual,
- EOpVectorNotEqual,
EOpLessThan,
EOpGreaterThan,
EOpLessThanEqual,
EOpGreaterThanEqual,
+
+ EOpEqualComponentWise,
+ EOpNotEqualComponentWise,
+ EOpLessThanComponentWise,
+ EOpLessThanEqualComponentWise,
+ EOpGreaterThanComponentWise,
+ EOpGreaterThanEqualComponentWise,
+
EOpComma,
EOpVectorTimesScalar,
EOpVectorTimesMatrix,
EOpMatrixTimesVector,
EOpMatrixTimesScalar,
+ EOpMatrixTimesMatrix,
EOpLogicalOr,
EOpLogicalXor,
@@ -77,10 +93,8 @@ enum TOperator
EOpIndexDirectStruct,
EOpIndexDirectInterfaceBlock,
- EOpVectorSwizzle,
-
//
- // Built-in functions potentially mapped to operators
+ // Built-in functions mapped to operators (either unary or with multiple parameters)
//
EOpRadians,
@@ -131,6 +145,9 @@ enum TOperator
EOpIntBitsToFloat,
EOpUintBitsToFloat,
+ EOpFrexp,
+ EOpLdexp,
+
EOpPackSnorm2x16,
EOpPackUnorm2x16,
EOpPackHalf2x16,
@@ -138,21 +155,25 @@ enum TOperator
EOpUnpackUnorm2x16,
EOpUnpackHalf2x16,
+ EOpPackUnorm4x8,
+ EOpPackSnorm4x8,
+ EOpUnpackUnorm4x8,
+ EOpUnpackSnorm4x8,
+
EOpLength,
EOpDistance,
EOpDot,
EOpCross,
EOpNormalize,
- EOpFaceForward,
+ EOpFaceforward,
EOpReflect,
EOpRefract,
- EOpDFdx, // Fragment only, OES_standard_derivatives extension
- EOpDFdy, // Fragment only, OES_standard_derivatives extension
- EOpFwidth, // Fragment only, OES_standard_derivatives extension
-
- EOpMatrixTimesMatrix,
+ EOpDFdx, // Fragment only, OES_standard_derivatives extension
+ EOpDFdy, // Fragment only, OES_standard_derivatives extension
+ EOpFwidth, // Fragment only, OES_standard_derivatives extension
+ EOpMulMatrixComponentWise,
EOpOuterProduct,
EOpTranspose,
EOpDeterminant,
@@ -160,46 +181,33 @@ enum TOperator
EOpAny,
EOpAll,
+ EOpLogicalNotComponentWise,
+
+ EOpBitfieldExtract,
+ EOpBitfieldInsert,
+ EOpBitfieldReverse,
+ EOpBitCount,
+ EOpFindLSB,
+ EOpFindMSB,
+ EOpUaddCarry,
+ EOpUsubBorrow,
+ EOpUmulExtended,
+ EOpImulExtended,
//
// Branch
//
- EOpKill, // Fragment only
+ EOpKill, // Fragment only
EOpReturn,
EOpBreak,
EOpContinue,
//
- // Constructors
+ // Constructor
//
- EOpConstructInt,
- EOpConstructUInt,
- EOpConstructBool,
- EOpConstructFloat,
- EOpConstructVec2,
- EOpConstructVec3,
- EOpConstructVec4,
- EOpConstructBVec2,
- EOpConstructBVec3,
- EOpConstructBVec4,
- EOpConstructIVec2,
- EOpConstructIVec3,
- EOpConstructIVec4,
- EOpConstructUVec2,
- EOpConstructUVec3,
- EOpConstructUVec4,
- EOpConstructMat2,
- EOpConstructMat2x3,
- EOpConstructMat2x4,
- EOpConstructMat3x2,
- EOpConstructMat3,
- EOpConstructMat3x4,
- EOpConstructMat4x2,
- EOpConstructMat4x3,
- EOpConstructMat4,
- EOpConstructStruct,
+ EOpConstruct,
//
// moves
@@ -222,10 +230,26 @@ enum TOperator
EOpBitShiftRightAssign,
EOpBitwiseAndAssign,
EOpBitwiseXorAssign,
- EOpBitwiseOrAssign
+ EOpBitwiseOrAssign,
+
+ // barriers
+ EOpBarrier,
+ EOpMemoryBarrier,
+ EOpMemoryBarrierAtomicCounter,
+ EOpMemoryBarrierBuffer,
+ EOpMemoryBarrierImage,
+ EOpMemoryBarrierShared,
+ EOpGroupMemoryBarrier,
+
+ // Geometry only
+ EOpEmitVertex,
+ EOpEndPrimitive
};
// Returns the string corresponding to the operator in GLSL
-const char* GetOperatorString(TOperator op);
+const char *GetOperatorString(TOperator op);
+
+// Say whether or not a binary or unary operation changes the value of a variable.
+bool IsAssignment(TOperator op);
#endif // COMPILER_TRANSLATOR_OPERATOR_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputESSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputESSL.cpp
index 77e0a8fb37..50626c91c0 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputESSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/OutputESSL.cpp
@@ -6,20 +6,27 @@
#include "compiler/translator/OutputESSL.h"
+namespace sh
+{
+
TOutputESSL::TOutputESSL(TInfoSinkBase &objSink,
ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction,
NameMap &nameMap,
- TSymbolTable &symbolTable,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
int shaderVersion,
- bool forceHighp)
+ bool forceHighp,
+ ShCompileOptions compileOptions)
: TOutputGLSLBase(objSink,
clampingStrategy,
hashFunction,
nameMap,
symbolTable,
+ shaderType,
shaderVersion,
- SH_ESSL_OUTPUT),
+ SH_ESSL_OUTPUT,
+ compileOptions),
mForceHighp(forceHighp)
{
}
@@ -29,10 +36,12 @@ bool TOutputESSL::writeVariablePrecision(TPrecision precision)
if (precision == EbpUndefined)
return false;
- TInfoSinkBase& out = objSink();
+ TInfoSinkBase &out = objSink();
if (mForceHighp)
out << getPrecisionString(EbpHigh);
else
out << getPrecisionString(precision);
return true;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputESSL.h b/src/3rdparty/angle/src/compiler/translator/OutputESSL.h
index c5a963499e..e0c7bf2ae6 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputESSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/OutputESSL.h
@@ -9,22 +9,29 @@
#include "compiler/translator/OutputGLSLBase.h"
+namespace sh
+{
+
class TOutputESSL : public TOutputGLSLBase
{
-public:
- TOutputESSL(TInfoSinkBase& objSink,
+ public:
+ TOutputESSL(TInfoSinkBase &objSink,
ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction,
- NameMap& nameMap,
- TSymbolTable& symbolTable,
+ NameMap &nameMap,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
int shaderVersion,
- bool forceHighp);
+ bool forceHighp,
+ ShCompileOptions compileOptions);
-protected:
- bool writeVariablePrecision(TPrecision precision) override;
+ protected:
+ bool writeVariablePrecision(TPrecision precision) override;
-private:
+ private:
bool mForceHighp;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_OUTPUTESSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp
index 431425020a..1bad05dab9 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp
@@ -6,20 +6,29 @@
#include "compiler/translator/OutputGLSL.h"
-TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink,
+#include "compiler/translator/Compiler.h"
+
+namespace sh
+{
+
+TOutputGLSL::TOutputGLSL(TInfoSinkBase &objSink,
ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction,
- NameMap& nameMap,
- TSymbolTable& symbolTable,
+ NameMap &nameMap,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
int shaderVersion,
- ShShaderOutput output)
+ ShShaderOutput output,
+ ShCompileOptions compileOptions)
: TOutputGLSLBase(objSink,
clampingStrategy,
hashFunction,
nameMap,
symbolTable,
+ shaderType,
shaderVersion,
- output)
+ output,
+ compileOptions)
{
}
@@ -30,18 +39,18 @@ bool TOutputGLSL::writeVariablePrecision(TPrecision)
void TOutputGLSL::visitSymbol(TIntermSymbol *node)
{
- TInfoSinkBase& out = objSink();
+ TInfoSinkBase &out = objSink();
const TString &symbol = node->getSymbol();
if (symbol == "gl_FragDepthEXT")
{
out << "gl_FragDepth";
}
- else if (symbol == "gl_FragColor" && IsGLSL130OrNewer(getShaderOutput()))
+ else if (symbol == "gl_FragColor" && sh::IsGLSL130OrNewer(getShaderOutput()))
{
out << "webgl_FragColor";
}
- else if (symbol == "gl_FragData" && IsGLSL130OrNewer(getShaderOutput()))
+ else if (symbol == "gl_FragData" && sh::IsGLSL130OrNewer(getShaderOutput()))
{
out << "webgl_FragData";
}
@@ -59,44 +68,43 @@ void TOutputGLSL::visitSymbol(TIntermSymbol *node)
}
}
-TString TOutputGLSL::translateTextureFunction(TString &name)
+TString TOutputGLSL::translateTextureFunction(const TString &name)
{
- static const char *simpleRename[] = {
- "texture2DLodEXT", "texture2DLod",
- "texture2DProjLodEXT", "texture2DProjLod",
- "textureCubeLodEXT", "textureCubeLod",
- "texture2DGradEXT", "texture2DGradARB",
- "texture2DProjGradEXT", "texture2DProjGradARB",
- "textureCubeGradEXT", "textureCubeGradARB",
- NULL, NULL
- };
+ static const char *simpleRename[] = {"texture2DLodEXT",
+ "texture2DLod",
+ "texture2DProjLodEXT",
+ "texture2DProjLod",
+ "textureCubeLodEXT",
+ "textureCubeLod",
+ "texture2DGradEXT",
+ "texture2DGradARB",
+ "texture2DProjGradEXT",
+ "texture2DProjGradARB",
+ "textureCubeGradEXT",
+ "textureCubeGradARB",
+ nullptr,
+ nullptr};
static const char *legacyToCoreRename[] = {
- "texture2D", "texture",
- "texture2DProj", "textureProj",
- "texture2DLod", "textureLod",
- "texture2DProjLod", "textureProjLod",
- "texture2DRect", "texture",
- "textureCube", "texture",
+ "texture2D", "texture", "texture2DProj", "textureProj", "texture2DLod", "textureLod",
+ "texture2DProjLod", "textureProjLod", "texture2DRect", "texture", "textureCube", "texture",
"textureCubeLod", "textureLod",
// Extensions
- "texture2DLodEXT", "textureLod",
- "texture2DProjLodEXT", "textureProjLod",
- "textureCubeLodEXT", "textureLod",
- "texture2DGradEXT", "textureGrad",
- "texture2DProjGradEXT", "textureProjGrad",
- "textureCubeGradEXT", "textureGrad",
- NULL, NULL
- };
- const char **mapping = (IsGLSL130OrNewer(getShaderOutput())) ?
- legacyToCoreRename : simpleRename;
+ "texture2DLodEXT", "textureLod", "texture2DProjLodEXT", "textureProjLod",
+ "textureCubeLodEXT", "textureLod", "texture2DGradEXT", "textureGrad",
+ "texture2DProjGradEXT", "textureProjGrad", "textureCubeGradEXT", "textureGrad", nullptr,
+ nullptr};
+ const char **mapping =
+ (sh::IsGLSL130OrNewer(getShaderOutput())) ? legacyToCoreRename : simpleRename;
- for (int i = 0; mapping[i] != NULL; i += 2)
+ for (int i = 0; mapping[i] != nullptr; i += 2)
{
if (name == mapping[i])
{
- return mapping[i+1];
+ return mapping[i + 1];
}
}
return name;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h
index 9b1aca4eab..c80abec1a6 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h
@@ -9,21 +9,28 @@
#include "compiler/translator/OutputGLSLBase.h"
+namespace sh
+{
+
class TOutputGLSL : public TOutputGLSLBase
{
public:
- TOutputGLSL(TInfoSinkBase& objSink,
+ TOutputGLSL(TInfoSinkBase &objSink,
ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction,
- NameMap& nameMap,
- TSymbolTable& symbolTable,
+ NameMap &nameMap,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
int shaderVersion,
- ShShaderOutput output);
+ ShShaderOutput output,
+ ShCompileOptions compileOptions);
protected:
bool writeVariablePrecision(TPrecision) override;
void visitSymbol(TIntermSymbol *node) override;
- TString translateTextureFunction(TString &name) override;
+ TString translateTextureFunction(const TString &name) override;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_OUTPUTGLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp
index f048b050b7..edaf2ebebf 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp
@@ -6,32 +6,33 @@
#include "compiler/translator/OutputGLSLBase.h"
+#include "angle_gl.h"
#include "common/debug.h"
+#include "common/mathutil.h"
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/util.h"
#include <cfloat>
-namespace
+namespace sh
{
-TString arrayBrackets(const TType &type)
+
+namespace
{
- ASSERT(type.isArray());
- TInfoSinkBase out;
- out << "[" << type.getArraySize() << "]";
- return TString(out.c_str());
-}
bool isSingleStatement(TIntermNode *node)
{
- if (const TIntermAggregate *aggregate = node->getAsAggregate())
+ if (node->getAsFunctionDefinition())
+ {
+ return false;
+ }
+ else if (node->getAsBlock())
{
- return (aggregate->getOp() != EOpFunction) &&
- (aggregate->getOp() != EOpSequence);
+ return false;
}
- else if (const TIntermSelection *selection = node->getAsSelectionNode())
+ else if (node->getAsIfElseNode())
{
- // Ternary operators are usually part of an assignment operator.
- // This handles those rare cases in which they are all by themselves.
- return selection->usesTernaryOperator();
+ return false;
}
else if (node->getAsLoopNode())
{
@@ -48,29 +49,79 @@ bool isSingleStatement(TIntermNode *node)
return true;
}
+class CommaSeparatedListItemPrefixGenerator
+{
+ public:
+ CommaSeparatedListItemPrefixGenerator() : mFirst(true) {}
+ private:
+ bool mFirst;
+
+ friend TInfoSinkBase &operator<<(TInfoSinkBase &out,
+ CommaSeparatedListItemPrefixGenerator &gen);
+};
+
+TInfoSinkBase &operator<<(TInfoSinkBase &out, CommaSeparatedListItemPrefixGenerator &gen)
+{
+ if (gen.mFirst)
+ {
+ gen.mFirst = false;
+ }
+ else
+ {
+ out << ", ";
+ }
+ return out;
+}
+
} // namespace
TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink,
ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction,
NameMap &nameMap,
- TSymbolTable &symbolTable,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
int shaderVersion,
- ShShaderOutput output)
- : TIntermTraverser(true, true, true),
+ ShShaderOutput output,
+ ShCompileOptions compileOptions)
+ : TIntermTraverser(true, true, true, symbolTable),
mObjSink(objSink),
- mDeclaringVariables(false),
+ mDeclaringVariable(false),
mClampingStrategy(clampingStrategy),
mHashFunction(hashFunction),
mNameMap(nameMap),
- mSymbolTable(symbolTable),
+ mShaderType(shaderType),
mShaderVersion(shaderVersion),
- mOutput(output)
+ mOutput(output),
+ mCompileOptions(compileOptions)
+{
+}
+
+void TOutputGLSLBase::writeInvariantQualifier(const TType &type)
{
+ if (!sh::RemoveInvariant(mShaderType, mShaderVersion, mOutput, mCompileOptions))
+ {
+ TInfoSinkBase &out = objSink();
+ out << "invariant ";
+ }
}
-void TOutputGLSLBase::writeTriplet(
- Visit visit, const char *preStr, const char *inStr, const char *postStr)
+void TOutputGLSLBase::writeFloat(TInfoSinkBase &out, float f)
+{
+ if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300)
+ {
+ out << "uintBitsToFloat(" << gl::bitCast<uint32_t>(f) << "u)";
+ }
+ else
+ {
+ out << std::min(FLT_MAX, std::max(-FLT_MAX, f));
+ }
+}
+
+void TOutputGLSLBase::writeTriplet(Visit visit,
+ const char *preStr,
+ const char *inStr,
+ const char *postStr)
{
TInfoSinkBase &out = objSink();
if (visit == PreVisit && preStr)
@@ -81,69 +132,181 @@ void TOutputGLSLBase::writeTriplet(
out << postStr;
}
-void TOutputGLSLBase::writeBuiltInFunctionTriplet(
- Visit visit, const char *preStr, bool useEmulatedFunction)
+void TOutputGLSLBase::writeBuiltInFunctionTriplet(Visit visit,
+ TOperator op,
+ bool useEmulatedFunction)
{
- TString preString = useEmulatedFunction ?
- BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr) : preStr;
- writeTriplet(visit, preString.c_str(), ", ", ")");
+ TInfoSinkBase &out = objSink();
+ if (visit == PreVisit)
+ {
+ const char *opStr(GetOperatorString(op));
+ if (useEmulatedFunction)
+ {
+ BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, opStr);
+ }
+ else
+ {
+ out << opStr;
+ }
+ out << "(";
+ }
+ else
+ {
+ writeTriplet(visit, nullptr, ", ", ")");
+ }
}
-void TOutputGLSLBase::writeLayoutQualifier(const TType &type)
+void TOutputGLSLBase::writeLayoutQualifier(TIntermTyped *variable)
{
- if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn)
+ const TType &type = variable->getType();
+
+ if (!NeedsToWriteLayoutQualifier(type))
+ {
+ return;
+ }
+
+ TInfoSinkBase &out = objSink();
+ const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
+ out << "layout(";
+
+ CommaSeparatedListItemPrefixGenerator listItemPrefix;
+
+ if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
+ IsVarying(type.getQualifier()))
{
- const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
if (layoutQualifier.location >= 0)
{
- TInfoSinkBase &out = objSink();
- out << "layout(location = " << layoutQualifier.location << ") ";
+ out << listItemPrefix << "location = " << layoutQualifier.location;
+ }
+ }
+
+ if (type.getQualifier() == EvqFragmentOut)
+ {
+ if (layoutQualifier.yuv == true)
+ {
+ out << listItemPrefix << "yuv";
+ }
+ }
+
+ if (IsOpaqueType(type.getBasicType()))
+ {
+ if (layoutQualifier.binding >= 0)
+ {
+ out << listItemPrefix << "binding = " << layoutQualifier.binding;
+ }
+ }
+
+ if (IsImage(type.getBasicType()))
+ {
+ if (layoutQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
+ out << listItemPrefix
+ << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
}
}
+
+ if (IsAtomicCounter(type.getBasicType()))
+ {
+ out << listItemPrefix << "offset = " << layoutQualifier.offset;
+ }
+
+ out << ") ";
+}
+
+const char *TOutputGLSLBase::mapQualifierToString(TQualifier qualifier)
+{
+ if (sh::IsGLSL410OrOlder(mOutput) && mShaderVersion >= 300 &&
+ (mCompileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0)
+ {
+ switch (qualifier)
+ {
+ // The return string is consistent with sh::getQualifierString() from
+ // BaseTypes.h minus the "centroid" keyword.
+ case EvqCentroid:
+ return "";
+ case EvqCentroidIn:
+ return "smooth in";
+ case EvqCentroidOut:
+ return "smooth out";
+ default:
+ break;
+ }
+ }
+ if (sh::IsGLSL130OrNewer(mOutput))
+ {
+ switch (qualifier)
+ {
+ case EvqAttribute:
+ return "in";
+ case EvqVaryingIn:
+ return "in";
+ case EvqVaryingOut:
+ return "out";
+ default:
+ break;
+ }
+ }
+ return sh::getQualifierString(qualifier);
}
void TOutputGLSLBase::writeVariableType(const TType &type)
{
- TInfoSinkBase &out = objSink();
+ TQualifier qualifier = type.getQualifier();
+ TInfoSinkBase &out = objSink();
if (type.isInvariant())
{
- out << "invariant ";
+ writeInvariantQualifier(type);
}
if (type.getBasicType() == EbtInterfaceBlock)
{
TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
declareInterfaceBlockLayout(interfaceBlock);
}
- TQualifier qualifier = type.getQualifier();
if (qualifier != EvqTemporary && qualifier != EvqGlobal)
{
- if (IsGLSL130OrNewer(mOutput))
+ const char *qualifierString = mapQualifierToString(qualifier);
+ if (qualifierString && qualifierString[0] != '\0')
{
- 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() << " ";
+ out << qualifierString << " ";
}
}
+
+ const TMemoryQualifier &memoryQualifier = type.getMemoryQualifier();
+ if (memoryQualifier.readonly)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "readonly ";
+ }
+
+ if (memoryQualifier.writeonly)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "writeonly ";
+ }
+
+ if (memoryQualifier.coherent)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "coherent ";
+ }
+
+ if (memoryQualifier.restrictQualifier)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "restrict ";
+ }
+
+ if (memoryQualifier.volatileQualifier)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "volatile ";
+ }
+
// Declare the struct if we have not done so already.
if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct()))
{
- TStructure *structure = type.getStruct();
+ const TStructure *structure = type.getStruct();
declareStruct(structure);
@@ -168,20 +331,18 @@ void TOutputGLSLBase::writeVariableType(const TType &type)
void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence &args)
{
TInfoSinkBase &out = objSink();
- for (TIntermSequence::const_iterator iter = args.begin();
- iter != args.end(); ++iter)
+ for (TIntermSequence::const_iterator iter = args.begin(); iter != args.end(); ++iter)
{
const TIntermSymbol *arg = (*iter)->getAsSymbolNode();
- ASSERT(arg != NULL);
+ ASSERT(arg != nullptr);
const TType &type = arg->getType();
writeVariableType(type);
- const TString &name = arg->getSymbol();
- if (!name.empty())
- out << " " << hashName(name);
+ if (!arg->getName().getString().empty())
+ out << " " << hashName(arg->getName());
if (type.isArray())
- out << arrayBrackets(type);
+ out << ArrayString(type);
// Put a comma if this is not the last argument.
if (iter != args.end() - 1)
@@ -189,21 +350,21 @@ void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence &args)
}
}
-const TConstantUnion *TOutputGLSLBase::writeConstantUnion(
- const TType &type, const TConstantUnion *pConstUnion)
+const TConstantUnion *TOutputGLSLBase::writeConstantUnion(const TType &type,
+ const TConstantUnion *pConstUnion)
{
TInfoSinkBase &out = objSink();
if (type.getBasicType() == EbtStruct)
{
const TStructure *structure = type.getStruct();
- out << hashName(structure->name()) << "(";
+ out << hashName(TName(structure->name())) << "(";
const TFieldList &fields = structure->fields();
for (size_t i = 0; i < fields.size(); ++i)
{
const TType *fieldType = fields[i]->type();
- ASSERT(fieldType != NULL);
+ ASSERT(fieldType != nullptr);
pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
if (i != fields.size() - 1)
out << ", ";
@@ -212,7 +373,7 @@ const TConstantUnion *TOutputGLSLBase::writeConstantUnion(
}
else
{
- size_t size = type.getObjectSize();
+ size_t size = type.getObjectSize();
bool writeType = size > 1;
if (writeType)
out << getTypeName(type) << "(";
@@ -220,19 +381,23 @@ const TConstantUnion *TOutputGLSLBase::writeConstantUnion(
{
switch (pConstUnion->getType())
{
- case EbtFloat:
- out << std::min(FLT_MAX, std::max(-FLT_MAX, pConstUnion->getFConst()));
- break;
- case EbtInt:
- out << pConstUnion->getIConst();
- break;
- case EbtUInt:
- out << pConstUnion->getUConst() << "u";
- break;
- case EbtBool:
- out << pConstUnion->getBConst();
- break;
- default: UNREACHABLE();
+ case EbtFloat:
+ writeFloat(out, pConstUnion->getFConst());
+ break;
+ case EbtInt:
+ out << pConstUnion->getIConst();
+ break;
+ case EbtUInt:
+ out << pConstUnion->getUConst() << "u";
+ break;
+ case EbtBool:
+ out << pConstUnion->getBConst();
+ break;
+ case EbtYuvCscStandardEXT:
+ out << getYuvCscStandardEXTString(pConstUnion->getYuvCscStandardEXTConst());
+ break;
+ default:
+ UNREACHABLE();
}
if (i != size - 1)
out << ", ";
@@ -243,20 +408,20 @@ const TConstantUnion *TOutputGLSLBase::writeConstantUnion(
return pConstUnion;
}
-void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type, const char *constructorBaseType)
+void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type)
{
TInfoSinkBase &out = objSink();
if (visit == PreVisit)
{
if (type.isArray())
{
- out << constructorBaseType;
- out << arrayBrackets(type);
+ out << getTypeName(type);
+ out << ArrayString(type);
out << "(";
}
else
{
- out << constructorBaseType << "(";
+ out << getTypeName(type) << "(";
}
}
else
@@ -268,13 +433,10 @@ void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type, co
void TOutputGLSLBase::visitSymbol(TIntermSymbol *node)
{
TInfoSinkBase &out = objSink();
- if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node))
- out << mLoopUnrollStack.getLoopIndexValue(node);
- else
- out << hashVariableName(node->getSymbol());
+ out << hashVariableName(node->getName());
- if (mDeclaringVariables && node->getType().isArray())
- out << arrayBrackets(node->getType());
+ if (mDeclaringVariable && node->getType().isArray())
+ out << ArrayString(node->getType());
}
void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node)
@@ -282,241 +444,246 @@ void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node)
writeConstantUnion(node->getType(), node->getUnionArrayPointer());
}
+bool TOutputGLSLBase::visitSwizzle(Visit visit, TIntermSwizzle *node)
+{
+ TInfoSinkBase &out = objSink();
+ if (visit == PostVisit)
+ {
+ out << ".";
+ node->writeOffsetsAsXYZW(&out);
+ }
+ return true;
+}
+
bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node)
{
bool visitChildren = true;
TInfoSinkBase &out = objSink();
switch (node->getOp())
{
- case EOpInitialize:
- if (visit == InVisit)
- {
- out << " = ";
- // RHS of initialize is not being declared.
- mDeclaringVariables = false;
- }
- break;
- case EOpAssign:
- writeTriplet(visit, "(", " = ", ")");
- break;
- case EOpAddAssign:
- writeTriplet(visit, "(", " += ", ")");
- break;
- case EOpSubAssign:
- writeTriplet(visit, "(", " -= ", ")");
- break;
- case EOpDivAssign:
- writeTriplet(visit, "(", " /= ", ")");
- break;
- case EOpIModAssign:
- writeTriplet(visit, "(", " %= ", ")");
- break;
- // Notice the fall-through.
- case EOpMulAssign:
- case EOpVectorTimesMatrixAssign:
- case EOpVectorTimesScalarAssign:
- case EOpMatrixTimesScalarAssign:
- case EOpMatrixTimesMatrixAssign:
- writeTriplet(visit, "(", " *= ", ")");
- break;
- case EOpBitShiftLeftAssign:
- writeTriplet(visit, "(", " <<= ", ")");
- break;
- case EOpBitShiftRightAssign:
- writeTriplet(visit, "(", " >>= ", ")");
- break;
- case EOpBitwiseAndAssign:
- writeTriplet(visit, "(", " &= ", ")");
- break;
- case EOpBitwiseXorAssign:
- writeTriplet(visit, "(", " ^= ", ")");
- break;
- case EOpBitwiseOrAssign:
- writeTriplet(visit, "(", " |= ", ")");
- break;
-
- case EOpIndexDirect:
- writeTriplet(visit, NULL, "[", "]");
- break;
- case EOpIndexIndirect:
- if (node->getAddIndexClamp())
- {
+ case EOpComma:
+ writeTriplet(visit, "(", ", ", ")");
+ break;
+ case EOpInitialize:
if (visit == InVisit)
{
- if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
- out << "[int(clamp(float(";
- else
- out << "[webgl_int_clamp(";
+ out << " = ";
+ // RHS of initialize is not being declared.
+ mDeclaringVariable = false;
}
- else if (visit == PostVisit)
- {
- int maxSize;
- TIntermTyped *left = node->getLeft();
- TType leftType = left->getType();
+ break;
+ case EOpAssign:
+ writeTriplet(visit, "(", " = ", ")");
+ break;
+ case EOpAddAssign:
+ writeTriplet(visit, "(", " += ", ")");
+ break;
+ case EOpSubAssign:
+ writeTriplet(visit, "(", " -= ", ")");
+ break;
+ case EOpDivAssign:
+ writeTriplet(visit, "(", " /= ", ")");
+ break;
+ case EOpIModAssign:
+ writeTriplet(visit, "(", " %= ", ")");
+ break;
+ // Notice the fall-through.
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ writeTriplet(visit, "(", " *= ", ")");
+ break;
+ case EOpBitShiftLeftAssign:
+ writeTriplet(visit, "(", " <<= ", ")");
+ break;
+ case EOpBitShiftRightAssign:
+ writeTriplet(visit, "(", " >>= ", ")");
+ break;
+ case EOpBitwiseAndAssign:
+ writeTriplet(visit, "(", " &= ", ")");
+ break;
+ case EOpBitwiseXorAssign:
+ writeTriplet(visit, "(", " ^= ", ")");
+ break;
+ case EOpBitwiseOrAssign:
+ writeTriplet(visit, "(", " |= ", ")");
+ break;
- if (left->isArray())
+ case EOpIndexDirect:
+ writeTriplet(visit, nullptr, "[", "]");
+ break;
+ case EOpIndexIndirect:
+ if (node->getAddIndexClamp())
+ {
+ if (visit == InVisit)
{
- // The shader will fail validation if the array length is not > 0.
- maxSize = leftType.getArraySize() - 1;
+ if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
+ out << "[int(clamp(float(";
+ else
+ out << "[webgl_int_clamp(";
}
- else
+ else if (visit == PostVisit)
{
- maxSize = leftType.getNominalSize() - 1;
+ TIntermTyped *left = node->getLeft();
+ TType leftType = left->getType();
+
+ if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
+ out << "), 0.0, float(";
+ else
+ out << ", 0, ";
+
+ if (leftType.isUnsizedArray())
+ {
+ // For runtime-sized arrays in ESSL 3.10 we need to call the length method
+ // to get the length to clamp against. See ESSL 3.10 section 4.1.9. Note
+ // that a runtime-sized array expression is guaranteed not to have side
+ // effects, so it's fine to add the expression to the output twice.
+ ASSERT(mShaderVersion >= 310);
+ ASSERT(!left->hasSideEffects());
+ left->traverse(this);
+ out << ".length() - 1";
+ }
+ else
+ {
+ int maxSize;
+ if (leftType.isArray())
+ {
+ maxSize = static_cast<int>(leftType.getOutermostArraySize()) - 1;
+ }
+ else
+ {
+ maxSize = leftType.getNominalSize() - 1;
+ }
+ out << maxSize;
+ }
+ if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
+ out << ")))]";
+ else
+ out << ")]";
}
-
- if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
- out << "), 0.0, float(" << maxSize << ")))]";
- else
- out << ", 0, " << maxSize << ")]";
}
- }
- else
- {
- writeTriplet(visit, NULL, "[", "]");
- }
- break;
- case EOpIndexDirectStruct:
- if (visit == InVisit)
- {
- // Here we are writing out "foo.bar", where "foo" is struct
- // and "bar" is field. In AST, it is represented as a binary
- // node, where left child represents "foo" and right child "bar".
- // The node itself represents ".". The struct field "bar" is
- // actually stored as an index into TStructure::fields.
- out << ".";
- const TStructure *structure = node->getLeft()->getType().getStruct();
- const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
- const TField *field = structure->fields()[index->getIConst(0)];
-
- TString fieldName = field->name();
- if (!mSymbolTable.findBuiltIn(structure->name(), mShaderVersion))
- fieldName = hashName(fieldName);
-
- out << fieldName;
- visitChildren = false;
- }
- break;
- case EOpIndexDirectInterfaceBlock:
- if (visit == InVisit)
- {
- out << ".";
- const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock();
- const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
- const TField *field = interfaceBlock->fields()[index->getIConst(0)];
-
- TString fieldName = field->name();
- ASSERT(!mSymbolTable.findBuiltIn(interfaceBlock->name(), mShaderVersion));
- fieldName = hashName(fieldName);
-
- out << fieldName;
- visitChildren = false;
- }
- break;
- case EOpVectorSwizzle:
- if (visit == InVisit)
- {
- out << ".";
- TIntermAggregate *rightChild = node->getRight()->getAsAggregate();
- TIntermSequence *sequence = rightChild->getSequence();
- for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); ++sit)
+ else
+ {
+ writeTriplet(visit, nullptr, "[", "]");
+ }
+ break;
+ case EOpIndexDirectStruct:
+ if (visit == InVisit)
{
- TIntermConstantUnion *element = (*sit)->getAsConstantUnion();
- ASSERT(element->getBasicType() == EbtInt);
- ASSERT(element->getNominalSize() == 1);
- const TConstantUnion& data = element->getUnionArrayPointer()[0];
- ASSERT(data.getType() == EbtInt);
- switch (data.getIConst())
+ // Here we are writing out "foo.bar", where "foo" is struct
+ // and "bar" is field. In AST, it is represented as a binary
+ // node, where left child represents "foo" and right child "bar".
+ // The node itself represents ".". The struct field "bar" is
+ // actually stored as an index into TStructure::fields.
+ out << ".";
+ const TStructure *structure = node->getLeft()->getType().getStruct();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = structure->fields()[index->getIConst(0)];
+
+ TString fieldName = field->name();
+ if (!mSymbolTable->findBuiltIn(structure->name(), mShaderVersion))
+ fieldName = hashName(TName(fieldName));
+
+ out << fieldName;
+ visitChildren = false;
+ }
+ break;
+ case EOpIndexDirectInterfaceBlock:
+ if (visit == InVisit)
+ {
+ out << ".";
+ const TInterfaceBlock *interfaceBlock =
+ node->getLeft()->getType().getInterfaceBlock();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = interfaceBlock->fields()[index->getIConst(0)];
+
+ TString fieldName = field->name();
+ if (!mSymbolTable->findBuiltIn(interfaceBlock->name(), mShaderVersion))
{
- case 0:
- out << "x";
- break;
- case 1:
- out << "y";
- break;
- case 2:
- out << "z";
- break;
- case 3:
- out << "w";
- break;
- default:
- UNREACHABLE();
+ fieldName = hashName(TName(fieldName));
}
+ else
+ {
+ ASSERT(interfaceBlock->name() == "gl_PerVertex");
+ }
+
+ out << fieldName;
+ visitChildren = false;
}
- visitChildren = false;
- }
- break;
-
- case EOpAdd:
- writeTriplet(visit, "(", " + ", ")");
- break;
- case EOpSub:
- writeTriplet(visit, "(", " - ", ")");
- break;
- case EOpMul:
- writeTriplet(visit, "(", " * ", ")");
- break;
- case EOpDiv:
- writeTriplet(visit, "(", " / ", ")");
- break;
- case EOpIMod:
- writeTriplet(visit, "(", " % ", ")");
- break;
- case EOpBitShiftLeft:
- writeTriplet(visit, "(", " << ", ")");
- break;
- case EOpBitShiftRight:
- writeTriplet(visit, "(", " >> ", ")");
- break;
- case EOpBitwiseAnd:
- writeTriplet(visit, "(", " & ", ")");
- break;
- case EOpBitwiseXor:
- writeTriplet(visit, "(", " ^ ", ")");
- break;
- case EOpBitwiseOr:
- writeTriplet(visit, "(", " | ", ")");
- break;
-
- case EOpEqual:
- writeTriplet(visit, "(", " == ", ")");
- break;
- case EOpNotEqual:
- writeTriplet(visit, "(", " != ", ")");
- break;
- case EOpLessThan:
- writeTriplet(visit, "(", " < ", ")");
- break;
- case EOpGreaterThan:
- writeTriplet(visit, "(", " > ", ")");
- break;
- case EOpLessThanEqual:
- writeTriplet(visit, "(", " <= ", ")");
- break;
- case EOpGreaterThanEqual:
- writeTriplet(visit, "(", " >= ", ")");
- break;
-
- // Notice the fall-through.
- case EOpVectorTimesScalar:
- case EOpVectorTimesMatrix:
- case EOpMatrixTimesVector:
- case EOpMatrixTimesScalar:
- case EOpMatrixTimesMatrix:
- writeTriplet(visit, "(", " * ", ")");
- break;
-
- case EOpLogicalOr:
- writeTriplet(visit, "(", " || ", ")");
- break;
- case EOpLogicalXor:
- writeTriplet(visit, "(", " ^^ ", ")");
- break;
- case EOpLogicalAnd:
- writeTriplet(visit, "(", " && ", ")");
- break;
- default:
- UNREACHABLE();
+ break;
+
+ case EOpAdd:
+ writeTriplet(visit, "(", " + ", ")");
+ break;
+ case EOpSub:
+ writeTriplet(visit, "(", " - ", ")");
+ break;
+ case EOpMul:
+ writeTriplet(visit, "(", " * ", ")");
+ break;
+ case EOpDiv:
+ writeTriplet(visit, "(", " / ", ")");
+ break;
+ case EOpIMod:
+ writeTriplet(visit, "(", " % ", ")");
+ break;
+ case EOpBitShiftLeft:
+ writeTriplet(visit, "(", " << ", ")");
+ break;
+ case EOpBitShiftRight:
+ writeTriplet(visit, "(", " >> ", ")");
+ break;
+ case EOpBitwiseAnd:
+ writeTriplet(visit, "(", " & ", ")");
+ break;
+ case EOpBitwiseXor:
+ writeTriplet(visit, "(", " ^ ", ")");
+ break;
+ case EOpBitwiseOr:
+ writeTriplet(visit, "(", " | ", ")");
+ break;
+
+ case EOpEqual:
+ writeTriplet(visit, "(", " == ", ")");
+ break;
+ case EOpNotEqual:
+ writeTriplet(visit, "(", " != ", ")");
+ break;
+ case EOpLessThan:
+ writeTriplet(visit, "(", " < ", ")");
+ break;
+ case EOpGreaterThan:
+ writeTriplet(visit, "(", " > ", ")");
+ break;
+ case EOpLessThanEqual:
+ writeTriplet(visit, "(", " <= ", ")");
+ break;
+ case EOpGreaterThanEqual:
+ writeTriplet(visit, "(", " >= ", ")");
+ break;
+
+ // Notice the fall-through.
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
+ writeTriplet(visit, "(", " * ", ")");
+ break;
+
+ case EOpLogicalOr:
+ writeTriplet(visit, "(", " || ", ")");
+ break;
+ case EOpLogicalXor:
+ writeTriplet(visit, "(", " ^^ ", ")");
+ break;
+ case EOpLogicalAnd:
+ writeTriplet(visit, "(", " && ", ")");
+ break;
+ default:
+ UNREACHABLE();
}
return visitChildren;
@@ -529,237 +696,148 @@ bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary *node)
switch (node->getOp())
{
- case EOpNegative: preString = "(-"; break;
- case EOpPositive: preString = "(+"; break;
- case EOpVectorLogicalNot: preString = "not("; break;
- case EOpLogicalNot: preString = "(!"; break;
- case EOpBitwiseNot: preString = "(~"; break;
-
- case EOpPostIncrement: preString = "("; postString = "++)"; break;
- case EOpPostDecrement: preString = "("; postString = "--)"; break;
- case EOpPreIncrement: preString = "(++"; break;
- case EOpPreDecrement: preString = "(--"; break;
-
- case EOpRadians:
- preString = "radians(";
- break;
- case EOpDegrees:
- preString = "degrees(";
- break;
- case EOpSin:
- preString = "sin(";
- break;
- case EOpCos:
- preString = "cos(";
- break;
- case EOpTan:
- preString = "tan(";
- break;
- case EOpAsin:
- preString = "asin(";
- break;
- case EOpAcos:
- preString = "acos(";
- break;
- case EOpAtan:
- preString = "atan(";
- break;
-
- case EOpSinh:
- preString = "sinh(";
- break;
- case EOpCosh:
- preString = "cosh(";
- break;
- case EOpTanh:
- preString = "tanh(";
- break;
- case EOpAsinh:
- preString = "asinh(";
- break;
- case EOpAcosh:
- preString = "acosh(";
- break;
- case EOpAtanh:
- preString = "atanh(";
- break;
-
- case EOpExp:
- preString = "exp(";
- break;
- case EOpLog:
- preString = "log(";
- break;
- case EOpExp2:
- preString = "exp2(";
- break;
- case EOpLog2:
- preString = "log2(";
- break;
- case EOpSqrt:
- preString = "sqrt(";
- break;
- case EOpInverseSqrt:
- preString = "inversesqrt(";
- break;
-
- case EOpAbs:
- preString = "abs(";
- break;
- case EOpSign:
- preString = "sign(";
- break;
- case EOpFloor:
- preString = "floor(";
- break;
- case EOpTrunc:
- preString = "trunc(";
- break;
- case EOpRound:
- preString = "round(";
- break;
- case EOpRoundEven:
- preString = "roundEven(";
- break;
- case EOpCeil:
- preString = "ceil(";
- break;
- case EOpFract:
- preString = "fract(";
- break;
- case EOpIsNan:
- preString = "isnan(";
- break;
- case EOpIsInf:
- preString = "isinf(";
- break;
-
- case EOpFloatBitsToInt:
- preString = "floatBitsToInt(";
- break;
- case EOpFloatBitsToUint:
- preString = "floatBitsToUint(";
- break;
- case EOpIntBitsToFloat:
- preString = "intBitsToFloat(";
- break;
- case EOpUintBitsToFloat:
- preString = "uintBitsToFloat(";
- break;
-
- case EOpPackSnorm2x16:
- preString = "packSnorm2x16(";
- break;
- case EOpPackUnorm2x16:
- preString = "packUnorm2x16(";
- break;
- case EOpPackHalf2x16:
- preString = "packHalf2x16(";
- break;
- case EOpUnpackSnorm2x16:
- preString = "unpackSnorm2x16(";
- break;
- case EOpUnpackUnorm2x16:
- preString = "unpackUnorm2x16(";
- break;
- case EOpUnpackHalf2x16:
- preString = "unpackHalf2x16(";
- break;
-
- case EOpLength:
- preString = "length(";
- break;
- case EOpNormalize:
- preString = "normalize(";
- break;
-
- case EOpDFdx:
- preString = "dFdx(";
- break;
- case EOpDFdy:
- preString = "dFdy(";
- break;
- case EOpFwidth:
- preString = "fwidth(";
- break;
-
- case EOpTranspose:
- preString = "transpose(";
- break;
- case EOpDeterminant:
- preString = "determinant(";
- break;
- case EOpInverse:
- preString = "inverse(";
- break;
-
- case EOpAny:
- preString = "any(";
- break;
- case EOpAll:
- preString = "all(";
- break;
-
- default:
- UNREACHABLE();
+ case EOpNegative:
+ preString = "(-";
+ break;
+ case EOpPositive:
+ preString = "(+";
+ break;
+ case EOpLogicalNot:
+ preString = "(!";
+ break;
+ case EOpBitwiseNot:
+ preString = "(~";
+ break;
+
+ case EOpPostIncrement:
+ preString = "(";
+ postString = "++)";
+ break;
+ case EOpPostDecrement:
+ preString = "(";
+ postString = "--)";
+ break;
+ case EOpPreIncrement:
+ preString = "(++";
+ break;
+ case EOpPreDecrement:
+ preString = "(--";
+ break;
+ case EOpArrayLength:
+ preString = "((";
+ postString = ").length())";
+ break;
+
+ case EOpRadians:
+ case EOpDegrees:
+ case EOpSin:
+ case EOpCos:
+ case EOpTan:
+ case EOpAsin:
+ case EOpAcos:
+ case EOpAtan:
+ case EOpSinh:
+ case EOpCosh:
+ case EOpTanh:
+ case EOpAsinh:
+ case EOpAcosh:
+ case EOpAtanh:
+ case EOpExp:
+ case EOpLog:
+ case EOpExp2:
+ case EOpLog2:
+ case EOpSqrt:
+ case EOpInverseSqrt:
+ case EOpAbs:
+ case EOpSign:
+ case EOpFloor:
+ case EOpTrunc:
+ case EOpRound:
+ case EOpRoundEven:
+ case EOpCeil:
+ case EOpFract:
+ case EOpIsNan:
+ case EOpIsInf:
+ case EOpFloatBitsToInt:
+ case EOpFloatBitsToUint:
+ case EOpIntBitsToFloat:
+ case EOpUintBitsToFloat:
+ case EOpPackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackUnorm2x16:
+ case EOpUnpackHalf2x16:
+ case EOpPackUnorm4x8:
+ case EOpPackSnorm4x8:
+ case EOpUnpackUnorm4x8:
+ case EOpUnpackSnorm4x8:
+ case EOpLength:
+ case EOpNormalize:
+ case EOpDFdx:
+ case EOpDFdy:
+ case EOpFwidth:
+ case EOpTranspose:
+ case EOpDeterminant:
+ case EOpInverse:
+ case EOpAny:
+ case EOpAll:
+ case EOpLogicalNotComponentWise:
+ case EOpBitfieldReverse:
+ case EOpBitCount:
+ case EOpFindLSB:
+ case EOpFindMSB:
+ writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction());
+ return true;
+ default:
+ UNREACHABLE();
}
- if (visit == PreVisit && node->getUseEmulatedFunction())
- preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString);
- writeTriplet(visit, preString.c_str(), NULL, postString.c_str());
+ writeTriplet(visit, preString.c_str(), nullptr, postString.c_str());
return true;
}
-bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection *node)
+bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node)
{
TInfoSinkBase &out = objSink();
+ // Notice two brackets at the beginning and end. The outer ones
+ // encapsulate the whole ternary expression. This preserves the
+ // order of precedence when ternary expressions are used in a
+ // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
+ out << "((";
+ node->getCondition()->traverse(this);
+ out << ") ? (";
+ node->getTrueExpression()->traverse(this);
+ out << ") : (";
+ node->getFalseExpression()->traverse(this);
+ out << "))";
+ return false;
+}
- if (node->usesTernaryOperator())
- {
- // Notice two brackets at the beginning and end. The outer ones
- // encapsulate the whole ternary expression. This preserves the
- // order of precedence when ternary expressions are used in a
- // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
- out << "((";
- node->getCondition()->traverse(this);
- out << ") ? (";
- node->getTrueBlock()->traverse(this);
- out << ") : (";
- node->getFalseBlock()->traverse(this);
- out << "))";
- }
- else
- {
- out << "if (";
- node->getCondition()->traverse(this);
- out << ")\n";
+bool TOutputGLSLBase::visitIfElse(Visit visit, TIntermIfElse *node)
+{
+ TInfoSinkBase &out = objSink();
- incrementDepth(node);
- visitCodeBlock(node->getTrueBlock());
+ out << "if (";
+ node->getCondition()->traverse(this);
+ out << ")\n";
- if (node->getFalseBlock())
- {
- out << "else\n";
- visitCodeBlock(node->getFalseBlock());
- }
- decrementDepth();
+ visitCodeBlock(node->getTrueBlock());
+
+ if (node->getFalseBlock())
+ {
+ out << "else\n";
+ visitCodeBlock(node->getFalseBlock());
}
return false;
}
bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node)
{
- if (node->getStatementList())
- {
- writeTriplet(visit, "switch (", ") ", nullptr);
- // The curly braces get written when visiting the statementList aggregate
- }
- else
- {
- // No statementList, so it won't output curly braces
- writeTriplet(visit, "switch (", ") {", "}\n");
- }
+ ASSERT(node->getStatementList());
+ writeTriplet(visit, "switch (", ") ", nullptr);
+ // The curly braces get written when visiting the statementList aggregate
return true;
}
@@ -778,369 +856,208 @@ bool TOutputGLSLBase::visitCase(Visit visit, TIntermCase *node)
}
}
-bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
+bool TOutputGLSLBase::visitBlock(Visit visit, TIntermBlock *node)
{
- bool visitChildren = true;
TInfoSinkBase &out = objSink();
- bool useEmulatedFunction = (visit == PreVisit && node->getUseEmulatedFunction());
- switch (node->getOp())
+ // Scope the blocks except when at the global scope.
+ if (mDepth > 0)
{
- case EOpSequence:
- // Scope the sequences except when at the global scope.
- if (mDepth > 0)
- {
- out << "{\n";
- }
+ out << "{\n";
+ }
- incrementDepth(node);
- for (TIntermSequence::const_iterator iter = node->getSequence()->begin();
- iter != node->getSequence()->end(); ++iter)
- {
- TIntermNode *curNode = *iter;
- ASSERT(curNode != NULL);
- curNode->traverse(this);
+ for (TIntermSequence::const_iterator iter = node->getSequence()->begin();
+ iter != node->getSequence()->end(); ++iter)
+ {
+ TIntermNode *curNode = *iter;
+ ASSERT(curNode != nullptr);
+ curNode->traverse(this);
- if (isSingleStatement(curNode))
- out << ";\n";
- }
- decrementDepth();
+ if (isSingleStatement(curNode))
+ out << ";\n";
+ }
- // Scope the sequences except when at the global scope.
- if (mDepth > 0)
- {
- out << "}\n";
- }
- visitChildren = false;
- break;
- case EOpPrototype:
- // Function declaration.
- ASSERT(visit == PreVisit);
- {
- const TType &type = node->getType();
- writeVariableType(type);
- if (type.isArray())
- out << arrayBrackets(type);
- }
+ // Scope the blocks except when at the global scope.
+ if (mDepth > 0)
+ {
+ out << "}\n";
+ }
+ return false;
+}
- out << " " << hashFunctionNameIfNeeded(node->getNameObj());
+bool TOutputGLSLBase::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ TIntermFunctionPrototype *prototype = node->getFunctionPrototype();
+ prototype->traverse(this);
+ visitCodeBlock(node->getBody());
- out << "(";
- writeFunctionParameters(*(node->getSequence()));
- out << ")";
+ // Fully processed; no need to visit children.
+ return false;
+}
- visitChildren = false;
- break;
- case EOpFunction: {
- // Function definition.
- ASSERT(visit == PreVisit);
- {
- const TType &type = node->getType();
- writeVariableType(type);
- if (type.isArray())
- out << arrayBrackets(type);
- }
+bool TOutputGLSLBase::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
+{
+ TInfoSinkBase &out = objSink();
+ ASSERT(visit == PreVisit);
+ const TIntermSymbol *symbol = node->getSymbol();
+ out << "invariant " << hashVariableName(symbol->getName());
+ return false;
+}
+
+bool TOutputGLSLBase::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
+{
+ TInfoSinkBase &out = objSink();
+ ASSERT(visit == PreVisit);
- out << " " << hashFunctionNameIfNeeded(node->getNameObj());
+ const TType &type = node->getType();
+ writeVariableType(type);
+ if (type.isArray())
+ out << ArrayString(type);
+
+ out << " " << hashFunctionNameIfNeeded(*node->getFunctionSymbolInfo());
+
+ out << "(";
+ writeFunctionParameters(*(node->getSequence()));
+ out << ")";
+
+ return false;
+}
+
+bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ bool visitChildren = true;
+ TInfoSinkBase &out = objSink();
+ switch (node->getOp())
+ {
+ case EOpCallFunctionInAST:
+ case EOpCallInternalRawFunction:
+ case EOpCallBuiltInFunction:
+ // Function call.
+ if (visit == PreVisit)
+ {
+ if (node->getOp() == EOpCallBuiltInFunction)
+ {
+ out << translateTextureFunction(node->getFunctionSymbolInfo()->getName());
+ }
+ else
+ {
+ out << hashFunctionNameIfNeeded(*node->getFunctionSymbolInfo());
+ }
+ out << "(";
+ }
+ else if (visit == InVisit)
+ out << ", ";
+ else
+ out << ")";
+ break;
+ case EOpConstruct:
+ writeConstructorTriplet(visit, node->getType());
+ break;
- incrementDepth(node);
- // Function definition node contains one or two children nodes
- // representing function parameters and function body. The latter
- // is not present in case of empty function bodies.
+ case EOpEqualComponentWise:
+ case EOpNotEqualComponentWise:
+ case EOpLessThanComponentWise:
+ case EOpGreaterThanComponentWise:
+ case EOpLessThanEqualComponentWise:
+ case EOpGreaterThanEqualComponentWise:
+ case EOpMod:
+ case EOpModf:
+ case EOpPow:
+ case EOpAtan:
+ case EOpMin:
+ case EOpMax:
+ case EOpClamp:
+ case EOpMix:
+ case EOpStep:
+ case EOpSmoothStep:
+ case EOpFrexp:
+ case EOpLdexp:
+ case EOpDistance:
+ case EOpDot:
+ case EOpCross:
+ case EOpFaceforward:
+ case EOpReflect:
+ case EOpRefract:
+ case EOpMulMatrixComponentWise:
+ case EOpOuterProduct:
+ case EOpBitfieldExtract:
+ case EOpBitfieldInsert:
+ case EOpUaddCarry:
+ case EOpUsubBorrow:
+ case EOpUmulExtended:
+ case EOpImulExtended:
+ case EOpBarrier:
+ case EOpMemoryBarrier:
+ case EOpMemoryBarrierAtomicCounter:
+ case EOpMemoryBarrierBuffer:
+ case EOpMemoryBarrierImage:
+ case EOpMemoryBarrierShared:
+ case EOpGroupMemoryBarrier:
+ case EOpEmitVertex:
+ case EOpEndPrimitive:
+ writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction());
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return visitChildren;
+}
+
+bool TOutputGLSLBase::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ TInfoSinkBase &out = objSink();
+
+ // Variable declaration.
+ if (visit == PreVisit)
+ {
const TIntermSequence &sequence = *(node->getSequence());
- ASSERT((sequence.size() == 1) || (sequence.size() == 2));
- TIntermSequence::const_iterator seqIter = sequence.begin();
-
- // Traverse function parameters.
- TIntermAggregate *params = (*seqIter)->getAsAggregate();
- ASSERT(params != NULL);
- ASSERT(params->getOp() == EOpParameters);
- params->traverse(this);
-
- // Traverse function body.
- TIntermAggregate *body = ++seqIter != sequence.end() ?
- (*seqIter)->getAsAggregate() : NULL;
- visitCodeBlock(body);
- decrementDepth();
-
- // Fully processed; no need to visit children.
- visitChildren = false;
- break;
- }
- case EOpFunctionCall:
- // Function call.
- if (visit == PreVisit)
- out << hashFunctionNameIfNeeded(node->getNameObj()) << "(";
- else if (visit == InVisit)
- out << ", ";
- else
- out << ")";
- break;
- case EOpParameters:
- // Function parameters.
- ASSERT(visit == PreVisit);
- out << "(";
- writeFunctionParameters(*(node->getSequence()));
- out << ")";
- visitChildren = false;
- break;
- case EOpDeclaration:
- // Variable declaration.
- if (visit == PreVisit)
+ TIntermTyped *variable = sequence.front()->getAsTyped();
+ writeLayoutQualifier(variable);
+ writeVariableType(variable->getType());
+ if (variable->getAsSymbolNode() == nullptr ||
+ !variable->getAsSymbolNode()->getSymbol().empty())
{
- const TIntermSequence &sequence = *(node->getSequence());
- const TIntermTyped *variable = sequence.front()->getAsTyped();
- writeLayoutQualifier(variable->getType());
- writeVariableType(variable->getType());
out << " ";
- mDeclaringVariables = true;
- }
- else if (visit == InVisit)
- {
- out << ", ";
- mDeclaringVariables = true;
- }
- else
- {
- mDeclaringVariables = false;
- }
- break;
- case EOpInvariantDeclaration:
- // Invariant declaration.
- ASSERT(visit == PreVisit);
- {
- const TIntermSequence *sequence = node->getSequence();
- ASSERT(sequence && sequence->size() == 1);
- const TIntermSymbol *symbol = sequence->front()->getAsSymbolNode();
- ASSERT(symbol);
- out << "invariant " << hashVariableName(symbol->getSymbol());
- }
- visitChildren = false;
- break;
- case EOpConstructFloat:
- writeConstructorTriplet(visit, node->getType(), "float");
- break;
- case EOpConstructVec2:
- writeConstructorTriplet(visit, node->getType(), "vec2");
- break;
- case EOpConstructVec3:
- writeConstructorTriplet(visit, node->getType(), "vec3");
- break;
- case EOpConstructVec4:
- writeConstructorTriplet(visit, node->getType(), "vec4");
- break;
- case EOpConstructBool:
- writeConstructorTriplet(visit, node->getType(), "bool");
- break;
- case EOpConstructBVec2:
- writeConstructorTriplet(visit, node->getType(), "bvec2");
- break;
- case EOpConstructBVec3:
- writeConstructorTriplet(visit, node->getType(), "bvec3");
- break;
- case EOpConstructBVec4:
- writeConstructorTriplet(visit, node->getType(), "bvec4");
- break;
- case EOpConstructInt:
- writeConstructorTriplet(visit, node->getType(), "int");
- break;
- case EOpConstructIVec2:
- writeConstructorTriplet(visit, node->getType(), "ivec2");
- break;
- case EOpConstructIVec3:
- writeConstructorTriplet(visit, node->getType(), "ivec3");
- break;
- case EOpConstructIVec4:
- writeConstructorTriplet(visit, node->getType(), "ivec4");
- break;
- case EOpConstructUInt:
- writeConstructorTriplet(visit, node->getType(), "uint");
- break;
- case EOpConstructUVec2:
- writeConstructorTriplet(visit, node->getType(), "uvec2");
- break;
- case EOpConstructUVec3:
- writeConstructorTriplet(visit, node->getType(), "uvec3");
- break;
- case EOpConstructUVec4:
- writeConstructorTriplet(visit, node->getType(), "uvec4");
- break;
- case EOpConstructMat2:
- writeConstructorTriplet(visit, node->getType(), "mat2");
- break;
- case EOpConstructMat2x3:
- writeConstructorTriplet(visit, node->getType(), "mat2x3");
- break;
- case EOpConstructMat2x4:
- writeConstructorTriplet(visit, node->getType(), "mat2x4");
- break;
- case EOpConstructMat3x2:
- writeConstructorTriplet(visit, node->getType(), "mat3x2");
- break;
- case EOpConstructMat3:
- writeConstructorTriplet(visit, node->getType(), "mat3");
- break;
- case EOpConstructMat3x4:
- writeConstructorTriplet(visit, node->getType(), "mat3x4");
- break;
- case EOpConstructMat4x2:
- writeConstructorTriplet(visit, node->getType(), "mat4x2");
- break;
- case EOpConstructMat4x3:
- writeConstructorTriplet(visit, node->getType(), "mat4x3");
- break;
- case EOpConstructMat4:
- writeConstructorTriplet(visit, node->getType(), "mat4");
- break;
- case EOpConstructStruct:
- {
- const TType &type = node->getType();
- ASSERT(type.getBasicType() == EbtStruct);
- TString constructorName = hashName(type.getStruct()->name());
- writeConstructorTriplet(visit, node->getType(), constructorName.c_str());
- break;
}
-
- case EOpOuterProduct:
- writeBuiltInFunctionTriplet(visit, "outerProduct(", useEmulatedFunction);
- break;
-
- case EOpLessThan:
- writeBuiltInFunctionTriplet(visit, "lessThan(", useEmulatedFunction);
- break;
- case EOpGreaterThan:
- writeBuiltInFunctionTriplet(visit, "greaterThan(", useEmulatedFunction);
- break;
- case EOpLessThanEqual:
- writeBuiltInFunctionTriplet(visit, "lessThanEqual(", useEmulatedFunction);
- break;
- case EOpGreaterThanEqual:
- writeBuiltInFunctionTriplet(visit, "greaterThanEqual(", useEmulatedFunction);
- break;
- case EOpVectorEqual:
- writeBuiltInFunctionTriplet(visit, "equal(", useEmulatedFunction);
- break;
- case EOpVectorNotEqual:
- writeBuiltInFunctionTriplet(visit, "notEqual(", useEmulatedFunction);
- break;
- case EOpComma:
- writeTriplet(visit, "(", ", ", ")");
- break;
-
- case EOpMod:
- writeBuiltInFunctionTriplet(visit, "mod(", useEmulatedFunction);
- break;
- case EOpModf:
- writeBuiltInFunctionTriplet(visit, "modf(", useEmulatedFunction);
- break;
- case EOpPow:
- writeBuiltInFunctionTriplet(visit, "pow(", useEmulatedFunction);
- break;
- case EOpAtan:
- writeBuiltInFunctionTriplet(visit, "atan(", useEmulatedFunction);
- break;
- case EOpMin:
- writeBuiltInFunctionTriplet(visit, "min(", useEmulatedFunction);
- break;
- case EOpMax:
- writeBuiltInFunctionTriplet(visit, "max(", useEmulatedFunction);
- break;
- case EOpClamp:
- writeBuiltInFunctionTriplet(visit, "clamp(", useEmulatedFunction);
- break;
- case EOpMix:
- writeBuiltInFunctionTriplet(visit, "mix(", useEmulatedFunction);
- break;
- case EOpStep:
- writeBuiltInFunctionTriplet(visit, "step(", useEmulatedFunction);
- break;
- case EOpSmoothStep:
- writeBuiltInFunctionTriplet(visit, "smoothstep(", useEmulatedFunction);
- break;
- case EOpDistance:
- writeBuiltInFunctionTriplet(visit, "distance(", useEmulatedFunction);
- break;
- case EOpDot:
- writeBuiltInFunctionTriplet(visit, "dot(", useEmulatedFunction);
- break;
- case EOpCross:
- writeBuiltInFunctionTriplet(visit, "cross(", useEmulatedFunction);
- break;
- case EOpFaceForward:
- writeBuiltInFunctionTriplet(visit, "faceforward(", useEmulatedFunction);
- break;
- case EOpReflect:
- writeBuiltInFunctionTriplet(visit, "reflect(", useEmulatedFunction);
- break;
- case EOpRefract:
- writeBuiltInFunctionTriplet(visit, "refract(", useEmulatedFunction);
- break;
- case EOpMul:
- writeBuiltInFunctionTriplet(visit, "matrixCompMult(", useEmulatedFunction);
- break;
-
- default:
+ mDeclaringVariable = true;
+ }
+ else if (visit == InVisit)
+ {
UNREACHABLE();
}
- return visitChildren;
+ else
+ {
+ mDeclaringVariable = false;
+ }
+ return true;
}
bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
{
TInfoSinkBase &out = objSink();
- incrementDepth(node);
-
TLoopType loopType = node->getType();
- // Only for loops can be unrolled
- ASSERT(!node->getUnrollFlag() || loopType == ELoopFor);
-
if (loopType == ELoopFor) // for loop
{
- if (!node->getUnrollFlag())
- {
- out << "for (";
- if (node->getInit())
- node->getInit()->traverse(this);
- out << "; ";
+ out << "for (";
+ if (node->getInit())
+ node->getInit()->traverse(this);
+ out << "; ";
- if (node->getCondition())
- node->getCondition()->traverse(this);
- out << "; ";
+ if (node->getCondition())
+ node->getCondition()->traverse(this);
+ out << "; ";
- if (node->getExpression())
- node->getExpression()->traverse(this);
- out << ")\n";
+ if (node->getExpression())
+ node->getExpression()->traverse(this);
+ out << ")\n";
- visitCodeBlock(node->getBody());
- }
- else
- {
- // Need to put a one-iteration loop here to handle break.
- TIntermSequence *declSeq =
- node->getInit()->getAsAggregate()->getSequence();
- TIntermSymbol *indexSymbol =
- (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
- TString name = hashVariableName(indexSymbol->getSymbol());
- out << "for (int " << name << " = 0; "
- << name << " < 1; "
- << "++" << name << ")\n";
-
- out << "{\n";
- mLoopUnrollStack.push(node);
- while (mLoopUnrollStack.satisfiesLoopCondition())
- {
- visitCodeBlock(node->getBody());
- mLoopUnrollStack.step();
- }
- mLoopUnrollStack.pop();
- out << "}\n";
- }
+ visitCodeBlock(node->getBody());
}
else if (loopType == ELoopWhile) // while loop
{
out << "while (";
- ASSERT(node->getCondition() != NULL);
+ ASSERT(node->getCondition() != nullptr);
node->getCondition()->traverse(this);
out << ")\n";
@@ -1154,13 +1071,11 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
visitCodeBlock(node->getBody());
out << "while (";
- ASSERT(node->getCondition() != NULL);
+ ASSERT(node->getCondition() != nullptr);
node->getCondition()->traverse(this);
out << ");\n";
}
- decrementDepth();
-
// No need to visit children. They have been already processed in
// this function.
return false;
@@ -1170,29 +1085,29 @@ bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch *node)
{
switch (node->getFlowOp())
{
- case EOpKill:
- writeTriplet(visit, "discard", NULL, NULL);
- break;
- case EOpBreak:
- writeTriplet(visit, "break", NULL, NULL);
- break;
- case EOpContinue:
- writeTriplet(visit, "continue", NULL, NULL);
- break;
- case EOpReturn:
- writeTriplet(visit, "return ", NULL, NULL);
- break;
- default:
- UNREACHABLE();
+ case EOpKill:
+ writeTriplet(visit, "discard", nullptr, nullptr);
+ break;
+ case EOpBreak:
+ writeTriplet(visit, "break", nullptr, nullptr);
+ break;
+ case EOpContinue:
+ writeTriplet(visit, "continue", nullptr, nullptr);
+ break;
+ case EOpReturn:
+ writeTriplet(visit, "return ", nullptr, nullptr);
+ break;
+ default:
+ UNREACHABLE();
}
return true;
}
-void TOutputGLSLBase::visitCodeBlock(TIntermNode *node)
+void TOutputGLSLBase::visitCodeBlock(TIntermBlock *node)
{
TInfoSinkBase &out = objSink();
- if (node != NULL)
+ if (node != nullptr)
{
node->traverse(this);
// Single statements not part of a sequence need to be terminated
@@ -1208,76 +1123,41 @@ void TOutputGLSLBase::visitCodeBlock(TIntermNode *node)
TString TOutputGLSLBase::getTypeName(const TType &type)
{
- TInfoSinkBase out;
- if (type.isMatrix())
- {
- out << "mat";
- out << type.getNominalSize();
- if (type.getSecondarySize() != type.getNominalSize())
- {
- out << "x" << type.getSecondarySize();
- }
- }
- else if (type.isVector())
- {
- switch (type.getBasicType())
- {
- case EbtFloat:
- out << "vec";
- break;
- case EbtInt:
- out << "ivec";
- break;
- case EbtBool:
- out << "bvec";
- break;
- case EbtUInt:
- out << "uvec";
- 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());
+ return GetTypeName(type, mHashFunction, &mNameMap);
}
-TString TOutputGLSLBase::hashName(const TString &name)
+TString TOutputGLSLBase::hashName(const TName &name)
{
- if (mHashFunction == NULL || name.empty())
- return name;
- NameMap::const_iterator it = mNameMap.find(name.c_str());
- if (it != mNameMap.end())
- return it->second.c_str();
- TString hashedName = TIntermTraverser::hash(name, mHashFunction);
- mNameMap[name.c_str()] = hashedName.c_str();
- return hashedName;
+ return HashName(name, mHashFunction, &mNameMap);
}
-TString TOutputGLSLBase::hashVariableName(const TString &name)
+TString TOutputGLSLBase::hashVariableName(const TName &name)
{
- if (mSymbolTable.findBuiltIn(name, mShaderVersion) != NULL)
- return name;
+ if (mSymbolTable->findBuiltIn(name.getString(), mShaderVersion) != nullptr ||
+ name.getString().substr(0, 3) == "gl_")
+ {
+ if (mCompileOptions & SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM &&
+ name.getString() == "gl_ViewID_OVR")
+ {
+ TName uniformName(TString("ViewID_OVR"));
+ uniformName.setInternal(true);
+ return hashName(uniformName);
+ }
+ return name.getString();
+ }
return hashName(name);
}
-TString TOutputGLSLBase::hashFunctionNameIfNeeded(const TName &mangledName)
+TString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunctionSymbolInfo &info)
{
- TString mangledStr = mangledName.getString();
- TString name = TFunction::unmangleName(mangledStr);
- if (mSymbolTable.findBuiltIn(mangledStr, mShaderVersion) != nullptr || name == "main")
- return translateTextureFunction(name);
- if (mangledName.isInternal())
- return name;
+ if (info.isMain())
+ {
+ return info.getName();
+ }
else
- return hashName(name);
+ {
+ return hashName(info.getNameObj());
+ }
}
bool TOutputGLSLBase::structDeclared(const TStructure *structure) const
@@ -1295,16 +1175,16 @@ void TOutputGLSLBase::declareStruct(const TStructure *structure)
{
TInfoSinkBase &out = objSink();
- out << "struct " << hashName(structure->name()) << "{\n";
+ out << "struct " << hashName(TName(structure->name())) << "{\n";
const TFieldList &fields = structure->fields();
for (size_t i = 0; i < fields.size(); ++i)
{
const TField *field = fields[i];
if (writeVariablePrecision(field->type()->getPrecision()))
out << " ";
- out << getTypeName(*field->type()) << " " << hashName(field->name());
+ out << getTypeName(*field->type()) << " " << hashName(TName(field->name()));
if (field->type()->isArray())
- out << arrayBrackets(*field->type());
+ out << ArrayString(*field->type());
out << ";\n";
}
out << "}";
@@ -1332,6 +1212,10 @@ void TOutputGLSLBase::declareInterfaceBlockLayout(const TInterfaceBlock *interfa
out << "std140";
break;
+ case EbsStd430:
+ out << "std430";
+ break;
+
default:
UNREACHABLE();
break;
@@ -1339,6 +1223,12 @@ void TOutputGLSLBase::declareInterfaceBlockLayout(const TInterfaceBlock *interfa
out << ", ";
+ if (interfaceBlock->blockBinding() > 0)
+ {
+ out << "binding = " << interfaceBlock->blockBinding();
+ out << ", ";
+ }
+
switch (interfaceBlock->matrixPacking())
{
case EmpUnspecified:
@@ -1363,17 +1253,105 @@ void TOutputGLSLBase::declareInterfaceBlock(const TInterfaceBlock *interfaceBloc
{
TInfoSinkBase &out = objSink();
- out << hashName(interfaceBlock->name()) << "{\n";
+ out << hashName(TName(interfaceBlock->name())) << "{\n";
const TFieldList &fields = interfaceBlock->fields();
for (size_t i = 0; i < fields.size(); ++i)
{
const TField *field = fields[i];
if (writeVariablePrecision(field->type()->getPrecision()))
out << " ";
- out << getTypeName(*field->type()) << " " << hashName(field->name());
+ out << getTypeName(*field->type()) << " " << hashName(TName(field->name()));
if (field->type()->isArray())
- out << arrayBrackets(*field->type());
+ out << ArrayString(*field->type());
out << ";\n";
}
out << "}";
}
+
+void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
+ sh::TLayoutPrimitiveType inputPrimitive,
+ int invocations,
+ sh::TLayoutPrimitiveType outputPrimitive,
+ int maxVertices)
+{
+ // Omit 'invocations = 1'
+ if (inputPrimitive != EptUndefined || invocations > 1)
+ {
+ out << "layout (";
+
+ if (inputPrimitive != EptUndefined)
+ {
+ out << getGeometryShaderPrimitiveTypeString(inputPrimitive);
+ }
+
+ if (invocations > 1)
+ {
+ if (inputPrimitive != EptUndefined)
+ {
+ out << ", ";
+ }
+ out << "invocations = " << invocations;
+ }
+ out << ") in;\n";
+ }
+
+ if (outputPrimitive != EptUndefined || maxVertices != -1)
+ {
+ out << "layout (";
+
+ if (outputPrimitive != EptUndefined)
+ {
+ out << getGeometryShaderPrimitiveTypeString(outputPrimitive);
+ }
+
+ if (maxVertices != -1)
+ {
+ if (outputPrimitive != EptUndefined)
+ {
+ out << ", ";
+ }
+ out << "max_vertices = " << maxVertices;
+ }
+ out << ") out;\n";
+ }
+}
+
+// If SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS is enabled, layout qualifiers are spilled whenever
+// variables with specified layout qualifiers are copied. Additional checks are needed against the
+// type and storage qualifier of the variable to verify that layout qualifiers have to be outputted.
+// TODO (mradev): Fix layout qualifier spilling in ScalarizeVecAndMatConstructorArgs and remove
+// NeedsToWriteLayoutQualifier.
+bool NeedsToWriteLayoutQualifier(const TType &type)
+{
+ if (type.getBasicType() == EbtInterfaceBlock)
+ {
+ return false;
+ }
+
+ const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
+
+ if ((type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
+ IsVarying(type.getQualifier())) &&
+ layoutQualifier.location >= 0)
+ {
+ return true;
+ }
+
+ if (type.getQualifier() == EvqFragmentOut && layoutQualifier.yuv == true)
+ {
+ return true;
+ }
+
+ if (IsOpaqueType(type.getBasicType()) && layoutQualifier.binding != -1)
+ {
+ return true;
+ }
+
+ if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ return true;
+ }
+ return false;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h
index 2ae82d15b2..592a310be4 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h
+++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h
@@ -9,9 +9,12 @@
#include <set>
-#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/LoopInfo.h"
-#include "compiler/translator/ParseContext.h"
+#include "compiler/translator/HashNames.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
class TOutputGLSLBase : public TIntermTraverser
{
@@ -20,48 +23,58 @@ class TOutputGLSLBase : public TIntermTraverser
ShArrayIndexClampingStrategy clampingStrategy,
ShHashFunction64 hashFunction,
NameMap &nameMap,
- TSymbolTable& symbolTable,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
int shaderVersion,
- ShShaderOutput output);
+ ShShaderOutput output,
+ ShCompileOptions compileOptions);
+
+ ShShaderOutput getShaderOutput() const { return mOutput; }
- ShShaderOutput getShaderOutput() const
- {
- return mOutput;
- }
+ // Return the original name if hash function pointer is NULL;
+ // otherwise return the hashed name. Has special handling for internal names, which are not
+ // hashed.
+ TString hashName(const TName &name);
protected:
TInfoSinkBase &objSink() { return mObjSink; }
+ void writeFloat(TInfoSinkBase &out, float f);
void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr);
- void writeLayoutQualifier(const TType &type);
+ virtual void writeLayoutQualifier(TIntermTyped *variable);
+ void writeInvariantQualifier(const TType &type);
void writeVariableType(const TType &type);
virtual bool writeVariablePrecision(TPrecision precision) = 0;
void writeFunctionParameters(const TIntermSequence &args);
const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion);
- void writeConstructorTriplet(Visit visit, const TType &type, const char *constructorBaseType);
+ void writeConstructorTriplet(Visit visit, const TType &type);
TString getTypeName(const TType &type);
void visitSymbol(TIntermSymbol *node) override;
void visitConstantUnion(TIntermConstantUnion *node) override;
+ bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
bool visitBinary(Visit visit, TIntermBinary *node) override;
bool visitUnary(Visit visit, TIntermUnary *node) override;
- bool visitSelection(Visit visit, TIntermSelection *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override;
bool visitSwitch(Visit visit, TIntermSwitch *node) override;
bool visitCase(Visit visit, TIntermCase *node) override;
+ bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit visit, TIntermBlock *node) override;
+ bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
bool visitLoop(Visit visit, TIntermLoop *node) override;
bool visitBranch(Visit visit, TIntermBranch *node) override;
- void visitCodeBlock(TIntermNode *node);
+ void visitCodeBlock(TIntermBlock *node);
- // Return the original name if hash function pointer is NULL;
- // otherwise return the hashed name.
- TString hashName(const TString &name);
// Same as hashName(), but without hashing built-in variables.
- TString hashVariableName(const TString &name);
- // Same as hashName(), but without hashing built-in functions and with unmangling.
- TString hashFunctionNameIfNeeded(const TName &mangledName);
+ TString hashVariableName(const TName &name);
+ // Same as hashName(), but without hashing internal functions or "main".
+ TString hashFunctionNameIfNeeded(const TFunctionSymbolInfo &info);
// Used to translate function names for differences between ESSL and GLSL
- virtual TString translateTextureFunction(TString &name) { return name; }
+ virtual TString translateTextureFunction(const TString &name) { return name; }
private:
bool structDeclared(const TStructure *structure) const;
@@ -70,17 +83,16 @@ class TOutputGLSLBase : public TIntermTraverser
void declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock);
void declareInterfaceBlock(const TInterfaceBlock *interfaceBlock);
- void writeBuiltInFunctionTriplet(Visit visit, const char *preStr, bool useEmulatedFunction);
+ void writeBuiltInFunctionTriplet(Visit visit, TOperator op, bool useEmulatedFunction);
+
+ const char *mapQualifierToString(TQualifier qialifier);
TInfoSinkBase &mObjSink;
- bool mDeclaringVariables;
+ bool mDeclaringVariable;
// This set contains all the ids of the structs from every scope.
std::set<int> mDeclaredStructs;
- // Stack of loops that need to be unrolled.
- TLoopStack mLoopUnrollStack;
-
ShArrayIndexClampingStrategy mClampingStrategy;
// name hashing.
@@ -88,11 +100,23 @@ class TOutputGLSLBase : public TIntermTraverser
NameMap &mNameMap;
- TSymbolTable &mSymbolTable;
+ sh::GLenum mShaderType;
const int mShaderVersion;
ShShaderOutput mOutput;
+
+ ShCompileOptions mCompileOptions;
};
+void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
+ sh::TLayoutPrimitiveType inputPrimitive,
+ int invocations,
+ sh::TLayoutPrimitiveType outputPrimitive,
+ int maxVertices);
+
+bool NeedsToWriteLayoutQualifier(const TType &type);
+
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp
index 253b96696c..d5ff761430 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp
@@ -15,33 +15,95 @@
#include "common/utilities.h"
#include "compiler/translator/BuiltInFunctionEmulator.h"
#include "compiler/translator/BuiltInFunctionEmulatorHLSL.h"
-#include "compiler/translator/FlagStd140Structs.h"
+#include "compiler/translator/ImageFunctionHLSL.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/NodeSearch.h"
#include "compiler/translator/RemoveSwitchFallThrough.h"
#include "compiler/translator/SearchSymbol.h"
#include "compiler/translator/StructureHLSL.h"
+#include "compiler/translator/TextureFunctionHLSL.h"
#include "compiler/translator/TranslatorHLSL.h"
#include "compiler/translator/UniformHLSL.h"
#include "compiler/translator/UtilsHLSL.h"
#include "compiler/translator/blocklayout.h"
#include "compiler/translator/util.h"
+namespace sh
+{
+
namespace
{
-bool IsSequence(TIntermNode *node)
+TString ArrayHelperFunctionName(const char *prefix, const TType &type)
+{
+ TStringStream fnName;
+ fnName << prefix << "_";
+ if (type.isArray())
+ {
+ for (unsigned int arraySize : *type.getArraySizes())
+ {
+ fnName << arraySize << "_";
+ }
+ }
+ fnName << TypeString(type);
+ return fnName.str();
+}
+
+bool IsDeclarationWrittenOut(TIntermDeclaration *node)
+{
+ TIntermSequence *sequence = node->getSequence();
+ TIntermTyped *variable = (*sequence)[0]->getAsTyped();
+ ASSERT(sequence->size() == 1);
+ ASSERT(variable);
+ return (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal ||
+ variable->getQualifier() == EvqConst);
+}
+
+bool IsInStd140InterfaceBlock(TIntermTyped *node)
+{
+ TIntermBinary *binaryNode = node->getAsBinaryNode();
+
+ if (binaryNode)
+ {
+ return IsInStd140InterfaceBlock(binaryNode->getLeft());
+ }
+
+ const TType &type = node->getType();
+
+ // determine if we are in the standard layout
+ const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
+ if (interfaceBlock)
+ {
+ return (interfaceBlock->blockStorage() == EbsStd140);
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+void OutputHLSL::writeFloat(TInfoSinkBase &out, float f)
{
- return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence;
+ // This is known not to work for NaN on all drivers but make the best effort to output NaNs
+ // regardless.
+ if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300 &&
+ mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ out << "asfloat(" << gl::bitCast<uint32_t>(f) << "u)";
+ }
+ else
+ {
+ out << std::min(FLT_MAX, std::max(-FLT_MAX, f));
+ }
}
-void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion)
+void OutputHLSL::writeSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion)
{
ASSERT(constUnion != nullptr);
switch (constUnion->getType())
{
case EbtFloat:
- out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst()));
+ writeFloat(out, constUnion->getFConst());
break;
case EbtInt:
out << constUnion->getIConst();
@@ -57,14 +119,14 @@ void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUn
}
}
-const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out,
- const TConstantUnion *const constUnion,
- const size_t size)
+const TConstantUnion *OutputHLSL::writeConstantUnionArray(TInfoSinkBase &out,
+ const TConstantUnion *const constUnion,
+ const size_t size)
{
const TConstantUnion *constUnionIterated = constUnion;
for (size_t i = 0; i < size; i++, constUnionIterated++)
{
- WriteSingleConstant(out, constUnionIterated);
+ writeSingleConstant(out, constUnionIterated);
if (i != size - 1)
{
@@ -74,71 +136,17 @@ const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out,
return constUnionIterated;
}
-} // namespace
-
-namespace sh
-{
-
-TString OutputHLSL::TextureFunction::name() const
-{
- TString name = "gl_texture";
-
- // We need to include full the sampler type in the function name to make the signature unique
- // on D3D11, where samplers are passed to texture functions as indices.
- name += TextureTypeSuffix(this->sampler);
-
- if (proj)
- {
- name += "Proj";
- }
-
- if (offset)
- {
- name += "Offset";
- }
-
- switch(method)
- {
- case IMPLICIT: break;
- case BIAS: break; // Extra parameter makes the signature unique
- case LOD: name += "Lod"; break;
- case LOD0: name += "Lod0"; break;
- case LOD0BIAS: name += "Lod0"; break; // Extra parameter makes the signature unique
- case SIZE: name += "Size"; break;
- case FETCH: name += "Fetch"; break;
- case GRAD: name += "Grad"; break;
- default: UNREACHABLE();
- }
-
- return name + "(";
-}
-
-bool OutputHLSL::TextureFunction::operator<(const TextureFunction &rhs) const
-{
- if (sampler < rhs.sampler) return true;
- if (sampler > rhs.sampler) return false;
-
- if (coords < rhs.coords) return true;
- if (coords > rhs.coords) return false;
-
- if (!proj && rhs.proj) return true;
- if (proj && !rhs.proj) return false;
-
- if (!offset && rhs.offset) return true;
- if (offset && !rhs.offset) return false;
-
- if (method < rhs.method) return true;
- if (method > rhs.method) return false;
-
- return false;
-}
-
-OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion,
- const TExtensionBehavior &extensionBehavior,
- const char *sourcePath, ShShaderOutput outputType,
- int numRenderTargets, const std::vector<Uniform> &uniforms,
- int compileOptions)
- : TIntermTraverser(true, true, true),
+OutputHLSL::OutputHLSL(sh::GLenum shaderType,
+ int shaderVersion,
+ const TExtensionBehavior &extensionBehavior,
+ const char *sourcePath,
+ ShShaderOutput outputType,
+ int numRenderTargets,
+ const std::vector<Uniform> &uniforms,
+ ShCompileOptions compileOptions,
+ TSymbolTable *symbolTable,
+ PerformanceDiagnostics *perfDiagnostics)
+ : TIntermTraverser(true, true, true, symbolTable),
mShaderType(shaderType),
mShaderVersion(shaderVersion),
mExtensionBehavior(extensionBehavior),
@@ -146,51 +154,66 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion,
mOutputType(outputType),
mCompileOptions(compileOptions),
mNumRenderTargets(numRenderTargets),
- mCurrentFunctionMetadata(nullptr)
+ mCurrentFunctionMetadata(nullptr),
+ mPerfDiagnostics(perfDiagnostics)
{
mInsideFunction = false;
- mUsesFragColor = false;
- mUsesFragData = false;
- mUsesDepthRange = false;
- mUsesFragCoord = false;
- mUsesPointCoord = false;
- mUsesFrontFacing = false;
- mUsesPointSize = false;
- mUsesInstanceID = false;
- mUsesFragDepth = false;
- mUsesXor = false;
- mUsesDiscardRewriting = false;
- mUsesNestedBreak = false;
+ mUsesFragColor = false;
+ mUsesFragData = false;
+ mUsesDepthRange = false;
+ mUsesFragCoord = false;
+ mUsesPointCoord = false;
+ mUsesFrontFacing = false;
+ mUsesPointSize = false;
+ mUsesInstanceID = false;
+ mHasMultiviewExtensionEnabled =
+ IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview);
+ mUsesViewID = false;
+ mUsesVertexID = false;
+ mUsesFragDepth = false;
+ mUsesNumWorkGroups = false;
+ mUsesWorkGroupID = false;
+ mUsesLocalInvocationID = false;
+ mUsesGlobalInvocationID = false;
+ mUsesLocalInvocationIndex = false;
+ mUsesXor = false;
+ mUsesDiscardRewriting = false;
+ mUsesNestedBreak = false;
mRequiresIEEEStrictCompiling = false;
mUniqueIndex = 0;
- mOutputLod0Function = false;
+ mOutputLod0Function = false;
mInsideDiscontinuousLoop = false;
- mNestedLoopDepth = 0;
+ mNestedLoopDepth = 0;
- mExcessiveLoopIndex = NULL;
+ mExcessiveLoopIndex = nullptr;
- mStructureHLSL = new StructureHLSL;
- mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms);
+ mStructureHLSL = new StructureHLSL;
+ mUniformHLSL = new UniformHLSL(shaderType, mStructureHLSL, outputType, uniforms);
+ mTextureFunctionHLSL = new TextureFunctionHLSL;
+ mImageFunctionHLSL = new ImageFunctionHLSL;
if (mOutputType == SH_HLSL_3_0_OUTPUT)
{
// Fragment shaders need dx_DepthRange, dx_ViewCoords and dx_DepthFront.
- // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and dx_ViewAdjust.
+ // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and
+ // dx_ViewAdjust.
// In both cases total 3 uniform registers need to be reserved.
mUniformHLSL->reserveUniformRegisters(3);
}
// Reserve registers for the default uniform block and driver constants
- mUniformHLSL->reserveInterfaceBlockRegisters(2);
+ mUniformHLSL->reserveUniformBlockRegisters(2);
}
OutputHLSL::~OutputHLSL()
{
SafeDelete(mStructureHLSL);
SafeDelete(mUniformHLSL);
+ SafeDelete(mTextureFunctionHLSL);
+ SafeDelete(mImageFunctionHLSL);
for (auto &eqFunction : mStructEqualityFunctions)
{
SafeDelete(eqFunction);
@@ -203,71 +226,48 @@ OutputHLSL::~OutputHLSL()
void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink)
{
- const std::vector<TIntermTyped*> &flaggedStructs = FlagStd140ValueStructs(treeRoot);
- makeFlaggedStructMaps(flaggedStructs);
-
BuiltInFunctionEmulator builtInFunctionEmulator;
InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator);
- builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(treeRoot);
+ if ((mCompileOptions & SH_EMULATE_ISNAN_FLOAT_FUNCTION) != 0)
+ {
+ InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(&builtInFunctionEmulator,
+ mShaderVersion);
+ }
+
+ builtInFunctionEmulator.markBuiltInFunctionsForEmulation(treeRoot);
// Now that we are done changing the AST, do the analyses need for HLSL generation
- CallDAG::InitResult success = mCallDag.init(treeRoot, &objSink);
+ CallDAG::InitResult success = mCallDag.init(treeRoot, nullptr);
ASSERT(success == CallDAG::INITDAG_SUCCESS);
- UNUSED_ASSERTION_VARIABLE(success);
mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag);
+ const std::vector<MappedStruct> std140Structs = FlagStd140Structs(treeRoot);
+ // TODO(oetuaho): The std140Structs could be filtered based on which ones actually get used in
+ // the shader code. When we add shader storage blocks we might also consider an alternative
+ // solution, since the struct mapping won't work very well for shader storage blocks.
+
// Output the body and footer first to determine what has to go in the header
mInfoSinkStack.push(&mBody);
treeRoot->traverse(this);
mInfoSinkStack.pop();
mInfoSinkStack.push(&mFooter);
- if (!mDeferredGlobalInitializers.empty())
- {
- writeDeferredGlobalInitializers(mFooter);
- }
mInfoSinkStack.pop();
mInfoSinkStack.push(&mHeader);
- header(mHeader, &builtInFunctionEmulator);
+ header(mHeader, std140Structs, &builtInFunctionEmulator);
mInfoSinkStack.pop();
objSink << mHeader.c_str();
objSink << mBody.c_str();
objSink << mFooter.c_str();
- builtInFunctionEmulator.Cleanup();
-}
-
-void OutputHLSL::makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs)
-{
- for (unsigned int structIndex = 0; structIndex < flaggedStructs.size(); structIndex++)
- {
- TIntermTyped *flaggedNode = flaggedStructs[structIndex];
-
- TInfoSinkBase structInfoSink;
- mInfoSinkStack.push(&structInfoSink);
-
- // This will mark the necessary block elements as referenced
- flaggedNode->traverse(this);
-
- TString structName(structInfoSink.c_str());
- mInfoSinkStack.pop();
-
- mFlaggedStructOriginalNames[flaggedNode] = structName;
-
- for (size_t pos = structName.find('.'); pos != std::string::npos; pos = structName.find('.'))
- {
- structName.erase(pos, 1);
- }
-
- mFlaggedStructMappedNames[flaggedNode] = "map" + structName;
- }
+ builtInFunctionEmulator.cleanup();
}
-const std::map<std::string, unsigned int> &OutputHLSL::getInterfaceBlockRegisterMap() const
+const std::map<std::string, unsigned int> &OutputHLSL::getUniformBlockRegisterMap() const
{
- return mUniformHLSL->getInterfaceBlockRegisterMap();
+ return mUniformHLSL->getUniformBlockRegisterMap();
}
const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() const
@@ -275,95 +275,161 @@ const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() c
return mUniformHLSL->getUniformRegisterMap();
}
-int OutputHLSL::vectorSize(const TType &type) const
-{
- int elementSize = type.isMatrix() ? type.getCols() : 1;
- int arraySize = type.isArray() ? type.getArraySize() : 1;
-
- return elementSize * arraySize;
-}
-
-TString OutputHLSL::structInitializerString(int indent, const TStructure &structure, const TString &rhsStructName)
+TString OutputHLSL::structInitializerString(int indent,
+ const TType &type,
+ const TString &name) const
{
TString init;
- TString preIndentString;
- TString fullIndentString;
+ TString indentString;
+ for (int spaces = 0; spaces < indent; spaces++)
+ {
+ indentString += " ";
+ }
- for (int spaces = 0; spaces < (indent * 4); spaces++)
+ if (type.isArray())
{
- preIndentString += ' ';
+ init += indentString + "{\n";
+ for (unsigned int arrayIndex = 0u; arrayIndex < type.getOutermostArraySize(); ++arrayIndex)
+ {
+ TStringStream indexedString;
+ indexedString << name << "[" << arrayIndex << "]";
+ TType elementType = type;
+ elementType.toArrayElementType();
+ init += structInitializerString(indent + 1, elementType, indexedString.str());
+ if (arrayIndex < type.getOutermostArraySize() - 1)
+ {
+ init += ",";
+ }
+ init += "\n";
+ }
+ init += indentString + "}";
}
+ else if (type.getBasicType() == EbtStruct)
+ {
+ init += indentString + "{\n";
+ const TStructure &structure = *type.getStruct();
+ const TFieldList &fields = structure.fields();
+ for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+ {
+ const TField &field = *fields[fieldIndex];
+ const TString &fieldName = name + "." + Decorate(field.name());
+ const TType &fieldType = *field.type();
- for (int spaces = 0; spaces < ((indent+1) * 4); spaces++)
+ init += structInitializerString(indent + 1, fieldType, fieldName);
+ if (fieldIndex < fields.size() - 1)
+ {
+ init += ",";
+ }
+ init += "\n";
+ }
+ init += indentString + "}";
+ }
+ else
{
- fullIndentString += ' ';
+ init += indentString + name;
}
- init += preIndentString + "{\n";
+ return init;
+}
+
+TString OutputHLSL::generateStructMapping(const std::vector<MappedStruct> &std140Structs) const
+{
+ TString mappedStructs;
- const TFieldList &fields = structure.fields();
- for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+ for (auto &mappedStruct : std140Structs)
{
- const TField &field = *fields[fieldIndex];
- const TString &fieldName = rhsStructName + "." + Decorate(field.name());
- const TType &fieldType = *field.type();
-
- if (fieldType.getStruct())
+ TInterfaceBlock *interfaceBlock =
+ mappedStruct.blockDeclarator->getType().getInterfaceBlock();
+ const TString &interfaceBlockName = interfaceBlock->name();
+ const TName &instanceName = mappedStruct.blockDeclarator->getName();
+ if (mReferencedUniformBlocks.count(interfaceBlockName) == 0 &&
+ (instanceName.getString() == "" ||
+ mReferencedUniformBlocks.count(instanceName.getString()) == 0))
{
- init += structInitializerString(indent + 1, *fieldType.getStruct(), fieldName);
+ continue;
}
- else
+
+ unsigned int instanceCount = 1u;
+ bool isInstanceArray = mappedStruct.blockDeclarator->isArray();
+ if (isInstanceArray)
{
- init += fullIndentString + fieldName + ",\n";
+ instanceCount = mappedStruct.blockDeclarator->getOutermostArraySize();
}
- }
- init += preIndentString + "}" + (indent == 0 ? ";" : ",") + "\n";
+ for (unsigned int instanceArrayIndex = 0; instanceArrayIndex < instanceCount;
+ ++instanceArrayIndex)
+ {
+ TString originalName;
+ TString mappedName("map");
- return init;
+ if (instanceName.getString() != "")
+ {
+ unsigned int instanceStringArrayIndex = GL_INVALID_INDEX;
+ if (isInstanceArray)
+ instanceStringArrayIndex = instanceArrayIndex;
+ TString instanceString = mUniformHLSL->uniformBlockInstanceString(
+ *interfaceBlock, instanceStringArrayIndex);
+ originalName += instanceString;
+ mappedName += instanceString;
+ originalName += ".";
+ mappedName += "_";
+ }
+
+ TString fieldName = Decorate(mappedStruct.field->name());
+ originalName += fieldName;
+ mappedName += fieldName;
+
+ TType *structType = mappedStruct.field->type();
+ mappedStructs +=
+ "static " + Decorate(structType->getStruct()->name()) + " " + mappedName;
+
+ if (structType->isArray())
+ {
+ mappedStructs += ArrayString(*mappedStruct.field->type());
+ }
+
+ mappedStructs += " =\n";
+ mappedStructs += structInitializerString(0, *structType, originalName);
+ mappedStructs += ";\n";
+ }
+ }
+ return mappedStructs;
}
-void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator)
+void OutputHLSL::header(TInfoSinkBase &out,
+ const std::vector<MappedStruct> &std140Structs,
+ const BuiltInFunctionEmulator *builtInFunctionEmulator) const
{
TString varyings;
TString attributes;
- TString flaggedStructs;
+ TString mappedStructs = generateStructMapping(std140Structs);
- for (std::map<TIntermTyped*, TString>::const_iterator flaggedStructIt = mFlaggedStructMappedNames.begin(); flaggedStructIt != mFlaggedStructMappedNames.end(); flaggedStructIt++)
+ for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin();
+ varying != mReferencedVaryings.end(); varying++)
{
- TIntermTyped *structNode = flaggedStructIt->first;
- const TString &mappedName = flaggedStructIt->second;
- const TStructure &structure = *structNode->getType().getStruct();
- const TString &originalName = mFlaggedStructOriginalNames[structNode];
-
- flaggedStructs += "static " + Decorate(structure.name()) + " " + mappedName + " =\n";
- flaggedStructs += structInitializerString(0, structure, originalName);
- flaggedStructs += "\n";
- }
-
- for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); varying != mReferencedVaryings.end(); varying++)
- {
- const TType &type = varying->second->getType();
+ const TType &type = varying->second->getType();
const TString &name = varying->second->getSymbol();
// Program linking depends on this exact format
- varyings += "static " + InterpolationString(type.getQualifier()) + " " + TypeString(type) + " " +
- Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n";
+ varyings += "static " + InterpolationString(type.getQualifier()) + " " + TypeString(type) +
+ " " + Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n";
}
- for (ReferencedSymbols::const_iterator attribute = mReferencedAttributes.begin(); attribute != mReferencedAttributes.end(); attribute++)
+ for (ReferencedSymbols::const_iterator attribute = mReferencedAttributes.begin();
+ attribute != mReferencedAttributes.end(); attribute++)
{
- const TType &type = attribute->second->getType();
+ const TType &type = attribute->second->getType();
const TString &name = attribute->second->getSymbol();
- attributes += "static " + TypeString(type) + " " + Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n";
+ attributes += "static " + TypeString(type) + " " + Decorate(name) + ArrayString(type) +
+ " = " + initializer(type) + ";\n";
}
out << mStructureHLSL->structsHeader();
- mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms);
- out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks);
+ mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms, mSymbolTable);
+ out << mUniformHLSL->uniformBlocksHeader(mReferencedUniformBlocks);
if (!mEqualityFunctions.empty())
{
@@ -415,22 +481,24 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
if (mShaderType == GL_FRAGMENT_SHADER)
{
- TExtensionBehavior::const_iterator iter = mExtensionBehavior.find("GL_EXT_draw_buffers");
- const bool usingMRTExtension = (iter != mExtensionBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire));
+ const bool usingMRTExtension =
+ IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers);
out << "// Varyings\n";
- out << varyings;
+ out << varyings;
out << "\n";
if (mShaderVersion >= 300)
{
- for (ReferencedSymbols::const_iterator outputVariableIt = mReferencedOutputVariables.begin(); outputVariableIt != mReferencedOutputVariables.end(); outputVariableIt++)
+ for (ReferencedSymbols::const_iterator outputVariableIt =
+ mReferencedOutputVariables.begin();
+ outputVariableIt != mReferencedOutputVariables.end(); outputVariableIt++)
{
const TString &variableName = outputVariableIt->first;
- const TType &variableType = outputVariableIt->second->getType();
+ const TType &variableType = outputVariableIt->second->getType();
- out << "static " + TypeString(variableType) + " out_" + variableName + ArrayString(variableType) +
- " = " + initializer(variableType) + ";\n";
+ out << "static " + TypeString(variableType) + " out_" + variableName +
+ ArrayString(variableType) + " = " + initializer(variableType) + ";\n";
}
}
else
@@ -438,7 +506,7 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1;
out << "static float4 gl_Color[" << numColorValues << "] =\n"
- "{\n";
+ "{\n";
for (unsigned int i = 0; i < numColorValues; i++)
{
out << " float4(0, 0, 0, 0)";
@@ -512,6 +580,18 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << " float2 dx_ViewScale : packoffset(c3);\n";
}
+ if (mHasMultiviewExtensionEnabled)
+ {
+ // We have to add a value which we can use to keep track of which multi-view code
+ // path is to be selected in the GS.
+ out << " float multiviewSelectViewportIndex : packoffset(c3.z);\n";
+ }
+
+ if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ mUniformHLSL->samplerMetadataUniforms(out, "c4");
+ }
+
out << "};\n";
}
else
@@ -536,15 +616,16 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
if (mUsesDepthRange)
{
- out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n"
+ out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, "
+ "dx_DepthRange.y, dx_DepthRange.z};\n"
"\n";
}
- if (!flaggedStructs.empty())
+ if (!mappedStructs.empty())
{
- out << "// Std140 Structures accessed by value\n";
+ out << "// Structures from std140 blocks with padding removed\n";
out << "\n";
- out << flaggedStructs;
+ out << mappedStructs;
out << "\n";
}
@@ -563,10 +644,10 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << "#define GL_USES_FRAG_DATA\n";
}
}
- else // Vertex shader
+ else if (mShaderType == GL_VERTEX_SHADER)
{
out << "// Attributes\n";
- out << attributes;
+ out << attributes;
out << "\n"
"static float4 gl_Position = float4(0, 0, 0, 0);\n";
@@ -580,9 +661,14 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << "static int gl_InstanceID;";
}
+ if (mUsesVertexID)
+ {
+ out << "static int gl_VertexID;";
+ }
+
out << "\n"
"// Varyings\n";
- out << varyings;
+ out << varyings;
out << "\n";
if (mUsesDepthRange)
@@ -599,7 +685,7 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
{
out << "cbuffer DriverConstants : register(b1)\n"
- "{\n";
+ "{\n";
if (mUsesDepthRange)
{
@@ -614,6 +700,18 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << " float2 dx_ViewCoords : packoffset(c2);\n";
out << " float2 dx_ViewScale : packoffset(c3);\n";
+ if (mHasMultiviewExtensionEnabled)
+ {
+ // We have to add a value which we can use to keep track of which multi-view code
+ // path is to be selected in the GS.
+ out << " float multiviewSelectViewportIndex : packoffset(c3.z);\n";
+ }
+
+ if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ mUniformHLSL->samplerMetadataUniforms(out, "c4");
+ }
+
out << "};\n"
"\n";
}
@@ -631,740 +729,62 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
if (mUsesDepthRange)
{
- out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n"
+ out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, "
+ "dx_DepthRange.y, dx_DepthRange.z};\n"
"\n";
}
- if (!flaggedStructs.empty())
+ if (!mappedStructs.empty())
{
- out << "// Std140 Structures accessed by value\n";
+ out << "// Structures from std140 blocks with padding removed\n";
out << "\n";
- out << flaggedStructs;
+ out << mappedStructs;
out << "\n";
}
}
-
- for (TextureFunctionSet::const_iterator textureFunction = mUsesTexture.begin(); textureFunction != mUsesTexture.end(); textureFunction++)
+ else // Compute shader
{
- // Return type
- if (textureFunction->method == TextureFunction::SIZE)
- {
- switch(textureFunction->sampler)
- {
- case EbtSampler2D: out << "int2 "; break;
- case EbtSampler3D: out << "int3 "; break;
- case EbtSamplerCube: out << "int2 "; break;
- case EbtSampler2DArray: out << "int3 "; break;
- case EbtISampler2D: out << "int2 "; break;
- case EbtISampler3D: out << "int3 "; break;
- case EbtISamplerCube: out << "int2 "; break;
- case EbtISampler2DArray: out << "int3 "; break;
- case EbtUSampler2D: out << "int2 "; break;
- case EbtUSampler3D: out << "int3 "; break;
- case EbtUSamplerCube: out << "int2 "; break;
- case EbtUSampler2DArray: out << "int3 "; break;
- case EbtSampler2DShadow: out << "int2 "; break;
- case EbtSamplerCubeShadow: out << "int2 "; break;
- case EbtSampler2DArrayShadow: out << "int3 "; break;
- default: UNREACHABLE();
- }
- }
- else // Sampling function
- {
- switch(textureFunction->sampler)
- {
- case EbtSampler2D: out << "float4 "; break;
- case EbtSampler3D: out << "float4 "; break;
- case EbtSamplerCube: out << "float4 "; break;
- case EbtSampler2DArray: out << "float4 "; break;
- case EbtISampler2D: out << "int4 "; break;
- case EbtISampler3D: out << "int4 "; break;
- case EbtISamplerCube: out << "int4 "; break;
- case EbtISampler2DArray: out << "int4 "; break;
- case EbtUSampler2D: out << "uint4 "; break;
- case EbtUSampler3D: out << "uint4 "; break;
- case EbtUSamplerCube: out << "uint4 "; break;
- case EbtUSampler2DArray: out << "uint4 "; break;
- case EbtSampler2DShadow: out << "float "; break;
- case EbtSamplerCubeShadow: out << "float "; break;
- case EbtSampler2DArrayShadow: out << "float "; break;
- default: UNREACHABLE();
- }
- }
-
- // Function name
- out << textureFunction->name();
-
- // Argument list
- int hlslCoords = 4;
-
- if (mOutputType == SH_HLSL_3_0_OUTPUT)
- {
- switch(textureFunction->sampler)
- {
- case EbtSampler2D: out << "sampler2D s"; hlslCoords = 2; break;
- case EbtSamplerCube: out << "samplerCUBE s"; hlslCoords = 3; break;
- default: UNREACHABLE();
- }
-
- switch(textureFunction->method)
- {
- case TextureFunction::IMPLICIT: break;
- case TextureFunction::BIAS: hlslCoords = 4; break;
- case TextureFunction::LOD: hlslCoords = 4; break;
- case TextureFunction::LOD0: hlslCoords = 4; break;
- case TextureFunction::LOD0BIAS: hlslCoords = 4; break;
- default: UNREACHABLE();
- }
- }
- else
- {
- hlslCoords = HLSLTextureCoordsCount(textureFunction->sampler);
- if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
- {
- out << TextureString(textureFunction->sampler) << " x, "
- << SamplerString(textureFunction->sampler) << " s";
- }
- else
- {
- ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT);
- out << "const uint samplerIndex";
- }
- }
-
- if (textureFunction->method == TextureFunction::FETCH) // Integer coordinates
- {
- switch(textureFunction->coords)
- {
- case 2: out << ", int2 t"; break;
- case 3: out << ", int3 t"; break;
- default: UNREACHABLE();
- }
- }
- else // Floating-point coordinates (except textureSize)
- {
- switch(textureFunction->coords)
- {
- case 1: out << ", int lod"; break; // textureSize()
- case 2: out << ", float2 t"; break;
- case 3: out << ", float3 t"; break;
- case 4: out << ", float4 t"; break;
- default: UNREACHABLE();
- }
- }
+ ASSERT(mShaderType == GL_COMPUTE_SHADER);
- if (textureFunction->method == TextureFunction::GRAD)
- {
- switch(textureFunction->sampler)
- {
- case EbtSampler2D:
- case EbtISampler2D:
- case EbtUSampler2D:
- case EbtSampler2DArray:
- case EbtISampler2DArray:
- case EbtUSampler2DArray:
- case EbtSampler2DShadow:
- case EbtSampler2DArrayShadow:
- out << ", float2 ddx, float2 ddy";
- break;
- case EbtSampler3D:
- case EbtISampler3D:
- case EbtUSampler3D:
- case EbtSamplerCube:
- case EbtISamplerCube:
- case EbtUSamplerCube:
- case EbtSamplerCubeShadow:
- out << ", float3 ddx, float3 ddy";
- break;
- default: UNREACHABLE();
- }
- }
-
- switch(textureFunction->method)
+ out << "cbuffer DriverConstants : register(b1)\n"
+ "{\n";
+ if (mUsesNumWorkGroups)
{
- case TextureFunction::IMPLICIT: break;
- case TextureFunction::BIAS: break; // Comes after the offset parameter
- case TextureFunction::LOD: out << ", float lod"; break;
- case TextureFunction::LOD0: break;
- case TextureFunction::LOD0BIAS: break; // Comes after the offset parameter
- case TextureFunction::SIZE: break;
- case TextureFunction::FETCH: out << ", int mip"; break;
- case TextureFunction::GRAD: break;
- default: UNREACHABLE();
+ out << " uint3 gl_NumWorkGroups : packoffset(c0);\n";
}
+ ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT);
+ mUniformHLSL->samplerMetadataUniforms(out, "c1");
+ out << "};\n";
- if (textureFunction->offset)
+ // Follow built-in variables would be initialized in
+ // DynamicHLSL::generateComputeShaderLinkHLSL, if they
+ // are used in compute shader.
+ if (mUsesWorkGroupID)
{
- switch(textureFunction->sampler)
- {
- case EbtSampler2D: out << ", int2 offset"; break;
- case EbtSampler3D: out << ", int3 offset"; break;
- case EbtSampler2DArray: out << ", int2 offset"; break;
- case EbtISampler2D: out << ", int2 offset"; break;
- case EbtISampler3D: out << ", int3 offset"; break;
- case EbtISampler2DArray: out << ", int2 offset"; break;
- case EbtUSampler2D: out << ", int2 offset"; break;
- case EbtUSampler3D: out << ", int3 offset"; break;
- case EbtUSampler2DArray: out << ", int2 offset"; break;
- case EbtSampler2DShadow: out << ", int2 offset"; break;
- case EbtSampler2DArrayShadow: out << ", int2 offset"; break;
- default: UNREACHABLE();
- }
+ out << "static uint3 gl_WorkGroupID = uint3(0, 0, 0);\n";
}
- if (textureFunction->method == TextureFunction::BIAS ||
- textureFunction->method == TextureFunction::LOD0BIAS)
+ if (mUsesLocalInvocationID)
{
- out << ", float bias";
+ out << "static uint3 gl_LocalInvocationID = uint3(0, 0, 0);\n";
}
- out << ")\n"
- "{\n";
-
- // In some cases we use a variable to store the texture/sampler objects, but to work around
- // a D3D11 compiler bug related to discard inside a loop that is conditional on texture
- // sampling we need to call the function directly on a reference to the array. The bug was
- // found using dEQP-GLES3.functional.shaders.discard*loop_texture* tests.
- TString textureReference("x");
- TString samplerReference("s");
- if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ if (mUsesGlobalInvocationID)
{
- TString suffix = TextureGroupSuffix(textureFunction->sampler);
- if (TextureGroup(textureFunction->sampler) == HLSL_TEXTURE_2D)
- {
- textureReference = TString("textures") + suffix + "[samplerIndex]";
- samplerReference = TString("samplers") + suffix + "[samplerIndex]";
- }
- else
- {
- out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix
- << ";\n";
- textureReference = TString("textures") + suffix + "[textureIndex]";
- out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset"
- << suffix << ";\n";
- samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]";
- }
+ out << "static uint3 gl_GlobalInvocationID = uint3(0, 0, 0);\n";
}
- if (textureFunction->method == TextureFunction::SIZE)
+ if (mUsesLocalInvocationIndex)
{
- if (IsSampler2D(textureFunction->sampler) || IsSamplerCube(textureFunction->sampler))
- {
- if (IsSamplerArray(textureFunction->sampler))
- {
- out << " uint width; uint height; uint layers; uint numberOfLevels;\n"
- << " " << textureReference
- << ".GetDimensions(lod, width, height, layers, numberOfLevels);\n";
- }
- else
- {
- out << " uint width; uint height; uint numberOfLevels;\n"
- << " " << textureReference
- << ".GetDimensions(lod, width, height, numberOfLevels);\n";
- }
- }
- else if (IsSampler3D(textureFunction->sampler))
- {
- out << " uint width; uint height; uint depth; uint numberOfLevels;\n"
- << " " << textureReference
- << ".GetDimensions(lod, width, height, depth, numberOfLevels);\n";
- }
- else UNREACHABLE();
-
- switch(textureFunction->sampler)
- {
- case EbtSampler2D: out << " return int2(width, height);"; break;
- case EbtSampler3D: out << " return int3(width, height, depth);"; break;
- case EbtSamplerCube: out << " return int2(width, height);"; break;
- case EbtSampler2DArray: out << " return int3(width, height, layers);"; break;
- case EbtISampler2D: out << " return int2(width, height);"; break;
- case EbtISampler3D: out << " return int3(width, height, depth);"; break;
- case EbtISamplerCube: out << " return int2(width, height);"; break;
- case EbtISampler2DArray: out << " return int3(width, height, layers);"; break;
- case EbtUSampler2D: out << " return int2(width, height);"; break;
- case EbtUSampler3D: out << " return int3(width, height, depth);"; break;
- case EbtUSamplerCube: out << " return int2(width, height);"; break;
- case EbtUSampler2DArray: out << " return int3(width, height, layers);"; break;
- case EbtSampler2DShadow: out << " return int2(width, height);"; break;
- case EbtSamplerCubeShadow: out << " return int2(width, height);"; break;
- case EbtSampler2DArrayShadow: out << " return int3(width, height, layers);"; break;
- default: UNREACHABLE();
- }
- }
- else
- {
- if (IsIntegerSampler(textureFunction->sampler) && IsSamplerCube(textureFunction->sampler))
- {
- out << " float width; float height; float layers; float levels;\n";
-
- out << " uint mip = 0;\n";
-
- out << " " << textureReference
- << ".GetDimensions(mip, width, height, layers, levels);\n";
-
- out << " bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n";
- out << " bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n";
- out << " bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n";
- out << " bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || (zMajor && t.z < 0.0f);\n";
-
- // FACE_POSITIVE_X = 000b
- // FACE_NEGATIVE_X = 001b
- // FACE_POSITIVE_Y = 010b
- // FACE_NEGATIVE_Y = 011b
- // FACE_POSITIVE_Z = 100b
- // FACE_NEGATIVE_Z = 101b
- out << " int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
-
- out << " float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
- out << " float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
- out << " float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
-
- out << " t.x = (u * 0.5f / m) + 0.5f;\n";
- out << " t.y = (v * 0.5f / m) + 0.5f;\n";
-
- // Mip level computation.
- if (textureFunction->method == TextureFunction::IMPLICIT)
- {
- out << " float2 tSized = float2(t.x * width, t.y * height);\n"
- " float2 dx = ddx(tSized);\n"
- " float2 dy = ddy(tSized);\n"
- " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n"
- " mip = uint(min(max(round(lod), 0), levels - 1));\n"
- << " " << textureReference
- << ".GetDimensions(mip, width, height, layers, levels);\n";
- }
- }
- else if (IsIntegerSampler(textureFunction->sampler) &&
- textureFunction->method != TextureFunction::FETCH)
- {
- if (IsSampler2D(textureFunction->sampler))
- {
- if (IsSamplerArray(textureFunction->sampler))
- {
- out << " float width; float height; float layers; float levels;\n";
-
- if (textureFunction->method == TextureFunction::LOD0)
- {
- out << " uint mip = 0;\n";
- }
- else if (textureFunction->method == TextureFunction::LOD0BIAS)
- {
- out << " uint mip = bias;\n";
- }
- else
- {
-
- out << " " << textureReference
- << ".GetDimensions(0, width, height, layers, levels);\n";
- if (textureFunction->method == TextureFunction::IMPLICIT ||
- textureFunction->method == TextureFunction::BIAS)
- {
- out << " float2 tSized = float2(t.x * width, t.y * height);\n"
- " float dx = length(ddx(tSized));\n"
- " float dy = length(ddy(tSized));\n"
- " float lod = log2(max(dx, dy));\n";
-
- if (textureFunction->method == TextureFunction::BIAS)
- {
- out << " lod += bias;\n";
- }
- }
- else if (textureFunction->method == TextureFunction::GRAD)
- {
- out << " float lod = log2(max(length(ddx), length(ddy)));\n";
- }
-
- out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
- }
-
- out << " " << textureReference
- << ".GetDimensions(mip, width, height, layers, levels);\n";
- }
- else
- {
- out << " float width; float height; float levels;\n";
-
- if (textureFunction->method == TextureFunction::LOD0)
- {
- out << " uint mip = 0;\n";
- }
- else if (textureFunction->method == TextureFunction::LOD0BIAS)
- {
- out << " uint mip = bias;\n";
- }
- else
- {
- out << " " << textureReference
- << ".GetDimensions(0, width, height, levels);\n";
-
- if (textureFunction->method == TextureFunction::IMPLICIT ||
- textureFunction->method == TextureFunction::BIAS)
- {
- out << " float2 tSized = float2(t.x * width, t.y * height);\n"
- " float dx = length(ddx(tSized));\n"
- " float dy = length(ddy(tSized));\n"
- " float lod = log2(max(dx, dy));\n";
-
- if (textureFunction->method == TextureFunction::BIAS)
- {
- out << " lod += bias;\n";
- }
- }
- else if (textureFunction->method == TextureFunction::GRAD)
- {
- out << " float lod = log2(max(length(ddx), length(ddy)));\n";
- }
-
- out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
- }
-
- out << " " << textureReference
- << ".GetDimensions(mip, width, height, levels);\n";
- }
- }
- else if (IsSampler3D(textureFunction->sampler))
- {
- out << " float width; float height; float depth; float levels;\n";
-
- if (textureFunction->method == TextureFunction::LOD0)
- {
- out << " uint mip = 0;\n";
- }
- else if (textureFunction->method == TextureFunction::LOD0BIAS)
- {
- out << " uint mip = bias;\n";
- }
- else
- {
- out << " " << textureReference
- << ".GetDimensions(0, width, height, depth, levels);\n";
-
- if (textureFunction->method == TextureFunction::IMPLICIT ||
- textureFunction->method == TextureFunction::BIAS)
- {
- out << " float3 tSized = float3(t.x * width, t.y * height, t.z * "
- "depth);\n"
- " float dx = length(ddx(tSized));\n"
- " float dy = length(ddy(tSized));\n"
- " float lod = log2(max(dx, dy));\n";
-
- if (textureFunction->method == TextureFunction::BIAS)
- {
- out << " lod += bias;\n";
- }
- }
- else if (textureFunction->method == TextureFunction::GRAD)
- {
- out << " float lod = log2(max(length(ddx), length(ddy)));\n";
- }
-
- out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
- }
-
- out << " " << textureReference
- << ".GetDimensions(mip, width, height, depth, levels);\n";
- }
- else UNREACHABLE();
- }
-
- out << " return ";
-
- // HLSL intrinsic
- if (mOutputType == SH_HLSL_3_0_OUTPUT)
- {
- switch(textureFunction->sampler)
- {
- case EbtSampler2D: out << "tex2D"; break;
- case EbtSamplerCube: out << "texCUBE"; break;
- default: UNREACHABLE();
- }
-
- switch(textureFunction->method)
- {
- case TextureFunction::IMPLICIT:
- out << "(" << samplerReference << ", ";
- break;
- case TextureFunction::BIAS:
- out << "bias(" << samplerReference << ", ";
- break;
- case TextureFunction::LOD:
- out << "lod(" << samplerReference << ", ";
- break;
- case TextureFunction::LOD0:
- out << "lod(" << samplerReference << ", ";
- break;
- case TextureFunction::LOD0BIAS:
- out << "lod(" << samplerReference << ", ";
- break;
- default: UNREACHABLE();
- }
- }
- else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
- {
- if (textureFunction->method == TextureFunction::GRAD)
- {
- if (IsIntegerSampler(textureFunction->sampler))
- {
- out << "" << textureReference << ".Load(";
- }
- else if (IsShadowSampler(textureFunction->sampler))
- {
- out << "" << textureReference << ".SampleCmpLevelZero(" << samplerReference
- << ", ";
- }
- else
- {
- out << "" << textureReference << ".SampleGrad(" << samplerReference << ", ";
- }
- }
- else if (IsIntegerSampler(textureFunction->sampler) ||
- textureFunction->method == TextureFunction::FETCH)
- {
- out << "" << textureReference << ".Load(";
- }
- else if (IsShadowSampler(textureFunction->sampler))
- {
- switch(textureFunction->method)
- {
- case TextureFunction::IMPLICIT:
- out << "" << textureReference << ".SampleCmp(" << samplerReference
- << ", ";
- break;
- case TextureFunction::BIAS:
- out << "" << textureReference << ".SampleCmp(" << samplerReference
- << ", ";
- break;
- case TextureFunction::LOD:
- out << "" << textureReference << ".SampleCmp(" << samplerReference
- << ", ";
- break;
- case TextureFunction::LOD0:
- out << "" << textureReference << ".SampleCmpLevelZero("
- << samplerReference << ", ";
- break;
- case TextureFunction::LOD0BIAS:
- out << "" << textureReference << ".SampleCmpLevelZero("
- << samplerReference << ", ";
- break;
- default: UNREACHABLE();
- }
- }
- else
- {
- switch(textureFunction->method)
- {
- case TextureFunction::IMPLICIT:
- out << "" << textureReference << ".Sample(" << samplerReference << ", ";
- break;
- case TextureFunction::BIAS:
- out << "" << textureReference << ".SampleBias(" << samplerReference
- << ", ";
- break;
- case TextureFunction::LOD:
- out << "" << textureReference << ".SampleLevel(" << samplerReference
- << ", ";
- break;
- case TextureFunction::LOD0:
- out << "" << textureReference << ".SampleLevel(" << samplerReference
- << ", ";
- break;
- case TextureFunction::LOD0BIAS:
- out << "" << textureReference << ".SampleLevel(" << samplerReference
- << ", ";
- break;
- default: UNREACHABLE();
- }
- }
- }
- else UNREACHABLE();
-
- // Integer sampling requires integer addresses
- TString addressx = "";
- TString addressy = "";
- TString addressz = "";
- TString close = "";
-
- if (IsIntegerSampler(textureFunction->sampler) ||
- textureFunction->method == TextureFunction::FETCH)
- {
- switch(hlslCoords)
- {
- case 2: out << "int3("; break;
- case 3: out << "int4("; break;
- default: UNREACHABLE();
- }
-
- // Convert from normalized floating-point to integer
- if (textureFunction->method != TextureFunction::FETCH)
- {
- addressx = "int(floor(width * frac((";
- addressy = "int(floor(height * frac((";
-
- if (IsSamplerArray(textureFunction->sampler))
- {
- addressz = "int(max(0, min(layers - 1, floor(0.5 + ";
- }
- else if (IsSamplerCube(textureFunction->sampler))
- {
- addressz = "((((";
- }
- else
- {
- addressz = "int(floor(depth * frac((";
- }
-
- close = "))))";
- }
- }
- else
- {
- switch(hlslCoords)
- {
- case 2: out << "float2("; break;
- case 3: out << "float3("; break;
- case 4: out << "float4("; break;
- default: UNREACHABLE();
- }
- }
-
- TString proj = ""; // Only used for projected textures
-
- if (textureFunction->proj)
- {
- switch(textureFunction->coords)
- {
- case 3: proj = " / t.z"; break;
- case 4: proj = " / t.w"; break;
- default: UNREACHABLE();
- }
- }
-
- out << addressx + ("t.x" + proj) + close + ", " + addressy + ("t.y" + proj) + close;
-
- if (mOutputType == SH_HLSL_3_0_OUTPUT)
- {
- if (hlslCoords >= 3)
- {
- if (textureFunction->coords < 3)
- {
- out << ", 0";
- }
- else
- {
- out << ", t.z" + proj;
- }
- }
-
- if (hlslCoords == 4)
- {
- switch(textureFunction->method)
- {
- case TextureFunction::BIAS: out << ", bias"; break;
- case TextureFunction::LOD: out << ", lod"; break;
- case TextureFunction::LOD0: out << ", 0"; break;
- case TextureFunction::LOD0BIAS: out << ", bias"; break;
- default: UNREACHABLE();
- }
- }
-
- out << "));\n";
- }
- else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
- {
- if (hlslCoords >= 3)
- {
- if (IsIntegerSampler(textureFunction->sampler) && IsSamplerCube(textureFunction->sampler))
- {
- out << ", face";
- }
- else
- {
- out << ", " + addressz + ("t.z" + proj) + close;
- }
- }
-
- if (textureFunction->method == TextureFunction::GRAD)
- {
- if (IsIntegerSampler(textureFunction->sampler))
- {
- out << ", mip)";
- }
- else if (IsShadowSampler(textureFunction->sampler))
- {
- // Compare value
- if (textureFunction->proj)
- {
- // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
- // The resulting third component of P' in the shadow forms is used as Dref
- out << "), t.z" << proj;
- }
- else
- {
- switch(textureFunction->coords)
- {
- case 3: out << "), t.z"; break;
- case 4: out << "), t.w"; break;
- default: UNREACHABLE();
- }
- }
- }
- else
- {
- out << "), ddx, ddy";
- }
- }
- else if (IsIntegerSampler(textureFunction->sampler) ||
- textureFunction->method == TextureFunction::FETCH)
- {
- out << ", mip)";
- }
- else if (IsShadowSampler(textureFunction->sampler))
- {
- // Compare value
- if (textureFunction->proj)
- {
- // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
- // The resulting third component of P' in the shadow forms is used as Dref
- out << "), t.z" << proj;
- }
- else
- {
- switch(textureFunction->coords)
- {
- case 3: out << "), t.z"; break;
- case 4: out << "), t.w"; break;
- default: UNREACHABLE();
- }
- }
- }
- else
- {
- switch(textureFunction->method)
- {
- case TextureFunction::IMPLICIT: out << ")"; break;
- case TextureFunction::BIAS: out << "), bias"; break;
- case TextureFunction::LOD: out << "), lod"; break;
- case TextureFunction::LOD0: out << "), 0"; break;
- case TextureFunction::LOD0BIAS: out << "), bias"; break;
- default: UNREACHABLE();
- }
- }
-
- if (textureFunction->offset)
- {
- out << ", offset";
- }
-
- out << ");";
- }
- else UNREACHABLE();
+ out << "static uint gl_LocalInvocationIndex = uint(0);\n";
}
-
- out << "\n"
- "}\n"
- "\n";
}
+ bool getDimensionsIgnoresBaseLevel =
+ (mCompileOptions & SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL) != 0;
+ mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel);
+ mImageFunctionHLSL->imageFunctionHeader(out);
+
if (mUsesFragCoord)
{
out << "#define GL_USES_FRAG_COORD\n";
@@ -1385,6 +805,16 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << "#define GL_USES_POINT_SIZE\n";
}
+ if (mHasMultiviewExtensionEnabled)
+ {
+ out << "#define GL_ANGLE_MULTIVIEW_ENABLED\n";
+ }
+
+ if (mUsesViewID)
+ {
+ out << "#define GL_USES_VIEW_ID\n";
+ }
+
if (mUsesFragDepth)
{
out << "#define GL_USES_FRAG_DEPTH\n";
@@ -1395,6 +825,31 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << "#define GL_USES_DEPTH_RANGE\n";
}
+ if (mUsesNumWorkGroups)
+ {
+ out << "#define GL_USES_NUM_WORK_GROUPS\n";
+ }
+
+ if (mUsesWorkGroupID)
+ {
+ out << "#define GL_USES_WORK_GROUP_ID\n";
+ }
+
+ if (mUsesLocalInvocationID)
+ {
+ out << "#define GL_USES_LOCAL_INVOCATION_ID\n";
+ }
+
+ if (mUsesGlobalInvocationID)
+ {
+ out << "#define GL_USES_GLOBAL_INVOCATION_ID\n";
+ }
+
+ if (mUsesLocalInvocationIndex)
+ {
+ out << "#define GL_USES_LOCAL_INVOCATION_INDEX\n";
+ }
+
if (mUsesXor)
{
out << "bool xor(bool p, bool q)\n"
@@ -1404,7 +859,7 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
"\n";
}
- builtInFunctionEmulator->OutputEmulatedFunctions(out);
+ builtInFunctionEmulator->outputEmulatedFunctions(out);
}
void OutputHLSL::visitSymbol(TIntermSymbol *node)
@@ -1412,10 +867,9 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node)
TInfoSinkBase &out = getInfoSink();
// Handle accessing std140 structs by value
- if (mFlaggedStructMappedNames.count(node) > 0)
+ if (IsInStd140InterfaceBlock(node) && node->getBasicType() == EbtStruct)
{
- out << mFlaggedStructMappedNames[node];
- return;
+ out << "map";
}
TString name = node->getSymbol();
@@ -1427,25 +881,25 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node)
}
else
{
+ const TType &nodeType = node->getType();
TQualifier qualifier = node->getQualifier();
+ ensureStructDefined(nodeType);
+
if (qualifier == EvqUniform)
{
- const TType &nodeType = node->getType();
const TInterfaceBlock *interfaceBlock = nodeType.getInterfaceBlock();
if (interfaceBlock)
{
- mReferencedInterfaceBlocks[interfaceBlock->name()] = node;
+ mReferencedUniformBlocks[interfaceBlock->name()] = node;
}
else
{
mReferencedUniforms[name] = node;
}
- ensureStructDefined(nodeType);
-
- out << DecorateUniform(name, nodeType);
+ out << DecorateVariableIfNeeded(node->getName());
}
else if (qualifier == EvqAttribute || qualifier == EvqVertexIn)
{
@@ -1456,6 +910,10 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node)
{
mReferencedVaryings[name] = node;
out << Decorate(name);
+ if (name == "ViewID_OVR")
+ {
+ mUsesViewID = true;
+ }
}
else if (qualifier == EvqFragmentOut)
{
@@ -1497,14 +955,44 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node)
mUsesInstanceID = true;
out << name;
}
+ else if (qualifier == EvqVertexID)
+ {
+ mUsesVertexID = true;
+ out << name;
+ }
else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth")
{
mUsesFragDepth = true;
out << "gl_Depth";
}
+ else if (qualifier == EvqNumWorkGroups)
+ {
+ mUsesNumWorkGroups = true;
+ out << name;
+ }
+ else if (qualifier == EvqWorkGroupID)
+ {
+ mUsesWorkGroupID = true;
+ out << name;
+ }
+ else if (qualifier == EvqLocalInvocationID)
+ {
+ mUsesLocalInvocationID = true;
+ out << name;
+ }
+ else if (qualifier == EvqGlobalInvocationID)
+ {
+ mUsesGlobalInvocationID = true;
+ out << name;
+ }
+ else if (qualifier == EvqLocalInvocationIndex)
+ {
+ mUsesLocalInvocationIndex = true;
+ out << name;
+ }
else
{
- out << DecorateIfNeeded(node->getName());
+ out << DecorateVariableIfNeeded(node->getName());
}
}
}
@@ -1553,315 +1041,372 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo
}
}
-bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
+void OutputHLSL::outputAssign(Visit visit, const TType &type, TInfoSinkBase &out)
{
- TInfoSinkBase &out = getInfoSink();
-
- // Handle accessing std140 structs by value
- if (mFlaggedStructMappedNames.count(node) > 0)
+ if (type.isArray())
{
- out << mFlaggedStructMappedNames[node];
- return false;
+ const TString &functionName = addArrayAssignmentFunction(type);
+ outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
+ }
+ else
+ {
+ outputTriplet(out, visit, "(", " = ", ")");
}
+}
- switch (node->getOp())
+bool OutputHLSL::ancestorEvaluatesToSamplerInStruct()
+{
+ for (unsigned int n = 0u; getAncestorNode(n) != nullptr; ++n)
{
- case EOpAssign:
- if (node->getLeft()->isArray())
+ TIntermNode *ancestor = getAncestorNode(n);
+ const TIntermBinary *ancestorBinary = ancestor->getAsBinaryNode();
+ if (ancestorBinary == nullptr)
{
- TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
- if (rightAgg != nullptr && rightAgg->isConstructor())
+ return false;
+ }
+ switch (ancestorBinary->getOp())
+ {
+ case EOpIndexDirectStruct:
{
- const TString &functionName = addArrayConstructIntoFunction(node->getType());
- out << functionName << "(";
- node->getLeft()->traverse(this);
- TIntermSequence *seq = rightAgg->getSequence();
- for (auto &arrayElement : *seq)
+ const TStructure *structure = ancestorBinary->getLeft()->getType().getStruct();
+ const TIntermConstantUnion *index =
+ ancestorBinary->getRight()->getAsConstantUnion();
+ const TField *field = structure->fields()[index->getIConst(0)];
+ if (IsSampler(field->type()->getBasicType()))
{
- out << ", ";
- arrayElement->traverse(this);
+ return true;
}
- out << ")";
- return false;
+ break;
}
- // ArrayReturnValueToOutParameter should have eliminated expressions where a function call is assigned.
- ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall);
-
- const TString &functionName = addArrayAssignmentFunction(node->getType());
- outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
- }
- else
- {
- outputTriplet(out, visit, "(", " = ", ")");
- }
- break;
- case EOpInitialize:
- if (visit == PreVisit)
- {
- // GLSL allows to write things like "float x = x;" where a new variable x is defined
- // and the value of an existing variable x is assigned. HLSL uses C semantics (the
- // new variable is created before the assignment is evaluated), so we need to convert
- // this to "float t = x, x = t;".
-
- TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
- ASSERT(symbolNode);
- TIntermTyped *expression = node->getRight();
-
- // TODO (jmadill): do a 'deep' scan to know if an expression is statically const
- if (symbolNode->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst)
- {
- // For variables which are not constant, defer their real initialization until
- // after we initialize uniforms.
- TIntermBinary *deferredInit = new TIntermBinary(EOpAssign);
- deferredInit->setLeft(node->getLeft());
- deferredInit->setRight(node->getRight());
- deferredInit->setType(node->getType());
- mDeferredGlobalInitializers.push_back(deferredInit);
- const TString &initString = initializer(node->getType());
- node->setRight(new TIntermRaw(node->getType(), initString));
- }
- else if (writeSameSymbolInitializer(out, symbolNode, expression))
- {
- // Skip initializing the rest of the expression
+ case EOpIndexDirect:
+ break;
+ default:
+ // Returning a sampler from indirect indexing is not supported.
return false;
+ }
+ }
+ return false;
+}
+
+bool OutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+ if (visit == PostVisit)
+ {
+ out << ".";
+ node->writeOffsetsAsXYZW(&out);
+ }
+ return true;
+}
+
+bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ switch (node->getOp())
+ {
+ case EOpComma:
+ outputTriplet(out, visit, "(", ", ", ")");
+ break;
+ case EOpAssign:
+ if (node->isArray())
+ {
+ TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
+ if (rightAgg != nullptr && rightAgg->isConstructor())
+ {
+ const TString &functionName = addArrayConstructIntoFunction(node->getType());
+ out << functionName << "(";
+ node->getLeft()->traverse(this);
+ TIntermSequence *seq = rightAgg->getSequence();
+ for (auto &arrayElement : *seq)
+ {
+ out << ", ";
+ arrayElement->traverse(this);
+ }
+ out << ")";
+ return false;
+ }
+ // ArrayReturnValueToOutParameter should have eliminated expressions where a
+ // function call is assigned.
+ ASSERT(rightAgg == nullptr);
}
- else if (writeConstantInitialization(out, symbolNode, expression))
+ outputAssign(visit, node->getType(), out);
+ break;
+ case EOpInitialize:
+ if (visit == PreVisit)
+ {
+ TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
+ ASSERT(symbolNode);
+ TIntermTyped *expression = node->getRight();
+
+ // Global initializers must be constant at this point.
+ ASSERT(symbolNode->getQualifier() != EvqGlobal ||
+ canWriteAsHLSLLiteral(expression));
+
+ // GLSL allows to write things like "float x = x;" where a new variable x is defined
+ // and the value of an existing variable x is assigned. HLSL uses C semantics (the
+ // new variable is created before the assignment is evaluated), so we need to
+ // convert
+ // this to "float t = x, x = t;".
+ if (writeSameSymbolInitializer(out, symbolNode, expression))
+ {
+ // Skip initializing the rest of the expression
+ return false;
+ }
+ else if (writeConstantInitialization(out, symbolNode, expression))
+ {
+ return false;
+ }
+ }
+ else if (visit == InVisit)
{
- return false;
+ out << " = ";
}
- }
- else if (visit == InVisit)
- {
- out << " = ";
- }
- break;
- case EOpAddAssign:
- outputTriplet(out, visit, "(", " += ", ")");
- break;
- case EOpSubAssign:
- outputTriplet(out, visit, "(", " -= ", ")");
- break;
- case EOpMulAssign:
- outputTriplet(out, visit, "(", " *= ", ")");
- break;
- case EOpVectorTimesScalarAssign:
- outputTriplet(out, visit, "(", " *= ", ")");
- break;
- case EOpMatrixTimesScalarAssign:
- outputTriplet(out, visit, "(", " *= ", ")");
- break;
- case EOpVectorTimesMatrixAssign:
- if (visit == PreVisit)
- {
- out << "(";
- }
- else if (visit == InVisit)
- {
- out << " = mul(";
- node->getLeft()->traverse(this);
- out << ", transpose(";
- }
- else
- {
- out << ")))";
- }
- break;
- case EOpMatrixTimesMatrixAssign:
- if (visit == PreVisit)
- {
- out << "(";
- }
- else if (visit == InVisit)
- {
- out << " = transpose(mul(transpose(";
- node->getLeft()->traverse(this);
- out << "), transpose(";
- }
- else
+ break;
+ case EOpAddAssign:
+ outputTriplet(out, visit, "(", " += ", ")");
+ break;
+ case EOpSubAssign:
+ outputTriplet(out, visit, "(", " -= ", ")");
+ break;
+ case EOpMulAssign:
+ outputTriplet(out, visit, "(", " *= ", ")");
+ break;
+ case EOpVectorTimesScalarAssign:
+ outputTriplet(out, visit, "(", " *= ", ")");
+ break;
+ case EOpMatrixTimesScalarAssign:
+ outputTriplet(out, visit, "(", " *= ", ")");
+ break;
+ case EOpVectorTimesMatrixAssign:
+ if (visit == PreVisit)
+ {
+ out << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << " = mul(";
+ node->getLeft()->traverse(this);
+ out << ", transpose(";
+ }
+ else
+ {
+ out << ")))";
+ }
+ break;
+ case EOpMatrixTimesMatrixAssign:
+ if (visit == PreVisit)
+ {
+ out << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << " = transpose(mul(transpose(";
+ node->getLeft()->traverse(this);
+ out << "), transpose(";
+ }
+ else
+ {
+ out << "))))";
+ }
+ break;
+ case EOpDivAssign:
+ outputTriplet(out, visit, "(", " /= ", ")");
+ break;
+ case EOpIModAssign:
+ outputTriplet(out, visit, "(", " %= ", ")");
+ break;
+ case EOpBitShiftLeftAssign:
+ outputTriplet(out, visit, "(", " <<= ", ")");
+ break;
+ case EOpBitShiftRightAssign:
+ outputTriplet(out, visit, "(", " >>= ", ")");
+ break;
+ case EOpBitwiseAndAssign:
+ outputTriplet(out, visit, "(", " &= ", ")");
+ break;
+ case EOpBitwiseXorAssign:
+ outputTriplet(out, visit, "(", " ^= ", ")");
+ break;
+ case EOpBitwiseOrAssign:
+ outputTriplet(out, visit, "(", " |= ", ")");
+ break;
+ case EOpIndexDirect:
{
- out << "))))";
- }
- break;
- case EOpDivAssign:
- outputTriplet(out, visit, "(", " /= ", ")");
- break;
- case EOpIModAssign:
- outputTriplet(out, visit, "(", " %= ", ")");
- break;
- case EOpBitShiftLeftAssign:
- outputTriplet(out, visit, "(", " <<= ", ")");
- break;
- case EOpBitShiftRightAssign:
- outputTriplet(out, visit, "(", " >>= ", ")");
- break;
- case EOpBitwiseAndAssign:
- outputTriplet(out, visit, "(", " &= ", ")");
- break;
- case EOpBitwiseXorAssign:
- outputTriplet(out, visit, "(", " ^= ", ")");
- break;
- case EOpBitwiseOrAssign:
- outputTriplet(out, visit, "(", " |= ", ")");
- break;
- case EOpIndexDirect:
- {
- const TType& leftType = node->getLeft()->getType();
+ const TType &leftType = node->getLeft()->getType();
if (leftType.isInterfaceBlock())
{
if (visit == PreVisit)
{
- TInterfaceBlock* interfaceBlock = leftType.getInterfaceBlock();
+ TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
- mReferencedInterfaceBlocks[interfaceBlock->instanceName()] = node->getLeft()->getAsSymbolNode();
- out << mUniformHLSL->interfaceBlockInstanceString(*interfaceBlock, arrayIndex);
+ mReferencedUniformBlocks[interfaceBlock->instanceName()] =
+ node->getLeft()->getAsSymbolNode();
+ out << mUniformHLSL->uniformBlockInstanceString(*interfaceBlock, arrayIndex);
return false;
}
}
+ else if (ancestorEvaluatesToSamplerInStruct())
+ {
+ // All parts of an expression that access a sampler in a struct need to use _ as
+ // separator to access the sampler variable that has been moved out of the struct.
+ outputTriplet(out, visit, "", "_", "");
+ }
else
{
outputTriplet(out, visit, "", "[", "]");
}
}
break;
- case EOpIndexIndirect:
- // We do not currently support indirect references to interface blocks
- ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
- outputTriplet(out, visit, "", "[", "]");
- break;
- case EOpIndexDirectStruct:
- if (visit == InVisit)
+ case EOpIndexIndirect:
+ // We do not currently support indirect references to interface blocks
+ ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
+ outputTriplet(out, visit, "", "[", "]");
+ break;
+ case EOpIndexDirectStruct:
{
- const TStructure* structure = node->getLeft()->getType().getStruct();
- const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion();
- const TField* field = structure->fields()[index->getIConst(0)];
- out << "." + DecorateField(field->name(), *structure);
+ const TStructure *structure = node->getLeft()->getType().getStruct();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = structure->fields()[index->getIConst(0)];
- return false;
- }
- break;
- case EOpIndexDirectInterfaceBlock:
- if (visit == InVisit)
- {
- const TInterfaceBlock* interfaceBlock = node->getLeft()->getType().getInterfaceBlock();
- const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion();
- const TField* field = interfaceBlock->fields()[index->getIConst(0)];
- out << "." + Decorate(field->name());
+ // In cases where indexing returns a sampler, we need to access the sampler variable
+ // that has been moved out of the struct.
+ bool indexingReturnsSampler = IsSampler(field->type()->getBasicType());
+ if (visit == PreVisit && indexingReturnsSampler)
+ {
+ // Samplers extracted from structs have "angle" prefix to avoid name conflicts.
+ // This prefix is only output at the beginning of the indexing expression, which
+ // may have multiple parts.
+ out << "angle";
+ }
+ if (!indexingReturnsSampler)
+ {
+ // All parts of an expression that access a sampler in a struct need to use _ as
+ // separator to access the sampler variable that has been moved out of the struct.
+ indexingReturnsSampler = ancestorEvaluatesToSamplerInStruct();
+ }
+ if (visit == InVisit)
+ {
+ if (indexingReturnsSampler)
+ {
+ out << "_" + field->name();
+ }
+ else
+ {
+ out << "." + DecorateField(field->name(), *structure);
+ }
- return false;
+ return false;
+ }
}
break;
- case EOpVectorSwizzle:
- if (visit == InVisit)
+ case EOpIndexDirectInterfaceBlock:
{
- out << ".";
-
- TIntermAggregate *swizzle = node->getRight()->getAsAggregate();
-
- if (swizzle)
+ bool structInStd140Block =
+ node->getBasicType() == EbtStruct && IsInStd140InterfaceBlock(node->getLeft());
+ if (visit == PreVisit && structInStd140Block)
{
- TIntermSequence *sequence = swizzle->getSequence();
-
- for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++)
+ out << "map";
+ }
+ if (visit == InVisit)
+ {
+ const TInterfaceBlock *interfaceBlock =
+ node->getLeft()->getType().getInterfaceBlock();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = interfaceBlock->fields()[index->getIConst(0)];
+ if (structInStd140Block)
{
- TIntermConstantUnion *element = (*sit)->getAsConstantUnion();
-
- if (element)
- {
- int i = element->getIConst(0);
-
- switch (i)
- {
- case 0: out << "x"; break;
- case 1: out << "y"; break;
- case 2: out << "z"; break;
- case 3: out << "w"; break;
- default: UNREACHABLE();
- }
- }
- else UNREACHABLE();
+ out << "_";
}
- }
- else UNREACHABLE();
+ else
+ {
+ out << ".";
+ }
+ out << Decorate(field->name());
- return false; // Fully processed
+ return false;
+ }
+ break;
}
- break;
- case EOpAdd:
- outputTriplet(out, visit, "(", " + ", ")");
- break;
- case EOpSub:
- outputTriplet(out, visit, "(", " - ", ")");
- break;
- case EOpMul:
- outputTriplet(out, visit, "(", " * ", ")");
- break;
- case EOpDiv:
- outputTriplet(out, visit, "(", " / ", ")");
- break;
- case EOpIMod:
- outputTriplet(out, visit, "(", " % ", ")");
- break;
- case EOpBitShiftLeft:
- outputTriplet(out, visit, "(", " << ", ")");
- break;
- case EOpBitShiftRight:
- outputTriplet(out, visit, "(", " >> ", ")");
- break;
- case EOpBitwiseAnd:
- outputTriplet(out, visit, "(", " & ", ")");
- break;
- case EOpBitwiseXor:
- outputTriplet(out, visit, "(", " ^ ", ")");
- break;
- case EOpBitwiseOr:
- outputTriplet(out, visit, "(", " | ", ")");
- break;
- case EOpEqual:
- case EOpNotEqual:
- outputEqual(visit, node->getLeft()->getType(), node->getOp(), out);
- break;
- case EOpLessThan:
- outputTriplet(out, visit, "(", " < ", ")");
- break;
- case EOpGreaterThan:
- outputTriplet(out, visit, "(", " > ", ")");
- break;
- case EOpLessThanEqual:
- outputTriplet(out, visit, "(", " <= ", ")");
- break;
- case EOpGreaterThanEqual:
- outputTriplet(out, visit, "(", " >= ", ")");
- break;
- case EOpVectorTimesScalar:
- outputTriplet(out, visit, "(", " * ", ")");
- break;
- case EOpMatrixTimesScalar:
- outputTriplet(out, visit, "(", " * ", ")");
- break;
- case EOpVectorTimesMatrix:
- outputTriplet(out, visit, "mul(", ", transpose(", "))");
- break;
- case EOpMatrixTimesVector:
- outputTriplet(out, visit, "mul(transpose(", "), ", ")");
- break;
- case EOpMatrixTimesMatrix:
- outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))");
- break;
- case EOpLogicalOr:
- // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have been unfolded.
- ASSERT(!node->getRight()->hasSideEffects());
- outputTriplet(out, visit, "(", " || ", ")");
- return true;
- case EOpLogicalXor:
- mUsesXor = true;
- outputTriplet(out, visit, "xor(", ", ", ")");
- break;
- case EOpLogicalAnd:
- // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have been unfolded.
- ASSERT(!node->getRight()->hasSideEffects());
- outputTriplet(out, visit, "(", " && ", ")");
- return true;
- default: UNREACHABLE();
+ case EOpAdd:
+ outputTriplet(out, visit, "(", " + ", ")");
+ break;
+ case EOpSub:
+ outputTriplet(out, visit, "(", " - ", ")");
+ break;
+ case EOpMul:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ case EOpDiv:
+ outputTriplet(out, visit, "(", " / ", ")");
+ break;
+ case EOpIMod:
+ outputTriplet(out, visit, "(", " % ", ")");
+ break;
+ case EOpBitShiftLeft:
+ outputTriplet(out, visit, "(", " << ", ")");
+ break;
+ case EOpBitShiftRight:
+ outputTriplet(out, visit, "(", " >> ", ")");
+ break;
+ case EOpBitwiseAnd:
+ outputTriplet(out, visit, "(", " & ", ")");
+ break;
+ case EOpBitwiseXor:
+ outputTriplet(out, visit, "(", " ^ ", ")");
+ break;
+ case EOpBitwiseOr:
+ outputTriplet(out, visit, "(", " | ", ")");
+ break;
+ case EOpEqual:
+ case EOpNotEqual:
+ outputEqual(visit, node->getLeft()->getType(), node->getOp(), out);
+ break;
+ case EOpLessThan:
+ outputTriplet(out, visit, "(", " < ", ")");
+ break;
+ case EOpGreaterThan:
+ outputTriplet(out, visit, "(", " > ", ")");
+ break;
+ case EOpLessThanEqual:
+ outputTriplet(out, visit, "(", " <= ", ")");
+ break;
+ case EOpGreaterThanEqual:
+ outputTriplet(out, visit, "(", " >= ", ")");
+ break;
+ case EOpVectorTimesScalar:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ case EOpMatrixTimesScalar:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ case EOpVectorTimesMatrix:
+ outputTriplet(out, visit, "mul(", ", transpose(", "))");
+ break;
+ case EOpMatrixTimesVector:
+ outputTriplet(out, visit, "mul(transpose(", "), ", ")");
+ break;
+ case EOpMatrixTimesMatrix:
+ outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))");
+ break;
+ case EOpLogicalOr:
+ // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have
+ // been unfolded.
+ ASSERT(!node->getRight()->hasSideEffects());
+ outputTriplet(out, visit, "(", " || ", ")");
+ return true;
+ case EOpLogicalXor:
+ mUsesXor = true;
+ outputTriplet(out, visit, "xor(", ", ", ")");
+ break;
+ case EOpLogicalAnd:
+ // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have
+ // been unfolded.
+ ASSERT(!node->getRight()->hasSideEffects());
+ outputTriplet(out, visit, "(", " && ", ")");
+ return true;
+ default:
+ UNREACHABLE();
}
return true;
@@ -1879,9 +1424,6 @@ bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
case EOpPositive:
outputTriplet(out, visit, "(+", "", ")");
break;
- case EOpVectorLogicalNot:
- outputTriplet(out, visit, "(!", "", ")");
- break;
case EOpLogicalNot:
outputTriplet(out, visit, "(!", "", ")");
break;
@@ -1933,542 +1475,516 @@ bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
case EOpTanh:
outputTriplet(out, visit, "tanh(", "", ")");
break;
- case EOpAsinh:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "asinh(");
- break;
- case EOpAcosh:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "acosh(");
- break;
- case EOpAtanh:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "atanh(");
- break;
- case EOpExp:
- outputTriplet(out, visit, "exp(", "", ")");
- break;
- case EOpLog:
- outputTriplet(out, visit, "log(", "", ")");
- break;
- case EOpExp2:
- outputTriplet(out, visit, "exp2(", "", ")");
- break;
- case EOpLog2:
- outputTriplet(out, visit, "log2(", "", ")");
- break;
- case EOpSqrt:
- outputTriplet(out, visit, "sqrt(", "", ")");
- break;
- case EOpInverseSqrt:
- outputTriplet(out, visit, "rsqrt(", "", ")");
- break;
- case EOpAbs:
- outputTriplet(out, visit, "abs(", "", ")");
- break;
- case EOpSign:
- outputTriplet(out, visit, "sign(", "", ")");
- break;
- case EOpFloor:
- outputTriplet(out, visit, "floor(", "", ")");
- break;
- case EOpTrunc:
- outputTriplet(out, visit, "trunc(", "", ")");
- break;
- case EOpRound:
- outputTriplet(out, visit, "round(", "", ")");
- break;
- case EOpRoundEven:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "roundEven(");
- break;
- case EOpCeil:
- outputTriplet(out, visit, "ceil(", "", ")");
- break;
- case EOpFract:
- outputTriplet(out, visit, "frac(", "", ")");
- break;
- case EOpIsNan:
- outputTriplet(out, visit, "isnan(", "", ")");
- mRequiresIEEEStrictCompiling = true;
- break;
- case EOpIsInf:
- outputTriplet(out, visit, "isinf(", "", ")");
- break;
- case EOpFloatBitsToInt:
- outputTriplet(out, visit, "asint(", "", ")");
- break;
- case EOpFloatBitsToUint:
- outputTriplet(out, visit, "asuint(", "", ")");
- break;
- case EOpIntBitsToFloat:
- outputTriplet(out, visit, "asfloat(", "", ")");
- break;
- case EOpUintBitsToFloat:
- outputTriplet(out, visit, "asfloat(", "", ")");
- break;
- case EOpPackSnorm2x16:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "packSnorm2x16(");
- break;
- case EOpPackUnorm2x16:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "packUnorm2x16(");
- break;
- case EOpPackHalf2x16:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "packHalf2x16(");
- break;
- case EOpUnpackSnorm2x16:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "unpackSnorm2x16(");
- break;
- case EOpUnpackUnorm2x16:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "unpackUnorm2x16(");
- break;
- case EOpUnpackHalf2x16:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "unpackHalf2x16(");
- break;
- case EOpLength:
- outputTriplet(out, visit, "length(", "", ")");
- break;
- case EOpNormalize:
- outputTriplet(out, visit, "normalize(", "", ")");
- break;
- case EOpDFdx:
- if(mInsideDiscontinuousLoop || mOutputLod0Function)
- {
- outputTriplet(out, visit, "(", "", ", 0.0)");
- }
- else
- {
- outputTriplet(out, visit, "ddx(", "", ")");
- }
- break;
- case EOpDFdy:
- if(mInsideDiscontinuousLoop || mOutputLod0Function)
- {
- outputTriplet(out, visit, "(", "", ", 0.0)");
- }
- else
+ case EOpAsinh:
+ case EOpAcosh:
+ case EOpAtanh:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
+ case EOpExp:
+ outputTriplet(out, visit, "exp(", "", ")");
+ break;
+ case EOpLog:
+ outputTriplet(out, visit, "log(", "", ")");
+ break;
+ case EOpExp2:
+ outputTriplet(out, visit, "exp2(", "", ")");
+ break;
+ case EOpLog2:
+ outputTriplet(out, visit, "log2(", "", ")");
+ break;
+ case EOpSqrt:
+ outputTriplet(out, visit, "sqrt(", "", ")");
+ break;
+ case EOpInverseSqrt:
+ outputTriplet(out, visit, "rsqrt(", "", ")");
+ break;
+ case EOpAbs:
+ outputTriplet(out, visit, "abs(", "", ")");
+ break;
+ case EOpSign:
+ outputTriplet(out, visit, "sign(", "", ")");
+ break;
+ case EOpFloor:
+ outputTriplet(out, visit, "floor(", "", ")");
+ break;
+ case EOpTrunc:
+ outputTriplet(out, visit, "trunc(", "", ")");
+ break;
+ case EOpRound:
+ outputTriplet(out, visit, "round(", "", ")");
+ break;
+ case EOpRoundEven:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
+ case EOpCeil:
+ outputTriplet(out, visit, "ceil(", "", ")");
+ break;
+ case EOpFract:
+ outputTriplet(out, visit, "frac(", "", ")");
+ break;
+ case EOpIsNan:
+ if (node->getUseEmulatedFunction())
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ else
+ outputTriplet(out, visit, "isnan(", "", ")");
+ mRequiresIEEEStrictCompiling = true;
+ break;
+ case EOpIsInf:
+ outputTriplet(out, visit, "isinf(", "", ")");
+ break;
+ case EOpFloatBitsToInt:
+ outputTriplet(out, visit, "asint(", "", ")");
+ break;
+ case EOpFloatBitsToUint:
+ outputTriplet(out, visit, "asuint(", "", ")");
+ break;
+ case EOpIntBitsToFloat:
+ outputTriplet(out, visit, "asfloat(", "", ")");
+ break;
+ case EOpUintBitsToFloat:
+ outputTriplet(out, visit, "asfloat(", "", ")");
+ break;
+ case EOpPackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackUnorm2x16:
+ case EOpUnpackHalf2x16:
+ case EOpPackUnorm4x8:
+ case EOpPackSnorm4x8:
+ case EOpUnpackUnorm4x8:
+ case EOpUnpackSnorm4x8:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
+ case EOpLength:
+ outputTriplet(out, visit, "length(", "", ")");
+ break;
+ case EOpNormalize:
+ outputTriplet(out, visit, "normalize(", "", ")");
+ break;
+ case EOpDFdx:
+ if (mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(out, visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(out, visit, "ddx(", "", ")");
+ }
+ break;
+ case EOpDFdy:
+ if (mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(out, visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(out, visit, "ddy(", "", ")");
+ }
+ break;
+ case EOpFwidth:
+ if (mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(out, visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(out, visit, "fwidth(", "", ")");
+ }
+ break;
+ case EOpTranspose:
+ outputTriplet(out, visit, "transpose(", "", ")");
+ break;
+ case EOpDeterminant:
+ outputTriplet(out, visit, "determinant(transpose(", "", "))");
+ break;
+ case EOpInverse:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
+
+ case EOpAny:
+ outputTriplet(out, visit, "any(", "", ")");
+ break;
+ case EOpAll:
+ outputTriplet(out, visit, "all(", "", ")");
+ break;
+ case EOpLogicalNotComponentWise:
+ outputTriplet(out, visit, "(!", "", ")");
+ break;
+ case EOpBitfieldReverse:
+ outputTriplet(out, visit, "reversebits(", "", ")");
+ break;
+ case EOpBitCount:
+ outputTriplet(out, visit, "countbits(", "", ")");
+ break;
+ case EOpFindLSB:
+ // Note that it's unclear from the HLSL docs what this returns for 0, but this is tested
+ // in GLSLTest and results are consistent with GL.
+ outputTriplet(out, visit, "firstbitlow(", "", ")");
+ break;
+ case EOpFindMSB:
+ // Note that it's unclear from the HLSL docs what this returns for 0 or -1, but this is
+ // tested in GLSLTest and results are consistent with GL.
+ outputTriplet(out, visit, "firstbithigh(", "", ")");
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return true;
+}
+
+TString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node)
+{
+ if (node->getAsSymbolNode())
+ {
+ return node->getAsSymbolNode()->getSymbol();
+ }
+ TIntermBinary *nodeBinary = node->getAsBinaryNode();
+ switch (nodeBinary->getOp())
+ {
+ case EOpIndexDirect:
{
- outputTriplet(out, visit, "ddy(", "", ")");
+ int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
+
+ TInfoSinkBase prefixSink;
+ prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" << index;
+ return TString(prefixSink.c_str());
}
- break;
- case EOpFwidth:
- if(mInsideDiscontinuousLoop || mOutputLod0Function)
+ case EOpIndexDirectStruct:
{
- outputTriplet(out, visit, "(", "", ", 0.0)");
+ const TStructure *s = nodeBinary->getLeft()->getAsTyped()->getType().getStruct();
+ int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
+ const TField *field = s->fields()[index];
+
+ TInfoSinkBase prefixSink;
+ prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_"
+ << field->name();
+ return TString(prefixSink.c_str());
}
- else
+ default:
+ UNREACHABLE();
+ return TString("");
+ }
+}
+
+bool OutputHLSL::visitBlock(Visit visit, TIntermBlock *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ if (mInsideFunction)
+ {
+ outputLineDirective(out, node->getLine().first_line);
+ out << "{\n";
+ }
+
+ for (TIntermNode *statement : *node->getSequence())
+ {
+ outputLineDirective(out, statement->getLine().first_line);
+
+ statement->traverse(this);
+
+ // Don't output ; after case labels, they're terminated by :
+ // This is needed especially since outputting a ; after a case statement would turn empty
+ // case statements into non-empty case statements, disallowing fall-through from them.
+ // Also the output code is clearer if we don't output ; after statements where it is not
+ // needed:
+ // * if statements
+ // * switch statements
+ // * blocks
+ // * function definitions
+ // * loops (do-while loops output the semicolon in VisitLoop)
+ // * declarations that don't generate output.
+ if (statement->getAsCaseNode() == nullptr && statement->getAsIfElseNode() == nullptr &&
+ statement->getAsBlock() == nullptr && statement->getAsLoopNode() == nullptr &&
+ statement->getAsSwitchNode() == nullptr &&
+ statement->getAsFunctionDefinition() == nullptr &&
+ (statement->getAsDeclarationNode() == nullptr ||
+ IsDeclarationWrittenOut(statement->getAsDeclarationNode())) &&
+ statement->getAsInvariantDeclarationNode() == nullptr)
{
- outputTriplet(out, visit, "fwidth(", "", ")");
+ out << ";\n";
}
- break;
- case EOpTranspose:
- outputTriplet(out, visit, "transpose(", "", ")");
- break;
- case EOpDeterminant:
- outputTriplet(out, visit, "determinant(transpose(", "", "))");
- break;
- case EOpInverse:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "inverse(");
- break;
+ }
- case EOpAny:
- outputTriplet(out, visit, "any(", "", ")");
- break;
- case EOpAll:
- outputTriplet(out, visit, "all(", "", ")");
- break;
- default: UNREACHABLE();
+ if (mInsideFunction)
+ {
+ outputLineDirective(out, node->getLine().last_line);
+ out << "}\n";
}
- return true;
+ return false;
}
-bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
+bool OutputHLSL::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
{
TInfoSinkBase &out = getInfoSink();
- switch (node->getOp())
+ ASSERT(mCurrentFunctionMetadata == nullptr);
+
+ size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(index != CallDAG::InvalidIndex);
+ mCurrentFunctionMetadata = &mASTMetadataList[index];
+
+ out << TypeString(node->getFunctionPrototype()->getType()) << " ";
+
+ TIntermSequence *parameters = node->getFunctionPrototype()->getSequence();
+
+ if (node->getFunctionSymbolInfo()->isMain())
{
- case EOpSequence:
- {
- if (mInsideFunction)
- {
- outputLineDirective(out, node->getLine().first_line);
- out << "{\n";
- }
+ out << "gl_main(";
+ }
+ else
+ {
+ out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj())
+ << DisambiguateFunctionName(parameters) << (mOutputLod0Function ? "Lod0(" : "(");
+ }
- for (TIntermSequence::iterator sit = node->getSequence()->begin(); sit != node->getSequence()->end(); sit++)
- {
- outputLineDirective(out, (*sit)->getLine().first_line);
+ for (unsigned int i = 0; i < parameters->size(); i++)
+ {
+ TIntermSymbol *symbol = (*parameters)[i]->getAsSymbolNode();
- (*sit)->traverse(this);
+ if (symbol)
+ {
+ ensureStructDefined(symbol->getType());
- // Don't output ; after case labels, they're terminated by :
- // This is needed especially since outputting a ; after a case statement would turn empty
- // case statements into non-empty case statements, disallowing fall-through from them.
- // Also no need to output ; after selection (if) statements or sequences. This is done just
- // for code clarity.
- TIntermSelection *asSelection = (*sit)->getAsSelectionNode();
- ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator());
- if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit))
- out << ";\n";
- }
+ out << argumentString(symbol);
- if (mInsideFunction)
+ if (i < parameters->size() - 1)
{
- outputLineDirective(out, node->getLine().last_line);
- out << "}\n";
+ out << ", ";
}
-
- return false;
}
- case EOpDeclaration:
- if (visit == PreVisit)
- {
- TIntermSequence *sequence = node->getSequence();
- TIntermTyped *variable = (*sequence)[0]->getAsTyped();
- ASSERT(sequence->size() == 1);
+ else
+ UNREACHABLE();
+ }
- if (variable &&
- (variable->getQualifier() == EvqTemporary ||
- variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst))
- {
- ensureStructDefined(variable->getType());
+ out << ")\n";
- if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration
- {
- if (!mInsideFunction)
- {
- out << "static ";
- }
+ mInsideFunction = true;
+ // The function body node will output braces.
+ node->getBody()->traverse(this);
+ mInsideFunction = false;
- out << TypeString(variable->getType()) + " ";
+ mCurrentFunctionMetadata = nullptr;
- TIntermSymbol *symbol = variable->getAsSymbolNode();
+ bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
+ if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
+ {
+ ASSERT(!node->getFunctionSymbolInfo()->isMain());
+ mOutputLod0Function = true;
+ node->traverse(this);
+ mOutputLod0Function = false;
+ }
- if (symbol)
- {
- symbol->traverse(this);
- out << ArrayString(symbol->getType());
- out << " = " + initializer(symbol->getType());
- }
- else
- {
- variable->traverse(this);
- }
- }
- else if (variable->getAsSymbolNode() && variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration
- {
- // Already added to constructor map
- }
- else UNREACHABLE();
- }
- else if (variable && IsVaryingOut(variable->getQualifier()))
- {
- for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++)
- {
- TIntermSymbol *symbol = (*sit)->getAsSymbolNode();
+ return false;
+}
- if (symbol)
- {
- // Vertex (output) varyings which are declared but not written to should still be declared to allow successful linking
- mReferencedVaryings[symbol->getSymbol()] = symbol;
- }
- else
- {
- (*sit)->traverse(this);
- }
- }
- }
+bool OutputHLSL::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ if (visit == PreVisit)
+ {
+ TIntermSequence *sequence = node->getSequence();
+ TIntermTyped *variable = (*sequence)[0]->getAsTyped();
+ ASSERT(sequence->size() == 1);
+ ASSERT(variable);
- return false;
- }
- else if (visit == InVisit)
- {
- out << ", ";
- }
- break;
- case EOpInvariantDeclaration:
- // Do not do any translation
- return false;
- case EOpPrototype:
- if (visit == PreVisit)
+ if (IsDeclarationWrittenOut(node))
{
- size_t index = mCallDag.findIndex(node);
- // Skip the prototype if it is not implemented (and thus not used)
- if (index == CallDAG::InvalidIndex)
- {
- return false;
- }
+ TInfoSinkBase &out = getInfoSink();
+ ensureStructDefined(variable->getType());
- TString name = DecorateFunctionIfNeeded(node->getNameObj());
- out << TypeString(node->getType()) << " " << name
- << (mOutputLod0Function ? "Lod0(" : "(");
+ if (!variable->getAsSymbolNode() ||
+ variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration
+ {
+ if (!mInsideFunction)
+ {
+ out << "static ";
+ }
- TIntermSequence *arguments = node->getSequence();
+ out << TypeString(variable->getType()) + " ";
- for (unsigned int i = 0; i < arguments->size(); i++)
- {
- TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode();
+ TIntermSymbol *symbol = variable->getAsSymbolNode();
if (symbol)
{
- out << argumentString(symbol);
-
- if (i < arguments->size() - 1)
- {
- out << ", ";
- }
+ symbol->traverse(this);
+ out << ArrayString(symbol->getType());
+ out << " = " + initializer(symbol->getType());
+ }
+ else
+ {
+ variable->traverse(this);
}
- else UNREACHABLE();
}
-
- out << ");\n";
-
- // Also prototype the Lod0 variant if needed
- bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
- if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
+ else if (variable->getAsSymbolNode() &&
+ variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration
{
- mOutputLod0Function = true;
- node->traverse(this);
- mOutputLod0Function = false;
+ ASSERT(variable->getBasicType() == EbtStruct);
+ // ensureStructDefined has already been called.
}
-
- return false;
+ else
+ UNREACHABLE();
}
- break;
- case EOpComma:
- outputTriplet(out, visit, "(", ", ", ")");
- break;
- case EOpFunction:
+ else if (IsVaryingOut(variable->getQualifier()))
{
- ASSERT(mCurrentFunctionMetadata == nullptr);
- TString name = TFunction::unmangleName(node->getNameObj().getString());
+ TIntermSymbol *symbol = variable->getAsSymbolNode();
+ ASSERT(symbol); // Varying declarations can't have initializers.
- size_t index = mCallDag.findIndex(node);
- ASSERT(index != CallDAG::InvalidIndex);
- mCurrentFunctionMetadata = &mASTMetadataList[index];
+ // Vertex outputs which are declared but not written to should still be declared to
+ // allow successful linking.
+ mReferencedVaryings[symbol->getSymbol()] = symbol;
+ }
+ }
+ return false;
+}
- out << TypeString(node->getType()) << " ";
+bool OutputHLSL::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
+{
+ // Do not do any translation
+ return false;
+}
- if (name == "main")
- {
- out << "gl_main(";
- }
- else
- {
- out << DecorateFunctionIfNeeded(node->getNameObj())
- << (mOutputLod0Function ? "Lod0(" : "(");
- }
+bool OutputHLSL::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
+{
+ TInfoSinkBase &out = getInfoSink();
- TIntermSequence *sequence = node->getSequence();
- TIntermSequence *arguments = (*sequence)[0]->getAsAggregate()->getSequence();
+ ASSERT(visit == PreVisit);
+ size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo());
+ // Skip the prototype if it is not implemented (and thus not used)
+ if (index == CallDAG::InvalidIndex)
+ {
+ return false;
+ }
- for (unsigned int i = 0; i < arguments->size(); i++)
- {
- TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode();
+ TIntermSequence *arguments = node->getSequence();
- if (symbol)
- {
- ensureStructDefined(symbol->getType());
+ TString name = DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj());
+ out << TypeString(node->getType()) << " " << name << DisambiguateFunctionName(arguments)
+ << (mOutputLod0Function ? "Lod0(" : "(");
- out << argumentString(symbol);
+ for (unsigned int i = 0; i < arguments->size(); i++)
+ {
+ TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode();
+ ASSERT(symbol != nullptr);
- if (i < arguments->size() - 1)
- {
- out << ", ";
- }
- }
- else UNREACHABLE();
- }
+ out << argumentString(symbol);
- out << ")\n";
+ if (i < arguments->size() - 1)
+ {
+ out << ", ";
+ }
+ }
- if (sequence->size() > 1)
- {
- mInsideFunction = true;
- TIntermNode *body = (*sequence)[1];
- // The function body node will output braces.
- ASSERT(IsSequence(body));
- body->traverse(this);
- mInsideFunction = false;
- }
- else
- {
- out << "{}\n";
- }
+ out << ");\n";
- mCurrentFunctionMetadata = nullptr;
+ // Also prototype the Lod0 variant if needed
+ bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
+ if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
+ {
+ mOutputLod0Function = true;
+ node->traverse(this);
+ mOutputLod0Function = false;
+ }
- bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
- if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
- {
- ASSERT(name != "main");
- mOutputLod0Function = true;
- node->traverse(this);
- mOutputLod0Function = false;
- }
+ return false;
+}
- return false;
- }
- break;
- case EOpFunctionCall:
+bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ switch (node->getOp())
+ {
+ case EOpCallBuiltInFunction:
+ case EOpCallFunctionInAST:
+ case EOpCallInternalRawFunction:
{
TIntermSequence *arguments = node->getSequence();
bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function;
- if (node->isUserDefined())
+ if (node->getOp() == EOpCallFunctionInAST)
{
if (node->isArray())
{
UNIMPLEMENTED();
}
- size_t index = mCallDag.findIndex(node);
+ size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo());
ASSERT(index != CallDAG::InvalidIndex);
lod0 &= mASTMetadataList[index].mNeedsLod0;
- out << DecorateFunctionIfNeeded(node->getNameObj()) << (lod0 ? "Lod0(" : "(");
+ out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj());
+ out << DisambiguateFunctionName(node->getSequence());
+ out << (lod0 ? "Lod0(" : "(");
+ }
+ else if (node->getOp() == EOpCallInternalRawFunction)
+ {
+ // This path is used for internal functions that don't have their definitions in the
+ // AST, such as precision emulation functions.
+ out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) << "(";
+ }
+ else if (node->getFunctionSymbolInfo()->isImageFunction())
+ {
+ TString name = node->getFunctionSymbolInfo()->getName();
+ TType type = (*arguments)[0]->getAsTyped()->getType();
+ TString imageFunctionName = mImageFunctionHLSL->useImageFunction(
+ name, type.getBasicType(), type.getLayoutQualifier().imageInternalFormat,
+ type.getMemoryQualifier().readonly);
+ out << imageFunctionName << "(";
}
else
{
- TString name = TFunction::unmangleName(node->getNameObj().getString());
+ const TString &name = node->getFunctionSymbolInfo()->getName();
TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType();
-
- TextureFunction textureFunction;
- textureFunction.sampler = samplerType;
- textureFunction.coords = (*arguments)[1]->getAsTyped()->getNominalSize();
- textureFunction.method = TextureFunction::IMPLICIT;
- textureFunction.proj = false;
- textureFunction.offset = false;
-
- if (name == "texture2D" || name == "textureCube" || name == "texture")
- {
- textureFunction.method = TextureFunction::IMPLICIT;
- }
- else if (name == "texture2DProj" || name == "textureProj")
- {
- textureFunction.method = TextureFunction::IMPLICIT;
- textureFunction.proj = true;
- }
- else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" ||
- name == "texture2DLodEXT" || name == "textureCubeLodEXT")
- {
- textureFunction.method = TextureFunction::LOD;
- }
- else if (name == "texture2DProjLod" || name == "textureProjLod" || name == "texture2DProjLodEXT")
- {
- textureFunction.method = TextureFunction::LOD;
- textureFunction.proj = true;
- }
- else if (name == "textureSize")
- {
- textureFunction.method = TextureFunction::SIZE;
- }
- else if (name == "textureOffset")
- {
- textureFunction.method = TextureFunction::IMPLICIT;
- textureFunction.offset = true;
- }
- else if (name == "textureProjOffset")
- {
- textureFunction.method = TextureFunction::IMPLICIT;
- textureFunction.offset = true;
- textureFunction.proj = true;
- }
- else if (name == "textureLodOffset")
- {
- textureFunction.method = TextureFunction::LOD;
- textureFunction.offset = true;
- }
- else if (name == "textureProjLodOffset")
- {
- textureFunction.method = TextureFunction::LOD;
- textureFunction.proj = true;
- textureFunction.offset = true;
- }
- else if (name == "texelFetch")
+ int coords = 0; // textureSize(gsampler2DMS) doesn't have a second argument.
+ if (arguments->size() > 1)
{
- textureFunction.method = TextureFunction::FETCH;
+ coords = (*arguments)[1]->getAsTyped()->getNominalSize();
}
- else if (name == "texelFetchOffset")
- {
- textureFunction.method = TextureFunction::FETCH;
- textureFunction.offset = true;
- }
- else if (name == "textureGrad" || name == "texture2DGradEXT")
- {
- textureFunction.method = TextureFunction::GRAD;
- }
- else if (name == "textureGradOffset")
- {
- textureFunction.method = TextureFunction::GRAD;
- textureFunction.offset = true;
- }
- else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" || name == "textureCubeGradEXT")
- {
- textureFunction.method = TextureFunction::GRAD;
- textureFunction.proj = true;
- }
- else if (name == "textureProjGradOffset")
- {
- textureFunction.method = TextureFunction::GRAD;
- textureFunction.proj = true;
- textureFunction.offset = true;
- }
- else UNREACHABLE();
+ TString textureFunctionName = mTextureFunctionHLSL->useTextureFunction(
+ name, samplerType, coords, arguments->size(), lod0, mShaderType);
+ out << textureFunctionName << "(";
+ }
- if (textureFunction.method == TextureFunction::IMPLICIT) // Could require lod 0 or have a bias argument
+ for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++)
+ {
+ TIntermTyped *typedArg = (*arg)->getAsTyped();
+ if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(typedArg->getBasicType()))
{
- unsigned int mandatoryArgumentCount = 2; // All functions have sampler and coordinate arguments
-
- if (textureFunction.offset)
- {
- mandatoryArgumentCount++;
- }
+ out << "texture_";
+ (*arg)->traverse(this);
+ out << ", sampler_";
+ }
- bool bias = (arguments->size() > mandatoryArgumentCount); // Bias argument is optional
+ (*arg)->traverse(this);
- if (lod0 || mShaderType == GL_VERTEX_SHADER)
+ if (typedArg->getType().isStructureContainingSamplers())
+ {
+ const TType &argType = typedArg->getType();
+ TVector<TIntermSymbol *> samplerSymbols;
+ TString structName = samplerNamePrefixFromStruct(typedArg);
+ argType.createSamplerSymbols("angle_" + structName, "", &samplerSymbols,
+ nullptr, mSymbolTable);
+ for (const TIntermSymbol *sampler : samplerSymbols)
{
- if (bias)
+ if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
{
- textureFunction.method = TextureFunction::LOD0BIAS;
+ out << ", texture_" << sampler->getSymbol();
+ out << ", sampler_" << sampler->getSymbol();
}
else
{
- textureFunction.method = TextureFunction::LOD0;
+ // In case of HLSL 4.1+, this symbol is the sampler index, and in case
+ // of D3D9, it's the sampler variable.
+ out << ", " + sampler->getSymbol();
}
}
- else if (bias)
- {
- textureFunction.method = TextureFunction::BIAS;
- }
- }
-
- mUsesTexture.insert(textureFunction);
-
- out << textureFunction.name();
- }
-
- for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++)
- {
- if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT &&
- IsSampler((*arg)->getAsTyped()->getBasicType()))
- {
- out << "texture_";
- (*arg)->traverse(this);
- out << ", sampler_";
}
- (*arg)->traverse(this);
-
if (arg < arguments->end() - 1)
{
out << ", ";
@@ -2479,160 +1995,79 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
return false;
}
- break;
- case EOpParameters:
- outputTriplet(out, visit, "(", ", ", ")\n{\n");
- break;
- case EOpConstructFloat:
- outputConstructor(out, visit, node->getType(), "vec1", node->getSequence());
- break;
- case EOpConstructVec2:
- outputConstructor(out, visit, node->getType(), "vec2", node->getSequence());
- break;
- case EOpConstructVec3:
- outputConstructor(out, visit, node->getType(), "vec3", node->getSequence());
- break;
- case EOpConstructVec4:
- outputConstructor(out, visit, node->getType(), "vec4", node->getSequence());
- break;
- case EOpConstructBool:
- outputConstructor(out, visit, node->getType(), "bvec1", node->getSequence());
- break;
- case EOpConstructBVec2:
- outputConstructor(out, visit, node->getType(), "bvec2", node->getSequence());
- break;
- case EOpConstructBVec3:
- outputConstructor(out, visit, node->getType(), "bvec3", node->getSequence());
- break;
- case EOpConstructBVec4:
- outputConstructor(out, visit, node->getType(), "bvec4", node->getSequence());
- break;
- case EOpConstructInt:
- outputConstructor(out, visit, node->getType(), "ivec1", node->getSequence());
- break;
- case EOpConstructIVec2:
- outputConstructor(out, visit, node->getType(), "ivec2", node->getSequence());
- break;
- case EOpConstructIVec3:
- outputConstructor(out, visit, node->getType(), "ivec3", node->getSequence());
- break;
- case EOpConstructIVec4:
- outputConstructor(out, visit, node->getType(), "ivec4", node->getSequence());
- break;
- case EOpConstructUInt:
- outputConstructor(out, visit, node->getType(), "uvec1", node->getSequence());
- break;
- case EOpConstructUVec2:
- outputConstructor(out, visit, node->getType(), "uvec2", node->getSequence());
- break;
- case EOpConstructUVec3:
- outputConstructor(out, visit, node->getType(), "uvec3", node->getSequence());
+ case EOpConstruct:
+ outputConstructor(out, visit, node);
break;
- case EOpConstructUVec4:
- outputConstructor(out, visit, node->getType(), "uvec4", node->getSequence());
- break;
- case EOpConstructMat2:
- outputConstructor(out, visit, node->getType(), "mat2", node->getSequence());
- break;
- case EOpConstructMat2x3:
- outputConstructor(out, visit, node->getType(), "mat2x3", node->getSequence());
- break;
- case EOpConstructMat2x4:
- outputConstructor(out, visit, node->getType(), "mat2x4", node->getSequence());
+ case EOpEqualComponentWise:
+ outputTriplet(out, visit, "(", " == ", ")");
break;
- case EOpConstructMat3x2:
- outputConstructor(out, visit, node->getType(), "mat3x2", node->getSequence());
+ case EOpNotEqualComponentWise:
+ outputTriplet(out, visit, "(", " != ", ")");
break;
- case EOpConstructMat3:
- outputConstructor(out, visit, node->getType(), "mat3", node->getSequence());
+ case EOpLessThanComponentWise:
+ outputTriplet(out, visit, "(", " < ", ")");
break;
- case EOpConstructMat3x4:
- outputConstructor(out, visit, node->getType(), "mat3x4", node->getSequence());
+ case EOpGreaterThanComponentWise:
+ outputTriplet(out, visit, "(", " > ", ")");
break;
- case EOpConstructMat4x2:
- outputConstructor(out, visit, node->getType(), "mat4x2", node->getSequence());
+ case EOpLessThanEqualComponentWise:
+ outputTriplet(out, visit, "(", " <= ", ")");
break;
- case EOpConstructMat4x3:
- outputConstructor(out, visit, node->getType(), "mat4x3", node->getSequence());
+ case EOpGreaterThanEqualComponentWise:
+ outputTriplet(out, visit, "(", " >= ", ")");
break;
- case EOpConstructMat4:
- outputConstructor(out, visit, node->getType(), "mat4", node->getSequence());
+ case EOpMod:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
break;
- case EOpConstructStruct:
- {
- if (node->getType().isArray())
- {
- UNIMPLEMENTED();
- }
- const TString &structName = StructNameString(*node->getType().getStruct());
- mStructureHLSL->addConstructor(node->getType(), structName, node->getSequence());
- outputTriplet(out, visit, (structName + "_ctor(").c_str(), ", ", ")");
- }
- break;
- case EOpLessThan:
- outputTriplet(out, visit, "(", " < ", ")");
+ case EOpModf:
+ outputTriplet(out, visit, "modf(", ", ", ")");
break;
- case EOpGreaterThan:
- outputTriplet(out, visit, "(", " > ", ")");
+ case EOpPow:
+ outputTriplet(out, visit, "pow(", ", ", ")");
break;
- case EOpLessThanEqual:
- outputTriplet(out, visit, "(", " <= ", ")");
+ case EOpAtan:
+ ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
break;
- case EOpGreaterThanEqual:
- outputTriplet(out, visit, "(", " >= ", ")");
+ case EOpMin:
+ outputTriplet(out, visit, "min(", ", ", ")");
break;
- case EOpVectorEqual:
- outputTriplet(out, visit, "(", " == ", ")");
+ case EOpMax:
+ outputTriplet(out, visit, "max(", ", ", ")");
break;
- case EOpVectorNotEqual:
- outputTriplet(out, visit, "(", " != ", ")");
+ case EOpClamp:
+ outputTriplet(out, visit, "clamp(", ", ", ")");
break;
- case EOpMod:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "mod(");
- break;
- case EOpModf:
- outputTriplet(out, visit, "modf(", ", ", ")");
- break;
- case EOpPow:
- outputTriplet(out, visit, "pow(", ", ", ")");
- break;
- case EOpAtan:
- ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "atan(");
- break;
- case EOpMin:
- outputTriplet(out, visit, "min(", ", ", ")");
- break;
- case EOpMax:
- outputTriplet(out, visit, "max(", ", ", ")");
- break;
- case EOpClamp:
- outputTriplet(out, visit, "clamp(", ", ", ")");
- break;
- case EOpMix:
+ case EOpMix:
{
TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped();
if (lastParamNode->getType().getBasicType() == EbtBool)
{
- // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType y, genBType a)",
+ // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType
+ // y, genBType a)",
// so use emulated version.
ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "mix(");
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
}
else
{
outputTriplet(out, visit, "lerp(", ", ", ")");
}
+ break;
}
- break;
case EOpStep:
outputTriplet(out, visit, "step(", ", ", ")");
break;
case EOpSmoothStep:
outputTriplet(out, visit, "smoothstep(", ", ", ")");
break;
+ case EOpFrexp:
+ case EOpLdexp:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
case EOpDistance:
outputTriplet(out, visit, "distance(", ", ", ")");
break;
@@ -2642,30 +2077,40 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
case EOpCross:
outputTriplet(out, visit, "cross(", ", ", ")");
break;
- case EOpFaceForward:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "faceforward(");
- break;
- case EOpReflect:
- outputTriplet(out, visit, "reflect(", ", ", ")");
- break;
- case EOpRefract:
- outputTriplet(out, visit, "refract(", ", ", ")");
- break;
- case EOpOuterProduct:
- ASSERT(node->getUseEmulatedFunction());
- writeEmulatedFunctionTriplet(out, visit, "outerProduct(");
- break;
- case EOpMul:
- outputTriplet(out, visit, "(", " * ", ")");
- break;
- default: UNREACHABLE();
+ case EOpFaceforward:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
+ case EOpReflect:
+ outputTriplet(out, visit, "reflect(", ", ", ")");
+ break;
+ case EOpRefract:
+ outputTriplet(out, visit, "refract(", ", ", ")");
+ break;
+ case EOpOuterProduct:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
+ case EOpMulMatrixComponentWise:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ case EOpBitfieldExtract:
+ case EOpBitfieldInsert:
+ case EOpUaddCarry:
+ case EOpUsubBorrow:
+ case EOpUmulExtended:
+ case EOpImulExtended:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, node->getOp());
+ break;
+ default:
+ UNREACHABLE();
}
return true;
}
-void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node)
+void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node)
{
out << "if (";
@@ -2680,8 +2125,6 @@ void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node)
if (node->getTrueBlock())
{
// The trueBlock child node will output braces.
- ASSERT(IsSequence(node->getTrueBlock()));
-
node->getTrueBlock()->traverse(this);
// Detect true discard
@@ -2702,9 +2145,7 @@ void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node)
outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
- // Either this is "else if" or the falseBlock child node will output braces.
- ASSERT(IsSequence(node->getFalseBlock()) || node->getFalseBlock()->getAsSelectionNode() != nullptr);
-
+ // The falseBlock child node will output braces.
node->getFalseBlock()->traverse(this);
outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
@@ -2720,18 +2161,19 @@ void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node)
}
}
-bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
+bool OutputHLSL::visitTernary(Visit, TIntermTernary *)
{
- TInfoSinkBase &out = getInfoSink();
+ // Ternary ops should have been already converted to something else in the AST. HLSL ternary
+ // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator.
+ UNREACHABLE();
+ return false;
+}
- ASSERT(!node->usesTernaryOperator());
+bool OutputHLSL::visitIfElse(Visit visit, TIntermIfElse *node)
+{
+ TInfoSinkBase &out = getInfoSink();
- if (!mInsideFunction)
- {
- // This is part of unfolded global initialization.
- mDeferredGlobalInitializers.push_back(node);
- return false;
- }
+ ASSERT(mInsideFunction);
// D3D errors when there is a gradient operation in a loop in an unflattened if.
if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node))
@@ -2739,7 +2181,7 @@ bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
out << "FLATTEN ";
}
- writeSelection(out, node);
+ writeIfElse(out, node);
return false;
}
@@ -2748,17 +2190,13 @@ bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node)
{
TInfoSinkBase &out = getInfoSink();
- if (node->getStatementList())
- {
- node->setStatementList(RemoveSwitchFallThrough::removeFallThrough(node->getStatementList()));
- outputTriplet(out, visit, "switch (", ") ", "");
- // The curly braces get written when visiting the statementList aggregate
- }
- else
+ ASSERT(node->getStatementList());
+ if (visit == PreVisit)
{
- // No statementList, so it won't output curly braces
- outputTriplet(out, visit, "switch (", ") {", "}\n");
+ node->setStatementList(RemoveSwitchFallThrough(node->getStatementList(), mPerfDiagnostics));
}
+ outputTriplet(out, visit, "switch (", ") ", "");
+ // The curly braces get written when visiting the statementList block.
return true;
}
@@ -2789,8 +2227,8 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
mNestedLoopDepth++;
bool wasDiscontinuous = mInsideDiscontinuousLoop;
- mInsideDiscontinuousLoop = mInsideDiscontinuousLoop ||
- mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0;
+ mInsideDiscontinuousLoop =
+ mInsideDiscontinuousLoop || mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0;
TInfoSinkBase &out = getInfoSink();
@@ -2843,7 +2281,6 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
if (node->getBody())
{
// The loop body node will output braces.
- ASSERT(IsSequence(node->getBody()));
node->getBody()->traverse(this);
}
else
@@ -2858,11 +2295,11 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
if (node->getType() == ELoopDoWhile)
{
outputLineDirective(out, node->getCondition()->getLine().first_line);
- out << "while(\n";
+ out << "while (";
node->getCondition()->traverse(this);
- out << ");";
+ out << ");\n";
}
out << "}\n";
@@ -2875,89 +2312,47 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node)
{
- TInfoSinkBase &out = getInfoSink();
-
- switch (node->getFlowOp())
+ if (visit == PreVisit)
{
- case EOpKill:
- outputTriplet(out, visit, "discard;\n", "", "");
- break;
- case EOpBreak:
- if (visit == PreVisit)
- {
- if (mNestedLoopDepth > 1)
- {
- mUsesNestedBreak = true;
- }
-
- if (mExcessiveLoopIndex)
- {
- out << "{Break";
- mExcessiveLoopIndex->traverse(this);
- out << " = true; break;}\n";
- }
- else
- {
- out << "break;\n";
- }
- }
- break;
- case EOpContinue:
- outputTriplet(out, visit, "continue;\n", "", "");
- break;
- case EOpReturn:
- if (visit == PreVisit)
- {
- if (node->getExpression())
- {
- out << "return ";
- }
- else
- {
- out << "return;\n";
- }
- }
- else if (visit == PostVisit)
- {
- if (node->getExpression())
- {
- out << ";\n";
- }
- }
- break;
- default: UNREACHABLE();
- }
-
- return true;
-}
+ TInfoSinkBase &out = getInfoSink();
-bool OutputHLSL::isSingleStatement(TIntermNode *node)
-{
- TIntermAggregate *aggregate = node->getAsAggregate();
-
- if (aggregate)
- {
- if (aggregate->getOp() == EOpSequence)
+ switch (node->getFlowOp())
{
- return false;
- }
- else if (aggregate->getOp() == EOpDeclaration)
- {
- // Declaring multiple comma-separated variables must be considered multiple statements
- // because each individual declaration has side effects which are visible in the next.
- return false;
- }
- else
- {
- for (TIntermSequence::iterator sit = aggregate->getSequence()->begin(); sit != aggregate->getSequence()->end(); sit++)
- {
- if (!isSingleStatement(*sit))
+ case EOpKill:
+ out << "discard";
+ break;
+ case EOpBreak:
+ if (mNestedLoopDepth > 1)
{
- return false;
+ mUsesNestedBreak = true;
}
- }
- return true;
+ if (mExcessiveLoopIndex)
+ {
+ out << "{Break";
+ mExcessiveLoopIndex->traverse(this);
+ out << " = true; break;}\n";
+ }
+ else
+ {
+ out << "break";
+ }
+ break;
+ case EOpContinue:
+ out << "continue";
+ break;
+ case EOpReturn:
+ if (node->getExpression())
+ {
+ out << "return ";
+ }
+ else
+ {
+ out << "return";
+ }
+ break;
+ default:
+ UNREACHABLE();
}
}
@@ -2965,28 +2360,29 @@ bool OutputHLSL::isSingleStatement(TIntermNode *node)
}
// Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them
-// (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254).
+// (The D3D documentation says 255 iterations, but the compiler complains at anything more than
+// 254).
bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
{
const int MAX_LOOP_ITERATIONS = 254;
// Parse loops of the form:
// for(int index = initial; index [comparator] limit; index += increment)
- TIntermSymbol *index = NULL;
+ TIntermSymbol *index = nullptr;
TOperator comparator = EOpNull;
- int initial = 0;
- int limit = 0;
- int increment = 0;
+ int initial = 0;
+ int limit = 0;
+ int increment = 0;
// Parse index name and intial value
if (node->getInit())
{
- TIntermAggregate *init = node->getInit()->getAsAggregate();
+ TIntermDeclaration *init = node->getInit()->getAsDeclarationNode();
if (init)
{
TIntermSequence *sequence = init->getSequence();
- TIntermTyped *variable = (*sequence)[0]->getAsTyped();
+ TIntermTyped *variable = (*sequence)[0]->getAsTyped();
if (variable && variable->getQualifier() == EvqTemporary)
{
@@ -2994,14 +2390,14 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
if (assign->getOp() == EOpInitialize)
{
- TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode();
+ TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode();
TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion();
if (symbol && constant)
{
if (constant->getBasicType() == EbtInt && constant->isScalar())
{
- index = symbol;
+ index = symbol;
initial = constant->getIConst(0);
}
}
@@ -3011,7 +2407,7 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
}
// Parse comparator and limit value
- if (index != NULL && node->getCondition())
+ if (index != nullptr && node->getCondition())
{
TIntermBinary *test = node->getCondition()->getAsBinaryNode();
@@ -3024,21 +2420,21 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
if (constant->getBasicType() == EbtInt && constant->isScalar())
{
comparator = test->getOp();
- limit = constant->getIConst(0);
+ limit = constant->getIConst(0);
}
}
}
}
// Parse increment
- if (index != NULL && comparator != EOpNull && node->getExpression())
+ if (index != nullptr && comparator != EOpNull && node->getExpression())
{
TIntermBinary *binaryTerminal = node->getExpression()->getAsBinaryNode();
- TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode();
+ TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode();
if (binaryTerminal)
{
- TOperator op = binaryTerminal->getOp();
+ TOperator op = binaryTerminal->getOp();
TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion();
if (constant)
@@ -3049,9 +2445,14 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
switch (op)
{
- case EOpAddAssign: increment = value; break;
- case EOpSubAssign: increment = -value; break;
- default: UNIMPLEMENTED();
+ case EOpAddAssign:
+ increment = value;
+ break;
+ case EOpSubAssign:
+ increment = -value;
+ break;
+ default:
+ UNIMPLEMENTED();
}
}
}
@@ -3062,16 +2463,25 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
switch (op)
{
- case EOpPostIncrement: increment = 1; break;
- case EOpPostDecrement: increment = -1; break;
- case EOpPreIncrement: increment = 1; break;
- case EOpPreDecrement: increment = -1; break;
- default: UNIMPLEMENTED();
+ case EOpPostIncrement:
+ increment = 1;
+ break;
+ case EOpPostDecrement:
+ increment = -1;
+ break;
+ case EOpPreIncrement:
+ increment = 1;
+ break;
+ case EOpPreDecrement:
+ increment = -1;
+ break;
+ default:
+ UNIMPLEMENTED();
}
}
}
- if (index != NULL && comparator != EOpNull && increment != 0)
+ if (index != nullptr && comparator != EOpNull && increment != 0)
{
if (comparator == EOpLessThanEqual)
{
@@ -3085,11 +2495,11 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
if (iterations <= MAX_LOOP_ITERATIONS)
{
- return false; // Not an excessive loop
+ return false; // Not an excessive loop
}
TIntermSymbol *restoreIndex = mExcessiveLoopIndex;
- mExcessiveLoopIndex = index;
+ mExcessiveLoopIndex = index;
out << "{int ";
index->traverse(this);
@@ -3111,13 +2521,14 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
out << ") {\n";
}
- if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment
+ if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment
{
- mExcessiveLoopIndex = NULL; // Stops setting the Break flag
+ mExcessiveLoopIndex = nullptr; // Stops setting the Break flag
}
// for(int index = initial; index < clampedLimit; index += increment)
- const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : "";
+ const char *unroll =
+ mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : "";
out << unroll << " for(";
index->traverse(this);
@@ -3163,10 +2574,11 @@ bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
return true;
}
- else UNIMPLEMENTED();
+ else
+ UNIMPLEMENTED();
}
- return false; // Not handled as an excessive loop
+ return false; // Not handled as an excessive loop
}
void OutputHLSL::outputTriplet(TInfoSinkBase &out,
@@ -3218,7 +2630,7 @@ TString OutputHLSL::argumentString(const TIntermSymbol *symbol)
}
else
{
- nameStr = DecorateIfNeeded(name);
+ nameStr = DecorateVariableIfNeeded(name);
}
if (IsSampler(type.getBasicType()))
@@ -3238,7 +2650,44 @@ TString OutputHLSL::argumentString(const TIntermSymbol *symbol)
}
}
- return QualifierString(qualifier) + " " + TypeString(type) + " " + nameStr + ArrayString(type);
+ TStringStream argString;
+ argString << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr
+ << ArrayString(type);
+
+ // If the structure parameter contains samplers, they need to be passed into the function as
+ // separate parameters. HLSL doesn't natively support samplers in structs.
+ if (type.isStructureContainingSamplers())
+ {
+ ASSERT(qualifier != EvqOut && qualifier != EvqInOut);
+ TVector<TIntermSymbol *> samplerSymbols;
+ type.createSamplerSymbols("angle" + nameStr, "", &samplerSymbols, nullptr, mSymbolTable);
+ for (const TIntermSymbol *sampler : samplerSymbols)
+ {
+ const TType &samplerType = sampler->getType();
+ if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ argString << ", const uint " << sampler->getSymbol() << ArrayString(samplerType);
+ }
+ else if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ ASSERT(IsSampler(samplerType.getBasicType()));
+ argString << ", " << QualifierString(qualifier) << " "
+ << TextureString(samplerType.getBasicType()) << " texture_"
+ << sampler->getSymbol() << ArrayString(samplerType) << ", "
+ << QualifierString(qualifier) << " "
+ << SamplerString(samplerType.getBasicType()) << " sampler_"
+ << sampler->getSymbol() << ArrayString(samplerType);
+ }
+ else
+ {
+ ASSERT(IsSampler(samplerType.getBasicType()));
+ argString << ", " << QualifierString(qualifier) << " " << TypeString(samplerType)
+ << " " << sampler->getSymbol() << ArrayString(samplerType);
+ }
+ }
+ }
+
+ return argString.str();
}
TString OutputHLSL::initializer(const TType &type)
@@ -3259,22 +2708,24 @@ TString OutputHLSL::initializer(const TType &type)
return "{" + string + "}";
}
-void OutputHLSL::outputConstructor(TInfoSinkBase &out,
- Visit visit,
- const TType &type,
- const char *name,
- const TIntermSequence *parameters)
+void OutputHLSL::outputConstructor(TInfoSinkBase &out, Visit visit, TIntermAggregate *node)
{
- if (type.isArray())
- {
- UNIMPLEMENTED();
- }
+ // Array constructors should have been already pruned from the code.
+ ASSERT(!node->getType().isArray());
if (visit == PreVisit)
{
- mStructureHLSL->addConstructor(type, name, parameters);
-
- out << name << "(";
+ TString constructorName;
+ if (node->getBasicType() == EbtStruct)
+ {
+ constructorName = mStructureHLSL->addStructConstructor(*node->getType().getStruct());
+ }
+ else
+ {
+ constructorName =
+ mStructureHLSL->addBuiltInConstructor(node->getType(), node->getSequence());
+ }
+ out << constructorName << "(";
}
else if (visit == InVisit)
{
@@ -3292,12 +2743,12 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out,
{
const TConstantUnion *constUnionIterated = constUnion;
- const TStructure* structure = type.getStruct();
+ const TStructure *structure = type.getStruct();
if (structure)
{
- out << StructNameString(*structure) + "_ctor(";
+ out << mStructureHLSL->addStructConstructor(*structure) << "(";
- const TFieldList& fields = structure->fields();
+ const TFieldList &fields = structure->fields();
for (size_t i = 0; i < fields.size(); i++)
{
@@ -3314,14 +2765,14 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out,
}
else
{
- size_t size = type.getObjectSize();
+ size_t size = type.getObjectSize();
bool writeType = size > 1;
if (writeType)
{
out << TypeString(type) << "(";
}
- constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size);
+ constUnionIterated = writeConstantUnionArray(out, constUnionIterated, size);
if (writeType)
{
out << ")";
@@ -3331,13 +2782,23 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out,
return constUnionIterated;
}
-void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr)
+void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, TOperator op)
{
- TString preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr);
- outputTriplet(out, visit, preString.c_str(), ", ", ")");
+ if (visit == PreVisit)
+ {
+ const char *opStr = GetOperatorString(op);
+ BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, opStr);
+ out << "(";
+ }
+ else
+ {
+ outputTriplet(out, visit, nullptr, ", ", ")");
+ }
}
-bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression)
+bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out,
+ TIntermSymbol *symbolNode,
+ TIntermTyped *expression)
{
sh::SearchSymbol searchSymbol(symbolNode->getSymbol());
expression->traverse(&searchSymbol);
@@ -3362,52 +2823,40 @@ bool OutputHLSL::canWriteAsHLSLLiteral(TIntermTyped *expression)
{
// We support writing constant unions and constructors that only take constant unions as
// parameters as HLSL literals.
- if (expression->getAsConstantUnion())
- {
- return true;
- }
- if (expression->getQualifier() != EvqConst || !expression->getAsAggregate() ||
- !expression->getAsAggregate()->isConstructor())
- {
- return false;
- }
- TIntermAggregate *constructor = expression->getAsAggregate();
- for (TIntermNode *&node : *constructor->getSequence())
- {
- if (!node->getAsConstantUnion())
- return false;
- }
- return true;
+ return !expression->getType().isArrayOfArrays() &&
+ (expression->getAsConstantUnion() ||
+ expression->isConstructorWithOnlyConstantUnionParameters());
}
bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
TIntermSymbol *symbolNode,
- TIntermTyped *expression)
+ TIntermTyped *initializer)
{
- if (canWriteAsHLSLLiteral(expression))
+ if (canWriteAsHLSLLiteral(initializer))
{
symbolNode->traverse(this);
- if (expression->getType().isArray())
+ ASSERT(!symbolNode->getType().isArrayOfArrays());
+ if (symbolNode->getType().isArray())
{
- out << "[" << expression->getType().getArraySize() << "]";
+ out << "[" << symbolNode->getType().getOutermostArraySize() << "]";
}
out << " = {";
- if (expression->getAsConstantUnion())
+ if (initializer->getAsConstantUnion())
{
- TIntermConstantUnion *nodeConst = expression->getAsConstantUnion();
+ TIntermConstantUnion *nodeConst = initializer->getAsConstantUnion();
const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
- WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+ writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
}
else
{
- TIntermAggregate *constructor = expression->getAsAggregate();
+ TIntermAggregate *constructor = initializer->getAsAggregate();
ASSERT(constructor != nullptr);
for (TIntermNode *&node : *constructor->getSequence())
{
TIntermConstantUnion *nodeConst = node->getAsConstantUnion();
ASSERT(nodeConst);
const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
- WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+ writeConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
if (node != constructor->getSequence()->back())
{
out << ", ";
@@ -3420,47 +2869,6 @@ bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
return false;
}
-void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out)
-{
- out << "#define ANGLE_USES_DEFERRED_INIT\n"
- << "\n"
- << "void initializeDeferredGlobals()\n"
- << "{\n";
-
- for (const auto &deferredGlobal : mDeferredGlobalInitializers)
- {
- TIntermBinary *binary = deferredGlobal->getAsBinaryNode();
- TIntermSelection *selection = deferredGlobal->getAsSelectionNode();
- if (binary != nullptr)
- {
- TIntermSymbol *symbol = binary->getLeft()->getAsSymbolNode();
- TIntermTyped *expression = binary->getRight();
- ASSERT(symbol);
- ASSERT(symbol->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst);
-
- out << " " << Decorate(symbol->getSymbol()) << " = ";
-
- if (!writeSameSymbolInitializer(out, symbol, expression))
- {
- ASSERT(mInfoSinkStack.top() == &out);
- expression->traverse(this);
- }
- out << ";\n";
- }
- else if (selection != nullptr)
- {
- writeSelection(out, selection);
- }
- else
- {
- UNREACHABLE();
- }
- }
-
- out << "}\n"
- << "\n";
-}
-
TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
{
const TFieldList &fields = structure.fields();
@@ -3476,18 +2884,19 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
const TString &structNameString = StructNameString(structure);
StructEqualityFunction *function = new StructEqualityFunction();
- function->structure = &structure;
- function->functionName = "angle_eq_" + structNameString;
+ function->structure = &structure;
+ function->functionName = "angle_eq_" + structNameString;
TInfoSinkBase fnOut;
- fnOut << "bool " << function->functionName << "(" << structNameString << " a, " << structNameString + " b)\n"
+ fnOut << "bool " << function->functionName << "(" << structNameString << " a, "
+ << structNameString + " b)\n"
<< "{\n"
" return ";
for (size_t i = 0; i < fields.size(); i++)
{
- const TField *field = fields[i];
+ const TField *field = fields[i];
const TType *fieldType = field->type();
const TString &fieldNameA = "a." + Decorate(field->name());
@@ -3507,7 +2916,8 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
fnOut << ")";
}
- fnOut << ";\n" << "}\n";
+ fnOut << ";\n"
+ << "}\n";
function->functionDefinition = fnOut.c_str();
@@ -3517,7 +2927,7 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
return function->functionName;
}
-TString OutputHLSL::addArrayEqualityFunction(const TType& type)
+TString OutputHLSL::addArrayEqualityFunction(const TType &type)
{
for (const auto &eqFunction : mArrayEqualityFunctions)
{
@@ -3527,33 +2937,31 @@ TString OutputHLSL::addArrayEqualityFunction(const TType& type)
}
}
- const TString &typeName = TypeString(type);
+ TType elementType(type);
+ elementType.toArrayElementType();
ArrayHelperFunction *function = new ArrayHelperFunction();
- function->type = type;
-
- TInfoSinkBase fnNameOut;
- fnNameOut << "angle_eq_" << type.getArraySize() << "_" << typeName;
- function->functionName = fnNameOut.c_str();
+ function->type = type;
- TType nonArrayType = type;
- nonArrayType.clearArrayness();
+ function->functionName = ArrayHelperFunctionName("angle_eq", type);
TInfoSinkBase fnOut;
- fnOut << "bool " << function->functionName << "("
- << typeName << " a[" << type.getArraySize() << "], "
- << typeName << " b[" << type.getArraySize() << "])\n"
+ const TString &typeName = TypeString(type);
+ fnOut << "bool " << function->functionName << "(" << typeName << " a" << ArrayString(type)
+ << ", " << typeName << " b" << ArrayString(type) << ")\n"
<< "{\n"
- " for (int i = 0; i < " << type.getArraySize() << "; ++i)\n"
+ " for (int i = 0; i < "
+ << type.getOutermostArraySize()
+ << "; ++i)\n"
" {\n"
" if (";
- outputEqual(PreVisit, nonArrayType, EOpNotEqual, fnOut);
+ outputEqual(PreVisit, elementType, EOpNotEqual, fnOut);
fnOut << "a[i]";
- outputEqual(InVisit, nonArrayType, EOpNotEqual, fnOut);
+ outputEqual(InVisit, elementType, EOpNotEqual, fnOut);
fnOut << "b[i]";
- outputEqual(PostVisit, nonArrayType, EOpNotEqual, fnOut);
+ outputEqual(PostVisit, elementType, EOpNotEqual, fnOut);
fnOut << ") { return false; }\n"
" }\n"
@@ -3568,7 +2976,7 @@ TString OutputHLSL::addArrayEqualityFunction(const TType& type)
return function->functionName;
}
-TString OutputHLSL::addArrayAssignmentFunction(const TType& type)
+TString OutputHLSL::addArrayAssignmentFunction(const TType &type)
{
for (const auto &assignFunction : mArrayAssignmentFunctions)
{
@@ -3578,26 +2986,35 @@ TString OutputHLSL::addArrayAssignmentFunction(const TType& type)
}
}
- const TString &typeName = TypeString(type);
+ TType elementType(type);
+ elementType.toArrayElementType();
ArrayHelperFunction function;
function.type = type;
- TInfoSinkBase fnNameOut;
- fnNameOut << "angle_assign_" << type.getArraySize() << "_" << typeName;
- function.functionName = fnNameOut.c_str();
+ function.functionName = ArrayHelperFunctionName("angle_assign", type);
TInfoSinkBase fnOut;
- fnOut << "void " << function.functionName << "(out "
- << typeName << " a[" << type.getArraySize() << "], "
- << typeName << " b[" << type.getArraySize() << "])\n"
- << "{\n"
- " for (int i = 0; i < " << type.getArraySize() << "; ++i)\n"
- " {\n"
- " a[i] = b[i];\n"
- " }\n"
- "}\n";
+ const TString &typeName = TypeString(type);
+ fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type)
+ << ", " << typeName << " b" << ArrayString(type) << ")\n"
+ << "{\n"
+ " for (int i = 0; i < "
+ << type.getOutermostArraySize()
+ << "; ++i)\n"
+ " {\n"
+ " ";
+
+ outputAssign(PreVisit, elementType, fnOut);
+ fnOut << "a[i]";
+ outputAssign(InVisit, elementType, fnOut);
+ fnOut << "b[i]";
+ outputAssign(PostVisit, elementType, fnOut);
+
+ fnOut << ";\n"
+ " }\n"
+ "}\n";
function.functionDefinition = fnOut.c_str();
@@ -3606,7 +3023,7 @@ TString OutputHLSL::addArrayAssignmentFunction(const TType& type)
return function.functionName;
}
-TString OutputHLSL::addArrayConstructIntoFunction(const TType& type)
+TString OutputHLSL::addArrayConstructIntoFunction(const TType &type)
{
for (const auto &constructIntoFunction : mArrayConstructIntoFunctions)
{
@@ -3616,29 +3033,34 @@ TString OutputHLSL::addArrayConstructIntoFunction(const TType& type)
}
}
- const TString &typeName = TypeString(type);
+ TType elementType(type);
+ elementType.toArrayElementType();
ArrayHelperFunction function;
function.type = type;
- TInfoSinkBase fnNameOut;
- fnNameOut << "angle_construct_into_" << type.getArraySize() << "_" << typeName;
- function.functionName = fnNameOut.c_str();
+ function.functionName = ArrayHelperFunctionName("angle_construct_into", type);
TInfoSinkBase fnOut;
- fnOut << "void " << function.functionName << "(out "
- << typeName << " a[" << type.getArraySize() << "]";
- for (int i = 0; i < type.getArraySize(); ++i)
+ const TString &typeName = TypeString(type);
+ fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type);
+ for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i)
{
- fnOut << ", " << typeName << " b" << i;
+ fnOut << ", " << typeName << " b" << i << ArrayString(elementType);
}
fnOut << ")\n"
"{\n";
- for (int i = 0; i < type.getArraySize(); ++i)
+ for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i)
{
- fnOut << " a[" << i << "] = b" << i << ";\n";
+ fnOut << " ";
+ outputAssign(PreVisit, elementType, fnOut);
+ fnOut << "a[" << i << "]";
+ outputAssign(InVisit, elementType, fnOut);
+ fnOut << "b" << i;
+ outputAssign(PostVisit, elementType, fnOut);
+ fnOut << ";\n";
}
fnOut << "}\n";
@@ -3651,14 +3073,12 @@ TString OutputHLSL::addArrayConstructIntoFunction(const TType& type)
void OutputHLSL::ensureStructDefined(const TType &type)
{
- TStructure *structure = type.getStruct();
-
+ const TStructure *structure = type.getStruct();
if (structure)
{
- mStructureHLSL->addConstructor(type, StructNameString(*structure), nullptr);
+ ASSERT(type.getBasicType() == EbtStruct);
+ mStructureHLSL->ensureStructDefined(*structure);
}
}
-
-
-}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h
index 8756d0ba4c..014f4f5002 100644
--- a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h
@@ -8,67 +8,94 @@
#define COMPILER_TRANSLATOR_OUTPUTHLSL_H_
#include <list>
-#include <set>
#include <map>
#include <stack>
#include "angle_gl.h"
#include "compiler/translator/ASTMetadataHLSL.h"
-#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/ParseContext.h"
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/FlagStd140Structs.h"
+#include "compiler/translator/IntermTraverse.h"
class BuiltInFunctionEmulator;
namespace sh
{
-class UnfoldShortCircuit;
class StructureHLSL;
+class TextureFunctionHLSL;
+class TSymbolTable;
+class ImageFunctionHLSL;
+class UnfoldShortCircuit;
class UniformHLSL;
-typedef std::map<TString, TIntermSymbol*> ReferencedSymbols;
+typedef std::map<TString, TIntermSymbol *> ReferencedSymbols;
class OutputHLSL : public TIntermTraverser
{
public:
- OutputHLSL(sh::GLenum shaderType, int shaderVersion,
- const TExtensionBehavior &extensionBehavior,
- const char *sourcePath, ShShaderOutput outputType,
- int numRenderTargets, const std::vector<Uniform> &uniforms,
- int compileOptions);
+ OutputHLSL(sh::GLenum shaderType,
+ int shaderVersion,
+ const TExtensionBehavior &extensionBehavior,
+ const char *sourcePath,
+ ShShaderOutput outputType,
+ int numRenderTargets,
+ const std::vector<Uniform> &uniforms,
+ ShCompileOptions compileOptions,
+ TSymbolTable *symbolTable,
+ PerformanceDiagnostics *perfDiagnostics);
~OutputHLSL();
void output(TIntermNode *treeRoot, TInfoSinkBase &objSink);
- const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const;
+ const std::map<std::string, unsigned int> &getUniformBlockRegisterMap() const;
const std::map<std::string, unsigned int> &getUniformRegisterMap() const;
static TString initializer(const TType &type);
- TInfoSinkBase &getInfoSink() { ASSERT(!mInfoSinkStack.empty()); return *mInfoSinkStack.top(); }
+ TInfoSinkBase &getInfoSink()
+ {
+ ASSERT(!mInfoSinkStack.empty());
+ return *mInfoSinkStack.top();
+ }
static bool canWriteAsHLSLLiteral(TIntermTyped *expression);
protected:
- void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator);
+ void header(TInfoSinkBase &out,
+ const std::vector<MappedStruct> &std140Structs,
+ const BuiltInFunctionEmulator *builtInFunctionEmulator) const;
+
+ void writeFloat(TInfoSinkBase &out, float f);
+ void writeSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion);
+ const TConstantUnion *writeConstantUnionArray(TInfoSinkBase &out,
+ const TConstantUnion *const constUnion,
+ const size_t size);
// Visit AST nodes and output their code to the body stream
- void visitSymbol(TIntermSymbol*);
- void visitRaw(TIntermRaw*);
- void visitConstantUnion(TIntermConstantUnion*);
- bool visitBinary(Visit visit, TIntermBinary*);
- bool visitUnary(Visit visit, TIntermUnary*);
- bool visitSelection(Visit visit, TIntermSelection*);
- bool visitSwitch(Visit visit, TIntermSwitch *);
- bool visitCase(Visit visit, TIntermCase *);
- bool visitAggregate(Visit visit, TIntermAggregate*);
- bool visitLoop(Visit visit, TIntermLoop*);
- bool visitBranch(Visit visit, TIntermBranch*);
-
- bool isSingleStatement(TIntermNode *node);
+ void visitSymbol(TIntermSymbol *) override;
+ void visitRaw(TIntermRaw *) override;
+ void visitConstantUnion(TIntermConstantUnion *) override;
+ bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *) override;
+ bool visitUnary(Visit visit, TIntermUnary *) override;
+ bool visitTernary(Visit visit, TIntermTernary *) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *) override;
+ bool visitSwitch(Visit visit, TIntermSwitch *) override;
+ bool visitCase(Visit visit, TIntermCase *) override;
+ bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *) override;
+ bool visitBlock(Visit visit, TIntermBlock *node) override;
+ bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitLoop(Visit visit, TIntermLoop *) override;
+ bool visitBranch(Visit visit, TIntermBranch *) override;
+
bool handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node);
- // Emit one of three strings depending on traverse phase. Called with literal strings so using const char* instead of TString.
+ // Emit one of three strings depending on traverse phase. Called with literal strings so using
+ // const char* instead of TString.
void outputTriplet(TInfoSinkBase &out,
Visit visit,
const char *preString,
@@ -76,32 +103,28 @@ class OutputHLSL : public TIntermTraverser
const char *postString);
void outputLineDirective(TInfoSinkBase &out, int line);
TString argumentString(const TIntermSymbol *symbol);
- int vectorSize(const TType &type) const;
-
- // Emit constructor. Called with literal names so using const char* instead of TString.
- void outputConstructor(TInfoSinkBase &out,
- Visit visit,
- const TType &type,
- const char *name,
- const TIntermSequence *parameters);
+
+ void outputConstructor(TInfoSinkBase &out, Visit visit, TIntermAggregate *node);
const TConstantUnion *writeConstantUnion(TInfoSinkBase &out,
const TType &type,
const TConstantUnion *constUnion);
void outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out);
+ void outputAssign(Visit visit, const TType &type, TInfoSinkBase &out);
- void writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr);
- void makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs);
+ void writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, TOperator op);
- // Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting)
- bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression);
+ // Returns true if it found a 'same symbol' initializer (initializer that references the
+ // variable it's initting)
+ bool writeSameSymbolInitializer(TInfoSinkBase &out,
+ TIntermSymbol *symbolNode,
+ TIntermTyped *expression);
// Returns true if variable initializer could be written using literal {} notation.
bool writeConstantInitialization(TInfoSinkBase &out,
TIntermSymbol *symbolNode,
TIntermTyped *expression);
- void writeDeferredGlobalInitializers(TInfoSinkBase &out);
- void writeSelection(TInfoSinkBase &out, TIntermSelection *node);
+ void writeIfElse(TInfoSinkBase &out, TIntermIfElse *node);
// Returns the function name
TString addStructEqualityFunction(const TStructure &structure);
@@ -117,7 +140,7 @@ class OutputHLSL : public TIntermTraverser
const TExtensionBehavior &mExtensionBehavior;
const char *mSourcePath;
const ShShaderOutput mOutputType;
- int mCompileOptions;
+ ShCompileOptions mCompileOptions;
bool mInsideFunction;
@@ -126,49 +149,23 @@ class OutputHLSL : public TIntermTraverser
TInfoSinkBase mBody;
TInfoSinkBase mFooter;
- // A stack is useful when we want to traverse in the header, or in helper functions, but not always
- // write to the body. Instead use an InfoSink stack to keep our current state intact.
+ // A stack is useful when we want to traverse in the header, or in helper functions, but not
+ // always write to the body. Instead use an InfoSink stack to keep our current state intact.
// TODO (jmadill): Just passing an InfoSink in function parameters would be simpler.
std::stack<TInfoSinkBase *> mInfoSinkStack;
ReferencedSymbols mReferencedUniforms;
- ReferencedSymbols mReferencedInterfaceBlocks;
+ ReferencedSymbols mReferencedUniformBlocks;
ReferencedSymbols mReferencedAttributes;
ReferencedSymbols mReferencedVaryings;
ReferencedSymbols mReferencedOutputVariables;
StructureHLSL *mStructureHLSL;
UniformHLSL *mUniformHLSL;
-
- struct TextureFunction
- {
- enum Method
- {
- IMPLICIT, // Mipmap LOD determined implicitly (standard lookup)
- BIAS,
- LOD,
- LOD0,
- LOD0BIAS,
- SIZE, // textureSize()
- FETCH,
- GRAD
- };
-
- TBasicType sampler;
- int coords;
- bool proj;
- bool offset;
- Method method;
-
- TString name() const;
-
- bool operator<(const TextureFunction &rhs) const;
- };
-
- typedef std::set<TextureFunction> TextureFunctionSet;
+ TextureFunctionHLSL *mTextureFunctionHLSL;
+ ImageFunctionHLSL *mImageFunctionHLSL;
// Parameters determining what goes in the header output
- TextureFunctionSet mUsesTexture;
bool mUsesFragColor;
bool mUsesFragData;
bool mUsesDepthRange;
@@ -177,16 +174,23 @@ class OutputHLSL : public TIntermTraverser
bool mUsesFrontFacing;
bool mUsesPointSize;
bool mUsesInstanceID;
+ bool mHasMultiviewExtensionEnabled;
+ bool mUsesViewID;
+ bool mUsesVertexID;
bool mUsesFragDepth;
+ bool mUsesNumWorkGroups;
+ bool mUsesWorkGroupID;
+ bool mUsesLocalInvocationID;
+ bool mUsesGlobalInvocationID;
+ bool mUsesLocalInvocationIndex;
bool mUsesXor;
bool mUsesDiscardRewriting;
bool mUsesNestedBreak;
bool mRequiresIEEEStrictCompiling;
-
int mNumRenderTargets;
- int mUniqueIndex; // For creating unique names
+ int mUniqueIndex; // For creating unique names
CallDAG mCallDag;
MetadataList mASTMetadataList;
@@ -197,15 +201,7 @@ class OutputHLSL : public TIntermTraverser
TIntermSymbol *mExcessiveLoopIndex;
- TString structInitializerString(int indent, const TStructure &structure, const TString &rhsStructName);
-
- std::map<TIntermTyped*, TString> mFlaggedStructMappedNames;
- std::map<TIntermTyped*, TString> mFlaggedStructOriginalNames;
-
- // Some initializers may have been unfolded into if statements, thus we can't evaluate all initializers
- // at global static scope in HLSL. Instead, we can initialize these static globals inside a helper function.
- // This also enables initialization of globals with uniforms.
- TIntermSequence mDeferredGlobalInitializers;
+ TString structInitializerString(int indent, const TType &type, const TString &name) const;
struct HelperFunction
{
@@ -219,28 +215,34 @@ class OutputHLSL : public TIntermTraverser
// which we add the functions, since nested structures call each other recursively, and
// structure equality functions may need to call array equality functions and vice versa.
// The ownership of the pointers is maintained by the type-specific arrays.
- std::vector<HelperFunction*> mEqualityFunctions;
+ std::vector<HelperFunction *> mEqualityFunctions;
struct StructEqualityFunction : public HelperFunction
{
const TStructure *structure;
};
- std::vector<StructEqualityFunction*> mStructEqualityFunctions;
+ std::vector<StructEqualityFunction *> mStructEqualityFunctions;
struct ArrayHelperFunction : public HelperFunction
{
TType type;
};
- std::vector<ArrayHelperFunction*> mArrayEqualityFunctions;
+ std::vector<ArrayHelperFunction *> mArrayEqualityFunctions;
std::vector<ArrayHelperFunction> mArrayAssignmentFunctions;
- // The construct-into functions are functions that fill an N-element array passed as an out parameter
- // with the other N parameters of the function. This is used to work around that arrays can't be
- // return values in HLSL.
+ // The construct-into functions are functions that fill an N-element array passed as an out
+ // parameter with the other N parameters of the function. This is used to work around that
+ // arrays can't be return values in HLSL.
std::vector<ArrayHelperFunction> mArrayConstructIntoFunctions;
-};
+ PerformanceDiagnostics *mPerfDiagnostics;
+
+ private:
+ TString generateStructMapping(const std::vector<MappedStruct> &std140Structs) const;
+ TString samplerNamePrefixFromStruct(TIntermTyped *node);
+ bool ancestorEvaluatesToSamplerInStruct();
+};
}
-#endif // COMPILER_TRANSLATOR_OUTPUTHLSL_H_
+#endif // COMPILER_TRANSLATOR_OUTPUTHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputTree.cpp b/src/3rdparty/angle/src/compiler/translator/OutputTree.cpp
new file mode 100644
index 0000000000..25e8298af3
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/OutputTree.cpp
@@ -0,0 +1,682 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void OutputFunction(TInfoSinkBase &out, const char *str, TFunctionSymbolInfo *info)
+{
+ const char *internal = info->getNameObj().isInternal() ? " (internal function)" : "";
+ out << str << internal << ": " << info->getNameObj().getString() << " (symbol id "
+ << info->getId().get() << ")";
+}
+
+// Two purposes:
+// 1. Show an example of how to iterate tree. Functions can also directly call traverse() on
+// children themselves to have finer grained control over the process than shown here, though
+// that's not recommended if it can be avoided.
+// 2. Print out a text based description of the tree.
+
+// The traverser subclass is used to carry along data from node to node in the traversal.
+class TOutputTraverser : public TIntermTraverser
+{
+ public:
+ TOutputTraverser(TInfoSinkBase &out) : TIntermTraverser(true, false, false), mOut(out) {}
+
+ protected:
+ void visitSymbol(TIntermSymbol *) override;
+ void visitConstantUnion(TIntermConstantUnion *) override;
+ bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *) override;
+ bool visitUnary(Visit visit, TIntermUnary *) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override;
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override;
+ bool visitCase(Visit visit, TIntermCase *node) override;
+ bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *) override;
+ bool visitBlock(Visit visit, TIntermBlock *) override;
+ bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitLoop(Visit visit, TIntermLoop *) override;
+ bool visitBranch(Visit visit, TIntermBranch *) override;
+
+ TInfoSinkBase &mOut;
+};
+
+//
+// Helper functions for printing, not part of traversing.
+//
+void OutputTreeText(TInfoSinkBase &out, TIntermNode *node, const int depth)
+{
+ int i;
+
+ out.location(node->getLine().first_file, node->getLine().first_line);
+
+ for (i = 0; i < depth; ++i)
+ out << " ";
+}
+
+//
+// The rest of the file are the traversal functions. The last one
+// is the one that starts the traversal.
+//
+// Return true from interior nodes to have the external traversal
+// continue on to children. If you process children yourself,
+// return false.
+//
+
+void TOutputTraverser::visitSymbol(TIntermSymbol *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ mOut << "'" << node->getSymbol() << "' ";
+ mOut << "(symbol id " << node->getId() << ") ";
+ mOut << "(" << node->getCompleteString() << ")";
+ mOut << "\n";
+}
+
+bool TOutputTraverser::visitSwizzle(Visit visit, TIntermSwizzle *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "vector swizzle (";
+ node->writeOffsetsAsXYZW(&mOut);
+ mOut << ")";
+
+ mOut << " (" << node->getCompleteString() << ")";
+ mOut << "\n";
+ return true;
+}
+
+bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ switch (node->getOp())
+ {
+ case EOpComma:
+ mOut << "comma";
+ break;
+ case EOpAssign:
+ mOut << "move second child to first child";
+ break;
+ case EOpInitialize:
+ mOut << "initialize first child with second child";
+ break;
+ case EOpAddAssign:
+ mOut << "add second child into first child";
+ break;
+ case EOpSubAssign:
+ mOut << "subtract second child into first child";
+ break;
+ case EOpMulAssign:
+ mOut << "multiply second child into first child";
+ break;
+ case EOpVectorTimesMatrixAssign:
+ mOut << "matrix mult second child into first child";
+ break;
+ case EOpVectorTimesScalarAssign:
+ mOut << "vector scale second child into first child";
+ break;
+ case EOpMatrixTimesScalarAssign:
+ mOut << "matrix scale second child into first child";
+ break;
+ case EOpMatrixTimesMatrixAssign:
+ mOut << "matrix mult second child into first child";
+ break;
+ case EOpDivAssign:
+ mOut << "divide second child into first child";
+ break;
+ case EOpIModAssign:
+ mOut << "modulo second child into first child";
+ break;
+ case EOpBitShiftLeftAssign:
+ mOut << "bit-wise shift first child left by second child";
+ break;
+ case EOpBitShiftRightAssign:
+ mOut << "bit-wise shift first child right by second child";
+ break;
+ case EOpBitwiseAndAssign:
+ mOut << "bit-wise and second child into first child";
+ break;
+ case EOpBitwiseXorAssign:
+ mOut << "bit-wise xor second child into first child";
+ break;
+ case EOpBitwiseOrAssign:
+ mOut << "bit-wise or second child into first child";
+ break;
+
+ case EOpIndexDirect:
+ mOut << "direct index";
+ break;
+ case EOpIndexIndirect:
+ mOut << "indirect index";
+ break;
+ case EOpIndexDirectStruct:
+ mOut << "direct index for structure";
+ break;
+ case EOpIndexDirectInterfaceBlock:
+ mOut << "direct index for interface block";
+ break;
+
+ case EOpAdd:
+ mOut << "add";
+ break;
+ case EOpSub:
+ mOut << "subtract";
+ break;
+ case EOpMul:
+ mOut << "component-wise multiply";
+ break;
+ case EOpDiv:
+ mOut << "divide";
+ break;
+ case EOpIMod:
+ mOut << "modulo";
+ break;
+ case EOpBitShiftLeft:
+ mOut << "bit-wise shift left";
+ break;
+ case EOpBitShiftRight:
+ mOut << "bit-wise shift right";
+ break;
+ case EOpBitwiseAnd:
+ mOut << "bit-wise and";
+ break;
+ case EOpBitwiseXor:
+ mOut << "bit-wise xor";
+ break;
+ case EOpBitwiseOr:
+ mOut << "bit-wise or";
+ break;
+
+ case EOpEqual:
+ mOut << "Compare Equal";
+ break;
+ case EOpNotEqual:
+ mOut << "Compare Not Equal";
+ break;
+ case EOpLessThan:
+ mOut << "Compare Less Than";
+ break;
+ case EOpGreaterThan:
+ mOut << "Compare Greater Than";
+ break;
+ case EOpLessThanEqual:
+ mOut << "Compare Less Than or Equal";
+ break;
+ case EOpGreaterThanEqual:
+ mOut << "Compare Greater Than or Equal";
+ break;
+
+ case EOpVectorTimesScalar:
+ mOut << "vector-scale";
+ break;
+ case EOpVectorTimesMatrix:
+ mOut << "vector-times-matrix";
+ break;
+ case EOpMatrixTimesVector:
+ mOut << "matrix-times-vector";
+ break;
+ case EOpMatrixTimesScalar:
+ mOut << "matrix-scale";
+ break;
+ case EOpMatrixTimesMatrix:
+ mOut << "matrix-multiply";
+ break;
+
+ case EOpLogicalOr:
+ mOut << "logical-or";
+ break;
+ case EOpLogicalXor:
+ mOut << "logical-xor";
+ break;
+ case EOpLogicalAnd:
+ mOut << "logical-and";
+ break;
+ default:
+ mOut << "<unknown op>";
+ }
+
+ mOut << " (" << node->getCompleteString() << ")";
+
+ mOut << "\n";
+
+ // Special handling for direct indexes. Because constant
+ // unions are not aware they are struct indexes, treat them
+ // here where we have that contextual knowledge.
+ if (node->getOp() == EOpIndexDirectStruct || node->getOp() == EOpIndexDirectInterfaceBlock)
+ {
+ node->getLeft()->traverse(this);
+
+ TIntermConstantUnion *intermConstantUnion = node->getRight()->getAsConstantUnion();
+ ASSERT(intermConstantUnion);
+
+ OutputTreeText(mOut, intermConstantUnion, mDepth + 1);
+
+ // The following code finds the field name from the constant union
+ const TConstantUnion *constantUnion = intermConstantUnion->getUnionArrayPointer();
+ const TStructure *structure = node->getLeft()->getType().getStruct();
+ const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock();
+ ASSERT(structure || interfaceBlock);
+
+ const TFieldList &fields = structure ? structure->fields() : interfaceBlock->fields();
+
+ const TField *field = fields[constantUnion->getIConst()];
+
+ mOut << constantUnion->getIConst() << " (field '" << field->name() << "')";
+
+ mOut << "\n";
+
+ return false;
+ }
+
+ return true;
+}
+
+bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ switch (node->getOp())
+ {
+ // Give verbose names for ops that have special syntax and some built-in functions that are
+ // easy to confuse with others, but mostly use GLSL names for functions.
+ case EOpNegative:
+ mOut << "Negate value";
+ break;
+ case EOpPositive:
+ mOut << "Positive sign";
+ break;
+ case EOpLogicalNot:
+ mOut << "negation";
+ break;
+ case EOpBitwiseNot:
+ mOut << "bit-wise not";
+ break;
+
+ case EOpPostIncrement:
+ mOut << "Post-Increment";
+ break;
+ case EOpPostDecrement:
+ mOut << "Post-Decrement";
+ break;
+ case EOpPreIncrement:
+ mOut << "Pre-Increment";
+ break;
+ case EOpPreDecrement:
+ mOut << "Pre-Decrement";
+ break;
+
+ case EOpArrayLength:
+ mOut << "Array length";
+ break;
+
+ case EOpLogicalNotComponentWise:
+ mOut << "component-wise not";
+ break;
+
+ default:
+ mOut << GetOperatorString(node->getOp());
+ break;
+ }
+
+ mOut << " (" << node->getCompleteString() << ")";
+
+ mOut << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "Function Definition:\n";
+ mOut << "\n";
+ return true;
+}
+
+bool TOutputTraverser::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "Invariant Declaration:\n";
+ return true;
+}
+
+bool TOutputTraverser::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+ OutputFunction(mOut, "Function Prototype", node->getFunctionSymbolInfo());
+ mOut << " (" << node->getCompleteString() << ")";
+ mOut << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ if (node->getOp() == EOpNull)
+ {
+ mOut.prefix(SH_ERROR);
+ mOut << "node is still EOpNull!\n";
+ return true;
+ }
+
+ // Give verbose names for some built-in functions that are easy to confuse with others, but
+ // mostly use GLSL names for functions.
+ switch (node->getOp())
+ {
+ case EOpCallFunctionInAST:
+ OutputFunction(mOut, "Call an user-defined function", node->getFunctionSymbolInfo());
+ break;
+ case EOpCallInternalRawFunction:
+ OutputFunction(mOut, "Call an internal function with raw implementation",
+ node->getFunctionSymbolInfo());
+ break;
+ case EOpCallBuiltInFunction:
+ OutputFunction(mOut, "Call a built-in function", node->getFunctionSymbolInfo());
+ break;
+
+ case EOpConstruct:
+ // The type of the constructor will be printed below.
+ mOut << "Construct";
+ break;
+
+ case EOpEqualComponentWise:
+ mOut << "component-wise equal";
+ break;
+ case EOpNotEqualComponentWise:
+ mOut << "component-wise not equal";
+ break;
+ case EOpLessThanComponentWise:
+ mOut << "component-wise less than";
+ break;
+ case EOpGreaterThanComponentWise:
+ mOut << "component-wise greater than";
+ break;
+ case EOpLessThanEqualComponentWise:
+ mOut << "component-wise less than or equal";
+ break;
+ case EOpGreaterThanEqualComponentWise:
+ mOut << "component-wise greater than or equal";
+ break;
+
+ case EOpDot:
+ mOut << "dot product";
+ break;
+ case EOpCross:
+ mOut << "cross product";
+ break;
+ case EOpMulMatrixComponentWise:
+ mOut << "component-wise multiply";
+ break;
+
+ default:
+ mOut << GetOperatorString(node->getOp());
+ break;
+ }
+
+ mOut << " (" << node->getCompleteString() << ")";
+
+ mOut << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitBlock(Visit visit, TIntermBlock *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "Code block\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "Declaration\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ mOut << "Ternary selection";
+ mOut << " (" << node->getCompleteString() << ")\n";
+
+ ++mDepth;
+
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "Condition\n";
+ node->getCondition()->traverse(this);
+
+ OutputTreeText(mOut, node, mDepth);
+ if (node->getTrueExpression())
+ {
+ mOut << "true case\n";
+ node->getTrueExpression()->traverse(this);
+ }
+ if (node->getFalseExpression())
+ {
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "false case\n";
+ node->getFalseExpression()->traverse(this);
+ }
+
+ --mDepth;
+
+ return false;
+}
+
+bool TOutputTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ mOut << "If test\n";
+
+ ++mDepth;
+
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "Condition\n";
+ node->getCondition()->traverse(this);
+
+ OutputTreeText(mOut, node, mDepth);
+ if (node->getTrueBlock())
+ {
+ mOut << "true case\n";
+ node->getTrueBlock()->traverse(this);
+ }
+ else
+ {
+ mOut << "true case is null\n";
+ }
+
+ if (node->getFalseBlock())
+ {
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "false case\n";
+ node->getFalseBlock()->traverse(this);
+ }
+
+ --mDepth;
+
+ return false;
+}
+
+bool TOutputTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ mOut << "Switch\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitCase(Visit visit, TIntermCase *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ if (node->getCondition() == nullptr)
+ {
+ mOut << "Default\n";
+ }
+ else
+ {
+ mOut << "Case\n";
+ }
+
+ return true;
+}
+
+void TOutputTraverser::visitConstantUnion(TIntermConstantUnion *node)
+{
+ size_t size = node->getType().getObjectSize();
+
+ for (size_t i = 0; i < size; i++)
+ {
+ OutputTreeText(mOut, node, mDepth);
+ switch (node->getUnionArrayPointer()[i].getType())
+ {
+ case EbtBool:
+ if (node->getUnionArrayPointer()[i].getBConst())
+ mOut << "true";
+ else
+ mOut << "false";
+
+ mOut << " ("
+ << "const bool"
+ << ")";
+ mOut << "\n";
+ break;
+ case EbtFloat:
+ mOut << node->getUnionArrayPointer()[i].getFConst();
+ mOut << " (const float)\n";
+ break;
+ case EbtInt:
+ mOut << node->getUnionArrayPointer()[i].getIConst();
+ mOut << " (const int)\n";
+ break;
+ case EbtUInt:
+ mOut << node->getUnionArrayPointer()[i].getUConst();
+ mOut << " (const uint)\n";
+ break;
+ case EbtYuvCscStandardEXT:
+ mOut << getYuvCscStandardEXTString(
+ node->getUnionArrayPointer()[i].getYuvCscStandardEXTConst());
+ mOut << " (const yuvCscStandardEXT)\n";
+ break;
+ default:
+ mOut.prefix(SH_ERROR);
+ mOut << "Unknown constant\n";
+ break;
+ }
+ }
+}
+
+bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ mOut << "Loop with condition ";
+ if (node->getType() == ELoopDoWhile)
+ mOut << "not ";
+ mOut << "tested first\n";
+
+ ++mDepth;
+
+ OutputTreeText(mOut, node, mDepth);
+ if (node->getCondition())
+ {
+ mOut << "Loop Condition\n";
+ node->getCondition()->traverse(this);
+ }
+ else
+ {
+ mOut << "No loop condition\n";
+ }
+
+ OutputTreeText(mOut, node, mDepth);
+ if (node->getBody())
+ {
+ mOut << "Loop Body\n";
+ node->getBody()->traverse(this);
+ }
+ else
+ {
+ mOut << "No loop body\n";
+ }
+
+ if (node->getExpression())
+ {
+ OutputTreeText(mOut, node, mDepth);
+ mOut << "Loop Terminal Expression\n";
+ node->getExpression()->traverse(this);
+ }
+
+ --mDepth;
+
+ return false;
+}
+
+bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch *node)
+{
+ OutputTreeText(mOut, node, mDepth);
+
+ switch (node->getFlowOp())
+ {
+ case EOpKill:
+ mOut << "Branch: Kill";
+ break;
+ case EOpBreak:
+ mOut << "Branch: Break";
+ break;
+ case EOpContinue:
+ mOut << "Branch: Continue";
+ break;
+ case EOpReturn:
+ mOut << "Branch: Return";
+ break;
+ default:
+ mOut << "Branch: Unknown Branch";
+ break;
+ }
+
+ if (node->getExpression())
+ {
+ mOut << " with expression\n";
+ ++mDepth;
+ node->getExpression()->traverse(this);
+ --mDepth;
+ }
+ else
+ {
+ mOut << "\n";
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+void OutputTree(TIntermNode *root, TInfoSinkBase &out)
+{
+ TOutputTraverser it(out);
+ ASSERT(root);
+ root->traverse(&it);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputTree.h b/src/3rdparty/angle/src/compiler/translator/OutputTree.h
new file mode 100644
index 0000000000..9f11989cb1
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/OutputTree.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Output the AST intermediate representation of the GLSL code.
+
+#ifndef COMPILER_TRANSLATOR_OUTPUTTREE_H_
+#define COMPILER_TRANSLATOR_OUTPUTTREE_H_
+
+namespace sh
+{
+
+class TIntermNode;
+class TInfoSinkBase;
+
+// Output the AST along with metadata.
+void OutputTree(TIntermNode *root, TInfoSinkBase &out);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_OUTPUTTREE_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.cpp
new file mode 100644
index 0000000000..6d11deb898
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.cpp
@@ -0,0 +1,80 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// OutputVulkanGLSL:
+// Code that outputs shaders that fit GL_KHR_vulkan_glsl.
+// The shaders are then fed into glslang to spit out SPIR-V (libANGLE-side).
+// See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt
+//
+
+#include "compiler/translator/OutputVulkanGLSL.h"
+
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+TOutputVulkanGLSL::TOutputVulkanGLSL(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput output,
+ ShCompileOptions compileOptions)
+ : TOutputGLSL(objSink,
+ clampingStrategy,
+ hashFunction,
+ nameMap,
+ symbolTable,
+ shaderType,
+ shaderVersion,
+ output,
+ compileOptions)
+{
+}
+
+// TODO(jmadill): This is not complete.
+void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
+{
+ const TType &type = variable->getType();
+
+ bool needsCustomLayout =
+ (type.getQualifier() == EvqAttribute || type.getQualifier() == EvqFragmentOut ||
+ type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier()) ||
+ IsSampler(type.getBasicType()));
+
+ if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout)
+ {
+ return;
+ }
+
+ TInfoSinkBase &out = objSink();
+ const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
+ out << "layout(";
+
+ // This isn't super clean, but it gets the job done.
+ // See corresponding code in GlslangWrapper.cpp.
+ // TODO(jmadill): Ensure declarations are separated.
+
+ TIntermSymbol *symbol = variable->getAsSymbolNode();
+ ASSERT(symbol);
+
+ if (needsCustomLayout)
+ {
+ out << "@@ LAYOUT-" << symbol->getName().getString() << " @@";
+ }
+
+ if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
+ out << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
+ }
+
+ out << ") ";
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.h b/src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.h
new file mode 100644
index 0000000000..6e5da8b53e
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.h
@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// OutputVulkanGLSL:
+// Code that outputs shaders that fit GL_KHR_vulkan_glsl.
+// The shaders are then fed into glslang to spit out SPIR-V (libANGLE-side).
+// See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt
+//
+
+#include "compiler/translator/OutputGLSL.h"
+
+namespace sh
+{
+
+class TOutputVulkanGLSL : public TOutputGLSL
+{
+ public:
+ TOutputVulkanGLSL(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable *symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput output,
+ ShCompileOptions compileOptions);
+
+ protected:
+ void writeLayoutQualifier(TIntermTyped *variable) override;
+};
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ParamType.h b/src/3rdparty/angle/src/compiler/translator/ParamType.h
new file mode 100644
index 0000000000..dddb4e9901
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ParamType.h
@@ -0,0 +1,102 @@
+//
+// Copyright 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ParamType:
+// Helper type for built-in function emulator tables. Defines types for parameters.
+
+#ifndef COMPILER_TRANSLATOR_PARAMTYPE_H_
+#define COMPILER_TRANSLATOR_PARAMTYPE_H_
+
+#include "common/angleutils.h"
+#include "compiler/translator/BaseTypes.h"
+
+namespace sh
+{
+
+enum class ParamType : uint8_t
+{
+ Void,
+ Bool1,
+ Bool2,
+ Bool3,
+ Bool4,
+ Float1,
+ Float2,
+ Float3,
+ Float4,
+ Int1,
+ Int2,
+ Int3,
+ Int4,
+ Mat2,
+ Mat3,
+ Mat4,
+ Uint1,
+ Uint2,
+ Uint3,
+ Uint4,
+ Last,
+};
+
+struct ParamTypeInfo
+{
+ ParamType self;
+ TBasicType basicType;
+ int primarySize;
+ int secondarySize;
+};
+
+constexpr ParamTypeInfo g_ParamTypeInfo[] = {
+ {ParamType::Void, EbtVoid, 1, 1}, {ParamType::Bool1, EbtBool, 1, 1},
+ {ParamType::Bool2, EbtBool, 2, 1}, {ParamType::Bool3, EbtBool, 3, 1},
+ {ParamType::Bool4, EbtBool, 4, 1}, {ParamType::Float1, EbtFloat, 1, 1},
+ {ParamType::Float2, EbtFloat, 2, 1}, {ParamType::Float3, EbtFloat, 3, 1},
+ {ParamType::Float4, EbtFloat, 4, 1}, {ParamType::Int1, EbtInt, 1, 1},
+ {ParamType::Int2, EbtInt, 2, 1}, {ParamType::Int3, EbtInt, 3, 1},
+ {ParamType::Int4, EbtInt, 4, 1}, {ParamType::Mat2, EbtFloat, 2, 2},
+ {ParamType::Mat3, EbtFloat, 3, 3}, {ParamType::Mat4, EbtFloat, 4, 4},
+ {ParamType::Uint1, EbtUInt, 1, 1}, {ParamType::Uint2, EbtUInt, 2, 1},
+ {ParamType::Uint3, EbtUInt, 3, 1}, {ParamType::Uint4, EbtUInt, 4, 1},
+};
+
+constexpr size_t ParamTypeIndex(ParamType paramType)
+{
+ return static_cast<size_t>(paramType);
+}
+
+constexpr size_t NumParamTypes()
+{
+ return ParamTypeIndex(ParamType::Last);
+}
+
+static_assert(ArraySize(g_ParamTypeInfo) == NumParamTypes(), "Invalid array size");
+
+constexpr TBasicType GetBasicType(ParamType paramType)
+{
+ return g_ParamTypeInfo[ParamTypeIndex(paramType)].basicType;
+}
+
+constexpr int GetPrimarySize(ParamType paramType)
+{
+ return g_ParamTypeInfo[ParamTypeIndex(paramType)].primarySize;
+}
+
+constexpr int GetSecondarySize(ParamType paramType)
+{
+ return g_ParamTypeInfo[ParamTypeIndex(paramType)].secondarySize;
+}
+
+constexpr bool SameParamType(ParamType paramType,
+ TBasicType basicType,
+ int primarySize,
+ int secondarySize)
+{
+ return GetBasicType(paramType) == basicType && primarySize == GetPrimarySize(paramType) &&
+ secondarySize == GetSecondarySize(paramType);
+}
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_PARAMTYPE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp b/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp
index 235351cf41..c97f91d781 100644
--- a/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp
@@ -9,34 +9,252 @@
#include <stdarg.h>
#include <stdio.h>
+#include "common/mathutil.h"
#include "compiler/preprocessor/SourceLocation.h"
#include "compiler/translator/Cache.h"
-#include "compiler/translator/glslang.h"
-#include "compiler/translator/ValidateSwitch.h"
+#include "compiler/translator/IntermNode_util.h"
#include "compiler/translator/ValidateGlobalInitializer.h"
+#include "compiler/translator/ValidateSwitch.h"
+#include "compiler/translator/glslang.h"
#include "compiler/translator/util.h"
+namespace sh
+{
+
///////////////////////////////////////////////////////////////////////
//
// Sub- vector and matrix fields
//
////////////////////////////////////////////////////////////////////////
-//
-// Look at a '.' field selector string and change it into offsets
-// for a vector.
-//
-bool TParseContext::parseVectorFields(const TString &compString,
+namespace
+{
+
+const int kWebGLMaxStructNesting = 4;
+
+const std::array<const char *, 8> kAtomicBuiltin = {{"atomicAdd", "atomicMin", "atomicMax",
+ "atomicAnd", "atomicOr", "atomicXor",
+ "atomicExchange", "atomicCompSwap"}};
+
+bool IsAtomicBuiltin(const TString &name)
+{
+ for (size_t i = 0; i < kAtomicBuiltin.size(); ++i)
+ {
+ if (name.compare(kAtomicBuiltin[i]) == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ContainsSampler(const TStructure *structType);
+
+bool ContainsSampler(const TType &type)
+{
+ if (IsSampler(type.getBasicType()))
+ {
+ return true;
+ }
+ if (type.getBasicType() == EbtStruct)
+ {
+ return ContainsSampler(type.getStruct());
+ }
+
+ return false;
+}
+
+bool ContainsSampler(const TStructure *structType)
+{
+ for (const auto &field : structType->fields())
+ {
+ if (ContainsSampler(*field->type()))
+ return true;
+ }
+ return false;
+}
+
+// Get a token from an image argument to use as an error message token.
+const char *GetImageArgumentToken(TIntermTyped *imageNode)
+{
+ ASSERT(IsImage(imageNode->getBasicType()));
+ while (imageNode->getAsBinaryNode() &&
+ (imageNode->getAsBinaryNode()->getOp() == EOpIndexIndirect ||
+ imageNode->getAsBinaryNode()->getOp() == EOpIndexDirect))
+ {
+ imageNode = imageNode->getAsBinaryNode()->getLeft();
+ }
+ TIntermSymbol *imageSymbol = imageNode->getAsSymbolNode();
+ if (imageSymbol)
+ {
+ return imageSymbol->getSymbol().c_str();
+ }
+ return "image";
+}
+
+bool CanSetDefaultPrecisionOnType(const TPublicType &type)
+{
+ if (!SupportsPrecision(type.getBasicType()))
+ {
+ return false;
+ }
+ if (type.getBasicType() == EbtUInt)
+ {
+ // ESSL 3.00.4 section 4.5.4
+ return false;
+ }
+ if (type.isAggregate())
+ {
+ // Not allowed to set for aggregate types
+ return false;
+ }
+ return true;
+}
+
+// Map input primitive types to input array sizes in a geometry shader.
+GLuint GetGeometryShaderInputArraySize(TLayoutPrimitiveType primitiveType)
+{
+ switch (primitiveType)
+ {
+ case EptPoints:
+ return 1u;
+ case EptLines:
+ return 2u;
+ case EptTriangles:
+ return 3u;
+ case EptLinesAdjacency:
+ return 4u;
+ case EptTrianglesAdjacency:
+ return 6u;
+ default:
+ UNREACHABLE();
+ return 0u;
+ }
+}
+
+bool IsBufferOrSharedVariable(TIntermTyped *var)
+{
+ if (var->isInterfaceBlock() || var->getQualifier() == EvqBuffer ||
+ var->getQualifier() == EvqShared)
+ {
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+// This tracks each binding point's current default offset for inheritance of subsequent
+// variables using the same binding, and keeps offsets unique and non overlapping.
+// See GLSL ES 3.1, section 4.4.6.
+class TParseContext::AtomicCounterBindingState
+{
+ public:
+ AtomicCounterBindingState() : mDefaultOffset(0) {}
+ // Inserts a new span and returns -1 if overlapping, else returns the starting offset of
+ // newly inserted span.
+ int insertSpan(int start, size_t length)
+ {
+ gl::RangeI newSpan(start, start + static_cast<int>(length));
+ for (const auto &span : mSpans)
+ {
+ if (newSpan.intersects(span))
+ {
+ return -1;
+ }
+ }
+ mSpans.push_back(newSpan);
+ mDefaultOffset = newSpan.high();
+ return start;
+ }
+ // Inserts a new span starting from the default offset.
+ int appendSpan(size_t length) { return insertSpan(mDefaultOffset, length); }
+ void setDefaultOffset(int offset) { mDefaultOffset = offset; }
+
+ private:
+ int mDefaultOffset;
+ std::vector<gl::RangeI> mSpans;
+};
+
+TParseContext::TParseContext(TSymbolTable &symt,
+ TExtensionBehavior &ext,
+ sh::GLenum type,
+ ShShaderSpec spec,
+ ShCompileOptions options,
+ bool checksPrecErrors,
+ TDiagnostics *diagnostics,
+ const ShBuiltInResources &resources)
+ : symbolTable(symt),
+ mDeferredNonEmptyDeclarationErrorCheck(false),
+ mShaderType(type),
+ mShaderSpec(spec),
+ mCompileOptions(options),
+ mShaderVersion(100),
+ mTreeRoot(nullptr),
+ mLoopNestingLevel(0),
+ mStructNestingLevel(0),
+ mSwitchNestingLevel(0),
+ mCurrentFunctionType(nullptr),
+ mFunctionReturnsValue(false),
+ mChecksPrecisionErrors(checksPrecErrors),
+ mFragmentPrecisionHighOnESSL1(false),
+ mDefaultUniformMatrixPacking(EmpColumnMajor),
+ mDefaultUniformBlockStorage(sh::IsWebGLBasedSpec(spec) ? EbsStd140 : EbsShared),
+ mDefaultBufferMatrixPacking(EmpColumnMajor),
+ mDefaultBufferBlockStorage(sh::IsWebGLBasedSpec(spec) ? EbsStd140 : EbsShared),
+ mDiagnostics(diagnostics),
+ mDirectiveHandler(ext,
+ *mDiagnostics,
+ mShaderVersion,
+ mShaderType,
+ resources.WEBGL_debug_shader_precision == 1),
+ mPreprocessor(mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings()),
+ mScanner(nullptr),
+ mUsesFragData(false),
+ mUsesFragColor(false),
+ mUsesSecondaryOutputs(false),
+ mMinProgramTexelOffset(resources.MinProgramTexelOffset),
+ mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
+ mMinProgramTextureGatherOffset(resources.MinProgramTextureGatherOffset),
+ mMaxProgramTextureGatherOffset(resources.MaxProgramTextureGatherOffset),
+ mComputeShaderLocalSizeDeclared(false),
+ mComputeShaderLocalSize(-1),
+ mNumViews(-1),
+ mMaxNumViews(resources.MaxViewsOVR),
+ mMaxImageUnits(resources.MaxImageUnits),
+ mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
+ mMaxUniformLocations(resources.MaxUniformLocations),
+ mMaxUniformBufferBindings(resources.MaxUniformBufferBindings),
+ mMaxAtomicCounterBindings(resources.MaxAtomicCounterBindings),
+ mMaxShaderStorageBufferBindings(resources.MaxShaderStorageBufferBindings),
+ mDeclaringFunction(false),
+ mGeometryShaderInputPrimitiveType(EptUndefined),
+ mGeometryShaderOutputPrimitiveType(EptUndefined),
+ mGeometryShaderInvocations(0),
+ mGeometryShaderMaxVertices(-1),
+ mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
+ mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices),
+ mGeometryShaderInputArraySize(0u)
+{
+}
+
+TParseContext::~TParseContext()
+{
+}
+
+bool TParseContext::parseVectorFields(const TSourceLoc &line,
+ const TString &compString,
int vecSize,
- TVectorFields &fields,
- const TSourceLoc &line)
+ TVector<int> *fieldOffsets)
{
- fields.num = (int)compString.size();
- if (fields.num > 4)
+ ASSERT(fieldOffsets);
+ size_t fieldCount = compString.size();
+ if (fieldCount > 4u)
{
error(line, "illegal vector field selection", compString.c_str());
return false;
}
+ fieldOffsets->resize(fieldCount);
enum
{
@@ -45,57 +263,57 @@ bool TParseContext::parseVectorFields(const TString &compString,
estpq
} fieldSet[4];
- for (int i = 0; i < fields.num; ++i)
+ for (unsigned int i = 0u; i < fieldOffsets->size(); ++i)
{
switch (compString[i])
{
case 'x':
- fields.offsets[i] = 0;
+ (*fieldOffsets)[i] = 0;
fieldSet[i] = exyzw;
break;
case 'r':
- fields.offsets[i] = 0;
+ (*fieldOffsets)[i] = 0;
fieldSet[i] = ergba;
break;
case 's':
- fields.offsets[i] = 0;
+ (*fieldOffsets)[i] = 0;
fieldSet[i] = estpq;
break;
case 'y':
- fields.offsets[i] = 1;
+ (*fieldOffsets)[i] = 1;
fieldSet[i] = exyzw;
break;
case 'g':
- fields.offsets[i] = 1;
+ (*fieldOffsets)[i] = 1;
fieldSet[i] = ergba;
break;
case 't':
- fields.offsets[i] = 1;
+ (*fieldOffsets)[i] = 1;
fieldSet[i] = estpq;
break;
case 'z':
- fields.offsets[i] = 2;
+ (*fieldOffsets)[i] = 2;
fieldSet[i] = exyzw;
break;
case 'b':
- fields.offsets[i] = 2;
+ (*fieldOffsets)[i] = 2;
fieldSet[i] = ergba;
break;
case 'p':
- fields.offsets[i] = 2;
+ (*fieldOffsets)[i] = 2;
fieldSet[i] = estpq;
break;
case 'w':
- fields.offsets[i] = 3;
+ (*fieldOffsets)[i] = 3;
fieldSet[i] = exyzw;
break;
case 'a':
- fields.offsets[i] = 3;
+ (*fieldOffsets)[i] = 3;
fieldSet[i] = ergba;
break;
case 'q':
- fields.offsets[i] = 3;
+ (*fieldOffsets)[i] = 3;
fieldSet[i] = estpq;
break;
default:
@@ -104,9 +322,9 @@ bool TParseContext::parseVectorFields(const TString &compString,
}
}
- for (int i = 0; i < fields.num; ++i)
+ for (unsigned int i = 0u; i < fieldOffsets->size(); ++i)
{
- if (fields.offsets[i] >= vecSize)
+ if ((*fieldOffsets)[i] >= vecSize)
{
error(line, "vector field selection out of range", compString.c_str());
return false;
@@ -133,51 +351,30 @@ bool TParseContext::parseVectorFields(const TString &compString,
////////////////////////////////////////////////////////////////////////
//
-// Track whether errors have occurred.
-//
-void TParseContext::recover()
-{
-}
-
-//
// Used by flex/bison to output all syntax and parsing errors.
//
-void TParseContext::error(const TSourceLoc &loc,
- const char *reason,
- const char *token,
- const char *extraInfo)
+void TParseContext::error(const TSourceLoc &loc, const char *reason, const char *token)
{
- pp::SourceLocation srcLoc;
- srcLoc.file = loc.first_file;
- srcLoc.line = loc.first_line;
- mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, srcLoc, reason, token, extraInfo);
+ mDiagnostics->error(loc, reason, token);
}
-void TParseContext::warning(const TSourceLoc &loc,
- const char *reason,
- const char *token,
- const char *extraInfo)
+void TParseContext::warning(const TSourceLoc &loc, const char *reason, const char *token)
{
- pp::SourceLocation srcLoc;
- srcLoc.file = loc.first_file;
- srcLoc.line = loc.first_line;
- mDiagnostics.writeInfo(pp::Diagnostics::PP_WARNING, srcLoc, reason, token, extraInfo);
+ mDiagnostics->warning(loc, reason, token);
}
void TParseContext::outOfRangeError(bool isError,
const TSourceLoc &loc,
const char *reason,
- const char *token,
- const char *extraInfo)
+ const char *token)
{
if (isError)
{
- error(loc, reason, token, extraInfo);
- recover();
+ error(loc, reason, token);
}
else
{
- warning(loc, reason, token, extraInfo);
+ warning(loc, reason, token);
}
}
@@ -186,10 +383,10 @@ void TParseContext::outOfRangeError(bool isError,
//
void TParseContext::assignError(const TSourceLoc &line, const char *op, TString left, TString right)
{
- std::stringstream extraInfoStream;
- extraInfoStream << "cannot convert from '" << right << "' to '" << left << "'";
- std::string extraInfo = extraInfoStream.str();
- error(line, "", op, extraInfo.c_str());
+ std::stringstream reasonStream;
+ reasonStream << "cannot convert from '" << right << "' to '" << left << "'";
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), op);
}
//
@@ -197,11 +394,12 @@ void TParseContext::assignError(const TSourceLoc &line, const char *op, TString
//
void TParseContext::unaryOpError(const TSourceLoc &line, const char *op, TString operand)
{
- std::stringstream extraInfoStream;
- extraInfoStream << "no operation '" << op << "' exists that takes an operand of type "
- << operand << " (or there is no acceptable conversion)";
- std::string extraInfo = extraInfoStream.str();
- error(line, " wrong operand type", op, extraInfo.c_str());
+ std::stringstream reasonStream;
+ reasonStream << "wrong operand type - no operation '" << op
+ << "' exists that takes an operand of type " << operand
+ << " (or there is no acceptable conversion)";
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), op);
}
//
@@ -212,102 +410,85 @@ void TParseContext::binaryOpError(const TSourceLoc &line,
TString left,
TString right)
{
- std::stringstream extraInfoStream;
- extraInfoStream << "no operation '" << op << "' exists that takes a left-hand operand of type '"
- << left << "' and a right operand of type '" << right
- << "' (or there is no acceptable conversion)";
- std::string extraInfo = extraInfoStream.str();
- error(line, " wrong operand types ", op, extraInfo.c_str());
+ std::stringstream reasonStream;
+ reasonStream << "wrong operand types - no operation '" << op
+ << "' exists that takes a left-hand operand of type '" << left
+ << "' and a right operand of type '" << right
+ << "' (or there is no acceptable conversion)";
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), op);
}
-bool TParseContext::precisionErrorCheck(const TSourceLoc &line,
- TPrecision precision,
- TBasicType type)
+void TParseContext::checkPrecisionSpecified(const TSourceLoc &line,
+ TPrecision precision,
+ TBasicType type)
{
if (!mChecksPrecisionErrors)
- return false;
+ return;
+
+ if (precision != EbpUndefined && !SupportsPrecision(type))
+ {
+ error(line, "illegal type for precision qualifier", getBasicString(type));
+ }
+
if (precision == EbpUndefined)
{
switch (type)
{
case EbtFloat:
error(line, "No precision specified for (float)", "");
- return true;
+ return;
case EbtInt:
case EbtUInt:
UNREACHABLE(); // there's always a predeclared qualifier
error(line, "No precision specified (int)", "");
- return true;
+ return;
default:
- if (IsSampler(type))
+ if (IsOpaqueType(type))
{
- error(line, "No precision specified (sampler)", "");
- return true;
+ error(line, "No precision specified", getBasicString(type));
+ return;
}
}
}
- return false;
}
-//
// Both test and if necessary, spit out an error, to see if the node is really
// an l-value that can be operated on this way.
-//
-// Returns true if the was an error.
-//
-bool TParseContext::lValueErrorCheck(const TSourceLoc &line, const char *op, TIntermTyped *node)
+bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node)
{
- TIntermSymbol *symNode = node->getAsSymbolNode();
- TIntermBinary *binaryNode = node->getAsBinaryNode();
+ TIntermSymbol *symNode = node->getAsSymbolNode();
+ TIntermBinary *binaryNode = node->getAsBinaryNode();
+ TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
- if (binaryNode)
+ if (swizzleNode)
{
- bool errorReturn;
+ bool ok = checkCanBeLValue(line, op, swizzleNode->getOperand());
+ if (ok && swizzleNode->hasDuplicateOffsets())
+ {
+ error(line, " l-value of swizzle cannot have duplicate components", op);
+ return false;
+ }
+ return ok;
+ }
+ if (binaryNode)
+ {
switch (binaryNode->getOp())
{
case EOpIndexDirect:
case EOpIndexIndirect:
case EOpIndexDirectStruct:
case EOpIndexDirectInterfaceBlock:
- return lValueErrorCheck(line, op, binaryNode->getLeft());
- case EOpVectorSwizzle:
- errorReturn = lValueErrorCheck(line, op, binaryNode->getLeft());
- if (!errorReturn)
- {
- int offset[4] = {0, 0, 0, 0};
-
- TIntermTyped *rightNode = binaryNode->getRight();
- TIntermAggregate *aggrNode = rightNode->getAsAggregate();
-
- for (TIntermSequence::iterator p = aggrNode->getSequence()->begin();
- p != aggrNode->getSequence()->end(); p++)
- {
- int value = (*p)->getAsTyped()->getAsConstantUnion()->getIConst(0);
- offset[value]++;
- if (offset[value] > 1)
- {
- error(line, " l-value of swizzle cannot have duplicate components", op);
-
- return true;
- }
- }
- }
-
- return errorReturn;
+ return checkCanBeLValue(line, op, binaryNode->getLeft());
default:
break;
}
error(line, " l-value required", op);
-
- return true;
+ return false;
}
- const char *symbol = 0;
- if (symNode != 0)
- symbol = symNode->getSymbol().c_str();
-
- const char *message = 0;
+ std::string message;
switch (node->getQualifier())
{
case EvqConst:
@@ -320,9 +501,11 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc &line, const char *op, TIn
message = "can't modify an attribute";
break;
case EvqFragmentIn:
- message = "can't modify an input";
- break;
case EvqVertexIn:
+ case EvqGeometryIn:
+ case EvqFlatIn:
+ case EvqSmoothIn:
+ case EvqCentroidIn:
message = "can't modify an input";
break;
case EvqUniform:
@@ -340,6 +523,51 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc &line, const char *op, TIn
case EvqPointCoord:
message = "can't modify gl_PointCoord";
break;
+ case EvqNumWorkGroups:
+ message = "can't modify gl_NumWorkGroups";
+ break;
+ case EvqWorkGroupSize:
+ message = "can't modify gl_WorkGroupSize";
+ break;
+ case EvqWorkGroupID:
+ message = "can't modify gl_WorkGroupID";
+ break;
+ case EvqLocalInvocationID:
+ message = "can't modify gl_LocalInvocationID";
+ break;
+ case EvqGlobalInvocationID:
+ message = "can't modify gl_GlobalInvocationID";
+ break;
+ case EvqLocalInvocationIndex:
+ message = "can't modify gl_LocalInvocationIndex";
+ break;
+ case EvqViewIDOVR:
+ message = "can't modify gl_ViewID_OVR";
+ break;
+ case EvqComputeIn:
+ message = "can't modify work group size variable";
+ break;
+ case EvqPerVertexIn:
+ message = "can't modify any member in gl_in";
+ break;
+ case EvqPrimitiveIDIn:
+ message = "can't modify gl_PrimitiveIDIn";
+ break;
+ case EvqInvocationID:
+ message = "can't modify gl_InvocationID";
+ break;
+ case EvqPrimitiveID:
+ if (mShaderType == GL_FRAGMENT_SHADER)
+ {
+ message = "can't modify gl_PrimitiveID in a fragment shader";
+ }
+ break;
+ case EvqLayer:
+ if (mShaderType == GL_FRAGMENT_SHADER)
+ {
+ message = "can't modify gl_Layer in a fragment shader";
+ }
+ break;
default:
//
// Type that can't be written to?
@@ -348,287 +576,270 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc &line, const char *op, TIn
{
message = "can't modify void";
}
- if (IsSampler(node->getBasicType()))
+ if (IsOpaqueType(node->getBasicType()))
{
- message = "can't modify a sampler";
+ message = "can't modify a variable with type ";
+ message += getBasicString(node->getBasicType());
+ }
+ else if (node->getMemoryQualifier().readonly)
+ {
+ message = "can't modify a readonly variable";
}
}
- if (message == 0 && binaryNode == 0 && symNode == 0)
+ if (message.empty() && binaryNode == 0 && symNode == 0)
{
- error(line, " l-value required", op);
+ error(line, "l-value required", op);
- return true;
+ return false;
}
//
// Everything else is okay, no error.
//
- if (message == 0)
- return false;
+ if (message.empty())
+ return true;
//
// If we get here, we have an error and a message.
//
if (symNode)
{
- std::stringstream extraInfoStream;
- extraInfoStream << "\"" << symbol << "\" (" << message << ")";
- std::string extraInfo = extraInfoStream.str();
- error(line, " l-value required", op, extraInfo.c_str());
+ const char *symbol = symNode->getSymbol().c_str();
+ std::stringstream reasonStream;
+ reasonStream << "l-value required (" << message << " \"" << symbol << "\")";
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), op);
}
else
{
- std::stringstream extraInfoStream;
- extraInfoStream << "(" << message << ")";
- std::string extraInfo = extraInfoStream.str();
- error(line, " l-value required", op, extraInfo.c_str());
+ std::stringstream reasonStream;
+ reasonStream << "l-value required (" << message << ")";
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), op);
}
- return true;
+ return false;
}
-//
// Both test, and if necessary spit out an error, to see if the node is really
// a constant.
-//
-// Returns true if the was an error.
-//
-bool TParseContext::constErrorCheck(TIntermTyped *node)
+void TParseContext::checkIsConst(TIntermTyped *node)
{
- if (node->getQualifier() == EvqConst)
- return false;
-
- error(node->getLine(), "constant expression required", "");
-
- return true;
+ if (node->getQualifier() != EvqConst)
+ {
+ error(node->getLine(), "constant expression required", "");
+ }
}
-//
// Both test, and if necessary spit out an error, to see if the node is really
// an integer.
-//
-// Returns true if the was an error.
-//
-bool TParseContext::integerErrorCheck(TIntermTyped *node, const char *token)
+void TParseContext::checkIsScalarInteger(TIntermTyped *node, const char *token)
{
- if (node->isScalarInt())
- return false;
-
- error(node->getLine(), "integer expression required", token);
-
- return true;
+ if (!node->isScalarInt())
+ {
+ error(node->getLine(), "integer expression required", token);
+ }
}
-//
// Both test, and if necessary spit out an error, to see if we are currently
// globally scoped.
-//
-// Returns true if the was an error.
-//
-bool TParseContext::globalErrorCheck(const TSourceLoc &line, bool global, const char *token)
+bool TParseContext::checkIsAtGlobalLevel(const TSourceLoc &line, const char *token)
{
- if (global)
+ if (!symbolTable.atGlobalLevel())
+ {
+ error(line, "only allowed at global scope", token);
return false;
-
- error(line, "only allowed at global scope", token);
-
+ }
return true;
}
-//
-// For now, keep it simple: if it starts "gl_", it's reserved, independent
-// of scope. Except, if the symbol table is at the built-in push-level,
-// which is when we are parsing built-ins.
-// Also checks for "webgl_" and "_webgl_" reserved identifiers if parsing a
-// webgl shader.
-//
-// Returns true if there was an error.
-//
-bool TParseContext::reservedErrorCheck(const TSourceLoc &line, const TString &identifier)
+// ESSL 3.00.5 sections 3.8 and 3.9.
+// If it starts "gl_" or contains two consecutive underscores, it's reserved.
+// Also checks for "webgl_" and "_webgl_" reserved identifiers if parsing a webgl shader.
+bool TParseContext::checkIsNotReserved(const TSourceLoc &line, const TString &identifier)
{
static const char *reservedErrMsg = "reserved built-in name";
- if (!symbolTable.atBuiltInLevel())
+ if (identifier.compare(0, 3, "gl_") == 0)
{
- if (identifier.compare(0, 3, "gl_") == 0)
- {
- error(line, reservedErrMsg, "gl_");
- return true;
- }
- if (IsWebGLBasedSpec(mShaderSpec))
+ error(line, reservedErrMsg, "gl_");
+ return false;
+ }
+ if (sh::IsWebGLBasedSpec(mShaderSpec))
+ {
+ if (identifier.compare(0, 6, "webgl_") == 0)
{
- if (identifier.compare(0, 6, "webgl_") == 0)
- {
- error(line, reservedErrMsg, "webgl_");
- return true;
- }
- if (identifier.compare(0, 7, "_webgl_") == 0)
- {
- error(line, reservedErrMsg, "_webgl_");
- return true;
- }
- if (mShaderSpec == SH_CSS_SHADERS_SPEC && identifier.compare(0, 4, "css_") == 0)
- {
- error(line, reservedErrMsg, "css_");
- return true;
- }
+ error(line, reservedErrMsg, "webgl_");
+ return false;
}
- if (identifier.find("__") != TString::npos)
+ if (identifier.compare(0, 7, "_webgl_") == 0)
{
- error(line,
- "identifiers containing two consecutive underscores (__) are reserved as "
- "possible future keywords",
- identifier.c_str());
- return true;
+ error(line, reservedErrMsg, "_webgl_");
+ return false;
}
}
-
- return false;
-}
-
-//
-// Make sure there is enough data provided to the constructor to build
-// something of the type of the constructor. Also returns the type of
-// the constructor.
-//
-// Returns true if there was an error in construction.
-//
-bool TParseContext::constructorErrorCheck(const TSourceLoc &line,
- TIntermNode *argumentsNode,
- TFunction &function,
- TOperator op,
- TType *type)
-{
- *type = function.getReturnType();
-
- bool constructingMatrix = false;
- switch (op)
+ if (identifier.find("__") != TString::npos)
{
- case EOpConstructMat2:
- case EOpConstructMat2x3:
- case EOpConstructMat2x4:
- case EOpConstructMat3x2:
- case EOpConstructMat3:
- case EOpConstructMat3x4:
- case EOpConstructMat4x2:
- case EOpConstructMat4x3:
- case EOpConstructMat4:
- constructingMatrix = true;
- break;
- default:
- break;
+ error(line,
+ "identifiers containing two consecutive underscores (__) are reserved as "
+ "possible future keywords",
+ identifier.c_str());
+ return false;
}
+ return true;
+}
- //
- // Note: It's okay to have too many components available, but not okay to have unused
- // arguments. 'full' will go to true when enough args have been seen. If we loop
- // again, there is an extra argument, so 'overfull' will become true.
- //
-
- size_t size = 0;
- bool constType = true;
- bool full = false;
- bool overFull = false;
- bool matrixInMatrix = false;
- bool arrayArg = false;
- for (size_t i = 0; i < function.getParamCount(); ++i)
+// Make sure the argument types are correct for constructing a specific type.
+bool TParseContext::checkConstructorArguments(const TSourceLoc &line,
+ const TIntermSequence *arguments,
+ const TType &type)
+{
+ if (arguments->empty())
{
- const TConstParameter &param = function.getParam(i);
- size += param.type->getObjectSize();
-
- if (constructingMatrix && param.type->isMatrix())
- matrixInMatrix = true;
- if (full)
- overFull = true;
- if (op != EOpConstructStruct && !type->isArray() && size >= type->getObjectSize())
- full = true;
- if (param.type->getQualifier() != EvqConst)
- constType = false;
- if (param.type->isArray())
- arrayArg = true;
+ error(line, "constructor does not have any arguments", "constructor");
+ return false;
}
- if (constType)
- type->setQualifier(EvqConst);
-
- if (type->isArray())
+ for (TIntermNode *arg : *arguments)
{
- if (type->isUnsizedArray())
+ const TIntermTyped *argTyped = arg->getAsTyped();
+ ASSERT(argTyped != nullptr);
+ if (type.getBasicType() != EbtStruct && IsOpaqueType(argTyped->getBasicType()))
{
- type->setArraySize(static_cast<int>(function.getParamCount()));
+ std::string reason("cannot convert a variable with type ");
+ reason += getBasicString(argTyped->getBasicType());
+ error(line, reason.c_str(), "constructor");
+ return false;
}
- else if (static_cast<size_t>(type->getArraySize()) != function.getParamCount())
+ else if (argTyped->getMemoryQualifier().writeonly)
{
- error(line, "array constructor needs one argument per array element", "constructor");
- return true;
+ error(line, "cannot convert a variable with writeonly", "constructor");
+ return false;
+ }
+ if (argTyped->getBasicType() == EbtVoid)
+ {
+ error(line, "cannot convert a void", "constructor");
+ return false;
}
}
- if (arrayArg && op != EOpConstructStruct)
+ if (type.isArray())
{
- error(line, "constructing from a non-dereferenced array", "constructor");
- return true;
+ // The size of an unsized constructor should already have been determined.
+ ASSERT(!type.isUnsizedArray());
+ if (static_cast<size_t>(type.getOutermostArraySize()) != arguments->size())
+ {
+ error(line, "array constructor needs one argument per array element", "constructor");
+ return false;
+ }
+ // GLSL ES 3.00 section 5.4.4: Each argument must be the same type as the element type of
+ // the array.
+ for (TIntermNode *const &argNode : *arguments)
+ {
+ const TType &argType = argNode->getAsTyped()->getType();
+ if (mShaderVersion < 310 && argType.isArray())
+ {
+ error(line, "constructing from a non-dereferenced array", "constructor");
+ return false;
+ }
+ if (!argType.isElementTypeOf(type))
+ {
+ error(line, "Array constructor argument has an incorrect type", "constructor");
+ return false;
+ }
+ }
}
-
- if (matrixInMatrix && !type->isArray())
+ else if (type.getBasicType() == EbtStruct)
{
- if (function.getParamCount() != 1)
+ const TFieldList &fields = type.getStruct()->fields();
+ if (fields.size() != arguments->size())
{
- error(line, "constructing matrix from matrix can only take one argument",
+ error(line,
+ "Number of constructor parameters does not match the number of structure fields",
"constructor");
- return true;
+ return false;
}
- }
- if (overFull)
- {
- error(line, "too many arguments", "constructor");
- return true;
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ if (i >= arguments->size() ||
+ (*arguments)[i]->getAsTyped()->getType() != *fields[i]->type())
+ {
+ error(line, "Structure constructor arguments do not match structure fields",
+ "constructor");
+ return false;
+ }
+ }
}
-
- if (op == EOpConstructStruct && !type->isArray() &&
- type->getStruct()->fields().size() != function.getParamCount())
+ else
{
- error(line,
- "Number of constructor parameters does not match the number of structure fields",
- "constructor");
- return true;
- }
+ // We're constructing a scalar, vector, or matrix.
- if (!type->isMatrix() || !matrixInMatrix)
- {
- if ((op != EOpConstructStruct && size != 1 && size < type->getObjectSize()) ||
- (op == EOpConstructStruct && size < type->getObjectSize()))
+ // Note: It's okay to have too many components available, but not okay to have unused
+ // arguments. 'full' will go to true when enough args have been seen. If we loop again,
+ // there is an extra argument, so 'overFull' will become true.
+
+ size_t size = 0;
+ bool full = false;
+ bool overFull = false;
+ bool matrixArg = false;
+ for (TIntermNode *arg : *arguments)
{
- error(line, "not enough data provided for construction", "constructor");
- return true;
- }
- }
+ const TIntermTyped *argTyped = arg->getAsTyped();
+ ASSERT(argTyped != nullptr);
- if (argumentsNode == nullptr)
- {
- error(line, "constructor does not have any arguments", "constructor");
- return true;
- }
+ if (argTyped->getBasicType() == EbtStruct)
+ {
+ error(line, "a struct cannot be used as a constructor argument for this type",
+ "constructor");
+ return false;
+ }
+ if (argTyped->getType().isArray())
+ {
+ error(line, "constructing from a non-dereferenced array", "constructor");
+ return false;
+ }
+ if (argTyped->getType().isMatrix())
+ {
+ matrixArg = true;
+ }
- TIntermAggregate *argumentsAgg = argumentsNode->getAsAggregate();
- for (TIntermNode *&argNode : *argumentsAgg->getSequence())
- {
- TIntermTyped *argTyped = argNode->getAsTyped();
- ASSERT(argTyped != nullptr);
- if (op != EOpConstructStruct && IsSampler(argTyped->getBasicType()))
+ size += argTyped->getType().getObjectSize();
+ if (full)
+ {
+ overFull = true;
+ }
+ if (size >= type.getObjectSize())
+ {
+ full = true;
+ }
+ }
+
+ if (type.isMatrix() && matrixArg)
{
- error(line, "cannot convert a sampler", "constructor");
- return true;
+ if (arguments->size() != 1)
+ {
+ error(line, "constructing matrix from matrix can only take one argument",
+ "constructor");
+ return false;
+ }
}
- if (argTyped->getBasicType() == EbtVoid)
+ else
{
- error(line, "cannot convert a void", "constructor");
- return true;
+ if (size != 1 && size < type.getObjectSize())
+ {
+ error(line, "not enough data provided for construction", "constructor");
+ return false;
+ }
+ if (overFull)
+ {
+ error(line, "too many arguments", "constructor");
+ return false;
+ }
}
}
- return false;
+ return true;
}
// This function checks to see if a void variable has been declared and raise an error message for
@@ -636,126 +847,116 @@ bool TParseContext::constructorErrorCheck(const TSourceLoc &line,
//
// returns true in case of an error
//
-bool TParseContext::voidErrorCheck(const TSourceLoc &line,
+bool TParseContext::checkIsNonVoid(const TSourceLoc &line,
const TString &identifier,
const TBasicType &type)
{
if (type == EbtVoid)
{
error(line, "illegal use of type 'void'", identifier.c_str());
- return true;
+ return false;
}
- return false;
+ return true;
}
// This function checks to see if the node (for the expression) contains a scalar boolean expression
-// or not
-//
-// returns true in case of an error
-//
-bool TParseContext::boolErrorCheck(const TSourceLoc &line, const TIntermTyped *type)
+// or not.
+bool TParseContext::checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type)
{
- if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector())
+ if (type->getBasicType() != EbtBool || !type->isScalar())
{
error(line, "boolean expression expected", "");
- return true;
+ return false;
}
-
- return false;
+ return true;
}
// This function checks to see if the node (for the expression) contains a scalar boolean expression
-// or not
-//
-// returns true in case of an error
-//
-bool TParseContext::boolErrorCheck(const TSourceLoc &line, const TPublicType &pType)
+// or not.
+void TParseContext::checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType)
{
- if (pType.type != EbtBool || pType.isAggregate())
+ if (pType.getBasicType() != EbtBool || pType.isAggregate())
{
error(line, "boolean expression expected", "");
- return true;
}
-
- return false;
}
-bool TParseContext::samplerErrorCheck(const TSourceLoc &line,
- const TPublicType &pType,
- const char *reason)
+bool TParseContext::checkIsNotOpaqueType(const TSourceLoc &line,
+ const TTypeSpecifierNonArray &pType,
+ const char *reason)
{
if (pType.type == EbtStruct)
{
- if (containsSampler(*pType.userDef))
+ if (ContainsSampler(pType.userDef))
{
- error(line, reason, getBasicString(pType.type), "(structure contains a sampler)");
-
- return true;
+ std::stringstream reasonStream;
+ reasonStream << reason << " (structure contains a sampler)";
+ std::string reasonStr = reasonStream.str();
+ error(line, reasonStr.c_str(), getBasicString(pType.type));
+ return false;
}
-
- return false;
+ // only samplers need to be checked from structs, since other opaque types can't be struct
+ // members.
+ return true;
}
- else if (IsSampler(pType.type))
+ else if (IsOpaqueType(pType.type))
{
error(line, reason, getBasicString(pType.type));
-
- return true;
+ return false;
}
- return false;
+ return true;
}
-bool TParseContext::locationDeclaratorListCheck(const TSourceLoc &line, const TPublicType &pType)
+void TParseContext::checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line,
+ const TPublicType &pType)
{
if (pType.layoutQualifier.location != -1)
{
error(line, "location must only be specified for a single input or output variable",
"location");
- return true;
}
-
- return false;
}
-bool TParseContext::parameterSamplerErrorCheck(const TSourceLoc &line,
- TQualifier qualifier,
- const TType &type)
+void TParseContext::checkLocationIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier)
{
- if ((qualifier == EvqOut || qualifier == EvqInOut) && type.getBasicType() != EbtStruct &&
- IsSampler(type.getBasicType()))
+ if (layoutQualifier.location != -1)
{
- error(line, "samplers cannot be output parameters", type.getBasicString());
- return true;
+ const char *errorMsg = "invalid layout qualifier: only valid on program inputs and outputs";
+ if (mShaderVersion >= 310)
+ {
+ errorMsg =
+ "invalid layout qualifier: only valid on shader inputs, outputs, and uniforms";
+ }
+ error(location, errorMsg, "location");
}
-
- return false;
}
-bool TParseContext::containsSampler(const TType &type)
+void TParseContext::checkStd430IsForShaderStorageBlock(const TSourceLoc &location,
+ const TLayoutBlockStorage &blockStorage,
+ const TQualifier &qualifier)
{
- if (IsSampler(type.getBasicType()))
- return true;
-
- if (type.getBasicType() == EbtStruct || type.isInterfaceBlock())
+ if (blockStorage == EbsStd430 && qualifier != EvqBuffer)
{
- const TFieldList &fields = type.getStruct()->fields();
- for (unsigned int i = 0; i < fields.size(); ++i)
- {
- if (containsSampler(*fields[i]->type()))
- return true;
- }
+ error(location, "The std430 layout is supported only for shader storage blocks.", "std430");
}
+}
- return false;
+void TParseContext::checkOutParameterIsNotOpaqueType(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type)
+{
+ ASSERT(qualifier == EvqOut || qualifier == EvqInOut);
+ if (IsOpaqueType(type.getBasicType()))
+ {
+ error(line, "opaque types cannot be output parameters", type.getBasicString());
+ }
}
-//
// Do size checking for an array type's size.
-//
-// Returns true if there was an error.
-//
-bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *expr, int &size)
+unsigned int TParseContext::checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr)
{
TIntermConstantUnion *constant = expr->getAsConstantUnion();
@@ -765,36 +966,32 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *ex
if (expr->getQualifier() != EvqConst || constant == nullptr || !constant->isScalarInt())
{
error(line, "array size must be a constant integer expression", "");
- size = 1;
- return true;
+ return 1u;
}
- unsigned int unsignedSize = 0;
+ unsigned int size = 0u;
if (constant->getBasicType() == EbtUInt)
{
- unsignedSize = constant->getUConst(0);
- size = static_cast<int>(unsignedSize);
+ size = constant->getUConst(0);
}
else
{
- size = constant->getIConst(0);
+ int signedSize = constant->getIConst(0);
- if (size < 0)
+ if (signedSize < 0)
{
error(line, "array size must be non-negative", "");
- size = 1;
- return true;
+ return 1u;
}
- unsignedSize = static_cast<unsigned int>(size);
+ size = static_cast<unsigned int>(signedSize);
}
- if (size == 0)
+ if (size == 0u)
{
error(line, "array size must be greater than zero", "");
- size = 1;
- return true;
+ return 1u;
}
// The size of arrays is restricted here to prevent issues further down the
@@ -802,76 +999,80 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *ex
// 4096 registers so this should be reasonable even for aggressively optimizable code.
const unsigned int sizeLimit = 65536;
- if (unsignedSize > sizeLimit)
+ if (size > sizeLimit)
{
error(line, "array size too large", "");
- size = 1;
- return true;
+ return 1u;
}
- return false;
+ return size;
}
-//
// See if this qualifier can be an array.
-//
-// Returns true if there is an error.
-//
-bool TParseContext::arrayQualifierErrorCheck(const TSourceLoc &line, const TPublicType &type)
+bool TParseContext::checkIsValidQualifierForArray(const TSourceLoc &line,
+ const TPublicType &elementQualifier)
{
- if ((type.qualifier == EvqAttribute) || (type.qualifier == EvqVertexIn) ||
- (type.qualifier == EvqConst && mShaderVersion < 300))
+ if ((elementQualifier.qualifier == EvqAttribute) ||
+ (elementQualifier.qualifier == EvqVertexIn) ||
+ (elementQualifier.qualifier == EvqConst && mShaderVersion < 300))
{
error(line, "cannot declare arrays of this qualifier",
- TType(type).getCompleteString().c_str());
- return true;
+ TType(elementQualifier).getQualifierString());
+ return false;
}
- return false;
+ return true;
}
-//
-// See if this type can be an array.
-//
-// Returns true if there is an error.
-//
-bool TParseContext::arrayTypeErrorCheck(const TSourceLoc &line, const TPublicType &type)
+// See if this element type can be formed into an array.
+bool TParseContext::checkArrayElementIsNotArray(const TSourceLoc &line,
+ const TPublicType &elementType)
{
- //
- // Can the type be an array?
- //
- if (type.array)
+ if (mShaderVersion < 310 && elementType.isArray())
{
- error(line, "cannot declare arrays of arrays", TType(type).getCompleteString().c_str());
- return true;
+ error(line, "cannot declare arrays of arrays",
+ TType(elementType).getCompleteString().c_str());
+ return false;
+ }
+ return true;
+}
+
+// Check if this qualified element type can be formed into an array. This is only called when array
+// brackets are associated with an identifier in a declaration, like this:
+// float a[2];
+// Similar checks are done in addFullySpecifiedType for array declarations where the array brackets
+// are associated with the type, like this:
+// float[2] a;
+bool TParseContext::checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
+ const TPublicType &elementType)
+{
+ if (!checkArrayElementIsNotArray(indexLocation, elementType))
+ {
+ return false;
}
// In ESSL1.00 shaders, structs cannot be varying (section 4.3.5). This is checked elsewhere.
// In ESSL3.00 shaders, struct inputs/outputs are allowed but not arrays of structs (section
// 4.3.4).
- if (mShaderVersion >= 300 && type.type == EbtStruct && sh::IsVarying(type.qualifier))
+ if (mShaderVersion >= 300 && elementType.getBasicType() == EbtStruct &&
+ sh::IsVarying(elementType.qualifier))
{
- error(line, "cannot declare arrays of structs of this qualifier",
- TType(type).getCompleteString().c_str());
- return true;
+ error(indexLocation, "cannot declare arrays of structs of this qualifier",
+ TType(elementType).getCompleteString().c_str());
+ return false;
}
-
- return false;
+ return checkIsValidQualifierForArray(indexLocation, elementType);
}
-//
// Enforce non-initializer type/qualifier rules.
-//
-// Returns true if there was an error.
-//
-bool TParseContext::nonInitErrorCheck(const TSourceLoc &line,
- const TString &identifier,
- TPublicType *type)
+void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
+ const TString &identifier,
+ TType *type)
{
ASSERT(type != nullptr);
- if (type->qualifier == EvqConst)
+ if (type->getQualifier() == EvqConst)
{
// Make the qualifier make sense.
- type->qualifier = EvqTemporary;
+ type->setQualifier(EvqTemporary);
// Generate informative error messages for ESSL1.
// In ESSL3 arrays and structures containing arrays can be constant.
@@ -886,15 +1087,10 @@ bool TParseContext::nonInitErrorCheck(const TSourceLoc &line,
{
error(line, "variables with qualifier 'const' must be initialized", identifier.c_str());
}
-
- return true;
}
- if (type->isUnsizedArray())
- {
- error(line, "implicitly sized arrays need to be initialized", identifier.c_str());
- return true;
- }
- return false;
+ // This will make the type sized if it isn't sized yet.
+ checkIsNotUnsizedArray(line, "implicitly sized arrays need to be initialized",
+ identifier.c_str(), type);
}
// Do some simple checks that are shared between all variable declarations,
@@ -909,18 +1105,27 @@ bool TParseContext::declareVariable(const TSourceLoc &line,
{
ASSERT((*variable) == nullptr);
- bool needsReservedErrorCheck = true;
+ checkBindingIsValid(line, type);
+
+ bool needsReservedCheck = true;
// gl_LastFragData may be redeclared with a new precision qualifier
if (type.isArray() && identifier.compare(0, 15, "gl_LastFragData") == 0)
{
const TVariable *maxDrawBuffers = static_cast<const TVariable *>(
symbolTable.findBuiltIn("gl_MaxDrawBuffers", mShaderVersion));
- if (type.getArraySize() == maxDrawBuffers->getConstPointer()->getIConst())
+ if (type.isArrayOfArrays())
+ {
+ error(line, "redeclaration of gl_LastFragData as an array of arrays",
+ identifier.c_str());
+ return false;
+ }
+ else if (static_cast<int>(type.getOutermostArraySize()) ==
+ maxDrawBuffers->getConstPointer()->getIConst())
{
if (TSymbol *builtInSymbol = symbolTable.findBuiltIn(identifier, mShaderVersion))
{
- needsReservedErrorCheck = extensionErrorCheck(line, builtInSymbol->getExtension());
+ needsReservedCheck = !checkCanUseExtension(line, builtInSymbol->getExtension());
}
}
else
@@ -931,77 +1136,237 @@ bool TParseContext::declareVariable(const TSourceLoc &line,
}
}
- if (needsReservedErrorCheck && reservedErrorCheck(line, identifier))
+ if (needsReservedCheck && !checkIsNotReserved(line, identifier))
return false;
- (*variable) = new TVariable(&identifier, type);
- if (!symbolTable.declare(*variable))
+ (*variable) = symbolTable.declareVariable(&identifier, type);
+ if (!(*variable))
{
error(line, "redefinition", identifier.c_str());
- *variable = nullptr;
return false;
}
- if (voidErrorCheck(line, identifier, type.getBasicType()))
+ if (!checkIsNonVoid(line, identifier, type.getBasicType()))
return false;
return true;
}
-bool TParseContext::paramErrorCheck(const TSourceLoc &line,
- TQualifier qualifier,
- TQualifier paramQualifier,
- TType *type)
+void TParseContext::checkIsParameterQualifierValid(
+ const TSourceLoc &line,
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TType *type)
+{
+ // The only parameter qualifiers a parameter can have are in, out, inout or const.
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(mDiagnostics);
+
+ if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut)
+ {
+ checkOutParameterIsNotOpaqueType(line, typeQualifier.qualifier, *type);
+ }
+
+ if (!IsImage(type->getBasicType()))
+ {
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, line);
+ }
+ else
+ {
+ type->setMemoryQualifier(typeQualifier.memoryQualifier);
+ }
+
+ type->setQualifier(typeQualifier.qualifier);
+
+ if (typeQualifier.precision != EbpUndefined)
+ {
+ type->setPrecision(typeQualifier.precision);
+ }
+}
+
+template <size_t size>
+bool TParseContext::checkCanUseOneOfExtensions(const TSourceLoc &line,
+ const std::array<TExtension, size> &extensions)
{
- if (qualifier != EvqConst && qualifier != EvqTemporary)
+ ASSERT(!extensions.empty());
+ const TExtensionBehavior &extBehavior = extensionBehavior();
+
+ bool canUseWithWarning = false;
+ bool canUseWithoutWarning = false;
+
+ const char *errorMsgString = "";
+ TExtension errorMsgExtension = TExtension::UNDEFINED;
+
+ for (TExtension extension : extensions)
+ {
+ auto extIter = extBehavior.find(extension);
+ if (canUseWithWarning)
+ {
+ // We already have an extension that we can use, but with a warning.
+ // See if we can use the alternative extension without a warning.
+ if (extIter == extBehavior.end())
+ {
+ continue;
+ }
+ if (extIter->second == EBhEnable || extIter->second == EBhRequire)
+ {
+ canUseWithoutWarning = true;
+ break;
+ }
+ continue;
+ }
+ if (extIter == extBehavior.end())
+ {
+ errorMsgString = "extension is not supported";
+ errorMsgExtension = extension;
+ }
+ else if (extIter->second == EBhUndefined || extIter->second == EBhDisable)
+ {
+ errorMsgString = "extension is disabled";
+ errorMsgExtension = extension;
+ }
+ else if (extIter->second == EBhWarn)
+ {
+ errorMsgExtension = extension;
+ canUseWithWarning = true;
+ }
+ else
+ {
+ ASSERT(extIter->second == EBhEnable || extIter->second == EBhRequire);
+ canUseWithoutWarning = true;
+ break;
+ }
+ }
+
+ if (canUseWithoutWarning)
{
- error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier));
return true;
}
- if (qualifier == EvqConst && paramQualifier != EvqIn)
+ if (canUseWithWarning)
{
- error(line, "qualifier not allowed with ", getQualifierString(qualifier),
- getQualifierString(paramQualifier));
+ warning(line, "extension is being used", GetExtensionNameString(errorMsgExtension));
return true;
}
+ error(line, errorMsgString, GetExtensionNameString(errorMsgExtension));
+ return false;
+}
- if (qualifier == EvqConst)
- type->setQualifier(EvqConstReadOnly);
+template bool TParseContext::checkCanUseOneOfExtensions(
+ const TSourceLoc &line,
+ const std::array<TExtension, 1> &extensions);
+template bool TParseContext::checkCanUseOneOfExtensions(
+ const TSourceLoc &line,
+ const std::array<TExtension, 2> &extensions);
+template bool TParseContext::checkCanUseOneOfExtensions(
+ const TSourceLoc &line,
+ const std::array<TExtension, 3> &extensions);
+
+bool TParseContext::checkCanUseExtension(const TSourceLoc &line, TExtension extension)
+{
+ ASSERT(extension != TExtension::UNDEFINED);
+ ASSERT(extension != TExtension::EXT_geometry_shader);
+ if (extension == TExtension::OES_geometry_shader)
+ {
+ // OES_geometry_shader and EXT_geometry_shader are always interchangeable.
+ constexpr std::array<TExtension, 2u> extensions{
+ {TExtension::EXT_geometry_shader, TExtension::OES_geometry_shader}};
+ return checkCanUseOneOfExtensions(line, extensions);
+ }
+ return checkCanUseOneOfExtensions(line, std::array<TExtension, 1u>{{extension}});
+}
+
+// ESSL 3.00.6 section 4.8 Empty Declarations: "The combinations of qualifiers that cause
+// compile-time or link-time errors are the same whether or not the declaration is empty".
+// This function implements all the checks that are done on qualifiers regardless of if the
+// declaration is empty.
+void TParseContext::declarationQualifierErrorCheck(const sh::TQualifier qualifier,
+ const sh::TLayoutQualifier &layoutQualifier,
+ const TSourceLoc &location)
+{
+ if (qualifier == EvqShared && !layoutQualifier.isEmpty())
+ {
+ error(location, "Shared memory declarations cannot have layout specified", "layout");
+ }
+
+ if (layoutQualifier.matrixPacking != EmpUnspecified)
+ {
+ error(location, "layout qualifier only valid for interface blocks",
+ getMatrixPackingString(layoutQualifier.matrixPacking));
+ return;
+ }
+
+ if (layoutQualifier.blockStorage != EbsUnspecified)
+ {
+ error(location, "layout qualifier only valid for interface blocks",
+ getBlockStorageString(layoutQualifier.blockStorage));
+ return;
+ }
+
+ if (qualifier == EvqFragmentOut)
+ {
+ if (layoutQualifier.location != -1 && layoutQualifier.yuv == true)
+ {
+ error(location, "invalid layout qualifier combination", "yuv");
+ return;
+ }
+ }
else
- type->setQualifier(paramQualifier);
+ {
+ checkYuvIsNotSpecified(location, layoutQualifier.yuv);
+ }
- return false;
+ // If multiview extension is enabled, "in" qualifier is allowed in the vertex shader in previous
+ // parsing steps. So it needs to be checked here.
+ if (isExtensionEnabled(TExtension::OVR_multiview) && mShaderVersion < 300 &&
+ qualifier == EvqVertexIn)
+ {
+ error(location, "storage qualifier supported in GLSL ES 3.00 and above only", "in");
+ }
+
+ bool canHaveLocation = qualifier == EvqVertexIn || qualifier == EvqFragmentOut;
+ if (mShaderVersion >= 310)
+ {
+ canHaveLocation = canHaveLocation || qualifier == EvqUniform || IsVarying(qualifier);
+ // We're not checking whether the uniform location is in range here since that depends on
+ // the type of the variable.
+ // The type can only be fully determined for non-empty declarations.
+ }
+ if (!canHaveLocation)
+ {
+ checkLocationIsNotSpecified(location, layoutQualifier);
+ }
}
-bool TParseContext::extensionErrorCheck(const TSourceLoc &line, const TString &extension)
+void TParseContext::atomicCounterQualifierErrorCheck(const TPublicType &publicType,
+ const TSourceLoc &location)
{
- const TExtensionBehavior &extBehavior = extensionBehavior();
- TExtensionBehavior::const_iterator iter = extBehavior.find(extension.c_str());
- if (iter == extBehavior.end())
+ if (publicType.precision != EbpHigh)
{
- error(line, "extension", extension.c_str(), "is not supported");
- return true;
+ error(location, "Can only be highp", "atomic counter");
}
- // In GLSL ES, an extension's default behavior is "disable".
- if (iter->second == EBhDisable || iter->second == EBhUndefined)
+ // dEQP enforces compile error if location is specified. See uniform_location.test.
+ if (publicType.layoutQualifier.location != -1)
{
- error(line, "extension", extension.c_str(), "is disabled");
- return true;
+ error(location, "location must not be set for atomic_uint", "layout");
}
- if (iter->second == EBhWarn)
+ if (publicType.layoutQualifier.binding == -1)
{
- warning(line, "extension", extension.c_str(), "is being used");
- return false;
+ error(location, "no binding specified", "atomic counter");
}
+}
- return false;
+void TParseContext::emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location)
+{
+ if (type.isUnsizedArray())
+ {
+ // ESSL3 spec section 4.1.9: Array declaration which leaves the size unspecified is an
+ // error. It is assumed that this applies to empty declarations as well.
+ error(location, "empty array declaration needs to specify a size", "");
+ }
}
-// These checks are common for all declarations starting a declarator list, and declarators that
-// follow an empty declaration.
-//
-bool TParseContext::singleDeclarationErrorCheck(const TPublicType &publicType,
- const TSourceLoc &identifierLocation)
+// These checks are done for all declarations that are non-empty. They're done for non-empty
+// declarations starting a declarator list, and declarators that follow an empty declaration.
+void TParseContext::nonEmptyDeclarationErrorCheck(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation)
{
switch (publicType.qualifier)
{
@@ -1010,105 +1375,363 @@ bool TParseContext::singleDeclarationErrorCheck(const TPublicType &publicType,
case EvqAttribute:
case EvqVertexIn:
case EvqFragmentOut:
- if (publicType.type == EbtStruct)
+ case EvqComputeIn:
+ if (publicType.getBasicType() == EbtStruct)
{
error(identifierLocation, "cannot be used with a structure",
getQualifierString(publicType.qualifier));
- return true;
+ return;
}
-
+ break;
+ case EvqBuffer:
+ if (publicType.getBasicType() != EbtInterfaceBlock)
+ {
+ error(identifierLocation,
+ "cannot declare buffer variables at global scope(outside a block)",
+ getQualifierString(publicType.qualifier));
+ return;
+ }
+ break;
default:
break;
}
-
+ std::string reason(getBasicString(publicType.getBasicType()));
+ reason += "s must be uniform";
if (publicType.qualifier != EvqUniform &&
- samplerErrorCheck(identifierLocation, publicType, "samplers must be uniform"))
+ !checkIsNotOpaqueType(identifierLocation, publicType.typeSpecifierNonArray, reason.c_str()))
{
- return true;
+ return;
+ }
+
+ if ((publicType.qualifier != EvqTemporary && publicType.qualifier != EvqGlobal &&
+ publicType.qualifier != EvqConst) &&
+ publicType.getBasicType() == EbtYuvCscStandardEXT)
+ {
+ error(identifierLocation, "cannot be used with a yuvCscStandardEXT",
+ getQualifierString(publicType.qualifier));
+ return;
+ }
+
+ if (mShaderVersion >= 310 && publicType.qualifier == EvqUniform)
+ {
+ // Valid uniform declarations can't be unsized arrays since uniforms can't be initialized.
+ // But invalid shaders may still reach here with an unsized array declaration.
+ TType type(publicType);
+ if (!type.isUnsizedArray())
+ {
+ checkUniformLocationInRange(identifierLocation, type.getLocationCount(),
+ publicType.layoutQualifier);
+ }
}
// check for layout qualifier issues
const TLayoutQualifier layoutQualifier = publicType.layoutQualifier;
- if (layoutQualifier.matrixPacking != EmpUnspecified)
+ if (IsImage(publicType.getBasicType()))
{
- error(identifierLocation, "layout qualifier",
- getMatrixPackingString(layoutQualifier.matrixPacking),
- "only valid for interface blocks");
- return true;
+
+ switch (layoutQualifier.imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ case EiifRGBA8:
+ case EiifRGBA8_SNORM:
+ if (!IsFloatImage(publicType.getBasicType()))
+ {
+ error(identifierLocation,
+ "internal image format requires a floating image type",
+ getBasicString(publicType.getBasicType()));
+ return;
+ }
+ break;
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ if (!IsIntegerImage(publicType.getBasicType()))
+ {
+ error(identifierLocation,
+ "internal image format requires an integer image type",
+ getBasicString(publicType.getBasicType()));
+ return;
+ }
+ break;
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ if (!IsUnsignedImage(publicType.getBasicType()))
+ {
+ error(identifierLocation,
+ "internal image format requires an unsigned image type",
+ getBasicString(publicType.getBasicType()));
+ return;
+ }
+ break;
+ case EiifUnspecified:
+ error(identifierLocation, "layout qualifier", "No image internal format specified");
+ return;
+ default:
+ error(identifierLocation, "layout qualifier", "unrecognized token");
+ return;
+ }
+
+ // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+ switch (layoutQualifier.imageInternalFormat)
+ {
+ case EiifR32F:
+ case EiifR32I:
+ case EiifR32UI:
+ break;
+ default:
+ if (!publicType.memoryQualifier.readonly && !publicType.memoryQualifier.writeonly)
+ {
+ error(identifierLocation, "layout qualifier",
+ "Except for images with the r32f, r32i and r32ui format qualifiers, "
+ "image variables must be qualified readonly and/or writeonly");
+ return;
+ }
+ break;
+ }
+ }
+ else
+ {
+ checkInternalFormatIsNotSpecified(identifierLocation, layoutQualifier.imageInternalFormat);
+ checkMemoryQualifierIsNotSpecified(publicType.memoryQualifier, identifierLocation);
}
- if (layoutQualifier.blockStorage != EbsUnspecified)
+ if (IsAtomicCounter(publicType.getBasicType()))
{
- error(identifierLocation, "layout qualifier",
- getBlockStorageString(layoutQualifier.blockStorage),
- "only valid for interface blocks");
- return true;
+ atomicCounterQualifierErrorCheck(publicType, identifierLocation);
}
+ else
+ {
+ checkOffsetIsNotSpecified(identifierLocation, layoutQualifier.offset);
+ }
+}
- if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut &&
- layoutLocationErrorCheck(identifierLocation, publicType.layoutQualifier))
+void TParseContext::checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type)
+{
+ TLayoutQualifier layoutQualifier = type.getLayoutQualifier();
+ // Note that the ESSL 3.10 section 4.4.5 is not particularly clear on how the binding qualifier
+ // on arrays of arrays should be handled. We interpret the spec so that the binding value is
+ // incremented for each element of the innermost nested arrays. This is in line with how arrays
+ // of arrays of blocks are specified to behave in GLSL 4.50 and a conservative interpretation
+ // when it comes to which shaders are accepted by the compiler.
+ int arrayTotalElementCount = type.getArraySizeProduct();
+ if (IsImage(type.getBasicType()))
{
- return true;
+ checkImageBindingIsValid(identifierLocation, layoutQualifier.binding,
+ arrayTotalElementCount);
+ }
+ else if (IsSampler(type.getBasicType()))
+ {
+ checkSamplerBindingIsValid(identifierLocation, layoutQualifier.binding,
+ arrayTotalElementCount);
+ }
+ else if (IsAtomicCounter(type.getBasicType()))
+ {
+ checkAtomicCounterBindingIsValid(identifierLocation, layoutQualifier.binding);
+ }
+ else
+ {
+ ASSERT(!IsOpaqueType(type.getBasicType()));
+ checkBindingIsNotSpecified(identifierLocation, layoutQualifier.binding);
}
+}
- return false;
+void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
+ const TString &layoutQualifierName,
+ int versionRequired)
+{
+
+ if (mShaderVersion < versionRequired)
+ {
+ error(location, "invalid layout qualifier: not supported", layoutQualifierName.c_str());
+ }
}
-bool TParseContext::layoutLocationErrorCheck(const TSourceLoc &location,
- const TLayoutQualifier &layoutQualifier)
+bool TParseContext::checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier)
{
- if (layoutQualifier.location != -1)
+ const sh::WorkGroupSize &localSize = layoutQualifier.localSize;
+ for (size_t i = 0u; i < localSize.size(); ++i)
{
- error(location, "invalid layout qualifier:", "location",
- "only valid on program inputs and outputs");
- return true;
+ if (localSize[i] != -1)
+ {
+ error(location,
+ "invalid layout qualifier: only valid when used with 'in' in a compute shader "
+ "global layout declaration",
+ getWorkGroupSizeString(i));
+ return false;
+ }
}
- return false;
+ return true;
+}
+
+void TParseContext::checkInternalFormatIsNotSpecified(const TSourceLoc &location,
+ TLayoutImageInternalFormat internalFormat)
+{
+ if (internalFormat != EiifUnspecified)
+ {
+ error(location, "invalid layout qualifier: only valid when used with images",
+ getImageInternalFormatString(internalFormat));
+ }
+}
+
+void TParseContext::checkBindingIsNotSpecified(const TSourceLoc &location, int binding)
+{
+ if (binding != -1)
+ {
+ error(location,
+ "invalid layout qualifier: only valid when used with opaque types or blocks",
+ "binding");
+ }
+}
+
+void TParseContext::checkOffsetIsNotSpecified(const TSourceLoc &location, int offset)
+{
+ if (offset != -1)
+ {
+ error(location, "invalid layout qualifier: only valid when used with atomic counters",
+ "offset");
+ }
+}
+
+void TParseContext::checkImageBindingIsValid(const TSourceLoc &location,
+ int binding,
+ int arrayTotalElementCount)
+{
+ // Expects arraySize to be 1 when setting binding for only a single variable.
+ if (binding >= 0 && binding + arrayTotalElementCount > mMaxImageUnits)
+ {
+ error(location, "image binding greater than gl_MaxImageUnits", "binding");
+ }
+}
+
+void TParseContext::checkSamplerBindingIsValid(const TSourceLoc &location,
+ int binding,
+ int arrayTotalElementCount)
+{
+ // Expects arraySize to be 1 when setting binding for only a single variable.
+ if (binding >= 0 && binding + arrayTotalElementCount > mMaxCombinedTextureImageUnits)
+ {
+ error(location, "sampler binding greater than maximum texture units", "binding");
+ }
+}
+
+void TParseContext::checkBlockBindingIsValid(const TSourceLoc &location,
+ const TQualifier &qualifier,
+ int binding,
+ int arraySize)
+{
+ int size = (arraySize == 0 ? 1 : arraySize);
+ if (qualifier == EvqUniform)
+ {
+ if (binding + size > mMaxUniformBufferBindings)
+ {
+ error(location, "uniform block binding greater than MAX_UNIFORM_BUFFER_BINDINGS",
+ "binding");
+ }
+ }
+ else if (qualifier == EvqBuffer)
+ {
+ if (binding + size > mMaxShaderStorageBufferBindings)
+ {
+ error(location,
+ "shader storage block binding greater than MAX_SHADER_STORAGE_BUFFER_BINDINGS",
+ "binding");
+ }
+ }
+}
+void TParseContext::checkAtomicCounterBindingIsValid(const TSourceLoc &location, int binding)
+{
+ if (binding >= mMaxAtomicCounterBindings)
+ {
+ error(location, "atomic counter binding greater than gl_MaxAtomicCounterBindings",
+ "binding");
+ }
+}
+
+void TParseContext::checkUniformLocationInRange(const TSourceLoc &location,
+ int objectLocationCount,
+ const TLayoutQualifier &layoutQualifier)
+{
+ int loc = layoutQualifier.location;
+ if (loc >= 0 && loc + objectLocationCount > mMaxUniformLocations)
+ {
+ error(location, "Uniform location out of range", "location");
+ }
+}
+
+void TParseContext::checkYuvIsNotSpecified(const TSourceLoc &location, bool yuv)
+{
+ if (yuv != false)
+ {
+ error(location, "invalid layout qualifier: only valid on program outputs", "yuv");
+ }
}
-bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
- TIntermAggregate *aggregate)
+void TParseContext::functionCallRValueLValueErrorCheck(const TFunction *fnCandidate,
+ TIntermAggregate *fnCall)
{
for (size_t i = 0; i < fnCandidate->getParamCount(); ++i)
{
TQualifier qual = fnCandidate->getParam(i).type->getQualifier();
+ TIntermTyped *argument = (*(fnCall->getSequence()))[i]->getAsTyped();
+ if (!IsImage(argument->getBasicType()) && (IsQualifierUnspecified(qual) || qual == EvqIn ||
+ qual == EvqInOut || qual == EvqConstReadOnly))
+ {
+ if (argument->getMemoryQualifier().writeonly)
+ {
+ error(argument->getLine(),
+ "Writeonly value cannot be passed for 'in' or 'inout' parameters.",
+ fnCall->getFunctionSymbolInfo()->getName().c_str());
+ return;
+ }
+ }
if (qual == EvqOut || qual == EvqInOut)
{
- TIntermTyped *node = (*(aggregate->getSequence()))[i]->getAsTyped();
- if (lValueErrorCheck(node->getLine(), "assign", node))
+ if (!checkCanBeLValue(argument->getLine(), "assign", argument))
{
- error(node->getLine(),
- "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
- recover();
- return true;
+ error(argument->getLine(),
+ "Constant value cannot be passed for 'out' or 'inout' parameters.",
+ fnCall->getFunctionSymbolInfo()->getName().c_str());
+ return;
}
}
}
- return false;
}
-void TParseContext::es3InvariantErrorCheck(const TQualifier qualifier,
- const TSourceLoc &invariantLocation)
+void TParseContext::checkInvariantVariableQualifier(bool invariant,
+ const TQualifier qualifier,
+ const TSourceLoc &invariantLocation)
{
- if (!sh::IsVaryingOut(qualifier) && qualifier != EvqFragmentOut)
+ if (!invariant)
+ return;
+
+ if (mShaderVersion < 300)
{
- error(invariantLocation, "Only out variables can be invariant.", "invariant");
- recover();
+ // input variables in the fragment shader can be also qualified as invariant
+ if (!sh::CanBeInvariantESSL1(qualifier))
+ {
+ error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
+ }
+ }
+ else
+ {
+ if (!sh::CanBeInvariantESSL3OrGreater(qualifier))
+ {
+ error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
+ }
}
}
-bool TParseContext::supportsExtension(const char *extension)
-{
- const TExtensionBehavior &extbehavior = extensionBehavior();
- TExtensionBehavior::const_iterator iter = extbehavior.find(extension);
- return (iter != extbehavior.end());
-}
-
-bool TParseContext::isExtensionEnabled(const char *extension) const
+bool TParseContext::isExtensionEnabled(TExtension extension) const
{
- return ::IsExtensionEnabled(extensionBehavior(), extension);
+ return IsExtensionEnabled(extensionBehavior(), extension);
}
void TParseContext::handleExtensionDirective(const TSourceLoc &loc,
@@ -1132,6 +1755,32 @@ void TParseContext::handlePragmaDirective(const TSourceLoc &loc,
mDirectiveHandler.handlePragma(srcLoc, name, value, stdgl);
}
+sh::WorkGroupSize TParseContext::getComputeShaderLocalSize() const
+{
+ sh::WorkGroupSize result(-1);
+ for (size_t i = 0u; i < result.size(); ++i)
+ {
+ if (mComputeShaderLocalSizeDeclared && mComputeShaderLocalSize[i] == -1)
+ {
+ result[i] = 1;
+ }
+ else
+ {
+ result[i] = mComputeShaderLocalSize[i];
+ }
+ }
+ return result;
+}
+
+TIntermConstantUnion *TParseContext::addScalarLiteral(const TConstantUnion *constantUnion,
+ const TSourceLoc &line)
+{
+ TIntermConstantUnion *node = new TIntermConstantUnion(
+ constantUnion, TType(constantUnion->getType(), EbpUndefined, EvqConst));
+ node->setLine(line);
+ return node;
+}
+
/////////////////////////////////////////////////////////////////////////////////
//
// Non-Errors.
@@ -1142,70 +1791,64 @@ const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location,
const TString *name,
const TSymbol *symbol)
{
- const TVariable *variable = NULL;
-
if (!symbol)
{
error(location, "undeclared identifier", name->c_str());
- recover();
+ return nullptr;
}
- else if (!symbol->isVariable())
+
+ if (!symbol->isVariable())
{
error(location, "variable expected", name->c_str());
- recover();
+ return nullptr;
}
- else
- {
- variable = static_cast<const TVariable *>(symbol);
- if (symbolTable.findBuiltIn(variable->getName(), mShaderVersion) &&
- !variable->getExtension().empty() &&
- extensionErrorCheck(location, variable->getExtension()))
- {
- recover();
- }
+ const TVariable *variable = static_cast<const TVariable *>(symbol);
- // Reject shaders using both gl_FragData and gl_FragColor
- TQualifier qualifier = variable->getType().getQualifier();
- if (qualifier == EvqFragData || qualifier == EvqSecondaryFragDataEXT)
- {
- mUsesFragData = true;
- }
- else if (qualifier == EvqFragColor || qualifier == EvqSecondaryFragColorEXT)
- {
- mUsesFragColor = true;
- }
- if (qualifier == EvqSecondaryFragDataEXT || qualifier == EvqSecondaryFragColorEXT)
- {
- mUsesSecondaryOutputs = true;
- }
+ if (variable->getExtension() != TExtension::UNDEFINED)
+ {
+ checkCanUseExtension(location, variable->getExtension());
+ }
+
+ // Reject shaders using both gl_FragData and gl_FragColor
+ TQualifier qualifier = variable->getType().getQualifier();
+ if (qualifier == EvqFragData || qualifier == EvqSecondaryFragDataEXT)
+ {
+ mUsesFragData = true;
+ }
+ else if (qualifier == EvqFragColor || qualifier == EvqSecondaryFragColorEXT)
+ {
+ mUsesFragColor = true;
+ }
+ if (qualifier == EvqSecondaryFragDataEXT || qualifier == EvqSecondaryFragColorEXT)
+ {
+ mUsesSecondaryOutputs = true;
+ }
- // This validation is not quite correct - it's only an error to write to
- // both FragData and FragColor. For simplicity, and because users shouldn't
- // be rewarded for reading from undefined varaibles, return an error
- // if they are both referenced, rather than assigned.
- if (mUsesFragData && mUsesFragColor)
+ // This validation is not quite correct - it's only an error to write to
+ // both FragData and FragColor. For simplicity, and because users shouldn't
+ // be rewarded for reading from undefined varaibles, return an error
+ // if they are both referenced, rather than assigned.
+ if (mUsesFragData && mUsesFragColor)
+ {
+ const char *errorMessage = "cannot use both gl_FragData and gl_FragColor";
+ if (mUsesSecondaryOutputs)
{
- const char *errorMessage = "cannot use both gl_FragData and gl_FragColor";
- if (mUsesSecondaryOutputs)
- {
- errorMessage =
- "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)"
- " and (gl_FragColor, gl_SecondaryFragColorEXT)";
- }
- error(location, errorMessage, name->c_str());
- recover();
+ errorMessage =
+ "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)"
+ " and (gl_FragColor, gl_SecondaryFragColorEXT)";
}
+ error(location, errorMessage, name->c_str());
}
- if (!variable)
+ // GLSL ES 3.1 Revision 4, 7.1.3 Compute Shader Special Variables
+ if (getShaderType() == GL_COMPUTE_SHADER && !mComputeShaderLocalSizeDeclared &&
+ qualifier == EvqWorkGroupSize)
{
- TType type(EbtFloat, EbpUndefined);
- TVariable *fakeVariable = new TVariable(name, type);
- symbolTable.declare(fakeVariable);
- variable = fakeVariable;
+ error(location,
+ "It is an error to use gl_WorkGroupSize before declaring the local group size",
+ "gl_WorkGroupSize");
}
-
return variable;
}
@@ -1215,75 +1858,82 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
{
const TVariable *variable = getNamedVariable(location, name, symbol);
+ if (!variable)
+ {
+ TIntermTyped *node = CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
+ node->setLine(location);
+ return node;
+ }
+
+ const TType &variableType = variable->getType();
+ TIntermTyped *node = nullptr;
+
if (variable->getConstPointer())
{
const TConstantUnion *constArray = variable->getConstPointer();
- return intermediate.addConstantUnion(constArray, variable->getType(), location);
+ node = new TIntermConstantUnion(constArray, variableType);
}
- else
+ else if (variableType.getQualifier() == EvqWorkGroupSize && mComputeShaderLocalSizeDeclared)
{
- return intermediate.addSymbol(variable->getUniqueId(), variable->getName(),
- variable->getType(), location);
- }
-}
+ // gl_WorkGroupSize can be used to size arrays according to the ESSL 3.10.4 spec, so it
+ // needs to be added to the AST as a constant and not as a symbol.
+ sh::WorkGroupSize workGroupSize = getComputeShaderLocalSize();
+ TConstantUnion *constArray = new TConstantUnion[3];
+ for (size_t i = 0; i < 3; ++i)
+ {
+ constArray[i].setUConst(static_cast<unsigned int>(workGroupSize[i]));
+ }
-//
-// Look up a function name in the symbol table, and make sure it is a function.
-//
-// Return the function symbol if found, otherwise 0.
-//
-const TFunction *TParseContext::findFunction(const TSourceLoc &line,
- TFunction *call,
- int inputShaderVersion,
- bool *builtIn)
-{
- // First find by unmangled name to check whether the function name has been
- // hidden by a variable name or struct typename.
- // If a function is found, check for one with a matching argument list.
- const TSymbol *symbol = symbolTable.find(call->getName(), inputShaderVersion, builtIn);
- if (symbol == 0 || symbol->isFunction())
- {
- symbol = symbolTable.find(call->getMangledName(), inputShaderVersion, builtIn);
- }
+ ASSERT(variableType.getBasicType() == EbtUInt);
+ ASSERT(variableType.getObjectSize() == 3);
- if (symbol == 0)
- {
- error(line, "no matching overloaded function found", call->getName().c_str());
- return 0;
+ TType type(variableType);
+ type.setQualifier(EvqConst);
+ node = new TIntermConstantUnion(constArray, type);
}
+ else if ((mGeometryShaderInputPrimitiveType != EptUndefined) &&
+ (variableType.getQualifier() == EvqPerVertexIn))
+ {
+ ASSERT(mGeometryShaderInputArraySize > 0u);
- if (!symbol->isFunction())
+ node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variableType);
+ node->getTypePointer()->sizeOutermostUnsizedArray(mGeometryShaderInputArraySize);
+ }
+ else
{
- error(line, "function name expected", call->getName().c_str());
- return 0;
+ node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variableType);
}
-
- return static_cast<const TFunction *>(symbol);
+ ASSERT(node != nullptr);
+ node->setLine(location);
+ return node;
}
-//
// Initializers show up in several places in the grammar. Have one set of
// code to handle them here.
//
-// Returns true on error, false if no error
-//
+// Returns true on success.
bool TParseContext::executeInitializer(const TSourceLoc &line,
const TString &identifier,
- const TPublicType &pType,
+ TType type,
TIntermTyped *initializer,
- TIntermNode **intermNode)
+ TIntermBinary **initNode)
{
- ASSERT(intermNode != nullptr);
- TType type = TType(pType);
+ ASSERT(initNode != nullptr);
+ ASSERT(*initNode == nullptr);
TVariable *variable = nullptr;
if (type.isUnsizedArray())
{
- type.setArraySize(initializer->getArraySize());
+ // In case initializer is not an array or type has more dimensions than initializer, this
+ // will default to setting array sizes to 1. We have not checked yet whether the initializer
+ // actually is an array or not. Having a non-array initializer for an unsized array will
+ // result in an error later, so we don't generate an error message here.
+ auto *arraySizes = initializer->getType().getArraySizes();
+ type.sizeUnsizedArrays(arraySizes);
}
if (!declareVariable(line, identifier, type, &variable))
{
- return true;
+ return false;
}
bool globalInitWarning = false;
@@ -1293,7 +1943,7 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
// Error message does not completely match behavior with ESSL 1.00, but
// we want to steer developers towards only using constant expressions.
error(line, "global variable initializers must be constant expressions", "=");
- return true;
+ return false;
}
if (globalInitWarning)
{
@@ -1312,7 +1962,7 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
{
error(line, " cannot initialize this type of qualifier ",
variable->getType().getQualifierString());
- return true;
+ return false;
}
//
// test for and propagate constant
@@ -1322,19 +1972,20 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
{
if (qualifier != initializer->getType().getQualifier())
{
- std::stringstream extraInfoStream;
- extraInfoStream << "'" << variable->getType().getCompleteString() << "'";
- std::string extraInfo = extraInfoStream.str();
- error(line, " assigning non-constant to", "=", extraInfo.c_str());
+ std::stringstream reasonStream;
+ reasonStream << "assigning non-constant to '" << variable->getType().getCompleteString()
+ << "'";
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), "=");
variable->getType().setQualifier(EvqTemporary);
- return true;
+ return false;
}
if (type != initializer->getType())
{
error(line, " non-matching types for const initializer ",
variable->getType().getQualifierString());
variable->getType().setQualifier(EvqTemporary);
- return true;
+ return false;
}
// Save the constant folded value to the variable if possible. For example array
@@ -1345,8 +1996,8 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
if (initializer->getAsConstantUnion())
{
variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer());
- *intermNode = nullptr;
- return false;
+ ASSERT(*initNode == nullptr);
+ return true;
}
else if (initializer->getAsSymbolNode())
{
@@ -1358,84 +2009,220 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
if (constArray)
{
variable->shareConstPointer(constArray);
- *intermNode = nullptr;
- return false;
+ ASSERT(*initNode == nullptr);
+ return true;
}
}
}
- TIntermSymbol *intermSymbol = intermediate.addSymbol(
- variable->getUniqueId(), variable->getName(), variable->getType(), line);
- *intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
- if (*intermNode == nullptr)
+ TIntermSymbol *intermSymbol =
+ new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType());
+ intermSymbol->setLine(line);
+ *initNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
+ if (*initNode == nullptr)
{
assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
- return true;
+ return false;
}
- return false;
+ return true;
+}
+
+TIntermNode *TParseContext::addConditionInitializer(const TPublicType &pType,
+ const TString &identifier,
+ TIntermTyped *initializer,
+ const TSourceLoc &loc)
+{
+ checkIsScalarBool(loc, pType);
+ TIntermBinary *initNode = nullptr;
+ TType type(pType);
+ if (executeInitializer(loc, identifier, type, initializer, &initNode))
+ {
+ // The initializer is valid. The init condition needs to have a node - either the
+ // initializer node, or a constant node in case the initialized variable is const and won't
+ // be recorded in the AST.
+ if (initNode == nullptr)
+ {
+ return initializer;
+ }
+ else
+ {
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->appendDeclarator(initNode);
+ return declaration;
+ }
+ }
+ return nullptr;
+}
+
+TIntermNode *TParseContext::addLoop(TLoopType type,
+ TIntermNode *init,
+ TIntermNode *cond,
+ TIntermTyped *expr,
+ TIntermNode *body,
+ const TSourceLoc &line)
+{
+ TIntermNode *node = nullptr;
+ TIntermTyped *typedCond = nullptr;
+ if (cond)
+ {
+ typedCond = cond->getAsTyped();
+ }
+ if (cond == nullptr || typedCond)
+ {
+ if (type == ELoopDoWhile)
+ {
+ checkIsScalarBool(line, typedCond);
+ }
+ // In the case of other loops, it was checked before that the condition is a scalar boolean.
+ ASSERT(mDiagnostics->numErrors() > 0 || typedCond == nullptr ||
+ (typedCond->getBasicType() == EbtBool && !typedCond->isArray() &&
+ !typedCond->isVector()));
+
+ node = new TIntermLoop(type, init, typedCond, expr, EnsureBlock(body));
+ node->setLine(line);
+ return node;
+ }
+
+ ASSERT(type != ELoopDoWhile);
+
+ TIntermDeclaration *declaration = cond->getAsDeclarationNode();
+ ASSERT(declaration);
+ TIntermBinary *declarator = declaration->getSequence()->front()->getAsBinaryNode();
+ ASSERT(declarator->getLeft()->getAsSymbolNode());
+
+ // The condition is a declaration. In the AST representation we don't support declarations as
+ // loop conditions. Wrap the loop to a block that declares the condition variable and contains
+ // the loop.
+ TIntermBlock *block = new TIntermBlock();
+
+ TIntermDeclaration *declareCondition = new TIntermDeclaration();
+ declareCondition->appendDeclarator(declarator->getLeft()->deepCopy());
+ block->appendStatement(declareCondition);
+
+ TIntermBinary *conditionInit = new TIntermBinary(EOpAssign, declarator->getLeft()->deepCopy(),
+ declarator->getRight()->deepCopy());
+ TIntermLoop *loop = new TIntermLoop(type, init, conditionInit, expr, EnsureBlock(body));
+ block->appendStatement(loop);
+ loop->setLine(line);
+ block->setLine(line);
+ return block;
+}
+
+TIntermNode *TParseContext::addIfElse(TIntermTyped *cond,
+ TIntermNodePair code,
+ const TSourceLoc &loc)
+{
+ bool isScalarBool = checkIsScalarBool(loc, cond);
+
+ // For compile time constant conditions, prune the code now.
+ if (isScalarBool && cond->getAsConstantUnion())
+ {
+ if (cond->getAsConstantUnion()->getBConst(0) == true)
+ {
+ return EnsureBlock(code.node1);
+ }
+ else
+ {
+ return EnsureBlock(code.node2);
+ }
+ }
+
+ TIntermIfElse *node = new TIntermIfElse(cond, EnsureBlock(code.node1), EnsureBlock(code.node2));
+ node->setLine(loc);
+
+ return node;
+}
+
+void TParseContext::addFullySpecifiedType(TPublicType *typeSpecifier)
+{
+ checkPrecisionSpecified(typeSpecifier->getLine(), typeSpecifier->precision,
+ typeSpecifier->getBasicType());
+
+ if (mShaderVersion < 300 && typeSpecifier->isArray())
+ {
+ error(typeSpecifier->getLine(), "not supported", "first-class array");
+ typeSpecifier->clearArrayness();
+ }
}
-TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier,
- bool invariant,
- TLayoutQualifier layoutQualifier,
+TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
const TPublicType &typeSpecifier)
{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
+
TPublicType returnType = typeSpecifier;
- returnType.qualifier = qualifier;
- returnType.invariant = invariant;
- returnType.layoutQualifier = layoutQualifier;
+ returnType.qualifier = typeQualifier.qualifier;
+ returnType.invariant = typeQualifier.invariant;
+ returnType.layoutQualifier = typeQualifier.layoutQualifier;
+ returnType.memoryQualifier = typeQualifier.memoryQualifier;
+ returnType.precision = typeSpecifier.precision;
+
+ if (typeQualifier.precision != EbpUndefined)
+ {
+ returnType.precision = typeQualifier.precision;
+ }
+
+ checkPrecisionSpecified(typeSpecifier.getLine(), returnType.precision,
+ typeSpecifier.getBasicType());
+
+ checkInvariantVariableQualifier(returnType.invariant, returnType.qualifier,
+ typeSpecifier.getLine());
+
+ checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), returnType.layoutQualifier);
if (mShaderVersion < 300)
{
- if (typeSpecifier.array)
+ if (typeSpecifier.isArray())
{
- error(typeSpecifier.line, "not supported", "first-class array");
- recover();
+ error(typeSpecifier.getLine(), "not supported", "first-class array");
returnType.clearArrayness();
}
- if (qualifier == EvqAttribute &&
- (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
+ if (returnType.qualifier == EvqAttribute &&
+ (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
{
- error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier));
- recover();
+ error(typeSpecifier.getLine(), "cannot be bool or int",
+ getQualifierString(returnType.qualifier));
}
- if ((qualifier == EvqVaryingIn || qualifier == EvqVaryingOut) &&
- (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
+ if ((returnType.qualifier == EvqVaryingIn || returnType.qualifier == EvqVaryingOut) &&
+ (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
{
- error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier));
- recover();
+ error(typeSpecifier.getLine(), "cannot be bool or int",
+ getQualifierString(returnType.qualifier));
}
}
else
{
- if (!layoutQualifier.isEmpty())
+ if (!returnType.layoutQualifier.isEmpty())
{
- if (globalErrorCheck(typeSpecifier.line, symbolTable.atGlobalLevel(), "layout"))
- {
- recover();
- }
+ checkIsAtGlobalLevel(typeSpecifier.getLine(), "layout");
}
- if (sh::IsVarying(qualifier) || qualifier == EvqVertexIn || qualifier == EvqFragmentOut)
+ if (sh::IsVarying(returnType.qualifier) || returnType.qualifier == EvqVertexIn ||
+ returnType.qualifier == EvqFragmentOut)
{
- es3InputOutputTypeCheck(qualifier, typeSpecifier, typeSpecifier.line);
+ checkInputOutputTypeIsValidES3(returnType.qualifier, typeSpecifier,
+ typeSpecifier.getLine());
+ }
+ if (returnType.qualifier == EvqComputeIn)
+ {
+ error(typeSpecifier.getLine(), "'in' can be only used to specify the local group size",
+ "in");
}
}
return returnType;
}
-void TParseContext::es3InputOutputTypeCheck(const TQualifier qualifier,
- const TPublicType &type,
- const TSourceLoc &qualifierLocation)
+void TParseContext::checkInputOutputTypeIsValidES3(const TQualifier qualifier,
+ const TPublicType &type,
+ const TSourceLoc &qualifierLocation)
{
// An input/output variable can never be bool or a sampler. Samplers are checked elsewhere.
- if (type.type == EbtBool)
+ if (type.getBasicType() == EbtBool)
{
error(qualifierLocation, "cannot be bool", getQualifierString(qualifier));
- recover();
}
// Specific restrictions apply for vertex shader inputs and fragment shader outputs.
@@ -1443,21 +2230,19 @@ void TParseContext::es3InputOutputTypeCheck(const TQualifier qualifier,
{
case EvqVertexIn:
// ESSL 3.00 section 4.3.4
- if (type.array)
+ if (type.isArray())
{
error(qualifierLocation, "cannot be array", getQualifierString(qualifier));
- recover();
}
- // Vertex inputs with a struct type are disallowed in singleDeclarationErrorCheck
+ // Vertex inputs with a struct type are disallowed in nonEmptyDeclarationErrorCheck
return;
case EvqFragmentOut:
// ESSL 3.00 section 4.3.6
- if (type.isMatrix())
+ if (type.typeSpecifierNonArray.isMatrix())
{
error(qualifierLocation, "cannot be matrix", getQualifierString(qualifier));
- recover();
}
- // Fragment outputs with a struct type are disallowed in singleDeclarationErrorCheck
+ // Fragment outputs with a struct type are disallowed in nonEmptyDeclarationErrorCheck
return;
default:
break;
@@ -1466,506 +2251,997 @@ void TParseContext::es3InputOutputTypeCheck(const TQualifier qualifier,
// Vertex shader outputs / fragment shader inputs have a different, slightly more lenient set of
// restrictions.
bool typeContainsIntegers =
- (type.type == EbtInt || type.type == EbtUInt || type.isStructureContainingType(EbtInt) ||
- type.isStructureContainingType(EbtUInt));
+ (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt ||
+ type.isStructureContainingType(EbtInt) || type.isStructureContainingType(EbtUInt));
if (typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut)
{
error(qualifierLocation, "must use 'flat' interpolation here",
getQualifierString(qualifier));
- recover();
}
- if (type.type == EbtStruct)
+ if (type.getBasicType() == EbtStruct)
{
// ESSL 3.00 sections 4.3.4 and 4.3.6.
// These restrictions are only implied by the ESSL 3.00 spec, but
// the ESSL 3.10 spec lists these restrictions explicitly.
- if (type.array)
+ if (type.isArray())
{
error(qualifierLocation, "cannot be an array of structures",
getQualifierString(qualifier));
- recover();
}
if (type.isStructureContainingArrays())
{
error(qualifierLocation, "cannot be a structure containing an array",
getQualifierString(qualifier));
- recover();
}
if (type.isStructureContainingType(EbtStruct))
{
error(qualifierLocation, "cannot be a structure containing a structure",
getQualifierString(qualifier));
- recover();
}
if (type.isStructureContainingType(EbtBool))
{
error(qualifierLocation, "cannot be a structure containing a bool",
getQualifierString(qualifier));
- recover();
}
}
}
-TIntermAggregate *TParseContext::parseSingleDeclaration(TPublicType &publicType,
- const TSourceLoc &identifierOrTypeLocation,
- const TString &identifier)
+void TParseContext::checkLocalVariableConstStorageQualifier(const TQualifierWrapperBase &qualifier)
{
- TIntermSymbol *symbol =
- intermediate.addSymbol(0, identifier, TType(publicType), identifierOrTypeLocation);
+ if (qualifier.getType() == QtStorage)
+ {
+ const TStorageQualifierWrapper &storageQualifier =
+ static_cast<const TStorageQualifierWrapper &>(qualifier);
+ if (!declaringFunction() && storageQualifier.getQualifier() != EvqConst &&
+ !symbolTable.atGlobalLevel())
+ {
+ error(storageQualifier.getLine(),
+ "Local variables can only use the const storage qualifier.",
+ storageQualifier.getQualifierString().c_str());
+ }
+ }
+}
- bool emptyDeclaration = (identifier == "");
+void TParseContext::checkMemoryQualifierIsNotSpecified(const TMemoryQualifier &memoryQualifier,
+ const TSourceLoc &location)
+{
+ const std::string reason(
+ "Only allowed with shader storage blocks, variables declared within shader storage blocks "
+ "and variables declared as image types.");
+ if (memoryQualifier.readonly)
+ {
+ error(location, reason.c_str(), "readonly");
+ }
+ if (memoryQualifier.writeonly)
+ {
+ error(location, reason.c_str(), "writeonly");
+ }
+ if (memoryQualifier.coherent)
+ {
+ error(location, reason.c_str(), "coherent");
+ }
+ if (memoryQualifier.restrictQualifier)
+ {
+ error(location, reason.c_str(), "restrict");
+ }
+ if (memoryQualifier.volatileQualifier)
+ {
+ error(location, reason.c_str(), "volatile");
+ }
+}
+
+// Make sure there is no offset overlapping, and store the newly assigned offset to "type" in
+// intermediate tree.
+void TParseContext::checkAtomicCounterOffsetDoesNotOverlap(bool forceAppend,
+ const TSourceLoc &loc,
+ TType *type)
+{
+ if (!IsAtomicCounter(type->getBasicType()))
+ {
+ return;
+ }
+
+ const size_t size = type->isArray() ? kAtomicCounterArrayStride * type->getArraySizeProduct()
+ : kAtomicCounterSize;
+ TLayoutQualifier layoutQualifier = type->getLayoutQualifier();
+ auto &bindingState = mAtomicCounterBindingStates[layoutQualifier.binding];
+ int offset;
+ if (layoutQualifier.offset == -1 || forceAppend)
+ {
+ offset = bindingState.appendSpan(size);
+ }
+ else
+ {
+ offset = bindingState.insertSpan(layoutQualifier.offset, size);
+ }
+ if (offset == -1)
+ {
+ error(loc, "Offset overlapping", "atomic counter");
+ return;
+ }
+ layoutQualifier.offset = offset;
+ type->setLayoutQualifier(layoutQualifier);
+}
+
+void TParseContext::checkGeometryShaderInputAndSetArraySize(const TSourceLoc &location,
+ const char *token,
+ TType *type)
+{
+ if (IsGeometryShaderInput(mShaderType, type->getQualifier()))
+ {
+ if (type->isArray() && type->getOutermostArraySize() == 0u)
+ {
+ // Set size for the unsized geometry shader inputs if they are declared after a valid
+ // input primitive declaration.
+ if (mGeometryShaderInputPrimitiveType != EptUndefined)
+ {
+ ASSERT(mGeometryShaderInputArraySize > 0u);
+ type->sizeOutermostUnsizedArray(mGeometryShaderInputArraySize);
+ }
+ else
+ {
+ // [GLSL ES 3.2 SPEC Chapter 4.4.1.2]
+ // An input can be declared without an array size if there is a previous layout
+ // which specifies the size.
+ error(location,
+ "Missing a valid input primitive declaration before declaring an unsized "
+ "array input",
+ token);
+ }
+ }
+ else if (type->isArray())
+ {
+ setGeometryShaderInputArraySize(type->getOutermostArraySize(), location);
+ }
+ else
+ {
+ error(location, "Geometry shader input variable must be declared as an array", token);
+ }
+ }
+}
+
+TIntermDeclaration *TParseContext::parseSingleDeclaration(
+ TPublicType &publicType,
+ const TSourceLoc &identifierOrTypeLocation,
+ const TString &identifier)
+{
+ TType type(publicType);
+ if ((mCompileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) &&
+ mDirectiveHandler.pragma().stdgl.invariantAll)
+ {
+ TQualifier qualifier = type.getQualifier();
+
+ // The directive handler has already taken care of rejecting invalid uses of this pragma
+ // (for example, in ESSL 3.00 fragment shaders), so at this point, flatten it into all
+ // affected variable declarations:
+ //
+ // 1. Built-in special variables which are inputs to the fragment shader. (These are handled
+ // elsewhere, in TranslatorGLSL.)
+ //
+ // 2. Outputs from vertex shaders in ESSL 1.00 and 3.00 (EvqVaryingOut and EvqVertexOut). It
+ // is actually less likely that there will be bugs in the handling of ESSL 3.00 shaders, but
+ // the way this is currently implemented we have to enable this compiler option before
+ // parsing the shader and determining the shading language version it uses. If this were
+ // implemented as a post-pass, the workaround could be more targeted.
+ //
+ // 3. Inputs in ESSL 1.00 fragment shaders (EvqVaryingIn). This is somewhat in violation of
+ // the specification, but there are desktop OpenGL drivers that expect that this is the
+ // behavior of the #pragma when specified in ESSL 1.00 fragment shaders.
+ if (qualifier == EvqVaryingOut || qualifier == EvqVertexOut || qualifier == EvqVaryingIn)
+ {
+ type.setInvariant(true);
+ }
+ }
- mDeferredSingleDeclarationErrorCheck = emptyDeclaration;
+ checkGeometryShaderInputAndSetArraySize(identifierOrTypeLocation, identifier.c_str(), &type);
+ declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier,
+ identifierOrTypeLocation);
+
+ bool emptyDeclaration = (identifier == "");
+ mDeferredNonEmptyDeclarationErrorCheck = emptyDeclaration;
+
+ TIntermSymbol *symbol = nullptr;
if (emptyDeclaration)
{
- if (publicType.isUnsizedArray())
+ emptyDeclarationErrorCheck(type, identifierOrTypeLocation);
+ // In most cases we don't need to create a symbol node for an empty declaration.
+ // But if the empty declaration is declaring a struct type, the symbol node will store that.
+ if (type.getBasicType() == EbtStruct)
{
- // ESSL3 spec section 4.1.9: Array declaration which leaves the size unspecified is an
- // error. It is assumed that this applies to empty declarations as well.
- error(identifierOrTypeLocation, "empty array declaration needs to specify a size",
- identifier.c_str());
+ symbol = new TIntermSymbol(symbolTable.getEmptySymbolId(), "", type);
+ }
+ else if (IsAtomicCounter(publicType.getBasicType()))
+ {
+ setAtomicCounterBindingDefaultOffset(publicType, identifierOrTypeLocation);
}
}
else
{
- if (singleDeclarationErrorCheck(publicType, identifierOrTypeLocation))
- recover();
+ nonEmptyDeclarationErrorCheck(publicType, identifierOrTypeLocation);
- if (nonInitErrorCheck(identifierOrTypeLocation, identifier, &publicType))
- recover();
+ checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &type);
+
+ checkAtomicCounterOffsetDoesNotOverlap(false, identifierOrTypeLocation, &type);
TVariable *variable = nullptr;
- if (!declareVariable(identifierOrTypeLocation, identifier, TType(publicType), &variable))
- recover();
+ declareVariable(identifierOrTypeLocation, identifier, type, &variable);
- if (variable && symbol)
- symbol->setId(variable->getUniqueId());
+ if (variable)
+ {
+ symbol = new TIntermSymbol(variable->getUniqueId(), identifier, type);
+ }
}
- return intermediate.makeAggregate(symbol, identifierOrTypeLocation);
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierOrTypeLocation);
+ if (symbol)
+ {
+ symbol->setLine(identifierOrTypeLocation);
+ declaration->appendDeclarator(symbol);
+ }
+ return declaration;
}
-TIntermAggregate *TParseContext::parseSingleArrayDeclaration(TPublicType &publicType,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &indexLocation,
- TIntermTyped *indexExpression)
+TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(
+ TPublicType &elementType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ const TVector<unsigned int> &arraySizes)
{
- mDeferredSingleDeclarationErrorCheck = false;
+ mDeferredNonEmptyDeclarationErrorCheck = false;
- if (singleDeclarationErrorCheck(publicType, identifierLocation))
- recover();
+ declarationQualifierErrorCheck(elementType.qualifier, elementType.layoutQualifier,
+ identifierLocation);
- if (nonInitErrorCheck(identifierLocation, identifier, &publicType))
- recover();
+ nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
- if (arrayTypeErrorCheck(indexLocation, publicType) ||
- arrayQualifierErrorCheck(indexLocation, publicType))
- {
- recover();
- }
+ checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
- TType arrayType(publicType);
+ TType arrayType(elementType);
+ arrayType.makeArrays(arraySizes);
- int size;
- if (arraySizeErrorCheck(identifierLocation, indexExpression, size))
- {
- recover();
- }
- // Make the type an array even if size check failed.
- // This ensures useless error messages regarding the variable's non-arrayness won't follow.
- arrayType.setArraySize(size);
+ checkGeometryShaderInputAndSetArraySize(indexLocation, identifier.c_str(), &arrayType);
+
+ checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &arrayType);
+
+ checkAtomicCounterOffsetDoesNotOverlap(false, identifierLocation, &arrayType);
TVariable *variable = nullptr;
- if (!declareVariable(identifierLocation, identifier, arrayType, &variable))
- recover();
+ declareVariable(identifierLocation, identifier, arrayType, &variable);
- TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation);
- if (variable && symbol)
- symbol->setId(variable->getUniqueId());
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierLocation);
- return intermediate.makeAggregate(symbol, identifierLocation);
+ if (variable)
+ {
+ TIntermSymbol *symbol = new TIntermSymbol(variable->getUniqueId(), identifier, arrayType);
+ symbol->setLine(identifierLocation);
+ declaration->appendDeclarator(symbol);
+ }
+
+ return declaration;
}
-TIntermAggregate *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &initLocation,
- TIntermTyped *initializer)
+TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer)
{
- mDeferredSingleDeclarationErrorCheck = false;
+ mDeferredNonEmptyDeclarationErrorCheck = false;
- if (singleDeclarationErrorCheck(publicType, identifierLocation))
- recover();
+ declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier,
+ identifierLocation);
- TIntermNode *intermNode = nullptr;
- if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode))
- {
- //
- // Build intermediate representation
- //
- return intermNode ? intermediate.makeAggregate(intermNode, initLocation) : nullptr;
- }
- else
+ nonEmptyDeclarationErrorCheck(publicType, identifierLocation);
+
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierLocation);
+
+ TIntermBinary *initNode = nullptr;
+ TType type(publicType);
+ if (executeInitializer(identifierLocation, identifier, type, initializer, &initNode))
{
- recover();
- return nullptr;
+ if (initNode)
+ {
+ declaration->appendDeclarator(initNode);
+ }
}
+ return declaration;
}
-TIntermAggregate *TParseContext::parseSingleArrayInitDeclaration(
- TPublicType &publicType,
+TIntermDeclaration *TParseContext::parseSingleArrayInitDeclaration(
+ TPublicType &elementType,
const TSourceLoc &identifierLocation,
const TString &identifier,
const TSourceLoc &indexLocation,
- TIntermTyped *indexExpression,
+ const TVector<unsigned int> &arraySizes,
const TSourceLoc &initLocation,
TIntermTyped *initializer)
{
- mDeferredSingleDeclarationErrorCheck = false;
+ mDeferredNonEmptyDeclarationErrorCheck = false;
- if (singleDeclarationErrorCheck(publicType, identifierLocation))
- recover();
+ declarationQualifierErrorCheck(elementType.qualifier, elementType.layoutQualifier,
+ identifierLocation);
- if (arrayTypeErrorCheck(indexLocation, publicType) ||
- arrayQualifierErrorCheck(indexLocation, publicType))
- {
- recover();
- }
+ nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
- TPublicType arrayType(publicType);
+ checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
- int size = 0;
- // If indexExpression is nullptr, then the array will eventually get its size implicitly from
- // the initializer.
- if (indexExpression != nullptr &&
- arraySizeErrorCheck(identifierLocation, indexExpression, size))
- {
- recover();
- }
- // Make the type an array even if size check failed.
- // This ensures useless error messages regarding the variable's non-arrayness won't follow.
- arrayType.setArraySize(size);
+ TType arrayType(elementType);
+ arrayType.makeArrays(arraySizes);
+
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierLocation);
// initNode will correspond to the whole of "type b[n] = initializer".
- TIntermNode *initNode = nullptr;
- if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
+ TIntermBinary *initNode = nullptr;
+ if (executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
{
- return initNode ? intermediate.makeAggregate(initNode, initLocation) : nullptr;
- }
- else
- {
- recover();
- return nullptr;
+ if (initNode)
+ {
+ declaration->appendDeclarator(initNode);
+ }
}
+
+ return declaration;
}
-TIntermAggregate *TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc,
- const TSourceLoc &identifierLoc,
- const TString *identifier,
- const TSymbol *symbol)
+TIntermInvariantDeclaration *TParseContext::parseInvariantDeclaration(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &identifierLoc,
+ const TString *identifier,
+ const TSymbol *symbol)
{
- // invariant declaration
- if (globalErrorCheck(invariantLoc, symbolTable.atGlobalLevel(), "invariant varying"))
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
+
+ if (!typeQualifier.invariant)
{
- recover();
+ error(identifierLoc, "Expected invariant", identifier->c_str());
+ return nullptr;
+ }
+ if (!checkIsAtGlobalLevel(identifierLoc, "invariant varying"))
+ {
+ return nullptr;
}
-
if (!symbol)
{
error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str());
- recover();
return nullptr;
}
- else
+ if (!IsQualifierUnspecified(typeQualifier.qualifier))
{
- const TString kGlFrontFacing("gl_FrontFacing");
- if (*identifier == kGlFrontFacing)
- {
- error(identifierLoc, "identifier should not be declared as invariant",
- identifier->c_str());
- recover();
- return nullptr;
- }
- symbolTable.addInvariantVarying(std::string(identifier->c_str()));
- const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
- ASSERT(variable);
- const TType &type = variable->getType();
- TIntermSymbol *intermSymbol =
- intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc);
+ error(identifierLoc, "invariant declaration specifies qualifier",
+ getQualifierString(typeQualifier.qualifier));
+ }
+ if (typeQualifier.precision != EbpUndefined)
+ {
+ error(identifierLoc, "invariant declaration specifies precision",
+ getPrecisionString(typeQualifier.precision));
+ }
+ if (!typeQualifier.layoutQualifier.isEmpty())
+ {
+ error(identifierLoc, "invariant declaration specifies layout", "'layout'");
+ }
- TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc);
- aggregate->setOp(EOpInvariantDeclaration);
- return aggregate;
+ const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
+ if (!variable)
+ {
+ return nullptr;
}
+ const TType &type = variable->getType();
+
+ checkInvariantVariableQualifier(typeQualifier.invariant, type.getQualifier(),
+ typeQualifier.line);
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+
+ symbolTable.addInvariantVarying(std::string(identifier->c_str()));
+
+ TIntermSymbol *intermSymbol = new TIntermSymbol(variable->getUniqueId(), *identifier, type);
+ intermSymbol->setLine(identifierLoc);
+
+ return new TIntermInvariantDeclaration(intermSymbol, identifierLoc);
}
-TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier)
+void TParseContext::parseDeclarator(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ TIntermDeclaration *declarationOut)
{
// If the declaration starting this declarator list was empty (example: int,), some checks were
// not performed.
- if (mDeferredSingleDeclarationErrorCheck)
+ if (mDeferredNonEmptyDeclarationErrorCheck)
{
- if (singleDeclarationErrorCheck(publicType, identifierLocation))
- recover();
- mDeferredSingleDeclarationErrorCheck = false;
+ nonEmptyDeclarationErrorCheck(publicType, identifierLocation);
+ mDeferredNonEmptyDeclarationErrorCheck = false;
}
- if (locationDeclaratorListCheck(identifierLocation, publicType))
- recover();
-
- if (nonInitErrorCheck(identifierLocation, identifier, &publicType))
- recover();
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
TVariable *variable = nullptr;
- if (!declareVariable(identifierLocation, identifier, TType(publicType), &variable))
- recover();
+ TType type(publicType);
- TIntermSymbol *symbol =
- intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation);
- if (variable && symbol)
- symbol->setId(variable->getUniqueId());
+ checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), &type);
- return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation);
+ checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &type);
+
+ checkAtomicCounterOffsetDoesNotOverlap(true, identifierLocation, &type);
+
+ declareVariable(identifierLocation, identifier, type, &variable);
+
+ if (variable)
+ {
+ TIntermSymbol *symbol = new TIntermSymbol(variable->getUniqueId(), identifier, type);
+ symbol->setLine(identifierLocation);
+ declarationOut->appendDeclarator(symbol);
+ }
}
-TIntermAggregate *TParseContext::parseArrayDeclarator(TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &arrayLocation,
- TIntermTyped *indexExpression)
+void TParseContext::parseArrayDeclarator(TPublicType &elementType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &arrayLocation,
+ const TVector<unsigned int> &arraySizes,
+ TIntermDeclaration *declarationOut)
{
// If the declaration starting this declarator list was empty (example: int,), some checks were
// not performed.
- if (mDeferredSingleDeclarationErrorCheck)
+ if (mDeferredNonEmptyDeclarationErrorCheck)
{
- if (singleDeclarationErrorCheck(publicType, identifierLocation))
- recover();
- mDeferredSingleDeclarationErrorCheck = false;
+ nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
+ mDeferredNonEmptyDeclarationErrorCheck = false;
}
- if (locationDeclaratorListCheck(identifierLocation, publicType))
- recover();
-
- if (nonInitErrorCheck(identifierLocation, identifier, &publicType))
- recover();
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, elementType);
- if (arrayTypeErrorCheck(arrayLocation, publicType) ||
- arrayQualifierErrorCheck(arrayLocation, publicType))
- {
- recover();
- }
- else
+ if (checkIsValidTypeAndQualifierForArray(arrayLocation, elementType))
{
- TType arrayType = TType(publicType);
- int size;
- if (arraySizeErrorCheck(arrayLocation, indexExpression, size))
- {
- recover();
- }
- arrayType.setArraySize(size);
+ TType arrayType(elementType);
+ arrayType.makeArrays(arraySizes);
- TVariable *variable = nullptr;
- if (!declareVariable(identifierLocation, identifier, arrayType, &variable))
- recover();
+ checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), &arrayType);
- TIntermSymbol *symbol =
- intermediate.addSymbol(0, identifier, arrayType, identifierLocation);
- if (variable && symbol)
- symbol->setId(variable->getUniqueId());
+ checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &arrayType);
- return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation);
- }
+ checkAtomicCounterOffsetDoesNotOverlap(true, identifierLocation, &arrayType);
- return nullptr;
+ TVariable *variable = nullptr;
+ declareVariable(identifierLocation, identifier, arrayType, &variable);
+
+ if (variable)
+ {
+ TIntermSymbol *symbol =
+ new TIntermSymbol(variable->getUniqueId(), identifier, arrayType);
+ symbol->setLine(identifierLocation);
+ declarationOut->appendDeclarator(symbol);
+ }
+ }
}
-TIntermAggregate *TParseContext::parseInitDeclarator(const TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &initLocation,
- TIntermTyped *initializer)
+void TParseContext::parseInitDeclarator(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut)
{
// If the declaration starting this declarator list was empty (example: int,), some checks were
// not performed.
- if (mDeferredSingleDeclarationErrorCheck)
+ if (mDeferredNonEmptyDeclarationErrorCheck)
{
- if (singleDeclarationErrorCheck(publicType, identifierLocation))
- recover();
- mDeferredSingleDeclarationErrorCheck = false;
+ nonEmptyDeclarationErrorCheck(publicType, identifierLocation);
+ mDeferredNonEmptyDeclarationErrorCheck = false;
}
- if (locationDeclaratorListCheck(identifierLocation, publicType))
- recover();
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
- TIntermNode *intermNode = nullptr;
- if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode))
+ TIntermBinary *initNode = nullptr;
+ TType type(publicType);
+ if (executeInitializer(identifierLocation, identifier, type, initializer, &initNode))
{
//
// build the intermediate representation
//
- if (intermNode)
+ if (initNode)
{
- return intermediate.growAggregate(aggregateDeclaration, intermNode, initLocation);
+ declarationOut->appendDeclarator(initNode);
}
- else
+ }
+}
+
+void TParseContext::parseArrayInitDeclarator(const TPublicType &elementType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ const TVector<unsigned int> &arraySizes,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut)
+{
+ // If the declaration starting this declarator list was empty (example: int,), some checks were
+ // not performed.
+ if (mDeferredNonEmptyDeclarationErrorCheck)
+ {
+ nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
+ mDeferredNonEmptyDeclarationErrorCheck = false;
+ }
+
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, elementType);
+
+ checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
+
+ TType arrayType(elementType);
+ arrayType.makeArrays(arraySizes);
+
+ // initNode will correspond to the whole of "b[n] = initializer".
+ TIntermBinary *initNode = nullptr;
+ if (executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
+ {
+ if (initNode)
{
- return aggregateDeclaration;
+ declarationOut->appendDeclarator(initNode);
}
}
- else
+}
+
+TIntermNode *TParseContext::addEmptyStatement(const TSourceLoc &location)
+{
+ // It's simpler to parse an empty statement as a constant expression rather than having a
+ // different type of node just for empty statements, that will be pruned from the AST anyway.
+ TIntermNode *node = CreateZeroNode(TType(EbtInt, EbpMedium));
+ node->setLine(location);
+ return node;
+}
+
+void TParseContext::setAtomicCounterBindingDefaultOffset(const TPublicType &publicType,
+ const TSourceLoc &location)
+{
+ const TLayoutQualifier &layoutQualifier = publicType.layoutQualifier;
+ checkAtomicCounterBindingIsValid(location, layoutQualifier.binding);
+ if (layoutQualifier.binding == -1 || layoutQualifier.offset == -1)
{
- recover();
- return nullptr;
+ error(location, "Requires both binding and offset", "layout");
+ return;
}
+ mAtomicCounterBindingStates[layoutQualifier.binding].setDefaultOffset(layoutQualifier.offset);
}
-TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &indexLocation,
- TIntermTyped *indexExpression,
- const TSourceLoc &initLocation,
- TIntermTyped *initializer)
+void TParseContext::parseDefaultPrecisionQualifier(const TPrecision precision,
+ const TPublicType &type,
+ const TSourceLoc &loc)
{
- // If the declaration starting this declarator list was empty (example: int,), some checks were
- // not performed.
- if (mDeferredSingleDeclarationErrorCheck)
+ if ((precision == EbpHigh) && (getShaderType() == GL_FRAGMENT_SHADER) &&
+ !getFragmentPrecisionHigh())
{
- if (singleDeclarationErrorCheck(publicType, identifierLocation))
- recover();
- mDeferredSingleDeclarationErrorCheck = false;
+ error(loc, "precision is not supported in fragment shader", "highp");
}
- if (locationDeclaratorListCheck(identifierLocation, publicType))
- recover();
+ if (!CanSetDefaultPrecisionOnType(type))
+ {
+ error(loc, "illegal type argument for default precision qualifier",
+ getBasicString(type.getBasicType()));
+ return;
+ }
+ symbolTable.setDefaultPrecision(type.getBasicType(), precision);
+}
- if (arrayTypeErrorCheck(indexLocation, publicType) ||
- arrayQualifierErrorCheck(indexLocation, publicType))
+bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier)
+{
+ switch (typeQualifier.layoutQualifier.primitiveType)
{
- recover();
+ case EptLines:
+ case EptLinesAdjacency:
+ case EptTriangles:
+ case EptTrianglesAdjacency:
+ return typeQualifier.qualifier == EvqGeometryIn;
+
+ case EptLineStrip:
+ case EptTriangleStrip:
+ return typeQualifier.qualifier == EvqGeometryOut;
+
+ case EptPoints:
+ return true;
+
+ default:
+ UNREACHABLE();
+ return false;
}
+}
- TPublicType arrayType(publicType);
+void TParseContext::setGeometryShaderInputArraySize(unsigned int inputArraySize,
+ const TSourceLoc &line)
+{
+ if (mGeometryShaderInputArraySize == 0u)
+ {
+ mGeometryShaderInputArraySize = inputArraySize;
+ }
+ else if (mGeometryShaderInputArraySize != inputArraySize)
+ {
+ error(line,
+ "Array size or input primitive declaration doesn't match the size of earlier sized "
+ "array inputs.",
+ "layout");
+ }
+}
+
+bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier)
+{
+ ASSERT(typeQualifier.qualifier == EvqGeometryIn);
+
+ const TLayoutQualifier &layoutQualifier = typeQualifier.layoutQualifier;
- int size = 0;
- // If indexExpression is nullptr, then the array will eventually get its size implicitly from
- // the initializer.
- if (indexExpression != nullptr &&
- arraySizeErrorCheck(identifierLocation, indexExpression, size))
+ if (layoutQualifier.maxVertices != -1)
{
- recover();
+ error(typeQualifier.line,
+ "max_vertices can only be declared in 'out' layout in a geometry shader", "layout");
+ return false;
}
- // Make the type an array even if size check failed.
- // This ensures useless error messages regarding the variable's non-arrayness won't follow.
- arrayType.setArraySize(size);
- // initNode will correspond to the whole of "b[n] = initializer".
- TIntermNode *initNode = nullptr;
- if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
+ // Set mGeometryInputPrimitiveType if exists
+ if (layoutQualifier.primitiveType != EptUndefined)
{
- if (initNode)
+ if (!checkPrimitiveTypeMatchesTypeQualifier(typeQualifier))
+ {
+ error(typeQualifier.line, "invalid primitive type for 'in' layout", "layout");
+ return false;
+ }
+
+ if (mGeometryShaderInputPrimitiveType == EptUndefined)
{
- return intermediate.growAggregate(aggregateDeclaration, initNode, initLocation);
+ mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType;
+ setGeometryShaderInputArraySize(
+ GetGeometryShaderInputArraySize(mGeometryShaderInputPrimitiveType),
+ typeQualifier.line);
}
- else
+ else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType)
{
- return aggregateDeclaration;
+ error(typeQualifier.line, "primitive doesn't match earlier input primitive declaration",
+ "layout");
+ return false;
}
}
- else
+
+ // Set mGeometryInvocations if exists
+ if (layoutQualifier.invocations > 0)
{
- recover();
- return nullptr;
+ if (mGeometryShaderInvocations == 0)
+ {
+ mGeometryShaderInvocations = layoutQualifier.invocations;
+ }
+ else if (mGeometryShaderInvocations != layoutQualifier.invocations)
+ {
+ error(typeQualifier.line, "invocations contradicts to the earlier declaration",
+ "layout");
+ return false;
+ }
}
+
+ return true;
}
-void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
+bool TParseContext::parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier)
{
- if (typeQualifier.qualifier != EvqUniform)
+ ASSERT(typeQualifier.qualifier == EvqGeometryOut);
+
+ const TLayoutQualifier &layoutQualifier = typeQualifier.layoutQualifier;
+
+ if (layoutQualifier.invocations > 0)
{
- error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier),
- "global layout must be uniform");
- recover();
- return;
+ error(typeQualifier.line,
+ "invocations can only be declared in 'in' layout in a geometry shader", "layout");
+ return false;
}
+ // Set mGeometryOutputPrimitiveType if exists
+ if (layoutQualifier.primitiveType != EptUndefined)
+ {
+ if (!checkPrimitiveTypeMatchesTypeQualifier(typeQualifier))
+ {
+ error(typeQualifier.line, "invalid primitive type for 'out' layout", "layout");
+ return false;
+ }
+
+ if (mGeometryShaderOutputPrimitiveType == EptUndefined)
+ {
+ mGeometryShaderOutputPrimitiveType = layoutQualifier.primitiveType;
+ }
+ else if (mGeometryShaderOutputPrimitiveType != layoutQualifier.primitiveType)
+ {
+ error(typeQualifier.line,
+ "primitive doesn't match earlier output primitive declaration", "layout");
+ return false;
+ }
+ }
+
+ // Set mGeometryMaxVertices if exists
+ if (layoutQualifier.maxVertices > -1)
+ {
+ if (mGeometryShaderMaxVertices == -1)
+ {
+ mGeometryShaderMaxVertices = layoutQualifier.maxVertices;
+ }
+ else if (mGeometryShaderMaxVertices != layoutQualifier.maxVertices)
+ {
+ error(typeQualifier.line, "max_vertices contradicts to the earlier declaration",
+ "layout");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
+{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
- ASSERT(!layoutQualifier.isEmpty());
- if (mShaderVersion < 300)
+ checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier,
+ typeQualifier.line);
+
+ // It should never be the case, but some strange parser errors can send us here.
+ if (layoutQualifier.isEmpty())
{
- error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 only", "layout");
- recover();
+ error(typeQualifier.line, "Error during layout qualifier parsing.", "?");
return;
}
- if (layoutLocationErrorCheck(typeQualifier.line, typeQualifier.layoutQualifier))
+ if (!layoutQualifier.isCombinationValid())
{
- recover();
+ error(typeQualifier.line, "invalid layout qualifier combination", "layout");
return;
}
- if (layoutQualifier.matrixPacking != EmpUnspecified)
+ checkBindingIsNotSpecified(typeQualifier.line, layoutQualifier.binding);
+
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+
+ checkInternalFormatIsNotSpecified(typeQualifier.line, layoutQualifier.imageInternalFormat);
+
+ checkYuvIsNotSpecified(typeQualifier.line, layoutQualifier.yuv);
+
+ checkOffsetIsNotSpecified(typeQualifier.line, layoutQualifier.offset);
+
+ checkStd430IsForShaderStorageBlock(typeQualifier.line, layoutQualifier.blockStorage,
+ typeQualifier.qualifier);
+
+ if (typeQualifier.qualifier == EvqComputeIn)
{
- mDefaultMatrixPacking = layoutQualifier.matrixPacking;
+ if (mComputeShaderLocalSizeDeclared &&
+ !layoutQualifier.isLocalSizeEqual(mComputeShaderLocalSize))
+ {
+ error(typeQualifier.line, "Work group size does not match the previous declaration",
+ "layout");
+ return;
+ }
+
+ if (mShaderVersion < 310)
+ {
+ error(typeQualifier.line, "in type qualifier supported in GLSL ES 3.10 only", "layout");
+ return;
+ }
+
+ if (!layoutQualifier.localSize.isAnyValueSet())
+ {
+ error(typeQualifier.line, "No local work group size specified", "layout");
+ return;
+ }
+
+ const TVariable *maxComputeWorkGroupSize = static_cast<const TVariable *>(
+ symbolTable.findBuiltIn("gl_MaxComputeWorkGroupSize", mShaderVersion));
+
+ const TConstantUnion *maxComputeWorkGroupSizeData =
+ maxComputeWorkGroupSize->getConstPointer();
+
+ for (size_t i = 0u; i < layoutQualifier.localSize.size(); ++i)
+ {
+ if (layoutQualifier.localSize[i] != -1)
+ {
+ mComputeShaderLocalSize[i] = layoutQualifier.localSize[i];
+ const int maxComputeWorkGroupSizeValue = maxComputeWorkGroupSizeData[i].getIConst();
+ if (mComputeShaderLocalSize[i] < 1 ||
+ mComputeShaderLocalSize[i] > maxComputeWorkGroupSizeValue)
+ {
+ std::stringstream reasonStream;
+ reasonStream << "invalid value: Value must be at least 1 and no greater than "
+ << maxComputeWorkGroupSizeValue;
+ const std::string &reason = reasonStream.str();
+
+ error(typeQualifier.line, reason.c_str(), getWorkGroupSizeString(i));
+ return;
+ }
+ }
+ }
+
+ mComputeShaderLocalSizeDeclared = true;
}
+ else if (typeQualifier.qualifier == EvqGeometryIn)
+ {
+ if (mShaderVersion < 310)
+ {
+ error(typeQualifier.line, "in type qualifier supported in GLSL ES 3.10 only", "layout");
+ return;
+ }
- if (layoutQualifier.blockStorage != EbsUnspecified)
+ if (!parseGeometryShaderInputLayoutQualifier(typeQualifier))
+ {
+ return;
+ }
+ }
+ else if (typeQualifier.qualifier == EvqGeometryOut)
+ {
+ if (mShaderVersion < 310)
+ {
+ error(typeQualifier.line, "out type qualifier supported in GLSL ES 3.10 only",
+ "layout");
+ return;
+ }
+
+ if (!parseGeometryShaderOutputLayoutQualifier(typeQualifier))
+ {
+ return;
+ }
+ }
+ else if (isExtensionEnabled(TExtension::OVR_multiview) &&
+ typeQualifier.qualifier == EvqVertexIn)
+ {
+ // This error is only specified in WebGL, but tightens unspecified behavior in the native
+ // specification.
+ if (mNumViews != -1 && layoutQualifier.numViews != mNumViews)
+ {
+ error(typeQualifier.line, "Number of views does not match the previous declaration",
+ "layout");
+ return;
+ }
+
+ if (layoutQualifier.numViews == -1)
+ {
+ error(typeQualifier.line, "No num_views specified", "layout");
+ return;
+ }
+
+ if (layoutQualifier.numViews > mMaxNumViews)
+ {
+ error(typeQualifier.line, "num_views greater than the value of GL_MAX_VIEWS_OVR",
+ "layout");
+ return;
+ }
+
+ mNumViews = layoutQualifier.numViews;
+ }
+ else
{
- mDefaultBlockStorage = layoutQualifier.blockStorage;
+ if (!checkWorkGroupSizeIsNotSpecified(typeQualifier.line, layoutQualifier))
+ {
+ return;
+ }
+
+ if (typeQualifier.qualifier != EvqUniform && typeQualifier.qualifier != EvqBuffer)
+ {
+ error(typeQualifier.line, "invalid qualifier: global layout can only be set for blocks",
+ getQualifierString(typeQualifier.qualifier));
+ return;
+ }
+
+ if (mShaderVersion < 300)
+ {
+ error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 and above",
+ "layout");
+ return;
+ }
+
+ checkLocationIsNotSpecified(typeQualifier.line, layoutQualifier);
+
+ if (layoutQualifier.matrixPacking != EmpUnspecified)
+ {
+ if (typeQualifier.qualifier == EvqUniform)
+ {
+ mDefaultUniformMatrixPacking = layoutQualifier.matrixPacking;
+ }
+ else if (typeQualifier.qualifier == EvqBuffer)
+ {
+ mDefaultBufferMatrixPacking = layoutQualifier.matrixPacking;
+ }
+ }
+
+ if (layoutQualifier.blockStorage != EbsUnspecified)
+ {
+ if (typeQualifier.qualifier == EvqUniform)
+ {
+ mDefaultUniformBlockStorage = layoutQualifier.blockStorage;
+ }
+ else if (typeQualifier.qualifier == EvqBuffer)
+ {
+ mDefaultBufferBlockStorage = layoutQualifier.blockStorage;
+ }
+ }
}
}
-TIntermAggregate *TParseContext::addFunctionPrototypeDeclaration(const TFunction &function,
- const TSourceLoc &location)
+TIntermFunctionPrototype *TParseContext::createPrototypeNodeFromFunction(
+ const TFunction &function,
+ const TSourceLoc &location,
+ bool insertParametersToSymbolTable)
{
- // Note: symbolTableFunction could be the same as function if this is the first declaration.
- // Either way the instance in the symbol table is used to track whether the function is declared
- // multiple times.
- TFunction *symbolTableFunction =
- static_cast<TFunction *>(symbolTable.find(function.getMangledName(), getShaderVersion()));
- if (symbolTableFunction->hasPrototypeDeclaration() && mShaderVersion == 100)
- {
- // ESSL 1.00.17 section 4.2.7.
- // Doesn't apply to ESSL 3.00.4: see section 4.2.3.
- error(location, "duplicate function prototype declarations are not allowed", "function");
- recover();
- }
- symbolTableFunction->setHasPrototypeDeclaration();
+ checkIsNotReserved(location, function.getName());
- TIntermAggregate *prototype = new TIntermAggregate;
- prototype->setType(function.getReturnType());
- prototype->setName(function.getMangledName());
- prototype->setFunctionId(function.getUniqueId());
+ TIntermFunctionPrototype *prototype =
+ new TIntermFunctionPrototype(function.getReturnType(), TSymbolUniqueId(function));
+ // TODO(oetuaho@nvidia.com): Instead of converting the function information here, the node could
+ // point to the data that already exists in the symbol table.
+ prototype->getFunctionSymbolInfo()->setFromFunction(function);
+ prototype->setLine(location);
for (size_t i = 0; i < function.getParamCount(); i++)
{
const TConstParameter &param = function.getParam(i);
- if (param.name != 0)
- {
- TVariable variable(param.name, *param.type);
- TIntermSymbol *paramSymbol = intermediate.addSymbol(
- variable.getUniqueId(), variable.getName(), variable.getType(), location);
- prototype = intermediate.growAggregate(prototype, paramSymbol, location);
+ TIntermSymbol *symbol = nullptr;
+
+ // If the parameter has no name, it's not an error, just don't add it to symbol table (could
+ // be used for unused args).
+ if (param.name != nullptr)
+ {
+ // Insert the parameter in the symbol table.
+ if (insertParametersToSymbolTable)
+ {
+ TVariable *variable = symbolTable.declareVariable(param.name, *param.type);
+ if (variable)
+ {
+ symbol = new TIntermSymbol(variable->getUniqueId(), variable->getName(),
+ variable->getType());
+ }
+ else
+ {
+ error(location, "redefinition", param.name->c_str());
+ }
+ }
+ // Unsized type of a named parameter should have already been checked and sanitized.
+ ASSERT(!param.type->isUnsizedArray());
}
else
{
- TIntermSymbol *paramSymbol = intermediate.addSymbol(0, "", *param.type, location);
- prototype = intermediate.growAggregate(prototype, paramSymbol, location);
+ if (param.type->isUnsizedArray())
+ {
+ error(location, "function parameter array must be sized at compile time", "[]");
+ // We don't need to size the arrays since the parameter is unnamed and hence
+ // inaccessible.
+ }
}
+ if (!symbol)
+ {
+ // The parameter had no name or declaring the symbol failed - either way, add a nameless
+ // symbol.
+ symbol = new TIntermSymbol(symbolTable.getEmptySymbolId(), "", *param.type);
+ }
+ symbol->setLine(location);
+ prototype->appendParameter(symbol);
+ }
+ return prototype;
+}
+
+TIntermFunctionPrototype *TParseContext::addFunctionPrototypeDeclaration(
+ const TFunction &parsedFunction,
+ const TSourceLoc &location)
+{
+ // Note: function found from the symbol table could be the same as parsedFunction if this is the
+ // first declaration. Either way the instance in the symbol table is used to track whether the
+ // function is declared multiple times.
+ TFunction *function = static_cast<TFunction *>(
+ symbolTable.find(parsedFunction.getMangledName(), getShaderVersion()));
+ if (function->hasPrototypeDeclaration() && mShaderVersion == 100)
+ {
+ // ESSL 1.00.17 section 4.2.7.
+ // Doesn't apply to ESSL 3.00.4: see section 4.2.3.
+ error(location, "duplicate function prototype declarations are not allowed", "function");
}
+ function->setHasPrototypeDeclaration();
- prototype->setOp(EOpPrototype);
+ TIntermFunctionPrototype *prototype =
+ createPrototypeNodeFromFunction(*function, location, false);
symbolTable.pop();
@@ -1973,135 +3249,80 @@ TIntermAggregate *TParseContext::addFunctionPrototypeDeclaration(const TFunction
{
// ESSL 3.00.4 section 4.2.4.
error(location, "local function prototype declarations are not allowed", "function");
- recover();
}
return prototype;
}
-TIntermAggregate *TParseContext::addFunctionDefinition(const TFunction &function,
- TIntermAggregate *functionPrototype,
- TIntermAggregate *functionBody,
- const TSourceLoc &location)
+TIntermFunctionDefinition *TParseContext::addFunctionDefinition(
+ TIntermFunctionPrototype *functionPrototype,
+ TIntermBlock *functionBody,
+ const TSourceLoc &location)
{
- //?? Check that all paths return a value if return type != void ?
- // May be best done as post process phase on intermediate code
+ // Check that non-void functions have at least one return statement.
if (mCurrentFunctionType->getBasicType() != EbtVoid && !mFunctionReturnsValue)
{
- error(location, "function does not return a value:", "", function.getName().c_str());
- recover();
+ error(location, "function does not return a value:",
+ functionPrototype->getFunctionSymbolInfo()->getName().c_str());
}
- TIntermAggregate *aggregate =
- intermediate.growAggregate(functionPrototype, functionBody, location);
- intermediate.setAggregateOperator(aggregate, EOpFunction, location);
- aggregate->setName(function.getMangledName().c_str());
- aggregate->setType(function.getReturnType());
- aggregate->setFunctionId(function.getUniqueId());
+ if (functionBody == nullptr)
+ {
+ functionBody = new TIntermBlock();
+ functionBody->setLine(location);
+ }
+ TIntermFunctionDefinition *functionNode =
+ new TIntermFunctionDefinition(functionPrototype, functionBody);
+ functionNode->setLine(location);
symbolTable.pop();
- return aggregate;
+ return functionNode;
}
-void TParseContext::parseFunctionPrototype(const TSourceLoc &location,
- TFunction *function,
- TIntermAggregate **aggregateOut)
+void TParseContext::parseFunctionDefinitionHeader(const TSourceLoc &location,
+ TFunction **function,
+ TIntermFunctionPrototype **prototypeOut)
{
+ ASSERT(function);
+ ASSERT(*function);
const TSymbol *builtIn =
- symbolTable.findBuiltIn(function->getMangledName(), getShaderVersion());
+ symbolTable.findBuiltIn((*function)->getMangledName(), getShaderVersion());
if (builtIn)
{
- error(location, "built-in functions cannot be redefined", function->getName().c_str());
- recover();
+ error(location, "built-in functions cannot be redefined", (*function)->getName().c_str());
}
-
- TFunction *prevDec =
- static_cast<TFunction *>(symbolTable.find(function->getMangledName(), getShaderVersion()));
- //
- // Note: 'prevDec' could be 'function' if this is the first time we've seen function
- // as it would have just been put in the symbol table. Otherwise, we're looking up
- // an earlier occurance.
- //
- if (prevDec->isDefined())
+ else
{
- // Then this function already has a body.
- error(location, "function already has a body", function->getName().c_str());
- recover();
- }
- prevDec->setDefined();
- //
- // Overload the unique ID of the definition to be the same unique ID as the declaration.
- // Eventually we will probably want to have only a single definition and just swap the
- // arguments to be the definition's arguments.
- //
- function->setUniqueId(prevDec->getUniqueId());
+ TFunction *prevDec = static_cast<TFunction *>(
+ symbolTable.find((*function)->getMangledName(), getShaderVersion()));
- // Raise error message if main function takes any parameters or return anything other than void
- if (function->getName() == "main")
- {
- if (function->getParamCount() > 0)
+ // Note: 'prevDec' could be 'function' if this is the first time we've seen function as it
+ // would have just been put in the symbol table. Otherwise, we're looking up an earlier
+ // occurance.
+ if (*function != prevDec)
{
- error(location, "function cannot take any parameter(s)", function->getName().c_str());
- recover();
+ // Swap the parameters of the previous declaration to the parameters of the function
+ // definition (parameter names may differ).
+ prevDec->swapParameters(**function);
+
+ // The function definition will share the same symbol as any previous declaration.
+ *function = prevDec;
}
- if (function->getReturnType().getBasicType() != EbtVoid)
+
+ if ((*function)->isDefined())
{
- error(location, "", function->getReturnType().getBasicString(),
- "main function cannot return a value");
- recover();
+ error(location, "function already has a body", (*function)->getName().c_str());
}
+
+ (*function)->setDefined();
}
- //
- // Remember the return type for later checking for RETURN statements.
- //
- mCurrentFunctionType = &(prevDec->getReturnType());
+ // Remember the return type for later checking for return statements.
+ mCurrentFunctionType = &((*function)->getReturnType());
mFunctionReturnsValue = false;
- //
- // Insert parameters into the symbol table.
- // If the parameter has no name, it's not an error, just don't insert it
- // (could be used for unused args).
- //
- // Also, accumulate the list of parameters into the HIL, so lower level code
- // knows where to find parameters.
- //
- TIntermAggregate *paramNodes = new TIntermAggregate;
- for (size_t i = 0; i < function->getParamCount(); i++)
- {
- const TConstParameter &param = function->getParam(i);
- if (param.name != 0)
- {
- TVariable *variable = new TVariable(param.name, *param.type);
- //
- // Insert the parameters with name in the symbol table.
- //
- if (!symbolTable.declare(variable))
- {
- error(location, "redefinition", variable->getName().c_str());
- recover();
- paramNodes = intermediate.growAggregate(
- paramNodes, intermediate.addSymbol(0, "", *param.type, location), location);
- continue;
- }
-
- //
- // Add the parameter to the HIL
- //
- TIntermSymbol *symbol = intermediate.addSymbol(
- variable->getUniqueId(), variable->getName(), variable->getType(), location);
-
- paramNodes = intermediate.growAggregate(paramNodes, symbol, location);
- }
- else
- {
- paramNodes = intermediate.growAggregate(
- paramNodes, intermediate.addSymbol(0, "", *param.type, location), location);
- }
- }
- intermediate.setAggregateOperator(paramNodes, EOpParameters, location);
- *aggregateOut = paramNodes;
+ *prototypeOut = createPrototypeNodeFromFunction(**function, location, true);
setLoopNestingLevel(0);
}
@@ -2117,22 +3338,42 @@ TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TF
//
TFunction *prevDec =
static_cast<TFunction *>(symbolTable.find(function->getMangledName(), getShaderVersion()));
- if (prevDec)
+
+ for (size_t i = 0u; i < function->getParamCount(); ++i)
+ {
+ auto &param = function->getParam(i);
+ if (param.type->isStructSpecifier())
+ {
+ // ESSL 3.00.6 section 12.10.
+ error(location, "Function parameter type cannot be a structure definition",
+ function->getName().c_str());
+ }
+ }
+
+ if (getShaderVersion() >= 300 &&
+ symbolTable.hasUnmangledBuiltInForShaderVersion(function->getName().c_str(),
+ getShaderVersion()))
+ {
+ // With ESSL 3.00 and above, names of built-in functions cannot be redeclared as functions.
+ // Therefore overloading or redefining builtin functions is an error.
+ error(location, "Name of a built-in function cannot be redeclared as function",
+ function->getName().c_str());
+ }
+ else if (prevDec)
{
if (prevDec->getReturnType() != function->getReturnType())
{
- error(location, "overloaded functions must have the same return type",
+ error(location, "function must have the same return type in all of its declarations",
function->getReturnType().getBasicString());
- recover();
}
for (size_t i = 0; i < prevDec->getParamCount(); ++i)
{
if (prevDec->getParam(i).type->getQualifier() !=
function->getParam(i).type->getQualifier())
{
- error(location, "overloaded functions must have the same parameter qualifiers",
+ error(location,
+ "function must have the same parameter qualifiers in all of its declarations",
function->getParam(i).type->getQualifierString());
- recover();
}
}
}
@@ -2145,22 +3386,33 @@ TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TF
{
if (!prevSym->isFunction())
{
- error(location, "redefinition", function->getName().c_str(), "function");
- recover();
+ error(location, "redefinition of a function", function->getName().c_str());
}
}
else
{
// Insert the unmangled name to detect potential future redefinition as a variable.
- TFunction *newFunction =
- new TFunction(NewPoolTString(function->getName().c_str()), &function->getReturnType());
- symbolTable.getOuterLevel()->insertUnmangled(newFunction);
+ symbolTable.getOuterLevel()->insertUnmangled(function);
}
// We're at the inner scope level of the function's arguments and body statement.
// Add the function prototype to the surrounding scope instead.
symbolTable.getOuterLevel()->insert(function);
+ // Raise error message if main function takes any parameters or return anything other than void
+ if (function->getName() == "main")
+ {
+ if (function->getParamCount() > 0)
+ {
+ error(location, "function cannot take any parameter(s)", "main");
+ }
+ if (function->getReturnType().getBasicType() != EbtVoid)
+ {
+ error(location, "main function cannot return a value",
+ function->getReturnType().getBasicString());
+ }
+ }
+
//
// If this is a redeclaration, it could also be a definition, in which case, we want to use the
// variable names from this one, and not the one that's
@@ -2169,420 +3421,302 @@ TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TF
return function;
}
-TFunction *TParseContext::addConstructorFunc(const TPublicType &publicTypeIn)
+TFunction *TParseContext::parseFunctionHeader(const TPublicType &type,
+ const TString *name,
+ const TSourceLoc &location)
{
- TPublicType publicType = publicTypeIn;
- if (publicType.isStructSpecifier)
+ if (type.qualifier != EvqGlobal && type.qualifier != EvqTemporary)
{
- error(publicType.line, "constructor can't be a structure definition",
- getBasicString(publicType.type));
- recover();
+ error(location, "no qualifiers allowed for function return",
+ getQualifierString(type.qualifier));
}
-
- TOperator op = EOpNull;
- if (publicType.userDef)
+ if (!type.layoutQualifier.isEmpty())
{
- op = EOpConstructStruct;
+ error(location, "no qualifiers allowed for function return", "layout");
}
- else
+ // make sure an opaque type is not involved as well...
+ std::string reason(getBasicString(type.getBasicType()));
+ reason += "s can't be function return values";
+ checkIsNotOpaqueType(location, type.typeSpecifierNonArray, reason.c_str());
+ if (mShaderVersion < 300)
{
- switch (publicType.type)
- {
- case EbtFloat:
- if (publicType.isMatrix())
- {
- switch (publicType.getCols())
- {
- case 2:
- switch (publicType.getRows())
- {
- case 2:
- op = EOpConstructMat2;
- break;
- case 3:
- op = EOpConstructMat2x3;
- break;
- case 4:
- op = EOpConstructMat2x4;
- break;
- }
- break;
- case 3:
- switch (publicType.getRows())
- {
- case 2:
- op = EOpConstructMat3x2;
- break;
- case 3:
- op = EOpConstructMat3;
- break;
- case 4:
- op = EOpConstructMat3x4;
- break;
- }
- break;
- case 4:
- switch (publicType.getRows())
- {
- case 2:
- op = EOpConstructMat4x2;
- break;
- case 3:
- op = EOpConstructMat4x3;
- break;
- case 4:
- op = EOpConstructMat4;
- break;
- }
- break;
- }
- }
- else
- {
- switch (publicType.getNominalSize())
- {
- case 1:
- op = EOpConstructFloat;
- break;
- case 2:
- op = EOpConstructVec2;
- break;
- case 3:
- op = EOpConstructVec3;
- break;
- case 4:
- op = EOpConstructVec4;
- break;
- }
- }
- break;
-
- case EbtInt:
- switch (publicType.getNominalSize())
- {
- case 1:
- op = EOpConstructInt;
- break;
- case 2:
- op = EOpConstructIVec2;
- break;
- case 3:
- op = EOpConstructIVec3;
- break;
- case 4:
- op = EOpConstructIVec4;
- break;
- }
- break;
+ // Array return values are forbidden, but there's also no valid syntax for declaring array
+ // return values in ESSL 1.00.
+ ASSERT(!type.isArray() || mDiagnostics->numErrors() > 0);
- case EbtUInt:
- switch (publicType.getNominalSize())
- {
- case 1:
- op = EOpConstructUInt;
- break;
- case 2:
- op = EOpConstructUVec2;
- break;
- case 3:
- op = EOpConstructUVec3;
- break;
- case 4:
- op = EOpConstructUVec4;
- break;
- }
- break;
-
- case EbtBool:
- switch (publicType.getNominalSize())
- {
- case 1:
- op = EOpConstructBool;
- break;
- case 2:
- op = EOpConstructBVec2;
- break;
- case 3:
- op = EOpConstructBVec3;
- break;
- case 4:
- op = EOpConstructBVec4;
- break;
- }
- break;
-
- default:
- break;
- }
-
- if (op == EOpNull)
+ if (type.isStructureContainingArrays())
{
- error(publicType.line, "cannot construct this type", getBasicString(publicType.type));
- recover();
- publicType.type = EbtFloat;
- op = EOpConstructFloat;
+ // ESSL 1.00.17 section 6.1 Function Definitions
+ error(location, "structures containing arrays can't be function return values",
+ TType(type).getCompleteString().c_str());
}
}
- TString tempString;
- const TType *type = new TType(publicType);
- return new TFunction(&tempString, type, op);
+ // Add the function as a prototype after parsing it (we do not support recursion)
+ return new TFunction(&symbolTable, name, new TType(type));
}
-// This function is used to test for the correctness of the parameters passed to various constructor
-// functions and also convert them to the right datatype if it is allowed and required.
-//
-// Returns 0 for an error or the constructed node (aggregate or typed) for no error.
-//
-TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments,
- TType *type,
- TOperator op,
- TFunction *fnCall,
- const TSourceLoc &line)
+TFunction *TParseContext::addNonConstructorFunc(const TString *name, const TSourceLoc &loc)
{
- TIntermAggregate *constructor = arguments->getAsAggregate();
- ASSERT(constructor != nullptr);
+ const TType *returnType = TCache::getType(EbtVoid, EbpUndefined);
+ return new TFunction(&symbolTable, name, returnType);
+}
- if (type->isArray())
- {
- // GLSL ES 3.00 section 5.4.4: Each argument must be the same type as the element type of
- // the array.
- TIntermSequence *args = constructor->getSequence();
- for (size_t i = 0; i < args->size(); i++)
- {
- const TType &argType = (*args)[i]->getAsTyped()->getType();
- // It has already been checked that the argument is not an array.
- ASSERT(!argType.isArray());
- if (!argType.sameElementType(*type))
- {
- error(line, "Array constructor argument has an incorrect type", "Error");
- recover();
- return nullptr;
- }
- }
- }
- else if (op == EOpConstructStruct)
+TFunction *TParseContext::addConstructorFunc(const TPublicType &publicType)
+{
+ if (mShaderVersion < 300 && publicType.isArray())
{
- const TFieldList &fields = type->getStruct()->fields();
- TIntermSequence *args = constructor->getSequence();
-
- for (size_t i = 0; i < fields.size(); i++)
- {
- if (i >= args->size() || (*args)[i]->getAsTyped()->getType() != *fields[i]->type())
- {
- error(line, "Structure constructor arguments do not match structure fields",
- "Error");
- recover();
-
- return 0;
- }
- }
+ error(publicType.getLine(), "array constructor supported in GLSL ES 3.00 and above only",
+ "[]");
}
-
- // Turn the argument list itself into a constructor
- constructor->setOp(op);
- constructor->setLine(line);
- ASSERT(constructor->isConstructor());
-
- // Need to set type before setPrecisionFromChildren() because bool doesn't have precision.
- constructor->setType(*type);
-
- // Structs should not be precision qualified, the individual members may be.
- // Built-in types on the other hand should be precision qualified.
- if (op != EOpConstructStruct)
+ if (publicType.isStructSpecifier())
{
- constructor->setPrecisionFromChildren();
- type->setPrecision(constructor->getPrecision());
+ error(publicType.getLine(), "constructor can't be a structure definition",
+ getBasicString(publicType.getBasicType()));
}
- TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor);
- if (constConstructor)
+ TType *type = new TType(publicType);
+ if (!type->canBeConstructed())
{
- return constConstructor;
+ error(publicType.getLine(), "cannot construct this type",
+ getBasicString(publicType.getBasicType()));
+ type->setBasicType(EbtFloat);
}
- return constructor;
+ return new TFunction(&symbolTable, nullptr, type, EOpConstruct);
}
-//
-// This function returns the tree representation for the vector field(s) being accessed from contant
-// vector.
-// If only one component of vector is accessed (v.x or v[0] where v is a contant vector), then a
-// contant node is returned, else an aggregate node is returned (for v.xy). The input to this
-// function could either be the symbol node or it could be the intermediate tree representation of
-// accessing fields in a constant structure or column of a constant matrix.
-//
-TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields,
- TIntermConstantUnion *node,
- const TSourceLoc &line,
- bool outOfRangeIndexIsError)
+void TParseContext::checkIsNotUnsizedArray(const TSourceLoc &line,
+ const char *errorMessage,
+ const char *token,
+ TType *arrayType)
{
- const TConstantUnion *unionArray = node->getUnionArrayPointer();
- ASSERT(unionArray);
-
- TConstantUnion *constArray = new TConstantUnion[fields.num];
-
- for (int i = 0; i < fields.num; i++)
+ if (arrayType->isUnsizedArray())
{
- if (fields.offsets[i] >= node->getType().getNominalSize())
- {
- std::stringstream extraInfoStream;
- extraInfoStream << "vector field selection out of range '" << fields.offsets[i] << "'";
- std::string extraInfo = extraInfoStream.str();
- outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str());
- fields.offsets[i] = node->getType().getNominalSize() - 1;
- }
-
- constArray[i] = unionArray[fields.offsets[i]];
+ error(line, errorMessage, token);
+ arrayType->sizeUnsizedArrays(nullptr);
}
- return intermediate.addConstantUnion(constArray, node->getType(), line);
}
-//
-// This function returns the column being accessed from a constant matrix. The values are retrieved
-// from the symbol table and parse-tree is built for a vector (each column of a matrix is a vector).
-// The input to the function could either be a symbol node (m[0] where m is a constant matrix)that
-// represents a constant matrix or it could be the tree representation of the constant matrix
-// (s.m1[0] where s is a constant structure)
-//
-TIntermTyped *TParseContext::addConstMatrixNode(int index,
- TIntermConstantUnion *node,
- const TSourceLoc &line,
- bool outOfRangeIndexIsError)
+TParameter TParseContext::parseParameterDeclarator(TType *type,
+ const TString *name,
+ const TSourceLoc &nameLoc)
{
- if (index >= node->getType().getCols())
+ ASSERT(type);
+ checkIsNotUnsizedArray(nameLoc, "function parameter array must specify a size", name->c_str(),
+ type);
+ if (type->getBasicType() == EbtVoid)
{
- std::stringstream extraInfoStream;
- extraInfoStream << "matrix field selection out of range '" << index << "'";
- std::string extraInfo = extraInfoStream.str();
- outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str());
- index = node->getType().getCols() - 1;
+ error(nameLoc, "illegal use of type 'void'", name->c_str());
}
+ checkIsNotReserved(nameLoc, *name);
+ TParameter param = {name, type};
+ return param;
+}
- const TConstantUnion *unionArray = node->getUnionArrayPointer();
- int size = node->getType().getCols();
- return intermediate.addConstantUnion(&unionArray[size * index], node->getType(), line);
+TParameter TParseContext::parseParameterDeclarator(const TPublicType &publicType,
+ const TString *name,
+ const TSourceLoc &nameLoc)
+{
+ TType *type = new TType(publicType);
+ return parseParameterDeclarator(type, name, nameLoc);
}
-//
-// This function returns an element of an array accessed from a constant array. The values are
-// retrieved from the symbol table and parse-tree is built for the type of the element. The input
-// to the function could either be a symbol node (a[0] where a is a constant array)that represents a
-// constant array or it could be the tree representation of the constant array (s.a1[0] where s is a
-// constant structure)
-//
-TIntermTyped *TParseContext::addConstArrayNode(int index,
- TIntermConstantUnion *node,
- const TSourceLoc &line,
- bool outOfRangeIndexIsError)
+TParameter TParseContext::parseParameterArrayDeclarator(const TString *name,
+ const TSourceLoc &nameLoc,
+ const TVector<unsigned int> &arraySizes,
+ const TSourceLoc &arrayLoc,
+ TPublicType *elementType)
{
- TType arrayElementType = node->getType();
- arrayElementType.clearArrayness();
+ checkArrayElementIsNotArray(arrayLoc, *elementType);
+ TType *arrayType = new TType(*elementType);
+ arrayType->makeArrays(arraySizes);
+ return parseParameterDeclarator(arrayType, name, nameLoc);
+}
- if (index >= node->getType().getArraySize())
+bool TParseContext::checkUnsizedArrayConstructorArgumentDimensionality(TIntermSequence *arguments,
+ TType type,
+ const TSourceLoc &line)
+{
+ if (arguments->empty())
+ {
+ error(line, "implicitly sized array constructor must have at least one argument", "[]");
+ return false;
+ }
+ for (TIntermNode *arg : *arguments)
{
- std::stringstream extraInfoStream;
- extraInfoStream << "array field selection out of range '" << index << "'";
- std::string extraInfo = extraInfoStream.str();
- outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str());
- index = node->getType().getArraySize() - 1;
+ TIntermTyped *element = arg->getAsTyped();
+ ASSERT(element);
+ size_t dimensionalityFromElement = element->getType().getNumArraySizes() + 1u;
+ if (dimensionalityFromElement > type.getNumArraySizes())
+ {
+ error(line, "constructing from a non-dereferenced array", "constructor");
+ return false;
+ }
+ else if (dimensionalityFromElement < type.getNumArraySizes())
+ {
+ if (dimensionalityFromElement == 1u)
+ {
+ error(line, "implicitly sized array of arrays constructor argument is not an array",
+ "constructor");
+ }
+ else
+ {
+ error(line,
+ "implicitly sized array of arrays constructor argument dimensionality is too "
+ "low",
+ "constructor");
+ }
+ return false;
+ }
}
- size_t arrayElementSize = arrayElementType.getObjectSize();
- const TConstantUnion *unionArray = node->getUnionArrayPointer();
- return intermediate.addConstantUnion(&unionArray[arrayElementSize * index], node->getType(),
- line);
+ return true;
}
+// This function is used to test for the correctness of the parameters passed to various constructor
+// functions and also convert them to the right datatype if it is allowed and required.
//
-// This function returns the value of a particular field inside a constant structure from the symbol
-// table.
-// If there is an embedded/nested struct, it appropriately calls addConstStructNested or
-// addConstStructFromAggr function and returns the parse-tree with the values of the embedded/nested
-// struct.
+// Returns a node to add to the tree regardless of if an error was generated or not.
//
-TIntermTyped *TParseContext::addConstStruct(const TString &identifier,
- TIntermTyped *node,
+TIntermTyped *TParseContext::addConstructor(TIntermSequence *arguments,
+ TType type,
const TSourceLoc &line)
{
- const TFieldList &fields = node->getType().getStruct()->fields();
- size_t instanceSize = 0;
-
- for (size_t index = 0; index < fields.size(); ++index)
+ if (type.isUnsizedArray())
{
- if (fields[index]->name() == identifier)
+ if (!checkUnsizedArrayConstructorArgumentDimensionality(arguments, type, line))
{
- break;
+ type.sizeUnsizedArrays(nullptr);
+ return CreateZeroNode(type);
}
- else
+ TIntermTyped *firstElement = arguments->at(0)->getAsTyped();
+ ASSERT(firstElement);
+ if (type.getOutermostArraySize() == 0u)
+ {
+ type.sizeOutermostUnsizedArray(static_cast<unsigned int>(arguments->size()));
+ }
+ for (size_t i = 0; i < firstElement->getType().getNumArraySizes(); ++i)
{
- instanceSize += fields[index]->type()->getObjectSize();
+ if ((*type.getArraySizes())[i] == 0u)
+ {
+ type.setArraySize(i, (*firstElement->getType().getArraySizes())[i]);
+ }
}
+ ASSERT(!type.isUnsizedArray());
}
- TIntermTyped *typedNode;
- TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion();
- if (tempConstantNode)
+ if (!checkConstructorArguments(line, arguments, type))
{
- const TConstantUnion *constArray = tempConstantNode->getUnionArrayPointer();
-
- // type will be changed in the calling function
- typedNode = intermediate.addConstantUnion(constArray + instanceSize,
- tempConstantNode->getType(), line);
+ return CreateZeroNode(type);
}
- else
- {
- error(line, "Cannot offset into the structure", "Error");
- recover();
- return 0;
- }
+ TIntermAggregate *constructorNode = TIntermAggregate::CreateConstructor(type, arguments);
+ constructorNode->setLine(line);
- return typedNode;
+ // TODO(oetuaho@nvidia.com): Add support for folding array constructors.
+ if (!constructorNode->isArray())
+ {
+ return constructorNode->fold(mDiagnostics);
+ }
+ return constructorNode;
}
//
// Interface/uniform blocks
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
//
-TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualifier,
- const TSourceLoc &nameLine,
- const TString &blockName,
- TFieldList *fieldList,
- const TString *instanceName,
- const TSourceLoc &instanceLine,
- TIntermTyped *arrayIndex,
- const TSourceLoc &arrayIndexLine)
+TIntermDeclaration *TParseContext::addInterfaceBlock(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &nameLine,
+ const TString &blockName,
+ TFieldList *fieldList,
+ const TString *instanceName,
+ const TSourceLoc &instanceLine,
+ TIntermTyped *arrayIndex,
+ const TSourceLoc &arrayIndexLine)
{
- if (reservedErrorCheck(nameLine, blockName))
- recover();
+ checkIsNotReserved(nameLine, blockName);
+
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
+
+ if (mShaderVersion < 310 && typeQualifier.qualifier != EvqUniform)
+ {
+ error(typeQualifier.line,
+ "invalid qualifier: interface blocks must be uniform in version lower than GLSL ES "
+ "3.10",
+ getQualifierString(typeQualifier.qualifier));
+ }
+ else if (typeQualifier.qualifier != EvqUniform && typeQualifier.qualifier != EvqBuffer)
+ {
+ error(typeQualifier.line, "invalid qualifier: interface blocks must be uniform or buffer",
+ getQualifierString(typeQualifier.qualifier));
+ }
- if (typeQualifier.qualifier != EvqUniform)
+ if (typeQualifier.invariant)
{
- error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier),
- "interface blocks must be uniform");
- recover();
+ error(typeQualifier.line, "invalid qualifier on interface block member", "invariant");
}
- TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
- if (layoutLocationErrorCheck(typeQualifier.line, blockLayoutQualifier))
+ if (typeQualifier.qualifier != EvqBuffer)
+ {
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+ }
+
+ // add array index
+ unsigned int arraySize = 0;
+ if (arrayIndex != nullptr)
{
- recover();
+ arraySize = checkIsValidArraySize(arrayIndexLine, arrayIndex);
}
+ if (mShaderVersion < 310)
+ {
+ checkBindingIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.binding);
+ }
+ else
+ {
+ checkBlockBindingIsValid(typeQualifier.line, typeQualifier.qualifier,
+ typeQualifier.layoutQualifier.binding, arraySize);
+ }
+
+ checkYuvIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.yuv);
+
+ TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
+ checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier);
+ checkStd430IsForShaderStorageBlock(typeQualifier.line, blockLayoutQualifier.blockStorage,
+ typeQualifier.qualifier);
+
if (blockLayoutQualifier.matrixPacking == EmpUnspecified)
{
- blockLayoutQualifier.matrixPacking = mDefaultMatrixPacking;
+ if (typeQualifier.qualifier == EvqUniform)
+ {
+ blockLayoutQualifier.matrixPacking = mDefaultUniformMatrixPacking;
+ }
+ else if (typeQualifier.qualifier == EvqBuffer)
+ {
+ blockLayoutQualifier.matrixPacking = mDefaultBufferMatrixPacking;
+ }
}
if (blockLayoutQualifier.blockStorage == EbsUnspecified)
{
- blockLayoutQualifier.blockStorage = mDefaultBlockStorage;
+ if (typeQualifier.qualifier == EvqUniform)
+ {
+ blockLayoutQualifier.blockStorage = mDefaultUniformBlockStorage;
+ }
+ else if (typeQualifier.qualifier == EvqBuffer)
+ {
+ blockLayoutQualifier.blockStorage = mDefaultBufferBlockStorage;
+ }
}
- TSymbol *blockNameSymbol = new TInterfaceBlockName(&blockName);
- if (!symbolTable.declare(blockNameSymbol))
+ checkWorkGroupSizeIsNotSpecified(nameLine, blockLayoutQualifier);
+
+ checkInternalFormatIsNotSpecified(nameLine, blockLayoutQualifier.imageInternalFormat);
+
+ if (!symbolTable.declareInterfaceBlockName(&blockName))
{
- error(nameLine, "redefinition", blockName.c_str(), "interface block name");
- recover();
+ error(nameLine, "redefinition of an interface block name", blockName.c_str());
}
// check for sampler types and apply layout qualifiers
@@ -2590,38 +3724,53 @@ TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualif
{
TField *field = (*fieldList)[memberIndex];
TType *fieldType = field->type();
- if (IsSampler(fieldType->getBasicType()))
+ if (IsOpaqueType(fieldType->getBasicType()))
{
- error(field->line(), "unsupported type", fieldType->getBasicString(),
- "sampler types are not allowed in interface blocks");
- recover();
+ std::string reason("unsupported type - ");
+ reason += fieldType->getBasicString();
+ reason += " types are not allowed in interface blocks";
+ error(field->line(), reason.c_str(), fieldType->getBasicString());
}
const TQualifier qualifier = fieldType->getQualifier();
switch (qualifier)
{
case EvqGlobal:
+ break;
case EvqUniform:
+ if (typeQualifier.qualifier == EvqBuffer)
+ {
+ error(field->line(), "invalid qualifier on shader storage block member",
+ getQualifierString(qualifier));
+ }
+ break;
+ case EvqBuffer:
+ if (typeQualifier.qualifier == EvqUniform)
+ {
+ error(field->line(), "invalid qualifier on uniform block member",
+ getQualifierString(qualifier));
+ }
break;
default:
error(field->line(), "invalid qualifier on interface block member",
getQualifierString(qualifier));
- recover();
break;
}
- // check layout qualifiers
- TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier();
- if (layoutLocationErrorCheck(field->line(), fieldLayoutQualifier))
+ if (fieldType->isInvariant())
{
- recover();
+ error(field->line(), "invalid qualifier on interface block member", "invariant");
}
+ // check layout qualifiers
+ TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier();
+ checkLocationIsNotSpecified(field->line(), fieldLayoutQualifier);
+ checkBindingIsNotSpecified(field->line(), fieldLayoutQualifier.binding);
+
if (fieldLayoutQualifier.blockStorage != EbsUnspecified)
{
- error(field->line(), "invalid layout qualifier:",
- getBlockStorageString(fieldLayoutQualifier.blockStorage), "cannot be used here");
- recover();
+ error(field->line(), "invalid layout qualifier: cannot be used here",
+ getBlockStorageString(fieldLayoutQualifier.blockStorage));
}
if (fieldLayoutQualifier.matrixPacking == EmpUnspecified)
@@ -2630,29 +3779,51 @@ TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualif
}
else if (!fieldType->isMatrix() && fieldType->getBasicType() != EbtStruct)
{
- warning(field->line(), "extraneous layout qualifier:",
- getMatrixPackingString(fieldLayoutQualifier.matrixPacking),
- "only has an effect on matrix types");
+ warning(field->line(),
+ "extraneous layout qualifier: only has an effect on matrix types",
+ getMatrixPackingString(fieldLayoutQualifier.matrixPacking));
}
fieldType->setLayoutQualifier(fieldLayoutQualifier);
- }
- // add array index
- int arraySize = 0;
- if (arrayIndex != NULL)
- {
- if (arraySizeErrorCheck(arrayIndexLine, arrayIndex, arraySize))
- recover();
+ if (mShaderVersion < 310 || memberIndex != fieldList->size() - 1u ||
+ typeQualifier.qualifier != EvqBuffer)
+ {
+ // ESSL 3.10 spec section 4.1.9 allows for runtime-sized arrays.
+ checkIsNotUnsizedArray(field->line(),
+ "array members of interface blocks must specify a size",
+ field->name().c_str(), field->type());
+ }
+
+ if (typeQualifier.qualifier == EvqBuffer)
+ {
+ // set memory qualifiers
+ // GLSL ES 3.10 session 4.9 [Memory Access Qualifiers]. When a block declaration is
+ // qualified with a memory qualifier, it is as if all of its members were declared with
+ // the same memory qualifier.
+ const TMemoryQualifier &blockMemoryQualifier = typeQualifier.memoryQualifier;
+ TMemoryQualifier fieldMemoryQualifier = fieldType->getMemoryQualifier();
+ fieldMemoryQualifier.readonly |= blockMemoryQualifier.readonly;
+ fieldMemoryQualifier.writeonly |= blockMemoryQualifier.writeonly;
+ fieldMemoryQualifier.coherent |= blockMemoryQualifier.coherent;
+ fieldMemoryQualifier.restrictQualifier |= blockMemoryQualifier.restrictQualifier;
+ fieldMemoryQualifier.volatileQualifier |= blockMemoryQualifier.volatileQualifier;
+ // TODO(jiajia.qin@intel.com): Decide whether if readonly and writeonly buffer variable
+ // is legal. See bug https://github.com/KhronosGroup/OpenGL-API/issues/7
+ fieldType->setMemoryQualifier(fieldMemoryQualifier);
+ }
}
TInterfaceBlock *interfaceBlock =
- new TInterfaceBlock(&blockName, fieldList, instanceName, arraySize, blockLayoutQualifier);
- TType interfaceBlockType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier,
- arraySize);
+ new TInterfaceBlock(&blockName, fieldList, instanceName, blockLayoutQualifier);
+ TType interfaceBlockType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier);
+ if (arrayIndex != nullptr)
+ {
+ interfaceBlockType.makeArray(arraySize);
+ }
TString symbolName = "";
- int symbolId = 0;
+ const TSymbolUniqueId *symbolId = nullptr;
if (!instanceName)
{
@@ -2665,60 +3836,64 @@ TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualif
// set parent pointer of the field variable
fieldType->setInterfaceBlock(interfaceBlock);
- TVariable *fieldVariable = new TVariable(&field->name(), *fieldType);
- fieldVariable->setQualifier(typeQualifier.qualifier);
+ TVariable *fieldVariable = symbolTable.declareVariable(&field->name(), *fieldType);
- if (!symbolTable.declare(fieldVariable))
+ if (fieldVariable)
{
- error(field->line(), "redefinition", field->name().c_str(),
- "interface block member name");
- recover();
+ fieldVariable->setQualifier(typeQualifier.qualifier);
+ }
+ else
+ {
+ error(field->line(), "redefinition of an interface block member name",
+ field->name().c_str());
}
}
+ symbolId = &symbolTable.getEmptySymbolId();
}
else
{
- if (reservedErrorCheck(instanceLine, *instanceName))
- recover();
+ checkIsNotReserved(instanceLine, *instanceName);
// add a symbol for this interface block
- TVariable *instanceTypeDef = new TVariable(instanceName, interfaceBlockType, false);
- instanceTypeDef->setQualifier(typeQualifier.qualifier);
-
- if (!symbolTable.declare(instanceTypeDef))
+ TVariable *instanceTypeDef = symbolTable.declareVariable(instanceName, interfaceBlockType);
+ if (instanceTypeDef)
{
- error(instanceLine, "redefinition", instanceName->c_str(),
- "interface block instance name");
- recover();
+ instanceTypeDef->setQualifier(typeQualifier.qualifier);
+ symbolId = &instanceTypeDef->getUniqueId();
}
-
- symbolId = instanceTypeDef->getUniqueId();
- symbolName = instanceTypeDef->getName();
+ else
+ {
+ error(instanceLine, "redefinition of an interface block instance name",
+ instanceName->c_str());
+ }
+ symbolName = *instanceName;
}
- TIntermAggregate *aggregate = intermediate.makeAggregate(
- intermediate.addSymbol(symbolId, symbolName, interfaceBlockType, typeQualifier.line),
- nameLine);
- aggregate->setOp(EOpDeclaration);
+ TIntermDeclaration *declaration = nullptr;
+
+ if (symbolId)
+ {
+ TIntermSymbol *blockSymbol = new TIntermSymbol(*symbolId, symbolName, interfaceBlockType);
+ blockSymbol->setLine(typeQualifier.line);
+ declaration = new TIntermDeclaration();
+ declaration->appendDeclarator(blockSymbol);
+ declaration->setLine(nameLine);
+ }
exitStructDeclaration();
- return aggregate;
+ return declaration;
}
-bool TParseContext::enterStructDeclaration(const TSourceLoc &line, const TString &identifier)
+void TParseContext::enterStructDeclaration(const TSourceLoc &line, const TString &identifier)
{
++mStructNestingLevel;
// Embedded structure definitions are not supported per GLSL ES spec.
- // They aren't allowed in GLSL either, but we need to detect this here
- // so we don't rely on the GLSL compiler to catch it.
+ // ESSL 1.00.17 section 10.9. ESSL 3.00.6 section 12.11.
if (mStructNestingLevel > 1)
{
- error(line, "", "Embedded struct definitions are not allowed");
- return true;
+ error(line, "Embedded struct definitions are not allowed", "struct");
}
-
- return false;
}
void TParseContext::exitStructDeclaration()
@@ -2726,22 +3901,16 @@ void TParseContext::exitStructDeclaration()
--mStructNestingLevel;
}
-namespace
-{
-const int kWebGLMaxStructNesting = 4;
-
-} // namespace
-
-bool TParseContext::structNestingErrorCheck(const TSourceLoc &line, const TField &field)
+void TParseContext::checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field)
{
- if (!IsWebGLBasedSpec(mShaderSpec))
+ if (!sh::IsWebGLBasedSpec(mShaderSpec))
{
- return false;
+ return;
}
if (field.type()->getBasicType() != EbtStruct)
{
- return false;
+ return;
}
// We're already inside a structure definition at this point, so add
@@ -2752,11 +3921,9 @@ bool TParseContext::structNestingErrorCheck(const TSourceLoc &line, const TField
reasonStream << "Reference of struct type " << field.type()->getStruct()->name().c_str()
<< " exceeds maximum allowed nesting level of " << kWebGLMaxStructNesting;
std::string reason = reasonStream.str();
- error(line, reason.c_str(), field.name().c_str(), "");
- return true;
+ error(line, reason.c_str(), field.name().c_str());
+ return;
}
-
- return false;
}
//
@@ -2766,8 +3933,6 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
const TSourceLoc &location,
TIntermTyped *indexExpression)
{
- TIntermTyped *indexedExpression = NULL;
-
if (!baseExpression->isArray() && !baseExpression->isMatrix() && !baseExpression->isVector())
{
if (baseExpression->getAsSymbolNode())
@@ -2779,7 +3944,18 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
{
error(location, " left of '[' is not of type array, matrix, or vector ", "expression");
}
- recover();
+
+ return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
+ }
+
+ if (baseExpression->getQualifier() == EvqPerVertexIn)
+ {
+ ASSERT(mShaderType == GL_GEOMETRY_SHADER_OES);
+ if (mGeometryShaderInputPrimitiveType == EptUndefined)
+ {
+ error(location, "missing input primitive declaration before indexing gl_in.", "[");
+ return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
+ }
}
TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
@@ -2792,174 +3968,139 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
{
if (baseExpression->isInterfaceBlock())
{
- error(
- location, "", "[",
- "array indexes for interface blocks arrays must be constant integral expressions");
- recover();
+ // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+ switch (baseExpression->getQualifier())
+ {
+ case EvqPerVertexIn:
+ break;
+ case EvqUniform:
+ case EvqBuffer:
+ error(location,
+ "array indexes for uniform block arrays and shader storage block arrays "
+ "must be constant integral expressions",
+ "[");
+ break;
+ default:
+ // We can reach here only in error cases.
+ ASSERT(mDiagnostics->numErrors() > 0);
+ break;
+ }
}
else if (baseExpression->getQualifier() == EvqFragmentOut)
{
- error(location, "", "[",
- "array indexes for fragment outputs must be constant integral expressions");
- recover();
+ error(location,
+ "array indexes for fragment outputs must be constant integral expressions", "[");
}
else if (mShaderSpec == SH_WEBGL2_SPEC && baseExpression->getQualifier() == EvqFragData)
{
- error(location, "", "[", "array index for gl_FragData must be constant zero");
- recover();
+ error(location, "array index for gl_FragData must be constant zero", "[");
}
}
if (indexConstantUnion)
{
- // If the index is not qualified as constant, the behavior in the spec is undefined. This
- // applies even if ANGLE has been able to constant fold it (ANGLE may constant fold
- // expressions that are not constant expressions). The most compatible way to handle this
- // case is to report a warning instead of an error and force the index to be in the
- // correct range.
+ // If an out-of-range index is not qualified as constant, the behavior in the spec is
+ // undefined. This applies even if ANGLE has been able to constant fold it (ANGLE may
+ // constant fold expressions that are not constant expressions). The most compatible way to
+ // handle this case is to report a warning instead of an error and force the index to be in
+ // the correct range.
bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst;
- int index = indexConstantUnion->getIConst(0);
- if (index < 0)
+ int index = 0;
+ if (indexConstantUnion->getBasicType() == EbtInt)
{
- std::stringstream infoStream;
- infoStream << index;
- std::string info = infoStream.str();
- outOfRangeError(outOfRangeIndexIsError, location, "negative index", info.c_str());
- index = 0;
+ index = indexConstantUnion->getIConst(0);
}
- TIntermConstantUnion *baseConstantUnion = baseExpression->getAsConstantUnion();
- if (baseConstantUnion)
+ else if (indexConstantUnion->getBasicType() == EbtUInt)
{
- if (baseExpression->isArray())
- {
- // constant folding for array indexing
- indexedExpression =
- addConstArrayNode(index, baseConstantUnion, location, outOfRangeIndexIsError);
- }
- else if (baseExpression->isVector())
- {
- // constant folding for vector indexing
- TVectorFields fields;
- fields.num = 1;
- fields.offsets[0] =
- index; // need to do it this way because v.xy sends fields integer array
- indexedExpression =
- addConstVectorNode(fields, baseConstantUnion, location, outOfRangeIndexIsError);
- }
- else if (baseExpression->isMatrix())
- {
- // constant folding for matrix indexing
- indexedExpression =
- addConstMatrixNode(index, baseConstantUnion, location, outOfRangeIndexIsError);
- }
+ index = static_cast<int>(indexConstantUnion->getUConst(0));
}
- else
+
+ int safeIndex = -1;
+
+ if (index < 0)
{
- int safeIndex = -1;
+ outOfRangeError(outOfRangeIndexIsError, location, "index expression is negative", "[]");
+ safeIndex = 0;
+ }
+ if (!baseExpression->getType().isUnsizedArray())
+ {
if (baseExpression->isArray())
{
if (baseExpression->getQualifier() == EvqFragData && index > 0)
{
- if (mShaderSpec == SH_WEBGL2_SPEC)
+ if (!isExtensionEnabled(TExtension::EXT_draw_buffers))
{
- // Error has been already generated if index is not const.
- if (indexExpression->getQualifier() == EvqConst)
- {
- error(location, "", "[",
- "array index for gl_FragData must be constant zero");
- recover();
- }
- safeIndex = 0;
- }
- else if (!isExtensionEnabled("GL_EXT_draw_buffers"))
- {
- outOfRangeError(outOfRangeIndexIsError, location, "", "[",
+ outOfRangeError(outOfRangeIndexIsError, location,
"array index for gl_FragData must be zero when "
- "GL_EXT_draw_buffers is disabled");
+ "GL_EXT_draw_buffers is disabled",
+ "[]");
safeIndex = 0;
}
}
// Only do generic out-of-range check if similar error hasn't already been reported.
- if (safeIndex < 0 && index >= baseExpression->getType().getArraySize())
+ if (safeIndex < 0)
{
- std::stringstream extraInfoStream;
- extraInfoStream << "array index out of range '" << index << "'";
- std::string extraInfo = extraInfoStream.str();
- outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str());
- safeIndex = baseExpression->getType().getArraySize() - 1;
+ safeIndex = checkIndexLessThan(outOfRangeIndexIsError, location, index,
+ baseExpression->getOutermostArraySize(),
+ "array index out of range");
}
}
- else if ((baseExpression->isVector() || baseExpression->isMatrix()) &&
- baseExpression->getType().getNominalSize() <= index)
+ else if (baseExpression->isMatrix())
+ {
+ safeIndex = checkIndexLessThan(outOfRangeIndexIsError, location, index,
+ baseExpression->getType().getCols(),
+ "matrix field selection out of range");
+ }
+ else if (baseExpression->isVector())
{
- std::stringstream extraInfoStream;
- extraInfoStream << "field selection out of range '" << index << "'";
- std::string extraInfo = extraInfoStream.str();
- outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str());
- safeIndex = baseExpression->getType().getNominalSize() - 1;
+ safeIndex = checkIndexLessThan(outOfRangeIndexIsError, location, index,
+ baseExpression->getType().getNominalSize(),
+ "vector field selection out of range");
}
+ ASSERT(safeIndex >= 0);
// Data of constant unions can't be changed, because it may be shared with other
// constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new
// sanitized object.
- if (safeIndex != -1)
+ if (safeIndex != index || indexConstantUnion->getBasicType() != EbtInt)
{
TConstantUnion *safeConstantUnion = new TConstantUnion();
safeConstantUnion->setIConst(safeIndex);
indexConstantUnion->replaceConstantUnion(safeConstantUnion);
+ indexConstantUnion->getTypePointer()->setBasicType(EbtInt);
}
- indexedExpression =
- intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location);
+ TIntermBinary *node =
+ new TIntermBinary(EOpIndexDirect, baseExpression, indexExpression);
+ node->setLine(location);
+ return node->fold(mDiagnostics);
}
}
- else
- {
- indexedExpression =
- intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location);
- }
- if (indexedExpression == 0)
- {
- TConstantUnion *unionArray = new TConstantUnion[1];
- unionArray->setFConst(0.0f);
- indexedExpression =
- intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), location);
- }
- else if (baseExpression->isArray())
- {
- TType indexedType = baseExpression->getType();
- indexedType.clearArrayness();
- indexedExpression->setType(indexedType);
- }
- else if (baseExpression->isMatrix())
- {
- indexedExpression->setType(TType(baseExpression->getBasicType(),
- baseExpression->getPrecision(), EvqTemporary,
- static_cast<unsigned char>(baseExpression->getRows())));
- }
- else if (baseExpression->isVector())
- {
- indexedExpression->setType(
- TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary));
- }
- else
- {
- indexedExpression->setType(baseExpression->getType());
- }
+ TIntermBinary *node = new TIntermBinary(EOpIndexIndirect, baseExpression, indexExpression);
+ node->setLine(location);
+ // Indirect indexing can never be constant folded.
+ return node;
+}
- if (baseExpression->getType().getQualifier() == EvqConst &&
- indexExpression->getType().getQualifier() == EvqConst)
- {
- indexedExpression->getTypePointer()->setQualifier(EvqConst);
- }
- else
+int TParseContext::checkIndexLessThan(bool outOfRangeIndexIsError,
+ const TSourceLoc &location,
+ int index,
+ int arraySize,
+ const char *reason)
+{
+ // Should not reach here with an unsized / runtime-sized array.
+ ASSERT(arraySize > 0);
+ if (index >= arraySize)
{
- indexedExpression->getTypePointer()->setQualifier(EvqTemporary);
+ std::stringstream reasonStream;
+ reasonStream << reason << " '" << index << "'";
+ std::string token = reasonStream.str();
+ outOfRangeError(outOfRangeIndexIsError, location, reason, "[]");
+ return arraySize - 1;
}
-
- return indexedExpression;
+ return index;
}
TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression,
@@ -2967,62 +4108,37 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
const TString &fieldString,
const TSourceLoc &fieldLocation)
{
- TIntermTyped *indexedExpression = NULL;
-
if (baseExpression->isArray())
{
error(fieldLocation, "cannot apply dot operator to an array", ".");
- recover();
+ return baseExpression;
}
if (baseExpression->isVector())
{
- TVectorFields fields;
- if (!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields,
- fieldLocation))
+ TVector<int> fieldOffsets;
+ if (!parseVectorFields(fieldLocation, fieldString, baseExpression->getNominalSize(),
+ &fieldOffsets))
{
- fields.num = 1;
- fields.offsets[0] = 0;
- recover();
+ fieldOffsets.resize(1);
+ fieldOffsets[0] = 0;
}
+ TIntermSwizzle *node = new TIntermSwizzle(baseExpression, fieldOffsets);
+ node->setLine(dotLocation);
- if (baseExpression->getAsConstantUnion())
- {
- // constant folding for vector fields
- indexedExpression = addConstVectorNode(fields, baseExpression->getAsConstantUnion(),
- fieldLocation, true);
- }
- else
- {
- TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation);
- indexedExpression =
- intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation);
- }
- if (indexedExpression == nullptr)
- {
- recover();
- indexedExpression = baseExpression;
- }
- else
- {
- // Note that the qualifier set here will be corrected later.
- indexedExpression->setType(TType(baseExpression->getBasicType(),
- baseExpression->getPrecision(), EvqTemporary,
- (unsigned char)(fieldString).size()));
- }
+ return node->fold();
}
else if (baseExpression->getBasicType() == EbtStruct)
{
- bool fieldFound = false;
const TFieldList &fields = baseExpression->getType().getStruct()->fields();
if (fields.empty())
{
error(dotLocation, "structure has no fields", "Internal Error");
- recover();
- indexedExpression = baseExpression;
+ return baseExpression;
}
else
{
+ bool fieldFound = false;
unsigned int i;
for (i = 0; i < fields.size(); ++i)
{
@@ -3034,50 +4150,31 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
}
if (fieldFound)
{
- if (baseExpression->getAsConstantUnion())
- {
- indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation);
- if (indexedExpression == 0)
- {
- recover();
- indexedExpression = baseExpression;
- }
- else
- {
- indexedExpression->setType(*fields[i]->type());
- }
- }
- else
- {
- TConstantUnion *unionArray = new TConstantUnion[1];
- unionArray->setIConst(i);
- TIntermTyped *index = intermediate.addConstantUnion(
- unionArray, *fields[i]->type(), fieldLocation);
- indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression,
- index, dotLocation);
- indexedExpression->setType(*fields[i]->type());
- }
+ TIntermTyped *index = CreateIndexNode(i);
+ index->setLine(fieldLocation);
+ TIntermBinary *node =
+ new TIntermBinary(EOpIndexDirectStruct, baseExpression, index);
+ node->setLine(dotLocation);
+ return node->fold(mDiagnostics);
}
else
{
error(dotLocation, " no such field in structure", fieldString.c_str());
- recover();
- indexedExpression = baseExpression;
+ return baseExpression;
}
}
}
else if (baseExpression->isInterfaceBlock())
{
- bool fieldFound = false;
const TFieldList &fields = baseExpression->getType().getInterfaceBlock()->fields();
if (fields.empty())
{
error(dotLocation, "interface block has no fields", "Internal Error");
- recover();
- indexedExpression = baseExpression;
+ return baseExpression;
}
else
{
+ bool fieldFound = false;
unsigned int i;
for (i = 0; i < fields.size(); ++i)
{
@@ -3089,19 +4186,18 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
}
if (fieldFound)
{
- TConstantUnion *unionArray = new TConstantUnion[1];
- unionArray->setIConst(i);
- TIntermTyped *index =
- intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation);
- indexedExpression = intermediate.addIndex(EOpIndexDirectInterfaceBlock,
- baseExpression, index, dotLocation);
- indexedExpression->setType(*fields[i]->type());
+ TIntermTyped *index = CreateIndexNode(i);
+ index->setLine(fieldLocation);
+ TIntermBinary *node =
+ new TIntermBinary(EOpIndexDirectInterfaceBlock, baseExpression, index);
+ node->setLine(dotLocation);
+ // Indexing interface blocks can never be constant folded.
+ return node;
}
else
{
error(dotLocation, " no such field in interface block", fieldString.c_str());
- recover();
- indexedExpression = baseExpression;
+ return baseExpression;
}
}
}
@@ -3119,39 +4215,36 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
"side",
fieldString.c_str());
}
- recover();
- indexedExpression = baseExpression;
- }
-
- if (baseExpression->getQualifier() == EvqConst)
- {
- indexedExpression->getTypePointer()->setQualifier(EvqConst);
- }
- else
- {
- indexedExpression->getTypePointer()->setQualifier(EvqTemporary);
+ return baseExpression;
}
-
- return indexedExpression;
}
TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine)
{
- TLayoutQualifier qualifier;
-
- qualifier.location = -1;
- qualifier.matrixPacking = EmpUnspecified;
- qualifier.blockStorage = EbsUnspecified;
+ TLayoutQualifier qualifier = TLayoutQualifier::Create();
if (qualifierType == "shared")
{
+ if (sh::IsWebGLBasedSpec(mShaderSpec))
+ {
+ error(qualifierTypeLine, "Only std140 layout is allowed in WebGL", "shared");
+ }
qualifier.blockStorage = EbsShared;
}
else if (qualifierType == "packed")
{
+ if (sh::IsWebGLBasedSpec(mShaderSpec))
+ {
+ error(qualifierTypeLine, "Only std140 layout is allowed in WebGL", "packed");
+ }
qualifier.blockStorage = EbsPacked;
}
+ else if (qualifierType == "std430")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.blockStorage = EbsStd430;
+ }
else if (qualifierType == "std140")
{
qualifier.blockStorage = EbsStd140;
@@ -3166,208 +4259,554 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp
}
else if (qualifierType == "location")
{
- error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(),
- "location requires an argument");
- recover();
+ error(qualifierTypeLine, "invalid layout qualifier: location requires an argument",
+ qualifierType.c_str());
+ }
+ else if (qualifierType == "yuv" && mShaderType == GL_FRAGMENT_SHADER)
+ {
+ if (checkCanUseExtension(qualifierTypeLine, TExtension::EXT_YUV_target))
+ {
+ qualifier.yuv = true;
+ }
+ }
+ else if (qualifierType == "rgba32f")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA32F;
+ }
+ else if (qualifierType == "rgba16f")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA16F;
+ }
+ else if (qualifierType == "r32f")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifR32F;
+ }
+ else if (qualifierType == "rgba8")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8;
+ }
+ else if (qualifierType == "rgba8_snorm")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8_SNORM;
+ }
+ else if (qualifierType == "rgba32i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA32I;
+ }
+ else if (qualifierType == "rgba16i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA16I;
+ }
+ else if (qualifierType == "rgba8i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8I;
+ }
+ else if (qualifierType == "r32i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifR32I;
+ }
+ else if (qualifierType == "rgba32ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA32UI;
+ }
+ else if (qualifierType == "rgba16ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA16UI;
+ }
+ else if (qualifierType == "rgba8ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8UI;
+ }
+ else if (qualifierType == "r32ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifR32UI;
}
+ else if (qualifierType == "points" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.primitiveType = EptPoints;
+ }
+ else if (qualifierType == "lines" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.primitiveType = EptLines;
+ }
+ else if (qualifierType == "lines_adjacency" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.primitiveType = EptLinesAdjacency;
+ }
+ else if (qualifierType == "triangles" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.primitiveType = EptTriangles;
+ }
+ else if (qualifierType == "triangles_adjacency" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.primitiveType = EptTrianglesAdjacency;
+ }
+ else if (qualifierType == "line_strip" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.primitiveType = EptLineStrip;
+ }
+ else if (qualifierType == "triangle_strip" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.primitiveType = EptTriangleStrip;
+ }
+
else
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
- recover();
}
return qualifier;
}
+void TParseContext::parseLocalSize(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ size_t index,
+ sh::WorkGroupSize *localSize)
+{
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ if (intValue < 1)
+ {
+ std::stringstream reasonStream;
+ reasonStream << "out of range: " << getWorkGroupSizeString(index) << " must be positive";
+ std::string reason = reasonStream.str();
+ error(intValueLine, reason.c_str(), intValueString.c_str());
+ }
+ (*localSize)[index] = intValue;
+}
+
+void TParseContext::parseNumViews(int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ int *numViews)
+{
+ // This error is only specified in WebGL, but tightens unspecified behavior in the native
+ // specification.
+ if (intValue < 1)
+ {
+ error(intValueLine, "out of range: num_views must be positive", intValueString.c_str());
+ }
+ *numViews = intValue;
+}
+
+void TParseContext::parseInvocations(int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ int *numInvocations)
+{
+ // Although SPEC isn't clear whether invocations can be less than 1, we add this limit because
+ // it doesn't make sense to accept invocations <= 0.
+ if (intValue < 1 || intValue > mMaxGeometryShaderInvocations)
+ {
+ error(intValueLine,
+ "out of range: invocations must be in the range of [1, "
+ "MAX_GEOMETRY_SHADER_INVOCATIONS_OES]",
+ intValueString.c_str());
+ }
+ else
+ {
+ *numInvocations = intValue;
+ }
+}
+
+void TParseContext::parseMaxVertices(int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ int *maxVertices)
+{
+ // Although SPEC isn't clear whether max_vertices can be less than 0, we add this limit because
+ // it doesn't make sense to accept max_vertices < 0.
+ if (intValue < 0 || intValue > mMaxGeometryShaderMaxVertices)
+ {
+ error(
+ intValueLine,
+ "out of range: max_vertices must be in the range of [0, gl_MaxGeometryOutputVertices]",
+ intValueString.c_str());
+ }
+ else
+ {
+ *maxVertices = intValue;
+ }
+}
+
TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine,
- const TString &intValueString,
int intValue,
const TSourceLoc &intValueLine)
{
- TLayoutQualifier qualifier;
+ TLayoutQualifier qualifier = TLayoutQualifier::Create();
- qualifier.location = -1;
- qualifier.matrixPacking = EmpUnspecified;
- qualifier.blockStorage = EbsUnspecified;
+ std::string intValueString = Str(intValue);
- if (qualifierType != "location")
+ if (qualifierType == "location")
+ {
+ // must check that location is non-negative
+ if (intValue < 0)
+ {
+ error(intValueLine, "out of range: location must be non-negative",
+ intValueString.c_str());
+ }
+ else
+ {
+ qualifier.location = intValue;
+ qualifier.locationsSpecified = 1;
+ }
+ }
+ else if (qualifierType == "binding")
{
- error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(),
- "only location may have arguments");
- recover();
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ if (intValue < 0)
+ {
+ error(intValueLine, "out of range: binding must be non-negative",
+ intValueString.c_str());
+ }
+ else
+ {
+ qualifier.binding = intValue;
+ }
}
- else
+ else if (qualifierType == "offset")
{
- // must check that location is non-negative
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
if (intValue < 0)
{
- error(intValueLine, "out of range:", intValueString.c_str(),
- "location must be non-negative");
- recover();
+ error(intValueLine, "out of range: offset must be non-negative",
+ intValueString.c_str());
}
else
{
- qualifier.location = intValue;
+ qualifier.offset = intValue;
+ }
+ }
+ else if (qualifierType == "local_size_x")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
+ &qualifier.localSize);
+ }
+ else if (qualifierType == "local_size_y")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 1u,
+ &qualifier.localSize);
+ }
+ else if (qualifierType == "local_size_z")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 2u,
+ &qualifier.localSize);
+ }
+ else if (qualifierType == "num_views" && mShaderType == GL_VERTEX_SHADER)
+ {
+ if (checkCanUseExtension(qualifierTypeLine, TExtension::OVR_multiview))
+ {
+ parseNumViews(intValue, intValueLine, intValueString, &qualifier.numViews);
}
}
+ else if (qualifierType == "invocations" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ parseInvocations(intValue, intValueLine, intValueString, &qualifier.invocations);
+ }
+ else if (qualifierType == "max_vertices" && mShaderType == GL_GEOMETRY_SHADER_OES &&
+ checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
+ {
+ parseMaxVertices(intValue, intValueLine, intValueString, &qualifier.maxVertices);
+ }
+
+ else
+ {
+ error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
+ }
return qualifier;
}
-TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier,
- TLayoutQualifier rightQualifier)
+TTypeQualifierBuilder *TParseContext::createTypeQualifierBuilder(const TSourceLoc &loc)
{
- TLayoutQualifier joinedQualifier = leftQualifier;
+ return new TTypeQualifierBuilder(
+ new TStorageQualifierWrapper(symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary, loc),
+ mShaderVersion);
+}
+
+TStorageQualifierWrapper *TParseContext::parseGlobalStorageQualifier(TQualifier qualifier,
+ const TSourceLoc &loc)
+{
+ checkIsAtGlobalLevel(loc, getQualifierString(qualifier));
+ return new TStorageQualifierWrapper(qualifier, loc);
+}
- if (rightQualifier.location != -1)
+TStorageQualifierWrapper *TParseContext::parseVaryingQualifier(const TSourceLoc &loc)
+{
+ if (getShaderType() == GL_VERTEX_SHADER)
{
- joinedQualifier.location = rightQualifier.location;
+ return parseGlobalStorageQualifier(EvqVaryingOut, loc);
}
- if (rightQualifier.matrixPacking != EmpUnspecified)
+ return parseGlobalStorageQualifier(EvqVaryingIn, loc);
+}
+
+TStorageQualifierWrapper *TParseContext::parseInQualifier(const TSourceLoc &loc)
+{
+ if (declaringFunction())
{
- joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
+ return new TStorageQualifierWrapper(EvqIn, loc);
}
- if (rightQualifier.blockStorage != EbsUnspecified)
+
+ switch (getShaderType())
{
- joinedQualifier.blockStorage = rightQualifier.blockStorage;
+ case GL_VERTEX_SHADER:
+ {
+ if (mShaderVersion < 300 && !isExtensionEnabled(TExtension::OVR_multiview))
+ {
+ error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "in");
+ }
+ return new TStorageQualifierWrapper(EvqVertexIn, loc);
+ }
+ case GL_FRAGMENT_SHADER:
+ {
+ if (mShaderVersion < 300)
+ {
+ error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "in");
+ }
+ return new TStorageQualifierWrapper(EvqFragmentIn, loc);
+ }
+ case GL_COMPUTE_SHADER:
+ {
+ return new TStorageQualifierWrapper(EvqComputeIn, loc);
+ }
+ case GL_GEOMETRY_SHADER_OES:
+ {
+ return new TStorageQualifierWrapper(EvqGeometryIn, loc);
+ }
+ default:
+ {
+ UNREACHABLE();
+ return new TStorageQualifierWrapper(EvqLast, loc);
+ }
}
-
- return joinedQualifier;
}
-TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc,
- TQualifier interpolationQualifier,
- const TSourceLoc &storageLoc,
- TQualifier storageQualifier)
+TStorageQualifierWrapper *TParseContext::parseOutQualifier(const TSourceLoc &loc)
{
- TQualifier mergedQualifier = EvqSmoothIn;
-
- if (storageQualifier == EvqFragmentIn)
+ if (declaringFunction())
{
- if (interpolationQualifier == EvqSmooth)
- mergedQualifier = EvqSmoothIn;
- else if (interpolationQualifier == EvqFlat)
- mergedQualifier = EvqFlatIn;
- else
- UNREACHABLE();
+ return new TStorageQualifierWrapper(EvqOut, loc);
}
- else if (storageQualifier == EvqCentroidIn)
+ switch (getShaderType())
{
- if (interpolationQualifier == EvqSmooth)
- mergedQualifier = EvqCentroidIn;
- else if (interpolationQualifier == EvqFlat)
- mergedQualifier = EvqFlatIn;
- else
+ case GL_VERTEX_SHADER:
+ {
+ if (mShaderVersion < 300)
+ {
+ error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "out");
+ }
+ return new TStorageQualifierWrapper(EvqVertexOut, loc);
+ }
+ case GL_FRAGMENT_SHADER:
+ {
+ if (mShaderVersion < 300)
+ {
+ error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "out");
+ }
+ return new TStorageQualifierWrapper(EvqFragmentOut, loc);
+ }
+ case GL_COMPUTE_SHADER:
+ {
+ error(loc, "storage qualifier isn't supported in compute shaders", "out");
+ return new TStorageQualifierWrapper(EvqLast, loc);
+ }
+ case GL_GEOMETRY_SHADER_OES:
+ {
+ return new TStorageQualifierWrapper(EvqGeometryOut, loc);
+ }
+ default:
+ {
UNREACHABLE();
+ return new TStorageQualifierWrapper(EvqLast, loc);
+ }
}
- else if (storageQualifier == EvqVertexOut)
+}
+
+TStorageQualifierWrapper *TParseContext::parseInOutQualifier(const TSourceLoc &loc)
+{
+ if (!declaringFunction())
{
- if (interpolationQualifier == EvqSmooth)
- mergedQualifier = EvqSmoothOut;
- else if (interpolationQualifier == EvqFlat)
- mergedQualifier = EvqFlatOut;
- else
- UNREACHABLE();
+ error(loc, "invalid qualifier: can be only used with function parameters", "inout");
}
- else if (storageQualifier == EvqCentroidOut)
+ return new TStorageQualifierWrapper(EvqInOut, loc);
+}
+
+TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation)
+{
+ return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation,
+ mDiagnostics);
+}
+
+TField *TParseContext::parseStructDeclarator(TString *identifier, const TSourceLoc &loc)
+{
+ checkIsNotReserved(loc, *identifier);
+ TType *type = new TType(EbtVoid, EbpUndefined);
+ return new TField(type, identifier, loc);
+}
+
+TField *TParseContext::parseStructArrayDeclarator(TString *identifier,
+ const TSourceLoc &loc,
+ const TVector<unsigned int> &arraySizes,
+ const TSourceLoc &arraySizeLoc)
+{
+ checkIsNotReserved(loc, *identifier);
+
+ TType *type = new TType(EbtVoid, EbpUndefined);
+ type->makeArrays(arraySizes);
+
+ return new TField(type, identifier, loc);
+}
+
+void TParseContext::checkDoesNotHaveDuplicateFieldName(const TFieldList::const_iterator begin,
+ const TFieldList::const_iterator end,
+ const TString &name,
+ const TSourceLoc &location)
+{
+ for (auto fieldIter = begin; fieldIter != end; ++fieldIter)
{
- if (interpolationQualifier == EvqSmooth)
- mergedQualifier = EvqCentroidOut;
- else if (interpolationQualifier == EvqFlat)
- mergedQualifier = EvqFlatOut;
- else
- UNREACHABLE();
+ if ((*fieldIter)->name() == name)
+ {
+ error(location, "duplicate field name in structure", name.c_str());
+ }
}
- else
- {
- error(interpolationLoc,
- "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier",
- getInterpolationString(interpolationQualifier));
- recover();
+}
- mergedQualifier = storageQualifier;
+TFieldList *TParseContext::addStructFieldList(TFieldList *fields, const TSourceLoc &location)
+{
+ for (TFieldList::const_iterator fieldIter = fields->begin(); fieldIter != fields->end();
+ ++fieldIter)
+ {
+ checkDoesNotHaveDuplicateFieldName(fields->begin(), fieldIter, (*fieldIter)->name(),
+ location);
}
-
- TPublicType type;
- type.setBasic(EbtVoid, mergedQualifier, storageLoc);
- return type;
+ return fields;
}
-TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier,
- TFieldList *fieldList)
+TFieldList *TParseContext::combineStructFieldLists(TFieldList *processedFields,
+ const TFieldList *newlyAddedFields,
+ const TSourceLoc &location)
{
- if (voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type))
+ for (TField *field : *newlyAddedFields)
{
- recover();
+ checkDoesNotHaveDuplicateFieldName(processedFields->begin(), processedFields->end(),
+ field->name(), location);
+ processedFields->push_back(field);
}
+ return processedFields;
+}
+
+TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TPublicType *typeSpecifier,
+ TFieldList *fieldList)
+{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
- for (unsigned int i = 0; i < fieldList->size(); ++i)
+ typeSpecifier->qualifier = typeQualifier.qualifier;
+ typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier;
+ typeSpecifier->memoryQualifier = typeQualifier.memoryQualifier;
+ typeSpecifier->invariant = typeQualifier.invariant;
+ if (typeQualifier.precision != EbpUndefined)
{
- //
- // Careful not to replace already known aspects of type, like array-ness
- //
- TType *type = (*fieldList)[i]->type();
- type->setBasicType(typeSpecifier.type);
- type->setPrimarySize(typeSpecifier.primarySize);
- type->setSecondarySize(typeSpecifier.secondarySize);
- type->setPrecision(typeSpecifier.precision);
- type->setQualifier(typeSpecifier.qualifier);
- type->setLayoutQualifier(typeSpecifier.layoutQualifier);
+ typeSpecifier->precision = typeQualifier.precision;
+ }
+ return addStructDeclaratorList(*typeSpecifier, fieldList);
+}
- // don't allow arrays of arrays
- if (type->isArray())
- {
- if (arrayTypeErrorCheck(typeSpecifier.line, typeSpecifier))
- recover();
- }
- if (typeSpecifier.array)
- type->setArraySize(typeSpecifier.arraySize);
- if (typeSpecifier.userDef)
+TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier,
+ TFieldList *declaratorList)
+{
+ checkPrecisionSpecified(typeSpecifier.getLine(), typeSpecifier.precision,
+ typeSpecifier.getBasicType());
+
+ checkIsNonVoid(typeSpecifier.getLine(), (*declaratorList)[0]->name(),
+ typeSpecifier.getBasicType());
+
+ checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier);
+
+ for (TField *declarator : *declaratorList)
+ {
+ // Don't allow arrays of arrays in ESSL < 3.10.
+ if (declarator->type()->isArray())
{
- type->setStruct(typeSpecifier.userDef->getStruct());
+ checkArrayElementIsNotArray(typeSpecifier.getLine(), typeSpecifier);
}
- if (structNestingErrorCheck(typeSpecifier.line, *(*fieldList)[i]))
+ auto *declaratorArraySizes = declarator->type()->getArraySizes();
+
+ TType *type = declarator->type();
+ *type = TType(typeSpecifier);
+ if (declaratorArraySizes != nullptr)
{
- recover();
+ for (unsigned int arraySize : *declaratorArraySizes)
+ {
+ type->makeArray(arraySize);
+ }
}
+
+ checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *declarator);
}
- return fieldList;
+ return declaratorList;
}
-TPublicType TParseContext::addStructure(const TSourceLoc &structLine,
- const TSourceLoc &nameLine,
- const TString *structName,
- TFieldList *fieldList)
+TTypeSpecifierNonArray TParseContext::addStructure(const TSourceLoc &structLine,
+ const TSourceLoc &nameLine,
+ const TString *structName,
+ TFieldList *fieldList)
{
- TStructure *structure = new TStructure(structName, fieldList);
- TType *structureType = new TType(structure);
+ TStructure *structure = new TStructure(&symbolTable, structName, fieldList);
// Store a bool in the struct if we're at global scope, to allow us to
// skip the local struct scoping workaround in HLSL.
- structure->setUniqueId(TSymbolTable::nextUniqueId());
structure->setAtGlobalScope(symbolTable.atGlobalLevel());
if (!structName->empty())
{
- if (reservedErrorCheck(nameLine, *structName))
- {
- recover();
- }
- TVariable *userTypeDef = new TVariable(structName, *structureType, true);
- if (!symbolTable.declare(userTypeDef))
+ checkIsNotReserved(nameLine, *structName);
+ if (!symbolTable.declareStructType(structure))
{
- error(nameLine, "redefinition", structName->c_str(), "struct");
- recover();
+ error(nameLine, "redefinition of a struct", structName->c_str());
}
}
// ensure we do not specify any storage qualifiers on the struct members
for (unsigned int typeListIndex = 0; typeListIndex < fieldList->size(); typeListIndex++)
{
- const TField &field = *(*fieldList)[typeListIndex];
+ TField &field = *(*fieldList)[typeListIndex];
const TQualifier qualifier = field.type()->getQualifier();
switch (qualifier)
{
@@ -3377,22 +4816,37 @@ TPublicType TParseContext::addStructure(const TSourceLoc &structLine,
default:
error(field.line(), "invalid qualifier on struct member",
getQualifierString(qualifier));
- recover();
break;
}
+ if (field.type()->isInvariant())
+ {
+ error(field.line(), "invalid qualifier on struct member", "invariant");
+ }
+ // ESSL 3.10 section 4.1.8 -- atomic_uint or images are not allowed as structure member.
+ if (IsImage(field.type()->getBasicType()) || IsAtomicCounter(field.type()->getBasicType()))
+ {
+ error(field.line(), "disallowed type in struct", field.type()->getBasicString());
+ }
+
+ checkIsNotUnsizedArray(field.line(), "array members of structs must specify a size",
+ field.name().c_str(), field.type());
+
+ checkMemoryQualifierIsNotSpecified(field.type()->getMemoryQualifier(), field.line());
+
+ checkBindingIsNotSpecified(field.line(), field.type()->getLayoutQualifier().binding);
+
+ checkLocationIsNotSpecified(field.line(), field.type()->getLayoutQualifier());
}
- TPublicType publicType;
- publicType.setBasic(EbtStruct, EvqTemporary, structLine);
- publicType.userDef = structureType;
- publicType.isStructSpecifier = true;
+ TTypeSpecifierNonArray typeSpecifierNonArray;
+ typeSpecifierNonArray.initializeStruct(structure, true, structLine);
exitStructDeclaration();
- return publicType;
+ return typeSpecifierNonArray;
}
TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init,
- TIntermAggregate *statementList,
+ TIntermBlock *statementList,
const TSourceLoc &loc)
{
TBasicType switchType = init->getBasicType();
@@ -3401,26 +4855,18 @@ TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init,
{
error(init->getLine(), "init-expression in a switch statement must be a scalar integer",
"switch");
- recover();
return nullptr;
}
- if (statementList)
+ ASSERT(statementList);
+ if (!ValidateSwitchStatementList(switchType, mShaderVersion, mDiagnostics, statementList, loc))
{
- if (!ValidateSwitch::validate(switchType, this, statementList, loc))
- {
- recover();
- return nullptr;
- }
- }
-
- TIntermSwitch *node = intermediate.addSwitch(init, statementList, loc);
- if (node == nullptr)
- {
- error(loc, "erroneous switch statement", "switch");
- recover();
+ ASSERT(mDiagnostics->numErrors() > 0);
return nullptr;
}
+
+ TIntermSwitch *node = new TIntermSwitch(init, statementList);
+ node->setLine(loc);
return node;
}
@@ -3429,20 +4875,17 @@ TIntermCase *TParseContext::addCase(TIntermTyped *condition, const TSourceLoc &l
if (mSwitchNestingLevel == 0)
{
error(loc, "case labels need to be inside switch statements", "case");
- recover();
return nullptr;
}
if (condition == nullptr)
{
error(loc, "case label must have a condition", "case");
- recover();
return nullptr;
}
if ((condition->getBasicType() != EbtInt && condition->getBasicType() != EbtUInt) ||
condition->isMatrix() || condition->isArray() || condition->isVector())
{
error(condition->getLine(), "case label must be a scalar integer", "case");
- recover();
}
TIntermConstantUnion *conditionConst = condition->getAsConstantUnion();
// TODO(oetuaho@nvidia.com): Get rid of the conditionConst == nullptr check once all constant
@@ -3451,15 +4894,9 @@ TIntermCase *TParseContext::addCase(TIntermTyped *condition, const TSourceLoc &l
if (condition->getQualifier() != EvqConst || conditionConst == nullptr)
{
error(condition->getLine(), "case label must be constant", "case");
- recover();
- }
- TIntermCase *node = intermediate.addCase(condition, loc);
- if (node == nullptr)
- {
- error(loc, "erroneous case statement", "case");
- recover();
- return nullptr;
}
+ TIntermCase *node = new TIntermCase(condition);
+ node->setLine(loc);
return node;
}
@@ -3468,28 +4905,18 @@ TIntermCase *TParseContext::addDefault(const TSourceLoc &loc)
if (mSwitchNestingLevel == 0)
{
error(loc, "default labels need to be inside switch statements", "default");
- recover();
- return nullptr;
- }
- TIntermCase *node = intermediate.addCase(nullptr, loc);
- if (node == nullptr)
- {
- error(loc, "erroneous default statement", "default");
- recover();
return nullptr;
}
+ TIntermCase *node = new TIntermCase(nullptr);
+ node->setLine(loc);
return node;
}
TIntermTyped *TParseContext::createUnaryMath(TOperator op,
TIntermTyped *child,
- const TSourceLoc &loc,
- const TType *funcReturnType)
+ const TSourceLoc &loc)
{
- if (child == nullptr)
- {
- return nullptr;
- }
+ ASSERT(child != nullptr);
switch (op)
{
@@ -3497,6 +4924,7 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op,
if (child->getBasicType() != EbtBool || child->isMatrix() || child->isArray() ||
child->isVector())
{
+ unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
return nullptr;
}
break;
@@ -3504,6 +4932,7 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op,
if ((child->getBasicType() != EbtInt && child->getBasicType() != EbtUInt) ||
child->isMatrix() || child->isArray())
{
+ unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
return nullptr;
}
break;
@@ -3513,9 +4942,11 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op,
case EOpPreDecrement:
case EOpNegative:
case EOpPositive:
- if (child->getBasicType() == EbtStruct || child->getBasicType() == EbtBool ||
- child->isArray())
+ if (child->getBasicType() == EbtStruct || child->isInterfaceBlock() ||
+ child->getBasicType() == EbtBool || child->isArray() ||
+ IsOpaqueType(child->getBasicType()))
{
+ unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
return nullptr;
}
// Operators for built-ins are already type checked against their prototype.
@@ -3523,16 +4954,24 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op,
break;
}
- return intermediate.addUnaryMath(op, child, loc, funcReturnType);
+ if (child->getMemoryQualifier().writeonly)
+ {
+ unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
+ return nullptr;
+ }
+
+ TIntermUnary *node = new TIntermUnary(op, child);
+ node->setLine(loc);
+
+ return node->fold(mDiagnostics);
}
TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
{
- TIntermTyped *node = createUnaryMath(op, child, loc, nullptr);
+ ASSERT(op != EOpNull);
+ TIntermTyped *node = createUnaryMath(op, child, loc);
if (node == nullptr)
{
- unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
- recover();
return child;
}
return node;
@@ -3542,8 +4981,7 @@ TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op,
TIntermTyped *child,
const TSourceLoc &loc)
{
- if (lValueErrorCheck(loc, GetOperatorString(op), child))
- recover();
+ checkCanBeLValue(loc, GetOperatorString(op), child);
return addUnaryMath(op, child, loc);
}
@@ -3552,17 +4990,95 @@ bool TParseContext::binaryOpCommonCheck(TOperator op,
TIntermTyped *right,
const TSourceLoc &loc)
{
- if (left->isArray() || right->isArray())
+ // Check opaque types are not allowed to be operands in expressions other than array indexing
+ // and structure member selection.
+ if (IsOpaqueType(left->getBasicType()) || IsOpaqueType(right->getBasicType()))
{
- if (mShaderVersion < 300)
+ switch (op)
{
- error(loc, "Invalid operation for arrays", GetOperatorString(op));
- return false;
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ break;
+ case EOpIndexDirectStruct:
+ UNREACHABLE();
+
+ default:
+ error(loc, "Invalid operation for variables with an opaque type",
+ GetOperatorString(op));
+ return false;
}
+ }
+
+ if (right->getMemoryQualifier().writeonly)
+ {
+ error(loc, "Invalid operation for variables with writeonly", GetOperatorString(op));
+ return false;
+ }
- if (left->isArray() != right->isArray())
+ if (left->getMemoryQualifier().writeonly)
+ {
+ switch (op)
{
- error(loc, "array / non-array mismatch", GetOperatorString(op));
+ case EOpAssign:
+ case EOpInitialize:
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ case EOpIndexDirectInterfaceBlock:
+ break;
+ default:
+ error(loc, "Invalid operation for variables with writeonly", GetOperatorString(op));
+ return false;
+ }
+ }
+
+ if (left->getType().getStruct() || right->getType().getStruct())
+ {
+ switch (op)
+ {
+ case EOpIndexDirectStruct:
+ ASSERT(left->getType().getStruct());
+ break;
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpAssign:
+ case EOpInitialize:
+ if (left->getType() != right->getType())
+ {
+ return false;
+ }
+ break;
+ default:
+ error(loc, "Invalid operation for structs", GetOperatorString(op));
+ return false;
+ }
+ }
+
+ if (left->isInterfaceBlock() || right->isInterfaceBlock())
+ {
+ switch (op)
+ {
+ case EOpIndexDirectInterfaceBlock:
+ ASSERT(left->getType().getInterfaceBlock());
+ break;
+ default:
+ error(loc, "Invalid operation for interface blocks", GetOperatorString(op));
+ return false;
+ }
+ }
+
+ if (left->isArray() != right->isArray())
+ {
+ error(loc, "array / non-array mismatch", GetOperatorString(op));
+ return false;
+ }
+
+ if (left->isArray())
+ {
+ ASSERT(right->isArray());
+ if (mShaderVersion < 300)
+ {
+ error(loc, "Invalid operation for arrays", GetOperatorString(op));
return false;
}
@@ -3578,7 +5094,7 @@ bool TParseContext::binaryOpCommonCheck(TOperator op,
return false;
}
// At this point, size of implicitly sized arrays should be resolved.
- if (left->getArraySize() != right->getArraySize())
+ if (*left->getType().getArraySizes() != *right->getType().getArraySizes())
{
error(loc, "array size mismatch", GetOperatorString(op));
return false;
@@ -3625,8 +5141,10 @@ bool TParseContext::binaryOpCommonCheck(TOperator op,
return false;
}
- // Check that type sizes match exactly on ops that require that.
- // Also check restrictions for structs that contain arrays or samplers.
+ // Check that:
+ // 1. Type sizes match exactly on ops that require that.
+ // 2. Restrictions for structs that contain arrays or samplers are respected.
+ // 3. Arithmetic op type dimensionality restrictions for ops other than multiply are respected.
switch (op)
{
case EOpAssign:
@@ -3650,15 +5168,65 @@ bool TParseContext::binaryOpCommonCheck(TOperator op,
GetOperatorString(op));
return false;
}
+
+ if ((left->getNominalSize() != right->getNominalSize()) ||
+ (left->getSecondarySize() != right->getSecondarySize()))
+ {
+ error(loc, "dimension mismatch", GetOperatorString(op));
+ return false;
+ }
+ break;
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
- if ((left->getNominalSize() != right->getNominalSize()) ||
- (left->getSecondarySize() != right->getSecondarySize()))
+ if (!left->isScalar() || !right->isScalar())
+ {
+ error(loc, "comparison operator only defined for scalars", GetOperatorString(op));
+ return false;
+ }
+ break;
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpIMod:
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ if ((left->isMatrix() && right->isVector()) || (left->isVector() && right->isMatrix()))
{
return false;
}
+
+ // Are the sizes compatible?
+ if (left->getNominalSize() != right->getNominalSize() ||
+ left->getSecondarySize() != right->getSecondarySize())
+ {
+ // If the nominal sizes of operands do not match:
+ // One of them must be a scalar.
+ if (!left->isScalar() && !right->isScalar())
+ return false;
+
+ // In the case of compound assignment other than multiply-assign,
+ // the right side needs to be a scalar. Otherwise a vector/matrix
+ // would be assigned to a scalar. A scalar can't be shifted by a
+ // vector either.
+ if (!right->isScalar() &&
+ (IsAssignment(op) || op == EOpBitShiftLeft || op == EOpBitShiftRight))
+ return false;
+ }
+ break;
default:
break;
}
@@ -3666,6 +5234,49 @@ bool TParseContext::binaryOpCommonCheck(TOperator op,
return true;
}
+bool TParseContext::isMultiplicationTypeCombinationValid(TOperator op,
+ const TType &left,
+ const TType &right)
+{
+ switch (op)
+ {
+ case EOpMul:
+ case EOpMulAssign:
+ return left.getNominalSize() == right.getNominalSize() &&
+ left.getSecondarySize() == right.getSecondarySize();
+ case EOpVectorTimesScalar:
+ return true;
+ case EOpVectorTimesScalarAssign:
+ ASSERT(!left.isMatrix() && !right.isMatrix());
+ return left.isVector() && !right.isVector();
+ case EOpVectorTimesMatrix:
+ return left.getNominalSize() == right.getRows();
+ case EOpVectorTimesMatrixAssign:
+ ASSERT(!left.isMatrix() && right.isMatrix());
+ return left.isVector() && left.getNominalSize() == right.getRows() &&
+ left.getNominalSize() == right.getCols();
+ case EOpMatrixTimesVector:
+ return left.getCols() == right.getNominalSize();
+ case EOpMatrixTimesScalar:
+ return true;
+ case EOpMatrixTimesScalarAssign:
+ ASSERT(left.isMatrix() && !right.isMatrix());
+ return !right.isVector();
+ case EOpMatrixTimesMatrix:
+ return left.getCols() == right.getRows();
+ case EOpMatrixTimesMatrixAssign:
+ ASSERT(left.isMatrix() && right.isMatrix());
+ // We need to check two things:
+ // 1. The matrix multiplication step is valid.
+ // 2. The result will have the same number of columns as the lvalue.
+ return left.getCols() == right.getRows() && left.getCols() == right.getCols();
+
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
@@ -3678,52 +5289,61 @@ TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op,
{
case EOpEqual:
case EOpNotEqual:
- break;
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
- ASSERT(!left->isArray() && !right->isArray());
- if (left->isMatrix() || left->isVector() || left->getBasicType() == EbtStruct)
- {
- return nullptr;
- }
break;
case EOpLogicalOr:
case EOpLogicalXor:
case EOpLogicalAnd:
- ASSERT(!left->isArray() && !right->isArray());
- if (left->getBasicType() != EbtBool || left->isMatrix() || left->isVector())
+ ASSERT(!left->isArray() && !right->isArray() && !left->getType().getStruct() &&
+ !right->getType().getStruct());
+ if (left->getBasicType() != EbtBool || !left->isScalar() || !right->isScalar())
{
return nullptr;
}
+ // Basic types matching should have been already checked.
+ ASSERT(right->getBasicType() == EbtBool);
break;
case EOpAdd:
case EOpSub:
case EOpDiv:
case EOpMul:
- ASSERT(!left->isArray() && !right->isArray());
- if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool)
+ ASSERT(!left->isArray() && !right->isArray() && !left->getType().getStruct() &&
+ !right->getType().getStruct());
+ if (left->getBasicType() == EbtBool)
{
return nullptr;
}
break;
case EOpIMod:
- ASSERT(!left->isArray() && !right->isArray());
+ ASSERT(!left->isArray() && !right->isArray() && !left->getType().getStruct() &&
+ !right->getType().getStruct());
// Note that this is only for the % operator, not for mod()
- if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool ||
- left->getBasicType() == EbtFloat)
+ if (left->getBasicType() == EbtBool || left->getBasicType() == EbtFloat)
{
return nullptr;
}
break;
- // Note that for bitwise ops, type checking is done in promote() to
- // share code between ops and compound assignment
default:
break;
}
- return intermediate.addBinaryMath(op, left, right, loc);
+ if (op == EOpMul)
+ {
+ op = TIntermBinary::GetMulOpBasedOnOperands(left->getType(), right->getType());
+ if (!isMultiplicationTypeCombinationValid(op, left->getType(), right->getType()))
+ {
+ return nullptr;
+ }
+ }
+
+ TIntermBinary *node = new TIntermBinary(op, left, right);
+ node->setLine(loc);
+
+ // See if we can fold constants.
+ return node->fold(mDiagnostics);
}
TIntermTyped *TParseContext::addBinaryMath(TOperator op,
@@ -3736,7 +5356,6 @@ TIntermTyped *TParseContext::addBinaryMath(TOperator op,
{
binaryOpError(loc, GetOperatorString(op), left->getCompleteString(),
right->getCompleteString());
- recover();
return left;
}
return node;
@@ -3748,27 +5367,35 @@ TIntermTyped *TParseContext::addBinaryMathBooleanResult(TOperator op,
const TSourceLoc &loc)
{
TIntermTyped *node = addBinaryMathInternal(op, left, right, loc);
- if (node == 0)
+ if (node == nullptr)
{
binaryOpError(loc, GetOperatorString(op), left->getCompleteString(),
right->getCompleteString());
- recover();
- TConstantUnion *unionArray = new TConstantUnion[1];
- unionArray->setBConst(false);
- return intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst),
- loc);
+ node = CreateBoolNode(false);
+ node->setLine(loc);
}
return node;
}
-TIntermTyped *TParseContext::createAssign(TOperator op,
- TIntermTyped *left,
- TIntermTyped *right,
- const TSourceLoc &loc)
+TIntermBinary *TParseContext::createAssign(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
{
if (binaryOpCommonCheck(op, left, right, loc))
{
- return intermediate.addAssign(op, left, right, loc);
+ if (op == EOpMulAssign)
+ {
+ op = TIntermBinary::GetMulAssignOpBasedOnOperands(left->getType(), right->getType());
+ if (!isMultiplicationTypeCombinationValid(op, left->getType(), right->getType()))
+ {
+ return nullptr;
+ }
+ }
+ TIntermBinary *node = new TIntermBinary(op, left, right);
+ node->setLine(loc);
+
+ return node;
}
return nullptr;
}
@@ -3778,11 +5405,11 @@ TIntermTyped *TParseContext::addAssign(TOperator op,
TIntermTyped *right,
const TSourceLoc &loc)
{
+ checkCanBeLValue(loc, "assign", left);
TIntermTyped *node = createAssign(op, left, right, loc);
if (node == nullptr)
{
assignError(loc, "assign", left->getCompleteString(), right->getCompleteString());
- recover();
return left;
}
return node;
@@ -3792,7 +5419,22 @@ TIntermTyped *TParseContext::addComma(TIntermTyped *left,
TIntermTyped *right,
const TSourceLoc &loc)
{
- return intermediate.addComma(left, right, loc, mShaderVersion);
+ // WebGL2 section 5.26, the following results in an error:
+ // "Sequence operator applied to void, arrays, or structs containing arrays"
+ if (mShaderSpec == SH_WEBGL2_SPEC &&
+ (left->isArray() || left->getBasicType() == EbtVoid ||
+ left->getType().isStructureContainingArrays() || right->isArray() ||
+ right->getBasicType() == EbtVoid || right->getType().isStructureContainingArrays()))
+ {
+ error(loc,
+ "sequence operator is not allowed for void, arrays, or structs containing arrays",
+ ",");
+ }
+
+ TIntermBinary *commaNode = new TIntermBinary(EOpComma, left, right);
+ TQualifier resultQualifier = TIntermBinary::GetCommaQualifier(mShaderVersion, left, right);
+ commaNode->getTypePointer()->setQualifier(resultQualifier);
+ return commaNode->fold(mDiagnostics);
}
TIntermBranch *TParseContext::addBranch(TOperator op, const TSourceLoc &loc)
@@ -3803,319 +5445,553 @@ TIntermBranch *TParseContext::addBranch(TOperator op, const TSourceLoc &loc)
if (mLoopNestingLevel <= 0)
{
error(loc, "continue statement only allowed in loops", "");
- recover();
}
break;
case EOpBreak:
if (mLoopNestingLevel <= 0 && mSwitchNestingLevel <= 0)
{
error(loc, "break statement only allowed in loops and switch statements", "");
- recover();
}
break;
case EOpReturn:
if (mCurrentFunctionType->getBasicType() != EbtVoid)
{
error(loc, "non-void function must return a value", "return");
- recover();
+ }
+ break;
+ case EOpKill:
+ if (mShaderType != GL_FRAGMENT_SHADER)
+ {
+ error(loc, "discard supported in fragment shaders only", "discard");
}
break;
default:
- // No checks for discard
+ UNREACHABLE();
break;
}
- return intermediate.addBranch(op, loc);
+ return addBranch(op, nullptr, loc);
}
TIntermBranch *TParseContext::addBranch(TOperator op,
- TIntermTyped *returnValue,
+ TIntermTyped *expression,
const TSourceLoc &loc)
{
- ASSERT(op == EOpReturn);
- mFunctionReturnsValue = true;
- if (mCurrentFunctionType->getBasicType() == EbtVoid)
+ if (expression != nullptr)
{
- error(loc, "void function cannot return a value", "return");
- recover();
+ ASSERT(op == EOpReturn);
+ mFunctionReturnsValue = true;
+ if (mCurrentFunctionType->getBasicType() == EbtVoid)
+ {
+ error(loc, "void function cannot return a value", "return");
+ }
+ else if (*mCurrentFunctionType != expression->getType())
+ {
+ error(loc, "function return is not matching type:", "return");
+ }
}
- else if (*mCurrentFunctionType != returnValue->getType())
- {
- error(loc, "function return is not matching type:", "return");
- recover();
+ TIntermBranch *node = new TIntermBranch(op, expression);
+ node->setLine(loc);
+ return node;
+}
+
+void TParseContext::checkTextureGather(TIntermAggregate *functionCall)
+{
+ ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
+ const TString &name = functionCall->getFunctionSymbolInfo()->getName();
+ bool isTextureGather = (name == "textureGather");
+ bool isTextureGatherOffset = (name == "textureGatherOffset");
+ if (isTextureGather || isTextureGatherOffset)
+ {
+ TIntermNode *componentNode = nullptr;
+ TIntermSequence *arguments = functionCall->getSequence();
+ ASSERT(arguments->size() >= 2u && arguments->size() <= 4u);
+ const TIntermTyped *sampler = arguments->front()->getAsTyped();
+ ASSERT(sampler != nullptr);
+ switch (sampler->getBasicType())
+ {
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ if ((isTextureGather && arguments->size() == 3u) ||
+ (isTextureGatherOffset && arguments->size() == 4u))
+ {
+ componentNode = arguments->back();
+ }
+ break;
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ ASSERT(!isTextureGatherOffset);
+ if (arguments->size() == 3u)
+ {
+ componentNode = arguments->back();
+ }
+ break;
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ case EbtSamplerCubeShadow:
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ if (componentNode)
+ {
+ const TIntermConstantUnion *componentConstantUnion =
+ componentNode->getAsConstantUnion();
+ if (componentNode->getAsTyped()->getQualifier() != EvqConst || !componentConstantUnion)
+ {
+ error(functionCall->getLine(), "Texture component must be a constant expression",
+ name.c_str());
+ }
+ else
+ {
+ int component = componentConstantUnion->getIConst(0);
+ if (component < 0 || component > 3)
+ {
+ error(functionCall->getLine(), "Component must be in the range [0;3]",
+ name.c_str());
+ }
+ }
+ }
}
- return intermediate.addBranch(op, returnValue, loc);
}
void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall)
{
- ASSERT(!functionCall->isUserDefined());
- const TString &name = functionCall->getName();
+ ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
+ const TString &name = functionCall->getFunctionSymbolInfo()->getName();
TIntermNode *offset = nullptr;
TIntermSequence *arguments = functionCall->getSequence();
- if (name.compare(0, 16, "texelFetchOffset") == 0 ||
- name.compare(0, 16, "textureLodOffset") == 0 ||
- name.compare(0, 20, "textureProjLodOffset") == 0 ||
- name.compare(0, 17, "textureGradOffset") == 0 ||
- name.compare(0, 21, "textureProjGradOffset") == 0)
+ bool useTextureGatherOffsetConstraints = false;
+ if (name == "texelFetchOffset" || name == "textureLodOffset" ||
+ name == "textureProjLodOffset" || name == "textureGradOffset" ||
+ name == "textureProjGradOffset")
{
offset = arguments->back();
}
- else if (name.compare(0, 13, "textureOffset") == 0 ||
- name.compare(0, 17, "textureProjOffset") == 0)
+ else if (name == "textureOffset" || name == "textureProjOffset")
{
// A bias parameter might follow the offset parameter.
ASSERT(arguments->size() >= 3);
offset = (*arguments)[2];
}
+ else if (name == "textureGatherOffset")
+ {
+ ASSERT(arguments->size() >= 3u);
+ const TIntermTyped *sampler = arguments->front()->getAsTyped();
+ ASSERT(sampler != nullptr);
+ switch (sampler->getBasicType())
+ {
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ offset = (*arguments)[2];
+ break;
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ offset = (*arguments)[3];
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ useTextureGatherOffsetConstraints = true;
+ }
if (offset != nullptr)
{
TIntermConstantUnion *offsetConstantUnion = offset->getAsConstantUnion();
if (offset->getAsTyped()->getQualifier() != EvqConst || !offsetConstantUnion)
{
- TString unmangledName = TFunction::unmangleName(name);
error(functionCall->getLine(), "Texture offset must be a constant expression",
- unmangledName.c_str());
- recover();
+ name.c_str());
}
else
{
ASSERT(offsetConstantUnion->getBasicType() == EbtInt);
size_t size = offsetConstantUnion->getType().getObjectSize();
const TConstantUnion *values = offsetConstantUnion->getUnionArrayPointer();
+ int minOffsetValue = useTextureGatherOffsetConstraints ? mMinProgramTextureGatherOffset
+ : mMinProgramTexelOffset;
+ int maxOffsetValue = useTextureGatherOffsetConstraints ? mMaxProgramTextureGatherOffset
+ : mMaxProgramTexelOffset;
for (size_t i = 0u; i < size; ++i)
{
int offsetValue = values[i].getIConst();
- if (offsetValue > mMaxProgramTexelOffset || offsetValue < mMinProgramTexelOffset)
+ if (offsetValue > maxOffsetValue || offsetValue < minOffsetValue)
{
std::stringstream tokenStream;
tokenStream << offsetValue;
std::string token = tokenStream.str();
error(offset->getLine(), "Texture offset value out of valid range",
token.c_str());
- recover();
}
}
}
}
}
-TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
- TIntermNode *paramNode,
- TIntermNode *thisNode,
- const TSourceLoc &loc,
- bool *fatalError)
+void TParseContext::checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall)
{
- *fatalError = false;
- TOperator op = fnCall->getBuiltInOp();
- TIntermTyped *callNode = nullptr;
-
- if (thisNode != nullptr)
+ const TString &name = functionCall->getFunctionSymbolInfo()->getName();
+ if (IsAtomicBuiltin(name))
{
- TConstantUnion *unionArray = new TConstantUnion[1];
- int arraySize = 0;
- TIntermTyped *typedThis = thisNode->getAsTyped();
- if (fnCall->getName() != "length")
+ TIntermSequence *arguments = functionCall->getSequence();
+ TIntermTyped *memNode = (*arguments)[0]->getAsTyped();
+
+ if (IsBufferOrSharedVariable(memNode))
{
- error(loc, "invalid method", fnCall->getName().c_str());
- recover();
+ return;
}
- else if (paramNode != nullptr)
+
+ while (memNode->getAsBinaryNode())
{
- error(loc, "method takes no parameters", "length");
- recover();
+ memNode = memNode->getAsBinaryNode()->getLeft();
+ if (IsBufferOrSharedVariable(memNode))
+ {
+ return;
+ }
}
- else if (typedThis == nullptr || !typedThis->isArray())
+
+ error(memNode->getLine(),
+ "The value passed to the mem argument of an atomic memory function does not "
+ "correspond to a buffer or shared variable.",
+ functionCall->getFunctionSymbolInfo()->getName().c_str());
+ }
+}
+
+// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall)
+{
+ ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
+ const TString &name = functionCall->getFunctionSymbolInfo()->getName();
+
+ if (name.compare(0, 5, "image") == 0)
+ {
+ TIntermSequence *arguments = functionCall->getSequence();
+ TIntermTyped *imageNode = (*arguments)[0]->getAsTyped();
+
+ const TMemoryQualifier &memoryQualifier = imageNode->getMemoryQualifier();
+
+ if (name.compare(5, 5, "Store") == 0)
{
- error(loc, "length can only be called on arrays", "length");
- recover();
+ if (memoryQualifier.readonly)
+ {
+ error(imageNode->getLine(),
+ "'imageStore' cannot be used with images qualified as 'readonly'",
+ GetImageArgumentToken(imageNode));
+ }
}
- else
+ else if (name.compare(5, 4, "Load") == 0)
{
- arraySize = typedThis->getArraySize();
- if (typedThis->getAsSymbolNode() == nullptr)
+ if (memoryQualifier.writeonly)
{
- // This code path can be hit with expressions like these:
- // (a = b).length()
- // (func()).length()
- // (int[3](0, 1, 2)).length()
- // ESSL 3.00 section 5.9 defines expressions so that this is not actually a valid
- // expression.
- // It allows "An array name with the length method applied" in contrast to GLSL 4.4
- // spec section 5.9 which allows "An array, vector or matrix expression with the
- // length method applied".
- error(loc, "length can only be called on array names, not on array expressions",
- "length");
- recover();
+ error(imageNode->getLine(),
+ "'imageLoad' cannot be used with images qualified as 'writeonly'",
+ GetImageArgumentToken(imageNode));
}
}
- unionArray->setIConst(arraySize);
- callNode =
- intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), loc);
}
- else if (op != EOpNull)
+}
+
+// GLSL ES 3.10 Revision 4, 13.51 Matching of Memory Qualifiers in Function Parameters
+void TParseContext::checkImageMemoryAccessForUserDefinedFunctions(
+ const TFunction *functionDefinition,
+ const TIntermAggregate *functionCall)
+{
+ ASSERT(functionCall->getOp() == EOpCallFunctionInAST);
+
+ const TIntermSequence &arguments = *functionCall->getSequence();
+
+ ASSERT(functionDefinition->getParamCount() == arguments.size());
+
+ for (size_t i = 0; i < arguments.size(); ++i)
{
- //
- // Then this should be a constructor.
- // Don't go through the symbol table for constructors.
- // Their parameters will be verified algorithmically.
- //
- TType type(EbtVoid, EbpUndefined); // use this to get the type back
- if (!constructorErrorCheck(loc, paramNode, *fnCall, op, &type))
- {
- //
- // It's a constructor, of type 'type'.
- //
- callNode = addConstructor(paramNode, &type, op, fnCall, loc);
- }
+ TIntermTyped *typedArgument = arguments[i]->getAsTyped();
+ const TType &functionArgumentType = typedArgument->getType();
+ const TType &functionParameterType = *functionDefinition->getParam(i).type;
+ ASSERT(functionArgumentType.getBasicType() == functionParameterType.getBasicType());
- if (callNode == nullptr)
+ if (IsImage(functionArgumentType.getBasicType()))
{
- recover();
- callNode = intermediate.setAggregateOperator(nullptr, op, loc);
+ const TMemoryQualifier &functionArgumentMemoryQualifier =
+ functionArgumentType.getMemoryQualifier();
+ const TMemoryQualifier &functionParameterMemoryQualifier =
+ functionParameterType.getMemoryQualifier();
+ if (functionArgumentMemoryQualifier.readonly &&
+ !functionParameterMemoryQualifier.readonly)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'readonly' qualifier from image",
+ GetImageArgumentToken(typedArgument));
+ }
+
+ if (functionArgumentMemoryQualifier.writeonly &&
+ !functionParameterMemoryQualifier.writeonly)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'writeonly' qualifier from image",
+ GetImageArgumentToken(typedArgument));
+ }
+
+ if (functionArgumentMemoryQualifier.coherent &&
+ !functionParameterMemoryQualifier.coherent)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'coherent' qualifier from image",
+ GetImageArgumentToken(typedArgument));
+ }
+
+ if (functionArgumentMemoryQualifier.volatileQualifier &&
+ !functionParameterMemoryQualifier.volatileQualifier)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'volatile' qualifier from image",
+ GetImageArgumentToken(typedArgument));
+ }
}
- callNode->setType(type);
+ }
+}
+
+TIntermSequence *TParseContext::createEmptyArgumentsList()
+{
+ return new TIntermSequence();
+}
+
+TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
+ TIntermSequence *arguments,
+ TIntermNode *thisNode,
+ const TSourceLoc &loc)
+{
+ if (thisNode != nullptr)
+ {
+ return addMethod(fnCall, arguments, thisNode, loc);
+ }
+
+ TOperator op = fnCall->getBuiltInOp();
+ if (op == EOpConstruct)
+ {
+ return addConstructor(arguments, fnCall->getReturnType(), loc);
}
else
{
- //
- // Not a constructor. Find it in the symbol table.
- //
- const TFunction *fnCandidate;
- bool builtIn;
- fnCandidate = findFunction(loc, fnCall, mShaderVersion, &builtIn);
- if (fnCandidate)
+ ASSERT(op == EOpNull);
+ return addNonConstructorFunctionCall(fnCall, arguments, loc);
+ }
+}
+
+TIntermTyped *TParseContext::addMethod(TFunction *fnCall,
+ TIntermSequence *arguments,
+ TIntermNode *thisNode,
+ const TSourceLoc &loc)
+{
+ TIntermTyped *typedThis = thisNode->getAsTyped();
+ // It's possible for the name pointer in the TFunction to be null in case it gets parsed as
+ // a constructor. But such a TFunction can't reach here, since the lexer goes into FIELDS
+ // mode after a dot, which makes type identifiers to be parsed as FIELD_SELECTION instead.
+ // So accessing fnCall->getName() below is safe.
+ if (fnCall->getName() != "length")
+ {
+ error(loc, "invalid method", fnCall->getName().c_str());
+ }
+ else if (!arguments->empty())
+ {
+ error(loc, "method takes no parameters", "length");
+ }
+ else if (typedThis == nullptr || !typedThis->isArray())
+ {
+ error(loc, "length can only be called on arrays", "length");
+ }
+ else if (typedThis->getQualifier() == EvqPerVertexIn &&
+ mGeometryShaderInputPrimitiveType == EptUndefined)
+ {
+ ASSERT(mShaderType == GL_GEOMETRY_SHADER_OES);
+ error(loc, "missing input primitive declaration before calling length on gl_in", "length");
+ }
+ else
+ {
+ TIntermUnary *node = new TIntermUnary(EOpArrayLength, typedThis);
+ node->setLine(loc);
+ return node->fold(mDiagnostics);
+ }
+ return CreateZeroNode(TType(EbtInt, EbpUndefined, EvqConst));
+}
+
+TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunction *fnCall,
+ TIntermSequence *arguments,
+ const TSourceLoc &loc)
+{
+ // First find by unmangled name to check whether the function name has been
+ // hidden by a variable name or struct typename.
+ // If a function is found, check for one with a matching argument list.
+ bool builtIn;
+ const TSymbol *symbol = symbolTable.find(fnCall->getName(), mShaderVersion, &builtIn);
+ if (symbol != nullptr && !symbol->isFunction())
+ {
+ error(loc, "function name expected", fnCall->getName().c_str());
+ }
+ else
+ {
+ symbol = symbolTable.find(TFunction::GetMangledNameFromCall(fnCall->getName(), *arguments),
+ mShaderVersion, &builtIn);
+ if (symbol == nullptr)
{
+ error(loc, "no matching overloaded function found", fnCall->getName().c_str());
+ }
+ else
+ {
+ const TFunction *fnCandidate = static_cast<const TFunction *>(symbol);
//
// A declared function.
//
- if (builtIn && !fnCandidate->getExtension().empty() &&
- extensionErrorCheck(loc, fnCandidate->getExtension()))
+ if (builtIn && fnCandidate->getExtension() != TExtension::UNDEFINED)
{
- recover();
+ checkCanUseExtension(loc, fnCandidate->getExtension());
}
- op = fnCandidate->getBuiltInOp();
+ TOperator op = fnCandidate->getBuiltInOp();
if (builtIn && op != EOpNull)
{
- //
// A function call mapped to a built-in operation.
- //
if (fnCandidate->getParamCount() == 1)
{
- //
// Treat it like a built-in unary operator.
- //
- TIntermAggregate *paramAgg = paramNode->getAsAggregate();
- paramNode = paramAgg->getSequence()->front();
- callNode = createUnaryMath(op, paramNode->getAsTyped(), loc,
- &fnCandidate->getReturnType());
- if (callNode == nullptr)
- {
- std::stringstream extraInfoStream;
- extraInfoStream
- << "built in unary operator function. Type: "
- << static_cast<TIntermTyped *>(paramNode)->getCompleteString();
- std::string extraInfo = extraInfoStream.str();
- error(paramNode->getLine(), " wrong operand type", "Internal Error",
- extraInfo.c_str());
- *fatalError = true;
- return nullptr;
- }
+ TIntermNode *unaryParamNode = arguments->front();
+ TIntermTyped *callNode = createUnaryMath(op, unaryParamNode->getAsTyped(), loc);
+ ASSERT(callNode != nullptr);
+ return callNode;
}
else
{
- TIntermAggregate *aggregate =
- intermediate.setAggregateOperator(paramNode, op, loc);
- aggregate->setType(fnCandidate->getReturnType());
- aggregate->setPrecisionFromChildren();
- if (aggregate->areChildrenConstQualified())
- {
- aggregate->getTypePointer()->setQualifier(EvqConst);
- }
+ TIntermAggregate *callNode =
+ TIntermAggregate::Create(fnCandidate->getReturnType(), op, arguments);
+ callNode->setLine(loc);
// Some built-in functions have out parameters too.
- functionCallLValueErrorCheck(fnCandidate, aggregate);
+ functionCallRValueLValueErrorCheck(fnCandidate, callNode);
- // See if we can constant fold a built-in. Note that this may be possible even
- // if it is not const-qualified.
- TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate);
- if (foldedNode)
+ if (TIntermAggregate::CanFoldAggregateBuiltInOp(callNode->getOp()))
{
- callNode = foldedNode;
+ // See if we can constant fold a built-in. Note that this may be possible
+ // even if it is not const-qualified.
+ return callNode->fold(mDiagnostics);
}
else
{
- callNode = aggregate;
+ return callNode;
}
}
}
else
{
// This is a real function call
- TIntermAggregate *aggregate =
- intermediate.setAggregateOperator(paramNode, EOpFunctionCall, loc);
- aggregate->setType(fnCandidate->getReturnType());
-
- // this is how we know whether the given function is a builtIn function or a user
- // defined function
- // if builtIn == false, it's a userDefined -> could be an overloaded
- // builtIn function also
- // if builtIn == true, it's definitely a builtIn function with EOpNull
- if (!builtIn)
- aggregate->setUserDefined();
- aggregate->setName(fnCandidate->getMangledName());
- aggregate->setFunctionId(fnCandidate->getUniqueId());
-
- // This needs to happen after the name is set
+ TIntermAggregate *callNode = nullptr;
+
+ // If builtIn == false, the function is user defined - could be an overloaded
+ // built-in as well.
+ // if builtIn == true, it's a builtIn function with no op associated with it.
+ // This needs to happen after the function info including name is set.
if (builtIn)
{
- aggregate->setBuiltInFunctionPrecision();
-
- checkTextureOffsetConst(aggregate);
+ callNode = TIntermAggregate::CreateBuiltInFunctionCall(*fnCandidate, arguments);
+ checkTextureOffsetConst(callNode);
+ checkTextureGather(callNode);
+ checkImageMemoryAccessForBuiltinFunctions(callNode);
+ checkAtomicMemoryBuiltinFunctions(callNode);
+ }
+ else
+ {
+ callNode = TIntermAggregate::CreateFunctionCall(*fnCandidate, arguments);
+ checkImageMemoryAccessForUserDefinedFunctions(fnCandidate, callNode);
}
- callNode = aggregate;
+ functionCallRValueLValueErrorCheck(fnCandidate, callNode);
- functionCallLValueErrorCheck(fnCandidate, aggregate);
+ callNode->setLine(loc);
+
+ return callNode;
}
}
- else
- {
- // error message was put out by findFunction()
- // Put on a dummy node for error recovery
- TConstantUnion *unionArray = new TConstantUnion[1];
- unionArray->setFConst(0.0f);
- callNode = intermediate.addConstantUnion(unionArray,
- TType(EbtFloat, EbpUndefined, EvqConst), loc);
- recover();
- }
}
- return callNode;
+
+ // Error message was already written. Put on a dummy node for error recovery.
+ return CreateZeroNode(TType(EbtFloat, EbpMedium, EvqConst));
}
TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond,
- TIntermTyped *trueBlock,
- TIntermTyped *falseBlock,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression,
const TSourceLoc &loc)
{
- if (boolErrorCheck(loc, cond))
- recover();
+ if (!checkIsScalarBool(loc, cond))
+ {
+ return falseExpression;
+ }
- if (trueBlock->getType() != falseBlock->getType())
+ if (trueExpression->getType() != falseExpression->getType())
{
- binaryOpError(loc, ":", trueBlock->getCompleteString(), falseBlock->getCompleteString());
- recover();
- return falseBlock;
+ std::stringstream reasonStream;
+ reasonStream << "mismatching ternary operator operand types '"
+ << trueExpression->getCompleteString() << " and '"
+ << falseExpression->getCompleteString() << "'";
+ std::string reason = reasonStream.str();
+ error(loc, reason.c_str(), "?:");
+ return falseExpression;
+ }
+ if (IsOpaqueType(trueExpression->getBasicType()))
+ {
+ // ESSL 1.00 section 4.1.7
+ // ESSL 3.00.6 section 4.1.7
+ // Opaque/sampler types are not allowed in most types of expressions, including ternary.
+ // Note that structs containing opaque types don't need to be checked as structs are
+ // forbidden below.
+ error(loc, "ternary operator is not allowed for opaque types", "?:");
+ return falseExpression;
+ }
+
+ if (cond->getMemoryQualifier().writeonly || trueExpression->getMemoryQualifier().writeonly ||
+ falseExpression->getMemoryQualifier().writeonly)
+ {
+ error(loc, "ternary operator is not allowed for variables with writeonly", "?:");
+ return falseExpression;
}
- // ESSL1 sections 5.2 and 5.7:
- // ESSL3 section 5.7:
+
+ // ESSL 1.00.17 sections 5.2 and 5.7:
// Ternary operator is not among the operators allowed for structures/arrays.
- if (trueBlock->isArray() || trueBlock->getBasicType() == EbtStruct)
+ // ESSL 3.00.6 section 5.7:
+ // Ternary operator support is optional for arrays. No certainty that it works across all
+ // devices with struct either, so we err on the side of caution here. TODO (oetuaho@nvidia.com):
+ // Would be nice to make the spec and implementation agree completely here.
+ if (trueExpression->isArray() || trueExpression->getBasicType() == EbtStruct)
{
- error(loc, "ternary operator is not allowed for structures or arrays", ":");
- recover();
- return falseBlock;
+ error(loc, "ternary operator is not allowed for structures or arrays", "?:");
+ return falseExpression;
}
- return intermediate.addSelection(cond, trueBlock, falseBlock, loc);
+ if (trueExpression->getBasicType() == EbtInterfaceBlock)
+ {
+ error(loc, "ternary operator is not allowed for interface blocks", "?:");
+ return falseExpression;
+ }
+
+ // WebGL2 section 5.26, the following results in an error:
+ // "Ternary operator applied to void, arrays, or structs containing arrays"
+ if (mShaderSpec == SH_WEBGL2_SPEC && trueExpression->getBasicType() == EbtVoid)
+ {
+ error(loc, "ternary operator is not allowed for void", "?:");
+ return falseExpression;
+ }
+
+ // Note that the node resulting from here can be a constant union without being qualified as
+ // constant.
+ TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression);
+ node->setLine(loc);
+
+ return node->fold();
}
//
@@ -4128,7 +6004,7 @@ int PaParseStrings(size_t count,
const int length[],
TParseContext *context)
{
- if ((count == 0) || (string == NULL))
+ if ((count == 0) || (string == nullptr))
return 1;
if (glslang_initialize(context))
@@ -4142,3 +6018,5 @@ int PaParseStrings(size_t count,
return (error == 0) && (context->numErrors() == 0) ? 0 : 1;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ParseContext.h b/src/3rdparty/angle/src/compiler/translator/ParseContext.h
index 1eaf1e5b42..8bfdbd5e3f 100644
--- a/src/3rdparty/angle/src/compiler/translator/ParseContext.h
+++ b/src/3rdparty/angle/src/compiler/translator/ParseContext.h
@@ -9,10 +9,13 @@
#include "compiler/translator/Compiler.h"
#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/DirectiveHandler.h"
-#include "compiler/translator/Intermediate.h"
#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/QualifierTypes.h"
#include "compiler/preprocessor/Preprocessor.h"
+namespace sh
+{
+
struct TMatrixFields
{
bool wholeRow;
@@ -30,44 +33,13 @@ class TParseContext : angle::NonCopyable
public:
TParseContext(TSymbolTable &symt,
TExtensionBehavior &ext,
- TIntermediate &interm,
sh::GLenum type,
ShShaderSpec spec,
- int options,
+ ShCompileOptions options,
bool checksPrecErrors,
- TInfoSink &is,
- const ShBuiltInResources &resources)
- : intermediate(interm),
- symbolTable(symt),
- mDeferredSingleDeclarationErrorCheck(false),
- mShaderType(type),
- mShaderSpec(spec),
- mShaderVersion(100),
- mTreeRoot(nullptr),
- mLoopNestingLevel(0),
- mStructNestingLevel(0),
- mSwitchNestingLevel(0),
- mCurrentFunctionType(nullptr),
- mFunctionReturnsValue(false),
- mChecksPrecisionErrors(checksPrecErrors),
- mFragmentPrecisionHighOnESSL1(false),
- mDefaultMatrixPacking(EmpColumnMajor),
- mDefaultBlockStorage(EbsShared),
- mDiagnostics(is),
- mDirectiveHandler(ext,
- mDiagnostics,
- mShaderVersion,
- mShaderType,
- resources.WEBGL_debug_shader_precision == 1),
- mPreprocessor(&mDiagnostics, &mDirectiveHandler),
- mScanner(nullptr),
- mUsesFragData(false),
- mUsesFragColor(false),
- mUsesSecondaryOutputs(false),
- mMinProgramTexelOffset(resources.MinProgramTexelOffset),
- mMaxProgramTexelOffset(resources.MaxProgramTexelOffset)
- {
- }
+ TDiagnostics *diagnostics,
+ const ShBuiltInResources &resources);
+ ~TParseContext();
const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
pp::Preprocessor &getPreprocessor() { return mPreprocessor; }
@@ -76,23 +48,18 @@ class TParseContext : angle::NonCopyable
int getShaderVersion() const { return mShaderVersion; }
sh::GLenum getShaderType() const { return mShaderType; }
ShShaderSpec getShaderSpec() const { return mShaderSpec; }
- int numErrors() const { return mDiagnostics.numErrors(); }
- TInfoSink &infoSink() { return mDiagnostics.infoSink(); }
- void error(const TSourceLoc &loc, const char *reason, const char *token,
- const char *extraInfo="");
- void warning(const TSourceLoc &loc, const char *reason, const char *token,
- const char *extraInfo="");
+ int numErrors() const { return mDiagnostics->numErrors(); }
+ void error(const TSourceLoc &loc, const char *reason, const char *token);
+ void warning(const TSourceLoc &loc, const char *reason, const char *token);
// If isError is false, a warning will be reported instead.
void outOfRangeError(bool isError,
const TSourceLoc &loc,
const char *reason,
- const char *token,
- const char *extraInfo = "");
+ const char *token);
- void recover();
- TIntermNode *getTreeRoot() const { return mTreeRoot; }
- void setTreeRoot(TIntermNode *treeRoot) { mTreeRoot = treeRoot; }
+ TIntermBlock *getTreeRoot() const { return mTreeRoot; }
+ void setTreeRoot(TIntermBlock *treeRoot) { mTreeRoot = treeRoot; }
bool getFragmentPrecisionHigh() const
{
@@ -103,10 +70,7 @@ class TParseContext : angle::NonCopyable
mFragmentPrecisionHighOnESSL1 = fragmentPrecisionHigh;
}
- void setLoopNestingLevel(int loopNestintLevel)
- {
- mLoopNestingLevel = loopNestintLevel;
- }
+ void setLoopNestingLevel(int loopNestintLevel) { mLoopNestingLevel = loopNestintLevel; }
void incrLoopNestingLevel() { ++mLoopNestingLevel; }
void decrLoopNestingLevel() { --mLoopNestingLevel; }
@@ -114,279 +78,580 @@ class TParseContext : angle::NonCopyable
void incrSwitchNestingLevel() { ++mSwitchNestingLevel; }
void decrSwitchNestingLevel() { --mSwitchNestingLevel; }
+ bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+ sh::WorkGroupSize getComputeShaderLocalSize() const;
+
+ int getNumViews() const { return mNumViews; }
+
+ void enterFunctionDeclaration() { mDeclaringFunction = true; }
+
+ void exitFunctionDeclaration() { mDeclaringFunction = false; }
+
+ bool declaringFunction() const { return mDeclaringFunction; }
+
+ TIntermConstantUnion *addScalarLiteral(const TConstantUnion *constantUnion,
+ const TSourceLoc &line);
+
// This method is guaranteed to succeed, even if no variable with 'name' exists.
- const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
+ const TVariable *getNamedVariable(const TSourceLoc &location,
+ const TString *name,
+ const TSymbol *symbol);
TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
const TString *name,
const TSymbol *symbol);
- bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc &line);
+ // Look at a '.' field selector string and change it into offsets for a vector.
+ bool parseVectorFields(const TSourceLoc &line,
+ const TString &compString,
+ int vecSize,
+ TVector<int> *fieldOffsets);
- bool reservedErrorCheck(const TSourceLoc &line, const TString &identifier);
void assignError(const TSourceLoc &line, const char *op, TString left, TString right);
void unaryOpError(const TSourceLoc &line, const char *op, TString operand);
void binaryOpError(const TSourceLoc &line, const char *op, TString left, TString right);
- bool precisionErrorCheck(const TSourceLoc &line, TPrecision precision, TBasicType type);
- bool lValueErrorCheck(const TSourceLoc &line, const char *op, TIntermTyped*);
- bool constErrorCheck(TIntermTyped *node);
- bool integerErrorCheck(TIntermTyped *node, const char *token);
- bool globalErrorCheck(const TSourceLoc &line, bool global, const char *token);
- bool constructorErrorCheck(const TSourceLoc &line,
- TIntermNode *argumentsNode,
- TFunction &function,
- TOperator op,
- TType *type);
- bool arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *expr, int &size);
- bool arrayQualifierErrorCheck(const TSourceLoc &line, const TPublicType &type);
- bool arrayTypeErrorCheck(const TSourceLoc &line, const TPublicType &type);
- bool voidErrorCheck(const TSourceLoc &line, const TString &identifier, const TBasicType &type);
- bool boolErrorCheck(const TSourceLoc&, const TIntermTyped*);
- bool boolErrorCheck(const TSourceLoc&, const TPublicType&);
- bool samplerErrorCheck(const TSourceLoc &line, const TPublicType &pType, const char *reason);
- bool locationDeclaratorListCheck(const TSourceLoc &line, const TPublicType &pType);
- bool parameterSamplerErrorCheck(const TSourceLoc &line, TQualifier qualifier, const TType &type);
- bool paramErrorCheck(const TSourceLoc &line, TQualifier qualifier, TQualifier paramQualifier, TType *type);
- bool extensionErrorCheck(const TSourceLoc &line, const TString&);
- bool singleDeclarationErrorCheck(const TPublicType &publicType, const TSourceLoc &identifierLocation);
- bool layoutLocationErrorCheck(const TSourceLoc &location, const TLayoutQualifier &layoutQualifier);
- bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *);
- void es3InvariantErrorCheck(const TQualifier qualifier, const TSourceLoc &invariantLocation);
- void es3InputOutputTypeCheck(const TQualifier qualifier,
- const TPublicType &type,
- const TSourceLoc &qualifierLocation);
+ // Check functions - the ones that return bool return false if an error was generated.
+
+ bool checkIsNotReserved(const TSourceLoc &line, const TString &identifier);
+ void checkPrecisionSpecified(const TSourceLoc &line, TPrecision precision, TBasicType type);
+ bool checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node);
+ void checkIsConst(TIntermTyped *node);
+ void checkIsScalarInteger(TIntermTyped *node, const char *token);
+ bool checkIsAtGlobalLevel(const TSourceLoc &line, const char *token);
+ bool checkConstructorArguments(const TSourceLoc &line,
+ const TIntermSequence *arguments,
+ const TType &type);
+
+ // Returns a sanitized array size to use (the size is at least 1).
+ unsigned int checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr);
+ bool checkIsValidQualifierForArray(const TSourceLoc &line, const TPublicType &elementQualifier);
+ bool checkArrayElementIsNotArray(const TSourceLoc &line, const TPublicType &elementType);
+ bool checkIsNonVoid(const TSourceLoc &line, const TString &identifier, const TBasicType &type);
+ bool checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type);
+ void checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType);
+ bool checkIsNotOpaqueType(const TSourceLoc &line,
+ const TTypeSpecifierNonArray &pType,
+ const char *reason);
+ void checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line, const TPublicType &pType);
+ void checkLocationIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier);
+ void checkStd430IsForShaderStorageBlock(const TSourceLoc &location,
+ const TLayoutBlockStorage &blockStorage,
+ const TQualifier &qualifier);
+ void checkIsParameterQualifierValid(const TSourceLoc &line,
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TType *type);
+
+ // Check if at least one of the specified extensions can be used, and generate error/warning as
+ // appropriate according to the spec.
+ // This function is only needed for a few different small constant sizes of extension array, and
+ // we want to avoid unnecessary dynamic allocations. That's why checkCanUseOneOfExtensions is a
+ // template function rather than one taking a vector.
+ template <size_t size>
+ bool checkCanUseOneOfExtensions(const TSourceLoc &line,
+ const std::array<TExtension, size> &extensions);
+ bool checkCanUseExtension(const TSourceLoc &line, TExtension extension);
+
+ // Done for all declarations, whether empty or not.
+ void declarationQualifierErrorCheck(const sh::TQualifier qualifier,
+ const sh::TLayoutQualifier &layoutQualifier,
+ const TSourceLoc &location);
+ // Done for the first non-empty declarator in a declaration.
+ void nonEmptyDeclarationErrorCheck(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation);
+ // Done only for empty declarations.
+ void emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location);
+
+ void checkLayoutQualifierSupported(const TSourceLoc &location,
+ const TString &layoutQualifierName,
+ int versionRequired);
+ bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier);
+ void functionCallRValueLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall);
+ void checkInvariantVariableQualifier(bool invariant,
+ const TQualifier qualifier,
+ const TSourceLoc &invariantLocation);
+ void checkInputOutputTypeIsValidES3(const TQualifier qualifier,
+ const TPublicType &type,
+ const TSourceLoc &qualifierLocation);
+ void checkLocalVariableConstStorageQualifier(const TQualifierWrapperBase &qualifier);
const TPragma &pragma() const { return mDirectiveHandler.pragma(); }
- const TExtensionBehavior &extensionBehavior() const { return mDirectiveHandler.extensionBehavior(); }
- bool supportsExtension(const char *extension);
- bool isExtensionEnabled(const char *extension) const;
+ const TExtensionBehavior &extensionBehavior() const
+ {
+ return mDirectiveHandler.extensionBehavior();
+ }
+
+ bool isExtensionEnabled(TExtension extension) const;
void handleExtensionDirective(const TSourceLoc &loc, const char *extName, const char *behavior);
- void handlePragmaDirective(const TSourceLoc &loc, const char *name, const char *value, bool stdgl);
+ void handlePragmaDirective(const TSourceLoc &loc,
+ const char *name,
+ const char *value,
+ bool stdgl);
- bool containsSampler(const TType &type);
- const TFunction* findFunction(
- const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0);
+ // Returns true on success. *initNode may still be nullptr on success in case the initialization
+ // is not needed in the AST.
bool executeInitializer(const TSourceLoc &line,
const TString &identifier,
- const TPublicType &pType,
+ TType type,
TIntermTyped *initializer,
- TIntermNode **intermNode);
-
- TPublicType addFullySpecifiedType(TQualifier qualifier,
- bool invariant,
- TLayoutQualifier layoutQualifier,
+ TIntermBinary **initNode);
+ TIntermNode *addConditionInitializer(const TPublicType &pType,
+ const TString &identifier,
+ TIntermTyped *initializer,
+ const TSourceLoc &loc);
+ TIntermNode *addLoop(TLoopType type,
+ TIntermNode *init,
+ TIntermNode *cond,
+ TIntermTyped *expr,
+ TIntermNode *body,
+ const TSourceLoc &loc);
+
+ // For "if" test nodes. There are three children: a condition, a true path, and a false path.
+ // The two paths are in TIntermNodePair code.
+ TIntermNode *addIfElse(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &loc);
+
+ void addFullySpecifiedType(TPublicType *typeSpecifier);
+ TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
const TPublicType &typeSpecifier);
- TIntermAggregate *parseSingleDeclaration(TPublicType &publicType,
- const TSourceLoc &identifierOrTypeLocation,
- const TString &identifier);
- TIntermAggregate *parseSingleArrayDeclaration(TPublicType &publicType,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &indexLocation,
- TIntermTyped *indexExpression);
- TIntermAggregate *parseSingleInitDeclaration(const TPublicType &publicType,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &initLocation,
- TIntermTyped *initializer);
+ TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType,
+ const TSourceLoc &identifierOrTypeLocation,
+ const TString &identifier);
+ TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &elementType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ const TVector<unsigned int> &arraySizes);
+ TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer);
// Parse a declaration like "type a[n] = initializer"
// Note that this does not apply to declarations like "type[n] a = initializer"
- TIntermAggregate *parseSingleArrayInitDeclaration(TPublicType &publicType,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &indexLocation,
- TIntermTyped *indexExpression,
- const TSourceLoc &initLocation,
- TIntermTyped *initializer);
-
- TIntermAggregate *parseInvariantDeclaration(const TSourceLoc &invariantLoc,
- const TSourceLoc &identifierLoc,
- const TString *identifier,
- const TSymbol *symbol);
-
- TIntermAggregate *parseDeclarator(TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier);
- TIntermAggregate *parseArrayDeclarator(TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &arrayLocation,
- TIntermTyped *indexExpression);
- TIntermAggregate *parseInitDeclarator(const TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &initLocation,
- TIntermTyped *initializer);
+ TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &elementType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ const TVector<unsigned int> &arraySizes,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer);
+
+ TIntermInvariantDeclaration *parseInvariantDeclaration(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &identifierLoc,
+ const TString *identifier,
+ const TSymbol *symbol);
+
+ void parseDeclarator(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ TIntermDeclaration *declarationOut);
+ void parseArrayDeclarator(TPublicType &elementType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &arrayLocation,
+ const TVector<unsigned int> &arraySizes,
+ TIntermDeclaration *declarationOut);
+ void parseInitDeclarator(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut);
// Parse a declarator like "a[n] = initializer"
- TIntermAggregate *parseArrayInitDeclarator(const TPublicType &publicType,
- TIntermAggregate *aggregateDeclaration,
- const TSourceLoc &identifierLocation,
- const TString &identifier,
- const TSourceLoc &indexLocation,
- TIntermTyped *indexExpression,
- const TSourceLoc &initLocation,
- TIntermTyped *initializer);
-
- void parseGlobalLayoutQualifier(const TPublicType &typeQualifier);
- TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &function,
- const TSourceLoc &location);
- TIntermAggregate *addFunctionDefinition(const TFunction &function,
- TIntermAggregate *functionPrototype,
- TIntermAggregate *functionBody,
- const TSourceLoc &location);
- void parseFunctionPrototype(const TSourceLoc &location,
- TFunction *function,
- TIntermAggregate **aggregateOut);
- TFunction *parseFunctionDeclarator(const TSourceLoc &location,
- TFunction *function);
+ void parseArrayInitDeclarator(const TPublicType &elementType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ const TVector<unsigned int> &arraySizes,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut);
+
+ TIntermNode *addEmptyStatement(const TSourceLoc &location);
+
+ void parseDefaultPrecisionQualifier(const TPrecision precision,
+ const TPublicType &type,
+ const TSourceLoc &loc);
+ void parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder);
+
+ TIntermFunctionPrototype *addFunctionPrototypeDeclaration(const TFunction &parsedFunction,
+ const TSourceLoc &location);
+ TIntermFunctionDefinition *addFunctionDefinition(TIntermFunctionPrototype *functionPrototype,
+ TIntermBlock *functionBody,
+ const TSourceLoc &location);
+ void parseFunctionDefinitionHeader(const TSourceLoc &location,
+ TFunction **function,
+ TIntermFunctionPrototype **prototypeOut);
+ TFunction *parseFunctionDeclarator(const TSourceLoc &location, TFunction *function);
+ TFunction *parseFunctionHeader(const TPublicType &type,
+ const TString *name,
+ const TSourceLoc &location);
+ TFunction *addNonConstructorFunc(const TString *name, const TSourceLoc &loc);
TFunction *addConstructorFunc(const TPublicType &publicType);
- TIntermTyped *addConstructor(TIntermNode *arguments,
- TType *type,
- TOperator op,
- TFunction *fnCall,
- const TSourceLoc &line);
- TIntermTyped *addConstVectorNode(TVectorFields &fields,
- TIntermConstantUnion *node,
- const TSourceLoc &line,
- bool outOfRangeIndexIsError);
- TIntermTyped *addConstMatrixNode(int index,
- TIntermConstantUnion *node,
- const TSourceLoc &line,
- bool outOfRangeIndexIsError);
- TIntermTyped *addConstArrayNode(int index,
- TIntermConstantUnion *node,
- const TSourceLoc &line,
- bool outOfRangeIndexIsError);
- TIntermTyped *addConstStruct(
- const TString &identifier, TIntermTyped *node, const TSourceLoc& line);
+ TParameter parseParameterDeclarator(const TPublicType &publicType,
+ const TString *name,
+ const TSourceLoc &nameLoc);
+
+ TParameter parseParameterArrayDeclarator(const TString *name,
+ const TSourceLoc &nameLoc,
+ const TVector<unsigned int> &arraySizes,
+ const TSourceLoc &arrayLoc,
+ TPublicType *elementType);
+
TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
- const TSourceLoc& location,
+ const TSourceLoc &location,
TIntermTyped *indexExpression);
- TIntermTyped* addFieldSelectionExpression(TIntermTyped *baseExpression,
+ TIntermTyped *addFieldSelectionExpression(TIntermTyped *baseExpression,
const TSourceLoc &dotLocation,
const TString &fieldString,
const TSourceLoc &fieldLocation);
- TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList);
- TPublicType addStructure(const TSourceLoc &structLine,
- const TSourceLoc &nameLine,
- const TString *structName,
- TFieldList *fieldList);
+ // Parse declarator for a single field
+ TField *parseStructDeclarator(TString *identifier, const TSourceLoc &loc);
+ TField *parseStructArrayDeclarator(TString *identifier,
+ const TSourceLoc &loc,
+ const TVector<unsigned int> &arraySizes,
+ const TSourceLoc &arraySizeLoc);
- TIntermAggregate* addInterfaceBlock(const TPublicType &typeQualifier,
+ void checkDoesNotHaveDuplicateFieldName(const TFieldList::const_iterator begin,
+ const TFieldList::const_iterator end,
+ const TString &name,
+ const TSourceLoc &location);
+ TFieldList *addStructFieldList(TFieldList *fields, const TSourceLoc &location);
+ TFieldList *combineStructFieldLists(TFieldList *processedFields,
+ const TFieldList *newlyAddedFields,
+ const TSourceLoc &location);
+ TFieldList *addStructDeclaratorListWithQualifiers(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TPublicType *typeSpecifier,
+ TFieldList *fieldList);
+ TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList);
+ TTypeSpecifierNonArray addStructure(const TSourceLoc &structLine,
const TSourceLoc &nameLine,
- const TString &blockName,
- TFieldList *fieldList,
- const TString *instanceName,
- const TSourceLoc &instanceLine,
- TIntermTyped *arrayIndex,
- const TSourceLoc& arrayIndexLine);
-
- TLayoutQualifier parseLayoutQualifier(
- const TString &qualifierType, const TSourceLoc &qualifierTypeLine);
+ const TString *structName,
+ TFieldList *fieldList);
+
+ TIntermDeclaration *addInterfaceBlock(const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &nameLine,
+ const TString &blockName,
+ TFieldList *fieldList,
+ const TString *instanceName,
+ const TSourceLoc &instanceLine,
+ TIntermTyped *arrayIndex,
+ const TSourceLoc &arrayIndexLine);
+
+ void parseLocalSize(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ size_t index,
+ sh::WorkGroupSize *localSize);
+ void parseNumViews(int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ int *numViews);
+ void parseInvocations(int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ int *numInvocations);
+ void parseMaxVertices(int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ int *numMaxVertices);
+ TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine,
- const TString &intValueString,
int intValue,
const TSourceLoc &intValueLine);
- TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier rightQualifier);
- TPublicType joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TQualifier interpolationQualifier,
- const TSourceLoc &storageLoc, TQualifier storageQualifier);
+ TTypeQualifierBuilder *createTypeQualifierBuilder(const TSourceLoc &loc);
+ TStorageQualifierWrapper *parseGlobalStorageQualifier(TQualifier qualifier,
+ const TSourceLoc &loc);
+ TStorageQualifierWrapper *parseVaryingQualifier(const TSourceLoc &loc);
+ TStorageQualifierWrapper *parseInQualifier(const TSourceLoc &loc);
+ TStorageQualifierWrapper *parseOutQualifier(const TSourceLoc &loc);
+ TStorageQualifierWrapper *parseInOutQualifier(const TSourceLoc &loc);
+ TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation);
// Performs an error check for embedded struct declarations.
- // Returns true if an error was raised due to the declaration of
- // this struct.
- bool enterStructDeclaration(const TSourceLoc &line, const TString &identifier);
+ void enterStructDeclaration(const TSourceLoc &line, const TString &identifier);
void exitStructDeclaration();
- bool structNestingErrorCheck(const TSourceLoc &line, const TField &field);
+ void checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field);
- TIntermSwitch *addSwitch(TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &loc);
+ TIntermSwitch *addSwitch(TIntermTyped *init,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc);
TIntermCase *addCase(TIntermTyped *condition, const TSourceLoc &loc);
TIntermCase *addDefault(const TSourceLoc &loc);
TIntermTyped *addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
TIntermTyped *addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
- TIntermTyped *addBinaryMath(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
- TIntermTyped *addBinaryMathBooleanResult(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
- TIntermTyped *addAssign(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+ TIntermTyped *addBinaryMath(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc);
+ TIntermTyped *addBinaryMathBooleanResult(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc);
+ TIntermTyped *addAssign(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc);
TIntermTyped *addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
- TIntermBranch *addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc);
+ TIntermBranch *addBranch(TOperator op, TIntermTyped *expression, const TSourceLoc &loc);
+ void checkTextureGather(TIntermAggregate *functionCall);
void checkTextureOffsetConst(TIntermAggregate *functionCall);
+ void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
+ void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition,
+ const TIntermAggregate *functionCall);
+ void checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall);
+ TIntermSequence *createEmptyArgumentsList();
+
+ // fnCall is only storing the built-in op, and function name or constructor type. arguments
+ // has the arguments.
TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall,
- TIntermNode *paramNode,
+ TIntermSequence *arguments,
TIntermNode *thisNode,
- const TSourceLoc &loc,
- bool *fatalError);
+ const TSourceLoc &loc);
- TIntermTyped *addTernarySelection(
- TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &line);
+ TIntermTyped *addTernarySelection(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression,
+ const TSourceLoc &line);
- // TODO(jmadill): make these private
- TIntermediate &intermediate; // to hold and build a parse tree
+ int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
+ int getGeometryShaderInvocations() const
+ {
+ return (mGeometryShaderInvocations > 0) ? mGeometryShaderInvocations : 1;
+ }
+ TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
+ {
+ return mGeometryShaderInputPrimitiveType;
+ }
+ TLayoutPrimitiveType getGeometryShaderOutputPrimitiveType() const
+ {
+ return mGeometryShaderOutputPrimitiveType;
+ }
+
+ // TODO(jmadill): make this private
TSymbolTable &symbolTable; // symbol table that goes with the language currently being parsed
private:
- bool declareVariable(const TSourceLoc &line, const TString &identifier, const TType &type, TVariable **variable);
-
- bool nonInitErrorCheck(const TSourceLoc &line, const TString &identifier, TPublicType *type);
-
- TIntermTyped *addBinaryMathInternal(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
- TIntermTyped *createAssign(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
- // The funcReturnType parameter is expected to be non-null when the operation is a built-in function.
- // It is expected to be null for other unary operators.
- TIntermTyped *createUnaryMath(
- TOperator op, TIntermTyped *child, const TSourceLoc &loc, const TType *funcReturnType);
+ class AtomicCounterBindingState;
+ constexpr static size_t kAtomicCounterSize = 4;
+ // UNIFORM_ARRAY_STRIDE for atomic counter arrays is an implementation-dependent value which
+ // can be queried after a program is linked according to ES 3.10 section 7.7.1. This is
+ // controversial with the offset inheritance as described in ESSL 3.10 section 4.4.6. Currently
+ // we treat it as always 4 in favour of the original interpretation in
+ // "ARB_shader_atomic_counters".
+ // TODO(jie.a.chen@intel.com): Double check this once the spec vagueness is resolved.
+ // Note that there may be tests in AtomicCounter_test that will need to be updated as well.
+ constexpr static size_t kAtomicCounterArrayStride = 4;
+
+ // Returns a clamped index. If it prints out an error message, the token is "[]".
+ int checkIndexLessThan(bool outOfRangeIndexIsError,
+ const TSourceLoc &location,
+ int index,
+ int arraySize,
+ const char *reason);
+
+ bool declareVariable(const TSourceLoc &line,
+ const TString &identifier,
+ const TType &type,
+ TVariable **variable);
+
+ void checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
+ const TString &identifier,
+ TType *type);
+
+ TParameter parseParameterDeclarator(TType *type,
+ const TString *name,
+ const TSourceLoc &nameLoc);
+
+ bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
+ const TPublicType &elementType);
+ // Done for all atomic counter declarations, whether empty or not.
+ void atomicCounterQualifierErrorCheck(const TPublicType &publicType,
+ const TSourceLoc &location);
+
+ // Assumes that multiplication op has already been set based on the types.
+ bool isMultiplicationTypeCombinationValid(TOperator op, const TType &left, const TType &right);
+
+ void checkOutParameterIsNotOpaqueType(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type);
+
+ void checkInternalFormatIsNotSpecified(const TSourceLoc &location,
+ TLayoutImageInternalFormat internalFormat);
+ void checkMemoryQualifierIsNotSpecified(const TMemoryQualifier &memoryQualifier,
+ const TSourceLoc &location);
+ void checkAtomicCounterOffsetDoesNotOverlap(bool forceAppend,
+ const TSourceLoc &loc,
+ TType *type);
+ void checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type);
+ void checkBindingIsNotSpecified(const TSourceLoc &location, int binding);
+ void checkOffsetIsNotSpecified(const TSourceLoc &location, int offset);
+ void checkImageBindingIsValid(const TSourceLoc &location,
+ int binding,
+ int arrayTotalElementCount);
+ void checkSamplerBindingIsValid(const TSourceLoc &location,
+ int binding,
+ int arrayTotalElementCount);
+ void checkBlockBindingIsValid(const TSourceLoc &location,
+ const TQualifier &qualifier,
+ int binding,
+ int arraySize);
+ void checkAtomicCounterBindingIsValid(const TSourceLoc &location, int binding);
+
+ void checkUniformLocationInRange(const TSourceLoc &location,
+ int objectLocationCount,
+ const TLayoutQualifier &layoutQualifier);
+
+ void checkYuvIsNotSpecified(const TSourceLoc &location, bool yuv);
+
+ bool checkUnsizedArrayConstructorArgumentDimensionality(TIntermSequence *arguments,
+ TType type,
+ const TSourceLoc &line);
+
+ // Will set the size of the outermost array according to geometry shader input layout.
+ void checkGeometryShaderInputAndSetArraySize(const TSourceLoc &location,
+ const char *token,
+ TType *type);
+
+ // Will size any unsized array type so unsized arrays won't need to be taken into account
+ // further along the line in parsing.
+ void checkIsNotUnsizedArray(const TSourceLoc &line,
+ const char *errorMessage,
+ const char *token,
+ TType *arrayType);
+
+ TIntermTyped *addBinaryMathInternal(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc);
+ TIntermBinary *createAssign(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc);
+ TIntermTyped *createUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
+
+ TIntermTyped *addMethod(TFunction *fnCall,
+ TIntermSequence *arguments,
+ TIntermNode *thisNode,
+ const TSourceLoc &loc);
+ TIntermTyped *addConstructor(TIntermSequence *arguments,
+ TType type,
+ const TSourceLoc &line);
+ TIntermTyped *addNonConstructorFunctionCall(TFunction *fnCall,
+ TIntermSequence *arguments,
+ const TSourceLoc &loc);
// Return true if the checks pass
- bool binaryOpCommonCheck(
- TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
-
- // Set to true when the last/current declarator list was started with an empty declaration.
- bool mDeferredSingleDeclarationErrorCheck;
-
- sh::GLenum mShaderType; // vertex or fragment language (future: pack or unpack)
- ShShaderSpec mShaderSpec; // The language specification compiler conforms to - GLES2 or WebGL.
+ bool binaryOpCommonCheck(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc);
+
+ TIntermFunctionPrototype *createPrototypeNodeFromFunction(const TFunction &function,
+ const TSourceLoc &location,
+ bool insertParametersToSymbolTable);
+
+ void setAtomicCounterBindingDefaultOffset(const TPublicType &declaration,
+ const TSourceLoc &location);
+
+ bool checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier);
+ bool parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);
+ bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
+ void setGeometryShaderInputArraySize(unsigned int inputArraySize, const TSourceLoc &line);
+
+ // Set to true when the last/current declarator list was started with an empty declaration. The
+ // non-empty declaration error check will need to be performed if the empty declaration is
+ // followed by a declarator.
+ bool mDeferredNonEmptyDeclarationErrorCheck;
+
+ sh::GLenum mShaderType; // vertex or fragment language (future: pack or unpack)
+ ShShaderSpec mShaderSpec; // The language specification compiler conforms to - GLES2 or WebGL.
+ ShCompileOptions mCompileOptions; // Options passed to TCompiler
int mShaderVersion;
- TIntermNode *mTreeRoot; // root of parse tree being created
- int mLoopNestingLevel; // 0 if outside all loops
- int mStructNestingLevel; // incremented while parsing a struct declaration
- int mSwitchNestingLevel; // 0 if outside all switch statements
- const TType *mCurrentFunctionType; // the return type of the function that's currently being parsed
- bool mFunctionReturnsValue; // true if a non-void function has a return
- bool mChecksPrecisionErrors; // true if an error will be generated when a variable is declared without precision, explicit or implicit.
+ TIntermBlock *mTreeRoot; // root of parse tree being created
+ int mLoopNestingLevel; // 0 if outside all loops
+ int mStructNestingLevel; // incremented while parsing a struct declaration
+ int mSwitchNestingLevel; // 0 if outside all switch statements
+ const TType
+ *mCurrentFunctionType; // the return type of the function that's currently being parsed
+ bool mFunctionReturnsValue; // true if a non-void function has a return
+ bool mChecksPrecisionErrors; // true if an error will be generated when a variable is declared
+ // without precision, explicit or implicit.
bool mFragmentPrecisionHighOnESSL1; // true if highp precision is supported when compiling
// ESSL1.
- TLayoutMatrixPacking mDefaultMatrixPacking;
- TLayoutBlockStorage mDefaultBlockStorage;
+ TLayoutMatrixPacking mDefaultUniformMatrixPacking;
+ TLayoutBlockStorage mDefaultUniformBlockStorage;
+ TLayoutMatrixPacking mDefaultBufferMatrixPacking;
+ TLayoutBlockStorage mDefaultBufferBlockStorage;
TString mHashErrMsg;
- TDiagnostics mDiagnostics;
+ TDiagnostics *mDiagnostics;
TDirectiveHandler mDirectiveHandler;
pp::Preprocessor mPreprocessor;
void *mScanner;
- bool mUsesFragData; // track if we are using both gl_FragData and gl_FragColor
+ bool mUsesFragData; // track if we are using both gl_FragData and gl_FragColor
bool mUsesFragColor;
bool mUsesSecondaryOutputs; // Track if we are using either gl_SecondaryFragData or
// gl_Secondary FragColor or both.
int mMinProgramTexelOffset;
int mMaxProgramTexelOffset;
+
+ int mMinProgramTextureGatherOffset;
+ int mMaxProgramTextureGatherOffset;
+
+ // keep track of local group size declared in layout. It should be declared only once.
+ bool mComputeShaderLocalSizeDeclared;
+ sh::WorkGroupSize mComputeShaderLocalSize;
+ // keep track of number of views declared in layout.
+ int mNumViews;
+ int mMaxNumViews;
+ int mMaxImageUnits;
+ int mMaxCombinedTextureImageUnits;
+ int mMaxUniformLocations;
+ int mMaxUniformBufferBindings;
+ int mMaxAtomicCounterBindings;
+ int mMaxShaderStorageBufferBindings;
+
+ // keeps track whether we are declaring / defining a function
+ bool mDeclaringFunction;
+
+ // Track the state of each atomic counter binding.
+ std::map<int, AtomicCounterBindingState> mAtomicCounterBindingStates;
+
+ // Track the geometry shader global parameters declared in layout.
+ TLayoutPrimitiveType mGeometryShaderInputPrimitiveType;
+ TLayoutPrimitiveType mGeometryShaderOutputPrimitiveType;
+ int mGeometryShaderInvocations;
+ int mGeometryShaderMaxVertices;
+ int mMaxGeometryShaderInvocations;
+ int mMaxGeometryShaderMaxVertices;
+
+ // Track if all input array sizes are same and matches the latter input primitive declaration.
+ unsigned int mGeometryShaderInputArraySize;
};
-int PaParseStrings(
- size_t count, const char *const string[], const int length[], TParseContext *context);
+int PaParseStrings(size_t count,
+ const char *const string[],
+ const int length[],
+ TParseContext *context);
+
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_
+#endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/PoolAlloc.cpp b/src/3rdparty/angle/src/compiler/translator/PoolAlloc.cpp
index 887cb66504..0f1cd8b5c9 100644
--- a/src/3rdparty/angle/src/compiler/translator/PoolAlloc.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/PoolAlloc.cpp
@@ -6,16 +6,16 @@
#include "compiler/translator/PoolAlloc.h"
-#include "compiler/translator/InitializeGlobals.h"
-
-#include "common/platform.h"
-#include "common/angleutils.h"
-#include "common/tls.h"
-
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
+#include "common/angleutils.h"
+#include "common/debug.h"
+#include "common/platform.h"
+#include "common/tls.h"
+#include "compiler/translator/InitializeGlobals.h"
+
TLSIndex PoolIndex = TLS_INVALID_INDEX;
bool InitializePoolIndex()
@@ -34,13 +34,13 @@ void FreePoolIndex()
PoolIndex = TLS_INVALID_INDEX;
}
-TPoolAllocator* GetGlobalPoolAllocator()
+TPoolAllocator *GetGlobalPoolAllocator()
{
assert(PoolIndex != TLS_INVALID_INDEX);
- return static_cast<TPoolAllocator*>(GetTLSValue(PoolIndex));
+ return static_cast<TPoolAllocator *>(GetTLSValue(PoolIndex));
}
-void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
+void SetGlobalPoolAllocator(TPoolAllocator *poolAllocator)
{
assert(PoolIndex != TLS_INVALID_INDEX);
SetTLSValue(PoolIndex, poolAllocator);
@@ -50,56 +50,66 @@ void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
// Implement the functionality of the TPoolAllocator class, which
// is documented in PoolAlloc.h.
//
-TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
- pageSize(growthIncrement),
- alignment(allocationAlignment),
- freeList(0),
- inUseList(0),
- numCalls(0),
- totalBytes(0)
+TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment)
+ : alignment(allocationAlignment),
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ pageSize(growthIncrement),
+ freeList(0),
+ inUseList(0),
+ numCalls(0),
+ totalBytes(0),
+#endif
+ mLocked(false)
{
//
- // Don't allow page sizes we know are smaller than all common
- // OS page sizes.
- //
- if (pageSize < 4*1024)
- pageSize = 4*1024;
-
- //
- // A large currentPageOffset indicates a new page needs to
- // be obtained to allocate memory.
- //
- currentPageOffset = pageSize;
-
- //
// Adjust alignment to be at least pointer aligned and
// power of 2.
//
- size_t minAlign = sizeof(void*);
+ size_t minAlign = sizeof(void *);
alignment &= ~(minAlign - 1);
if (alignment < minAlign)
alignment = minAlign;
- size_t a = 1;
+ size_t a = 1;
while (a < alignment)
a <<= 1;
- alignment = a;
+ alignment = a;
alignmentMask = a - 1;
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ //
+ // Don't allow page sizes we know are smaller than all common
+ // OS page sizes.
+ //
+ if (pageSize < 4 * 1024)
+ pageSize = 4 * 1024;
+
+ //
+ // A large currentPageOffset indicates a new page needs to
+ // be obtained to allocate memory.
+ //
+ currentPageOffset = pageSize;
+
//
// Align header skip
//
headerSkip = minAlign;
- if (headerSkip < sizeof(tHeader)) {
+ if (headerSkip < sizeof(tHeader))
+ {
headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
}
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ mStack.push_back({});
+#endif
}
TPoolAllocator::~TPoolAllocator()
{
- while (inUseList) {
- tHeader* next = inUseList->nextPage;
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ while (inUseList)
+ {
+ tHeader *next = inUseList->nextPage;
inUseList->~tHeader();
- delete [] reinterpret_cast<char*>(inUseList);
+ delete[] reinterpret_cast<char *>(inUseList);
inUseList = next;
}
@@ -107,11 +117,22 @@ TPoolAllocator::~TPoolAllocator()
// here, because we did it already when the block was
// placed into the free list.
//
- while (freeList) {
- tHeader* next = freeList->nextPage;
- delete [] reinterpret_cast<char*>(freeList);
+ while (freeList)
+ {
+ tHeader *next = freeList->nextPage;
+ delete[] reinterpret_cast<char *>(freeList);
freeList = next;
}
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ for (auto &allocs : mStack)
+ {
+ for (auto alloc : allocs)
+ {
+ free(alloc);
+ }
+ }
+ mStack.clear();
+#endif
}
// Support MSVC++ 6.0
@@ -120,28 +141,32 @@ const unsigned char TAllocation::guardBlockEndVal = 0xfe;
const unsigned char TAllocation::userDataFill = 0xcd;
#ifdef GUARD_BLOCKS
- const size_t TAllocation::guardBlockSize = 16;
+const size_t TAllocation::guardBlockSize = 16;
#else
- const size_t TAllocation::guardBlockSize = 0;
+const size_t TAllocation::guardBlockSize = 0;
#endif
//
// Check a single guard block for damage
//
-void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
+void TAllocation::checkGuardBlock(unsigned char *blockMem,
+ unsigned char val,
+ const char *locText) const
{
#ifdef GUARD_BLOCKS
- for (size_t x = 0; x < guardBlockSize; x++) {
- if (blockMem[x] != val) {
+ for (size_t x = 0; x < guardBlockSize; x++)
+ {
+ if (blockMem[x] != val)
+ {
char assertMsg[80];
- // We don't print the assert message. It's here just to be helpful.
+// We don't print the assert message. It's here just to be helpful.
#if defined(_MSC_VER)
- snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n",
- locText, size, data());
+ snprintf(assertMsg, sizeof(assertMsg),
+ "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n", locText, size, data());
#else
- snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
- locText, size, data());
+ snprintf(assertMsg, sizeof(assertMsg),
+ "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", locText, size, data());
#endif
assert(0 && "PoolAlloc: Damage in guard block");
}
@@ -149,17 +174,20 @@ void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, co
#endif
}
-
void TPoolAllocator::push()
{
- tAllocState state = { currentPageOffset, inUseList };
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ tAllocState state = {currentPageOffset, inUseList};
+
+ mStack.push_back(state);
- stack.push_back(state);
-
//
// Indicate there is no current page to allocate from.
//
currentPageOffset = pageSize;
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ mStack.push_back({});
+#endif
}
//
@@ -171,27 +199,37 @@ void TPoolAllocator::push()
//
void TPoolAllocator::pop()
{
- if (stack.size() < 1)
+ if (mStack.size() < 1)
return;
- tHeader* page = stack.back().page;
- currentPageOffset = stack.back().offset;
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ tHeader *page = mStack.back().page;
+ currentPageOffset = mStack.back().offset;
- while (inUseList != page) {
+ while (inUseList != page)
+ {
// invoke destructor to free allocation list
inUseList->~tHeader();
-
- tHeader* nextInUse = inUseList->nextPage;
+
+ tHeader *nextInUse = inUseList->nextPage;
if (inUseList->pageCount > 1)
- delete [] reinterpret_cast<char*>(inUseList);
- else {
+ delete[] reinterpret_cast<char *>(inUseList);
+ else
+ {
inUseList->nextPage = freeList;
- freeList = inUseList;
+ freeList = inUseList;
}
inUseList = nextInUse;
}
- stack.pop_back();
+ mStack.pop_back();
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ for (auto &alloc : mStack.back())
+ {
+ free(alloc);
+ }
+ mStack.pop_back();
+#endif
}
//
@@ -200,12 +238,15 @@ void TPoolAllocator::pop()
//
void TPoolAllocator::popAll()
{
- while (stack.size() > 0)
+ while (mStack.size() > 0)
pop();
}
-void* TPoolAllocator::allocate(size_t numBytes)
+void *TPoolAllocator::allocate(size_t numBytes)
{
+ ASSERT(!mLocked);
+
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
//
// Just keep some interesting statistics.
//
@@ -226,18 +267,20 @@ void* TPoolAllocator::allocate(size_t numBytes)
// Do the allocation, most likely case first, for efficiency.
// This step could be moved to be inline sometime.
//
- if (allocationSize <= pageSize - currentPageOffset) {
+ if (allocationSize <= pageSize - currentPageOffset)
+ {
//
// Safe to allocate from currentPageOffset.
//
- unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
+ unsigned char *memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
currentPageOffset += allocationSize;
currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
return initializeAllocation(inUseList, memory, numBytes);
}
- if (allocationSize > pageSize - headerSkip) {
+ if (allocationSize > pageSize - headerSkip)
+ {
//
// Do a multi-page allocation. Don't mix these with the others.
// The OS is efficient and allocating and free-ing multiple pages.
@@ -247,49 +290,71 @@ void* TPoolAllocator::allocate(size_t numBytes)
if (numBytesToAlloc < allocationSize)
return 0;
- tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
+ tHeader *memory = reinterpret_cast<tHeader *>(::new char[numBytesToAlloc]);
if (memory == 0)
return 0;
// Use placement-new to initialize header
- new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
+ new (memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
inUseList = memory;
currentPageOffset = pageSize; // make next allocation come from a new page
// No guard blocks for multi-page allocations (yet)
- return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
+ return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
}
//
// Need a simple page to allocate from.
//
- tHeader* memory;
- if (freeList) {
- memory = freeList;
+ tHeader *memory;
+ if (freeList)
+ {
+ memory = freeList;
freeList = freeList->nextPage;
- } else {
- memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
+ }
+ else
+ {
+ memory = reinterpret_cast<tHeader *>(::new char[pageSize]);
if (memory == 0)
return 0;
}
// Use placement-new to initialize header
- new(memory) tHeader(inUseList, 1);
+ new (memory) tHeader(inUseList, 1);
inUseList = memory;
-
- unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
- currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
+
+ unsigned char *ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
+ currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
return initializeAllocation(inUseList, ret, numBytes);
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ void *alloc = malloc(numBytes + alignmentMask);
+ mStack.back().push_back(alloc);
+
+ intptr_t intAlloc = reinterpret_cast<intptr_t>(alloc);
+ intAlloc = (intAlloc + alignmentMask) & ~alignmentMask;
+ return reinterpret_cast<void *>(intAlloc);
+#endif
+}
+
+void TPoolAllocator::lock()
+{
+ ASSERT(!mLocked);
+ mLocked = true;
}
+void TPoolAllocator::unlock()
+{
+ ASSERT(mLocked);
+ mLocked = false;
+}
//
// Check all allocations in a list for damage by calling check on each.
//
void TAllocation::checkAllocList() const
{
- for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
+ for (const TAllocation *alloc = this; alloc != 0; alloc = alloc->prevAlloc)
alloc->check();
}
diff --git a/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h b/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h
index dab2926c90..ad63bc4cd6 100644
--- a/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h
+++ b/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h
@@ -13,8 +13,8 @@
//
// This header defines an allocator that can be used to efficiently
-// allocate a large number of small requests for heap memory, with the
-// intention that they are not individually deallocated, but rather
+// allocate a large number of small requests for heap memory, with the
+// intention that they are not individually deallocated, but rather
// collectively deallocated at one time.
//
// This simultaneously
@@ -38,53 +38,58 @@
// If we are using guard blocks, we must track each indivual
// allocation. If we aren't using guard blocks, these
// never get instantiated, so won't have any impact.
-//
-
-class TAllocation {
-public:
- TAllocation(size_t size, unsigned char* mem, TAllocation* prev = 0) :
- size(size), mem(mem), prevAlloc(prev) {
- // Allocations are bracketed:
- // [allocationHeader][initialGuardBlock][userData][finalGuardBlock]
- // This would be cleaner with if (guardBlockSize)..., but that
- // makes the compiler print warnings about 0 length memsets,
- // even with the if() protecting them.
+//
+
+class TAllocation
+{
+ public:
+ TAllocation(size_t size, unsigned char *mem, TAllocation *prev = 0)
+ : size(size), mem(mem), prevAlloc(prev)
+ {
+// Allocations are bracketed:
+// [allocationHeader][initialGuardBlock][userData][finalGuardBlock]
+// This would be cleaner with if (guardBlockSize)..., but that
+// makes the compiler print warnings about 0 length memsets,
+// even with the if() protecting them.
#ifdef GUARD_BLOCKS
memset(preGuard(), guardBlockBeginVal, guardBlockSize);
- memset(data(), userDataFill, size);
- memset(postGuard(), guardBlockEndVal, guardBlockSize);
+ memset(data(), userDataFill, size);
+ memset(postGuard(), guardBlockEndVal, guardBlockSize);
#endif
}
- void check() const {
- checkGuardBlock(preGuard(), guardBlockBeginVal, "before");
- checkGuardBlock(postGuard(), guardBlockEndVal, "after");
+ void check() const
+ {
+ checkGuardBlock(preGuard(), guardBlockBeginVal, "before");
+ checkGuardBlock(postGuard(), guardBlockEndVal, "after");
}
void checkAllocList() const;
// Return total size needed to accomodate user buffer of 'size',
// plus our tracking data.
- inline static size_t allocationSize(size_t size) {
+ inline static size_t allocationSize(size_t size)
+ {
return size + 2 * guardBlockSize + headerSize();
}
// Offset from surrounding buffer to get to user data buffer.
- inline static unsigned char* offsetAllocation(unsigned char* m) {
+ inline static unsigned char *offsetAllocation(unsigned char *m)
+ {
return m + guardBlockSize + headerSize();
}
-private:
- void checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const;
+ private:
+ void checkGuardBlock(unsigned char *blockMem, unsigned char val, const char *locText) const;
// Find offsets to pre and post guard blocks, and user data buffer
- unsigned char* preGuard() const { return mem + headerSize(); }
- unsigned char* data() const { return preGuard() + guardBlockSize; }
- unsigned char* postGuard() const { return data() + size; }
+ unsigned char *preGuard() const { return mem + headerSize(); }
+ unsigned char *data() const { return preGuard() + guardBlockSize; }
+ unsigned char *postGuard() const { return data() + size; }
- size_t size; // size of the user data area
- unsigned char* mem; // beginning of our allocation (pts to header)
- TAllocation* prevAlloc; // prior allocation in the chain
+ size_t size; // size of the user data area
+ unsigned char *mem; // beginning of our allocation (pts to header)
+ TAllocation *prevAlloc; // prior allocation in the chain
// Support MSVC++ 6.0
const static unsigned char guardBlockBeginVal;
@@ -101,7 +106,7 @@ private:
//
// There are several stacks. One is to track the pushing and popping
-// of the user, and not yet implemented. The others are simply a
+// of the user, and not yet implemented. The others are simply a
// repositories of free pages or used pages.
//
// Page stacks are linked together with a simple header at the beginning
@@ -110,12 +115,13 @@ private:
// re-use.
//
// The "page size" used is not, nor must it match, the underlying OS
-// page size. But, having it be about that size or equal to a set of
+// page size. But, having it be about that size or equal to a set of
// pages is likely most optimal.
//
-class TPoolAllocator {
-public:
- TPoolAllocator(int growthIncrement = 8*1024, int allocationAlignment = 16);
+class TPoolAllocator
+{
+ public:
+ TPoolAllocator(int growthIncrement = 8 * 1024, int allocationAlignment = 16);
//
// Don't call the destructor just to free up the memory, call pop()
@@ -143,7 +149,7 @@ public:
// Call allocate() to actually acquire memory. Returns 0 if no memory
// available, otherwise a properly aligned pointer to 'numBytes' of memory.
//
- void* allocate(size_t numBytes);
+ void *allocate(size_t numBytes);
//
// There is no deallocate. The point of this class is that
@@ -152,75 +158,92 @@ public:
// by calling pop(), and to not have to solve memory leak problems.
//
-protected:
+ // Catch unwanted allocations.
+ // TODO(jmadill): Remove this when we remove the global allocator.
+ void lock();
+ void unlock();
+
+ private:
+ size_t alignment; // all returned allocations will be aligned at
+ // this granularity, which will be a power of 2
+ size_t alignmentMask;
+
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
friend struct tHeader;
-
- struct tHeader {
- tHeader(tHeader* nextPage, size_t pageCount) :
- nextPage(nextPage),
- pageCount(pageCount)
+
+ struct tHeader
+ {
+ tHeader(tHeader *nextPage, size_t pageCount)
+ : nextPage(nextPage),
+ pageCount(pageCount)
#ifdef GUARD_BLOCKS
- , lastAllocation(0)
+ ,
+ lastAllocation(0)
#endif
- { }
+ {
+ }
- ~tHeader() {
+ ~tHeader()
+ {
#ifdef GUARD_BLOCKS
if (lastAllocation)
lastAllocation->checkAllocList();
#endif
}
- tHeader* nextPage;
+ tHeader *nextPage;
size_t pageCount;
#ifdef GUARD_BLOCKS
- TAllocation* lastAllocation;
+ TAllocation *lastAllocation;
#endif
};
- struct tAllocState {
+ struct tAllocState
+ {
size_t offset;
- tHeader* page;
+ tHeader *page;
};
typedef std::vector<tAllocState> tAllocStack;
// Track allocations if and only if we're using guard blocks
- void* initializeAllocation(tHeader* block, unsigned char* memory, size_t numBytes) {
+ void *initializeAllocation(tHeader *block, unsigned char *memory, size_t numBytes)
+ {
#ifdef GUARD_BLOCKS
- new(memory) TAllocation(numBytes, memory, block->lastAllocation);
- block->lastAllocation = reinterpret_cast<TAllocation*>(memory);
+ new (memory) TAllocation(numBytes, memory, block->lastAllocation);
+ block->lastAllocation = reinterpret_cast<TAllocation *>(memory);
#endif
// This is optimized entirely away if GUARD_BLOCKS is not defined.
return TAllocation::offsetAllocation(memory);
}
- size_t pageSize; // granularity of allocation from the OS
- size_t alignment; // all returned allocations will be aligned at
- // this granularity, which will be a power of 2
- size_t alignmentMask;
- size_t headerSkip; // amount of memory to skip to make room for the
- // header (basically, size of header, rounded
- // up to make it aligned
+ size_t pageSize; // granularity of allocation from the OS
+ size_t headerSkip; // amount of memory to skip to make room for the
+ // header (basically, size of header, rounded
+ // up to make it aligned
size_t currentPageOffset; // next offset in top of inUseList to allocate from
- tHeader* freeList; // list of popped memory
- tHeader* inUseList; // list of all memory currently being used
- tAllocStack stack; // stack of where to allocate from, to partition pool
-
- int numCalls; // just an interesting statistic
- size_t totalBytes; // just an interesting statistic
-private:
- TPoolAllocator& operator=(const TPoolAllocator&); // dont allow assignment operator
- TPoolAllocator(const TPoolAllocator&); // dont allow default copy constructor
-};
+ tHeader *freeList; // list of popped memory
+ tHeader *inUseList; // list of all memory currently being used
+ tAllocStack mStack; // stack of where to allocate from, to partition pool
+ int numCalls; // just an interesting statistic
+ size_t totalBytes; // just an interesting statistic
+
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ std::vector<std::vector<void *>> mStack;
+#endif
+
+ TPoolAllocator &operator=(const TPoolAllocator &); // dont allow assignment operator
+ TPoolAllocator(const TPoolAllocator &); // dont allow default copy constructor
+ bool mLocked;
+};
//
// There could potentially be many pools with pops happening at
// different times. But a simple use is to have a global pop
// with everyone using the same global allocator.
//
-extern TPoolAllocator* GetGlobalPoolAllocator();
-extern void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator);
+extern TPoolAllocator *GetGlobalPoolAllocator();
+extern void SetGlobalPoolAllocator(TPoolAllocator *poolAllocator);
//
// This STL compatible allocator is intended to be used as the allocator
@@ -229,63 +252,68 @@ extern void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator);
// It will use the pools for allocation, and not
// do any deallocation, but will still do destruction.
//
-template<class T>
-class pool_allocator {
-public:
+template <class T>
+class pool_allocator
+{
+ public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
- typedef T* pointer;
- typedef const T* const_pointer;
- typedef T& reference;
- typedef const T& const_reference;
+ typedef T *pointer;
+ typedef const T *const_pointer;
+ typedef T &reference;
+ typedef const T &const_reference;
typedef T value_type;
- template<class Other>
- struct rebind {
+ template <class Other>
+ struct rebind
+ {
typedef pool_allocator<Other> other;
};
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const { return &x; }
- pool_allocator() { }
+ pool_allocator() {}
- template<class Other>
- pool_allocator(const pool_allocator<Other>& p) { }
+ template <class Other>
+ pool_allocator(const pool_allocator<Other> &p)
+ {
+ }
template <class Other>
- pool_allocator<T>& operator=(const pool_allocator<Other>& p) { return *this; }
+ pool_allocator<T> &operator=(const pool_allocator<Other> &p)
+ {
+ return *this;
+ }
#if defined(__SUNPRO_CC) && !defined(_RWSTD_ALLOCATOR)
// libCStd on some platforms have a different allocate/deallocate interface.
// Caller pre-bakes sizeof(T) into 'n' which is the number of bytes to be
// allocated, not the number of elements.
- void* allocate(size_type n) {
- return getAllocator().allocate(n);
- }
- void* allocate(size_type n, const void*) {
- return getAllocator().allocate(n);
- }
- void deallocate(void*, size_type) {}
+ void *allocate(size_type n) { return getAllocator().allocate(n); }
+ void *allocate(size_type n, const void *) { return getAllocator().allocate(n); }
+ void deallocate(void *, size_type) {}
#else
- pointer allocate(size_type n) {
+ pointer allocate(size_type n)
+ {
return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T)));
}
- pointer allocate(size_type n, const void*) {
+ pointer allocate(size_type n, const void *)
+ {
return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T)));
}
void deallocate(pointer, size_type) {}
#endif // _RWSTD_ALLOCATOR
- void construct(pointer p, const T& val) { new ((void *)p) T(val); }
+ void construct(pointer p, const T &val) { new ((void *)p) T(val); }
void destroy(pointer p) { p->T::~T(); }
- bool operator==(const pool_allocator& rhs) const { return true; }
- bool operator!=(const pool_allocator& rhs) const { return false; }
+ bool operator==(const pool_allocator &rhs) const { return true; }
+ bool operator!=(const pool_allocator &rhs) const { return false; }
size_type max_size() const { return static_cast<size_type>(-1) / sizeof(T); }
size_type max_size(int size) const { return static_cast<size_type>(-1) / size; }
- TPoolAllocator& getAllocator() const { return *GetGlobalPoolAllocator(); }
+ TPoolAllocator &getAllocator() const { return *GetGlobalPoolAllocator(); }
};
-#endif // COMPILER_TRANSLATOR_POOLALLOC_H_
+#endif // COMPILER_TRANSLATOR_POOLALLOC_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Pragma.h b/src/3rdparty/angle/src/compiler/translator/Pragma.h
index 57b1134970..8c419fc17e 100644
--- a/src/3rdparty/angle/src/compiler/translator/Pragma.h
+++ b/src/3rdparty/angle/src/compiler/translator/Pragma.h
@@ -11,17 +11,16 @@ struct TPragma
{
struct STDGL
{
- STDGL() : invariantAll(false) { }
+ STDGL() : invariantAll(false) {}
bool invariantAll;
};
-
// By default optimization is turned on and debug is turned off.
// Precision emulation is turned on by default, but has no effect unless
// the extension is enabled.
- TPragma() : optimize(true), debug(false), debugShaderPrecision(true) { }
- TPragma(bool o, bool d) : optimize(o), debug(d), debugShaderPrecision(true) { }
+ TPragma() : optimize(true), debug(false), debugShaderPrecision(true) {}
+ TPragma(bool o, bool d) : optimize(o), debug(d), debugShaderPrecision(true) {}
bool optimize;
bool debug;
@@ -29,4 +28,4 @@ struct TPragma
STDGL stdgl;
};
-#endif // COMPILER_TRANSLATOR_PRAGMA_H_
+#endif // COMPILER_TRANSLATOR_PRAGMA_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp b/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp
deleted file mode 100644
index ef62dbfce7..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// The PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST.
-
-#include "compiler/translator/PruneEmptyDeclarations.h"
-
-#include "compiler/translator/IntermNode.h"
-
-namespace
-{
-
-class PruneEmptyDeclarationsTraverser : private TIntermTraverser
-{
- public:
- static void apply(TIntermNode *root);
- private:
- PruneEmptyDeclarationsTraverser();
- bool visitAggregate(Visit, TIntermAggregate *node) override;
-};
-
-void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root)
-{
- PruneEmptyDeclarationsTraverser prune;
- root->traverse(&prune);
- prune.updateTree();
-}
-
-PruneEmptyDeclarationsTraverser::PruneEmptyDeclarationsTraverser()
- : TIntermTraverser(true, false, false)
-{
-}
-
-bool PruneEmptyDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node)
-{
- if (node->getOp() == EOpDeclaration)
- {
- TIntermSequence *sequence = node->getSequence();
- if (sequence->size() >= 1)
- {
- TIntermSymbol *sym = sequence->front()->getAsSymbolNode();
- // Prune declarations without a variable name, unless it's an interface block declaration.
- if (sym != nullptr && sym->getSymbol() == "" && !sym->isInterfaceBlock())
- {
- if (sequence->size() > 1)
- {
- // Generate a replacement that will remove the empty declarator in the beginning of a declarator
- // list. Example of a declaration that will be changed:
- // float, a;
- // will be changed to
- // float a;
- // This applies also to struct declarations.
- TIntermSequence emptyReplacement;
- mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(node, sym, emptyReplacement));
- }
- else if (sym->getBasicType() != EbtStruct)
- {
- // Single struct declarations may just declare the struct type and no variables, so they should
- // not be pruned. All other single empty declarations can be pruned entirely. Example of an empty
- // declaration that will be pruned:
- // float;
- TIntermSequence emptyReplacement;
- TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
- ASSERT(parentAgg != nullptr);
- mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, emptyReplacement));
- }
- }
- }
- return false;
- }
- return true;
-}
-
-} // namespace
-
-void PruneEmptyDeclarations(TIntermNode *root)
-{
- PruneEmptyDeclarationsTraverser::apply(root);
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h b/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h
deleted file mode 100644
index 122e830902..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// The PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST.
-
-#ifndef COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
-#define COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
-
-class TIntermNode;
-
-void PruneEmptyDeclarations(TIntermNode *root);
-
-#endif // COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/PruneNoOps.cpp b/src/3rdparty/angle/src/compiler/translator/PruneNoOps.cpp
new file mode 100644
index 0000000000..6c9a02cd1c
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/PruneNoOps.cpp
@@ -0,0 +1,156 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// PruneNoOps.cpp: The PruneNoOps function prunes:
+// 1. Empty declarations "int;". Empty declarators will be pruned as well, so for example:
+// int , a;
+// is turned into
+// int a;
+// 2. Literal statements: "1.0;". The ESSL output doesn't define a default precision for float,
+// so float literal statements would end up with no precision which is invalid ESSL.
+
+#include "compiler/translator/PruneNoOps.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+bool IsNoOp(TIntermNode *node)
+{
+ if (node->getAsConstantUnion() != nullptr)
+ {
+ return true;
+ }
+ bool isEmptyDeclaration = node->getAsDeclarationNode() != nullptr &&
+ node->getAsDeclarationNode()->getSequence()->empty();
+ if (isEmptyDeclaration)
+ {
+ return true;
+ }
+ return false;
+}
+
+class PruneNoOpsTraverser : private TIntermTraverser
+{
+ public:
+ static void apply(TIntermBlock *root);
+
+ private:
+ PruneNoOpsTraverser();
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+ bool visitBlock(Visit visit, TIntermBlock *node) override;
+ bool visitLoop(Visit visit, TIntermLoop *loop) override;
+};
+
+void PruneNoOpsTraverser::apply(TIntermBlock *root)
+{
+ PruneNoOpsTraverser prune;
+ root->traverse(&prune);
+ prune.updateTree();
+}
+
+PruneNoOpsTraverser::PruneNoOpsTraverser() : TIntermTraverser(true, false, false)
+{
+}
+
+bool PruneNoOpsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ TIntermSequence *sequence = node->getSequence();
+ if (sequence->size() >= 1)
+ {
+ TIntermSymbol *sym = sequence->front()->getAsSymbolNode();
+ // Prune declarations without a variable name, unless it's an interface block declaration.
+ if (sym != nullptr && sym->getSymbol() == "" && !sym->isInterfaceBlock())
+ {
+ if (sequence->size() > 1)
+ {
+ // Generate a replacement that will remove the empty declarator in the beginning of
+ // a declarator list. Example of a declaration that will be changed:
+ // float, a;
+ // will be changed to
+ // float a;
+ // This applies also to struct declarations.
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(node, sym, emptyReplacement));
+ }
+ else if (sym->getBasicType() != EbtStruct)
+ {
+ // If there are entirely empty non-struct declarations, they result in
+ // TIntermDeclaration nodes without any children in the parsing stage. These are
+ // handled in visitBlock and visitLoop.
+ UNREACHABLE();
+ }
+ else if (sym->getType().getQualifier() != EvqGlobal &&
+ sym->getType().getQualifier() != EvqTemporary)
+ {
+ // Single struct declarations may just declare the struct type and no variables, so
+ // they should not be pruned. Here we handle an empty struct declaration with a
+ // qualifier, for example like this:
+ // const struct a { int i; };
+ // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
+ // convert the declaration to a regular struct declaration. This is okay, since ESSL
+ // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
+ // apply to any declarators, and are not part of the type being defined for name."
+
+ if (mInGlobalScope)
+ {
+ sym->getTypePointer()->setQualifier(EvqGlobal);
+ }
+ else
+ {
+ sym->getTypePointer()->setQualifier(EvqTemporary);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
+{
+ TIntermSequence *statements = node->getSequence();
+
+ for (TIntermNode *statement : *statements)
+ {
+ if (IsNoOp(statement))
+ {
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(node, statement, emptyReplacement));
+ }
+ }
+
+ return true;
+}
+
+bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
+{
+ TIntermTyped *expr = loop->getExpression();
+ if (expr != nullptr && IsNoOp(expr))
+ {
+ loop->setExpression(nullptr);
+ }
+ TIntermNode *init = loop->getInit();
+ if (init != nullptr && IsNoOp(init))
+ {
+ loop->setInit(nullptr);
+ }
+
+ return true;
+}
+
+} // namespace
+
+void PruneNoOps(TIntermBlock *root)
+{
+ PruneNoOpsTraverser::apply(root);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/PruneNoOps.h b/src/3rdparty/angle/src/compiler/translator/PruneNoOps.h
new file mode 100644
index 0000000000..c421cecca4
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/PruneNoOps.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// PruneNoOps.h: The PruneNoOps function prunes:
+// 1. Empty declarations "int;". Empty declarators will be pruned as well, so for example:
+// int , a;
+// is turned into
+// int a;
+// 2. Literal statements: "1.0;". The ESSL output doesn't define a default precision for float,
+// so float literal statements would end up with no precision which is invalid ESSL.
+
+#ifndef COMPILER_TRANSLATOR_PRUNENOOPS_H_
+#define COMPILER_TRANSLATOR_PRUNENOOPS_H_
+
+namespace sh
+{
+class TIntermBlock;
+
+void PruneNoOps(TIntermBlock *root);
+}
+
+#endif // COMPILER_TRANSLATOR_PRUNENOOPS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/QualifierAlive.cpp b/src/3rdparty/angle/src/compiler/translator/QualifierAlive.cpp
deleted file mode 100644
index 3d950aab5a..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/QualifierAlive.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/IntermNode.h"
-
-class TAliveTraverser : public TIntermTraverser {
-public:
- TAliveTraverser(TQualifier q) : TIntermTraverser(true, false, false, true), found(false), qualifier(q)
- {
- }
-
- bool wasFound() { return found; }
-
-protected:
- bool found;
- TQualifier qualifier;
-
- void visitSymbol(TIntermSymbol*);
- bool visitSelection(Visit, TIntermSelection*);
-};
-
-//
-// Report whether or not a variable of the given qualifier type
-// is guaranteed written. Not always possible to determine if
-// it is written conditionally.
-//
-// ?? It does not do this well yet, this is just a place holder
-// that simply determines if it was reference at all, anywhere.
-//
-bool QualifierWritten(TIntermNode* node, TQualifier qualifier)
-{
- TAliveTraverser it(qualifier);
-
- if (node)
- node->traverse(&it);
-
- return it.wasFound();
-}
-
-void TAliveTraverser::visitSymbol(TIntermSymbol* node)
-{
- //
- // If it's what we're looking for, record it.
- //
- if (node->getQualifier() == qualifier)
- found = true;
-}
-
-bool TAliveTraverser::visitSelection(Visit, TIntermSelection*)
-{
- if (wasFound())
- return false;
-
- return true;
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/QualifierAlive.h b/src/3rdparty/angle/src/compiler/translator/QualifierAlive.h
deleted file mode 100644
index 3e4f18012a..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/QualifierAlive.h
+++ /dev/null
@@ -1,12 +0,0 @@
-//
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_QUALIFIERALIVE_H_
-#define COMPILER_TRANSLATOR_QUALIFIERALIVE_H_
-
-bool QualifierWritten(TIntermNode* root, TQualifier);
-
-#endif // COMPILER_TRANSLATOR_QUALIFIERALIVE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/QualifierTypes.cpp b/src/3rdparty/angle/src/compiler/translator/QualifierTypes.cpp
new file mode 100644
index 0000000000..f748175957
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/QualifierTypes.cpp
@@ -0,0 +1,784 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/QualifierTypes.h"
+
+#include "compiler/translator/Diagnostics.h"
+
+#include <algorithm>
+
+namespace sh
+{
+
+namespace
+{
+
+// GLSL ES 3.10 does not impose a strict order on type qualifiers and allows multiple layout
+// declarations.
+// GLSL ES 3.10 Revision 4, 4.10 Order of Qualification
+bool AreTypeQualifierChecksRelaxed(int shaderVersion)
+{
+ return shaderVersion >= 310;
+}
+
+bool IsScopeQualifier(TQualifier qualifier)
+{
+ return qualifier == EvqGlobal || qualifier == EvqTemporary;
+}
+
+bool IsScopeQualifierWrapper(const TQualifierWrapperBase *qualifier)
+{
+ if (qualifier->getType() != QtStorage)
+ return false;
+ const TStorageQualifierWrapper *storageQualifier =
+ static_cast<const TStorageQualifierWrapper *>(qualifier);
+ TQualifier q = storageQualifier->getQualifier();
+ return IsScopeQualifier(q);
+}
+
+// Returns true if the invariant for the qualifier sequence holds
+bool IsInvariantCorrect(const TTypeQualifierBuilder::QualifierSequence &qualifiers)
+{
+ // We should have at least one qualifier.
+ // The first qualifier always tells the scope.
+ return qualifiers.size() >= 1 && IsScopeQualifierWrapper(qualifiers[0]);
+}
+
+// Returns true if there are qualifiers which have been specified multiple times
+// If areQualifierChecksRelaxed is set to true, then layout qualifier repetition is allowed.
+bool HasRepeatingQualifiers(const TTypeQualifierBuilder::QualifierSequence &qualifiers,
+ bool areQualifierChecksRelaxed,
+ std::string *errorMessage)
+{
+ bool invariantFound = false;
+ bool precisionFound = false;
+ bool layoutFound = false;
+ bool interpolationFound = false;
+
+ unsigned int locationsSpecified = 0;
+ bool isOut = false;
+
+ // The iteration starts from one since the first qualifier only reveals the scope of the
+ // expression. It is inserted first whenever the sequence gets created.
+ for (size_t i = 1; i < qualifiers.size(); ++i)
+ {
+ switch (qualifiers[i]->getType())
+ {
+ case QtInvariant:
+ {
+ if (invariantFound)
+ {
+ *errorMessage = "The invariant qualifier specified multiple times.";
+ return true;
+ }
+ invariantFound = true;
+ break;
+ }
+ case QtPrecision:
+ {
+ if (precisionFound)
+ {
+ *errorMessage = "The precision qualifier specified multiple times.";
+ return true;
+ }
+ precisionFound = true;
+ break;
+ }
+ case QtLayout:
+ {
+ if (layoutFound && !areQualifierChecksRelaxed)
+ {
+ *errorMessage = "The layout qualifier specified multiple times.";
+ return true;
+ }
+ if (invariantFound && !areQualifierChecksRelaxed)
+ {
+ // This combination is not correct according to the syntax specified in the
+ // formal grammar in the ESSL 3.00 spec. In ESSL 3.10 the grammar does not have
+ // a similar restriction.
+ *errorMessage =
+ "The layout qualifier and invariant qualifier cannot coexist in the same "
+ "declaration according to the grammar.";
+ return true;
+ }
+ layoutFound = true;
+ const TLayoutQualifier &currentQualifier =
+ static_cast<const TLayoutQualifierWrapper *>(qualifiers[i])->getQualifier();
+ locationsSpecified += currentQualifier.locationsSpecified;
+ break;
+ }
+ case QtInterpolation:
+ {
+ // 'centroid' is treated as a storage qualifier
+ // 'flat centroid' will be squashed to 'flat'
+ // 'smooth centroid' will be squashed to 'centroid'
+ if (interpolationFound)
+ {
+ *errorMessage = "The interpolation qualifier specified multiple times.";
+ return true;
+ }
+ interpolationFound = true;
+ break;
+ }
+ case QtStorage:
+ {
+ // Go over all of the storage qualifiers up until the current one and check for
+ // repetitions.
+ TQualifier currentQualifier =
+ static_cast<const TStorageQualifierWrapper *>(qualifiers[i])->getQualifier();
+ if (currentQualifier == EvqVertexOut || currentQualifier == EvqFragmentOut)
+ {
+ isOut = true;
+ }
+ for (size_t j = 1; j < i; ++j)
+ {
+ if (qualifiers[j]->getType() == QtStorage)
+ {
+ const TStorageQualifierWrapper *previousQualifierWrapper =
+ static_cast<const TStorageQualifierWrapper *>(qualifiers[j]);
+ TQualifier previousQualifier = previousQualifierWrapper->getQualifier();
+ if (currentQualifier == previousQualifier)
+ {
+ *errorMessage = previousQualifierWrapper->getQualifierString().c_str();
+ *errorMessage += " specified multiple times";
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ case QtMemory:
+ {
+ // Go over all of the memory qualifiers up until the current one and check for
+ // repetitions.
+ // Having both readonly and writeonly in a sequence is valid.
+ // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+ TQualifier currentQualifier =
+ static_cast<const TMemoryQualifierWrapper *>(qualifiers[i])->getQualifier();
+ for (size_t j = 1; j < i; ++j)
+ {
+ if (qualifiers[j]->getType() == QtMemory)
+ {
+ const TMemoryQualifierWrapper *previousQualifierWrapper =
+ static_cast<const TMemoryQualifierWrapper *>(qualifiers[j]);
+ TQualifier previousQualifier = previousQualifierWrapper->getQualifier();
+ if (currentQualifier == previousQualifier)
+ {
+ *errorMessage = previousQualifierWrapper->getQualifierString().c_str();
+ *errorMessage += " specified multiple times";
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (locationsSpecified > 1 && isOut)
+ {
+ // GLSL ES 3.00.6 section 4.3.8.2 Output Layout Qualifiers
+ // GLSL ES 3.10 section 4.4.2 Output Layout Qualifiers
+ // "The qualifier may appear at most once within a declaration."
+ *errorMessage = "Output layout location specified multiple times.";
+ return true;
+ }
+
+ return false;
+}
+
+// GLSL ES 3.00_6, 4.7 Order of Qualification
+// The correct order of qualifiers is:
+// invariant-qualifier interpolation-qualifier storage-qualifier precision-qualifier
+// layout-qualifier has to be before storage-qualifier.
+bool AreQualifiersInOrder(const TTypeQualifierBuilder::QualifierSequence &qualifiers,
+ std::string *errorMessage)
+{
+ bool foundInterpolation = false;
+ bool foundStorage = false;
+ bool foundPrecision = false;
+ for (size_t i = 1; i < qualifiers.size(); ++i)
+ {
+ switch (qualifiers[i]->getType())
+ {
+ case QtInvariant:
+ if (foundInterpolation || foundStorage || foundPrecision)
+ {
+ *errorMessage = "The invariant qualifier has to be first in the expression.";
+ return false;
+ }
+ break;
+ case QtInterpolation:
+ if (foundStorage)
+ {
+ *errorMessage = "Storage qualifiers have to be after interpolation qualifiers.";
+ return false;
+ }
+ else if (foundPrecision)
+ {
+ *errorMessage =
+ "Precision qualifiers have to be after interpolation qualifiers.";
+ return false;
+ }
+ foundInterpolation = true;
+ break;
+ case QtLayout:
+ if (foundStorage)
+ {
+ *errorMessage = "Storage qualifiers have to be after layout qualifiers.";
+ return false;
+ }
+ else if (foundPrecision)
+ {
+ *errorMessage = "Precision qualifiers have to be after layout qualifiers.";
+ return false;
+ }
+ break;
+ case QtStorage:
+ if (foundPrecision)
+ {
+ *errorMessage = "Precision qualifiers have to be after storage qualifiers.";
+ return false;
+ }
+ foundStorage = true;
+ break;
+ case QtMemory:
+ if (foundPrecision)
+ {
+ *errorMessage = "Precision qualifiers have to be after memory qualifiers.";
+ return false;
+ }
+ break;
+ case QtPrecision:
+ foundPrecision = true;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ return true;
+}
+
+struct QualifierComparator
+{
+ bool operator()(const TQualifierWrapperBase *q1, const TQualifierWrapperBase *q2)
+ {
+ return q1->getRank() < q2->getRank();
+ }
+};
+
+void SortSequence(TTypeQualifierBuilder::QualifierSequence &qualifiers)
+{
+ // We need a stable sorting algorithm since the order of layout-qualifier declarations matter.
+ // The sorting starts from index 1, instead of 0, since the element at index 0 tells the scope
+ // and we always want it to be first.
+ std::stable_sort(qualifiers.begin() + 1, qualifiers.end(), QualifierComparator());
+}
+
+// Handles the joining of storage qualifiers for variables.
+bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storageQualifier)
+{
+ switch (*joinedQualifier)
+ {
+ case EvqGlobal:
+ *joinedQualifier = storageQualifier;
+ break;
+ case EvqTemporary:
+ {
+ switch (storageQualifier)
+ {
+ case EvqConst:
+ *joinedQualifier = storageQualifier;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case EvqSmooth:
+ {
+ switch (storageQualifier)
+ {
+ case EvqCentroid:
+ *joinedQualifier = EvqCentroid;
+ break;
+ case EvqVertexOut:
+ case EvqGeometryOut:
+ *joinedQualifier = EvqSmoothOut;
+ break;
+ case EvqFragmentIn:
+ case EvqGeometryIn:
+ *joinedQualifier = EvqSmoothIn;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case EvqFlat:
+ {
+ switch (storageQualifier)
+ {
+ case EvqCentroid:
+ *joinedQualifier = EvqFlat;
+ break;
+ case EvqVertexOut:
+ case EvqGeometryOut:
+ *joinedQualifier = EvqFlatOut;
+ break;
+ case EvqFragmentIn:
+ case EvqGeometryIn:
+ *joinedQualifier = EvqFlatIn;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case EvqCentroid:
+ {
+ switch (storageQualifier)
+ {
+ case EvqVertexOut:
+ case EvqGeometryOut:
+ *joinedQualifier = EvqCentroidOut;
+ break;
+ case EvqFragmentIn:
+ case EvqGeometryIn:
+ *joinedQualifier = EvqCentroidIn;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+// Handles the joining of storage qualifiers for a parameter in a function.
+bool JoinParameterStorageQualifier(TQualifier *joinedQualifier, TQualifier storageQualifier)
+{
+ switch (*joinedQualifier)
+ {
+ case EvqTemporary:
+ *joinedQualifier = storageQualifier;
+ break;
+ case EvqConst:
+ {
+ switch (storageQualifier)
+ {
+ case EvqIn:
+ *joinedQualifier = EvqConstReadOnly;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool JoinMemoryQualifier(TMemoryQualifier *joinedMemoryQualifier, TQualifier memoryQualifier)
+{
+ switch (memoryQualifier)
+ {
+ case EvqReadOnly:
+ joinedMemoryQualifier->readonly = true;
+ break;
+ case EvqWriteOnly:
+ joinedMemoryQualifier->writeonly = true;
+ break;
+ case EvqCoherent:
+ joinedMemoryQualifier->coherent = true;
+ break;
+ case EvqRestrict:
+ joinedMemoryQualifier->restrictQualifier = true;
+ break;
+ case EvqVolatile:
+ // Variables having the volatile qualifier are automatcally treated as coherent as well.
+ // GLSL ES 3.10, Revision 4, 4.9 Memory Access Qualifiers
+ joinedMemoryQualifier->volatileQualifier = true;
+ joinedMemoryQualifier->coherent = true;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return true;
+}
+
+TTypeQualifier GetVariableTypeQualifierFromSortedSequence(
+ const TTypeQualifierBuilder::QualifierSequence &sortedSequence,
+ TDiagnostics *diagnostics)
+{
+ TTypeQualifier typeQualifier(
+ static_cast<const TStorageQualifierWrapper *>(sortedSequence[0])->getQualifier(),
+ sortedSequence[0]->getLine());
+ for (size_t i = 1; i < sortedSequence.size(); ++i)
+ {
+ const TQualifierWrapperBase *qualifier = sortedSequence[i];
+ bool isQualifierValid = false;
+ switch (qualifier->getType())
+ {
+ case QtInvariant:
+ isQualifierValid = true;
+ typeQualifier.invariant = true;
+ break;
+ case QtInterpolation:
+ {
+ switch (typeQualifier.qualifier)
+ {
+ case EvqGlobal:
+ isQualifierValid = true;
+ typeQualifier.qualifier =
+ static_cast<const TInterpolationQualifierWrapper *>(qualifier)
+ ->getQualifier();
+ break;
+ default:
+ isQualifierValid = false;
+ }
+ break;
+ }
+ case QtLayout:
+ {
+ const TLayoutQualifierWrapper *layoutQualifierWrapper =
+ static_cast<const TLayoutQualifierWrapper *>(qualifier);
+ isQualifierValid = true;
+ typeQualifier.layoutQualifier = sh::JoinLayoutQualifiers(
+ typeQualifier.layoutQualifier, layoutQualifierWrapper->getQualifier(),
+ layoutQualifierWrapper->getLine(), diagnostics);
+ break;
+ }
+ case QtStorage:
+ isQualifierValid = JoinVariableStorageQualifier(
+ &typeQualifier.qualifier,
+ static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ case QtPrecision:
+ isQualifierValid = true;
+ typeQualifier.precision =
+ static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
+ ASSERT(typeQualifier.precision != EbpUndefined);
+ break;
+ case QtMemory:
+ isQualifierValid = JoinMemoryQualifier(
+ &typeQualifier.memoryQualifier,
+ static_cast<const TMemoryQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (!isQualifierValid)
+ {
+ const TString &qualifierString = qualifier->getQualifierString();
+ diagnostics->error(qualifier->getLine(), "invalid qualifier combination",
+ qualifierString.c_str());
+ break;
+ }
+ }
+ return typeQualifier;
+}
+
+TTypeQualifier GetParameterTypeQualifierFromSortedSequence(
+ const TTypeQualifierBuilder::QualifierSequence &sortedSequence,
+ TDiagnostics *diagnostics)
+{
+ TTypeQualifier typeQualifier(EvqTemporary, sortedSequence[0]->getLine());
+ for (size_t i = 1; i < sortedSequence.size(); ++i)
+ {
+ const TQualifierWrapperBase *qualifier = sortedSequence[i];
+ bool isQualifierValid = false;
+ switch (qualifier->getType())
+ {
+ case QtInvariant:
+ case QtInterpolation:
+ case QtLayout:
+ break;
+ case QtMemory:
+ isQualifierValid = JoinMemoryQualifier(
+ &typeQualifier.memoryQualifier,
+ static_cast<const TMemoryQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ case QtStorage:
+ isQualifierValid = JoinParameterStorageQualifier(
+ &typeQualifier.qualifier,
+ static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ case QtPrecision:
+ isQualifierValid = true;
+ typeQualifier.precision =
+ static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
+ ASSERT(typeQualifier.precision != EbpUndefined);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (!isQualifierValid)
+ {
+ const TString &qualifierString = qualifier->getQualifierString();
+ diagnostics->error(qualifier->getLine(), "invalid parameter qualifier",
+ qualifierString.c_str());
+ break;
+ }
+ }
+
+ switch (typeQualifier.qualifier)
+ {
+ case EvqIn:
+ case EvqConstReadOnly: // const in
+ case EvqOut:
+ case EvqInOut:
+ break;
+ case EvqConst:
+ typeQualifier.qualifier = EvqConstReadOnly;
+ break;
+ case EvqTemporary:
+ // no qualifier has been specified, set it to EvqIn which is the default
+ typeQualifier.qualifier = EvqIn;
+ break;
+ default:
+ diagnostics->error(sortedSequence[0]->getLine(), "Invalid parameter qualifier ",
+ getQualifierString(typeQualifier.qualifier));
+ }
+ return typeQualifier;
+}
+} // namespace
+
+TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation,
+ TDiagnostics *diagnostics)
+{
+ TLayoutQualifier joinedQualifier = leftQualifier;
+
+ if (rightQualifier.location != -1)
+ {
+ joinedQualifier.location = rightQualifier.location;
+ ++joinedQualifier.locationsSpecified;
+ }
+ if (rightQualifier.yuv != false)
+ {
+ joinedQualifier.yuv = rightQualifier.yuv;
+ }
+ if (rightQualifier.binding != -1)
+ {
+ joinedQualifier.binding = rightQualifier.binding;
+ }
+ if (rightQualifier.offset != -1)
+ {
+ joinedQualifier.offset = rightQualifier.offset;
+ }
+ if (rightQualifier.matrixPacking != EmpUnspecified)
+ {
+ joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
+ }
+ if (rightQualifier.blockStorage != EbsUnspecified)
+ {
+ joinedQualifier.blockStorage = rightQualifier.blockStorage;
+ }
+
+ for (size_t i = 0u; i < rightQualifier.localSize.size(); ++i)
+ {
+ if (rightQualifier.localSize[i] != -1)
+ {
+ if (joinedQualifier.localSize[i] != -1 &&
+ joinedQualifier.localSize[i] != rightQualifier.localSize[i])
+ {
+ diagnostics->error(rightQualifierLocation,
+ "Cannot have multiple different work group size specifiers",
+ getWorkGroupSizeString(i));
+ }
+ joinedQualifier.localSize[i] = rightQualifier.localSize[i];
+ }
+ }
+
+ if (rightQualifier.numViews != -1)
+ {
+ joinedQualifier.numViews = rightQualifier.numViews;
+ }
+
+ if (rightQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ joinedQualifier.imageInternalFormat = rightQualifier.imageInternalFormat;
+ }
+
+ if (rightQualifier.primitiveType != EptUndefined)
+ {
+ if (joinedQualifier.primitiveType != EptUndefined &&
+ joinedQualifier.primitiveType != rightQualifier.primitiveType)
+ {
+ diagnostics->error(rightQualifierLocation,
+ "Cannot have multiple different primitive specifiers",
+ getGeometryShaderPrimitiveTypeString(rightQualifier.primitiveType));
+ }
+ joinedQualifier.primitiveType = rightQualifier.primitiveType;
+ }
+
+ if (rightQualifier.invocations != 0)
+ {
+ if (joinedQualifier.invocations != 0 &&
+ joinedQualifier.invocations != rightQualifier.invocations)
+ {
+ diagnostics->error(rightQualifierLocation,
+ "Cannot have multiple different invocations specifiers",
+ "invocations");
+ }
+ joinedQualifier.invocations = rightQualifier.invocations;
+ }
+
+ if (rightQualifier.maxVertices != -1)
+ {
+ if (joinedQualifier.maxVertices != -1 &&
+ joinedQualifier.maxVertices != rightQualifier.maxVertices)
+ {
+ diagnostics->error(rightQualifierLocation,
+ "Cannot have multiple different max_vertices specifiers",
+ "max_vertices");
+ }
+ joinedQualifier.maxVertices = rightQualifier.maxVertices;
+ }
+
+ return joinedQualifier;
+}
+
+unsigned int TInvariantQualifierWrapper::getRank() const
+{
+ return 0u;
+}
+
+unsigned int TInterpolationQualifierWrapper::getRank() const
+{
+ return 1u;
+}
+
+unsigned int TLayoutQualifierWrapper::getRank() const
+{
+ return 2u;
+}
+
+unsigned int TStorageQualifierWrapper::getRank() const
+{
+ // Force the 'centroid' auxilary storage qualifier to be always first among all storage
+ // qualifiers.
+ if (mStorageQualifier == EvqCentroid)
+ {
+ return 3u;
+ }
+ else
+ {
+ return 4u;
+ }
+}
+
+unsigned int TMemoryQualifierWrapper::getRank() const
+{
+ return 4u;
+}
+
+unsigned int TPrecisionQualifierWrapper::getRank() const
+{
+ return 5u;
+}
+
+TTypeQualifier::TTypeQualifier(TQualifier scope, const TSourceLoc &loc)
+ : layoutQualifier(TLayoutQualifier::Create()),
+ memoryQualifier(TMemoryQualifier::Create()),
+ precision(EbpUndefined),
+ qualifier(scope),
+ invariant(false),
+ line(loc)
+{
+ ASSERT(IsScopeQualifier(qualifier));
+}
+
+TTypeQualifierBuilder::TTypeQualifierBuilder(const TStorageQualifierWrapper *scope,
+ int shaderVersion)
+ : mShaderVersion(shaderVersion)
+{
+ ASSERT(IsScopeQualifier(scope->getQualifier()));
+ mQualifiers.push_back(scope);
+}
+
+void TTypeQualifierBuilder::appendQualifier(const TQualifierWrapperBase *qualifier)
+{
+ mQualifiers.push_back(qualifier);
+}
+
+bool TTypeQualifierBuilder::checkSequenceIsValid(TDiagnostics *diagnostics) const
+{
+ bool areQualifierChecksRelaxed = AreTypeQualifierChecksRelaxed(mShaderVersion);
+ std::string errorMessage;
+ if (HasRepeatingQualifiers(mQualifiers, areQualifierChecksRelaxed, &errorMessage))
+ {
+ diagnostics->error(mQualifiers[0]->getLine(), errorMessage.c_str(), "qualifier sequence");
+ return false;
+ }
+
+ if (!areQualifierChecksRelaxed && !AreQualifiersInOrder(mQualifiers, &errorMessage))
+ {
+ diagnostics->error(mQualifiers[0]->getLine(), errorMessage.c_str(), "qualifier sequence");
+ return false;
+ }
+
+ return true;
+}
+
+TTypeQualifier TTypeQualifierBuilder::getParameterTypeQualifier(TDiagnostics *diagnostics) const
+{
+ ASSERT(IsInvariantCorrect(mQualifiers));
+ ASSERT(static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier() ==
+ EvqTemporary);
+
+ if (!checkSequenceIsValid(diagnostics))
+ {
+ return TTypeQualifier(EvqTemporary, mQualifiers[0]->getLine());
+ }
+
+ // If the qualifier checks are relaxed, then it is easier to sort the qualifiers so
+ // that the order imposed by the GLSL ES 3.00 spec is kept. Then we can use the same code to
+ // combine the qualifiers.
+ if (AreTypeQualifierChecksRelaxed(mShaderVersion))
+ {
+ // Copy the qualifier sequence so that we can sort them.
+ QualifierSequence sortedQualifierSequence = mQualifiers;
+ SortSequence(sortedQualifierSequence);
+ return GetParameterTypeQualifierFromSortedSequence(sortedQualifierSequence, diagnostics);
+ }
+ return GetParameterTypeQualifierFromSortedSequence(mQualifiers, diagnostics);
+}
+
+TTypeQualifier TTypeQualifierBuilder::getVariableTypeQualifier(TDiagnostics *diagnostics) const
+{
+ ASSERT(IsInvariantCorrect(mQualifiers));
+
+ if (!checkSequenceIsValid(diagnostics))
+ {
+ return TTypeQualifier(
+ static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier(),
+ mQualifiers[0]->getLine());
+ }
+
+ // If the qualifier checks are relaxed, then it is easier to sort the qualifiers so
+ // that the order imposed by the GLSL ES 3.00 spec is kept. Then we can use the same code to
+ // combine the qualifiers.
+ if (AreTypeQualifierChecksRelaxed(mShaderVersion))
+ {
+ // Copy the qualifier sequence so that we can sort them.
+ QualifierSequence sortedQualifierSequence = mQualifiers;
+ SortSequence(sortedQualifierSequence);
+ return GetVariableTypeQualifierFromSortedSequence(sortedQualifierSequence, diagnostics);
+ }
+ return GetVariableTypeQualifierFromSortedSequence(mQualifiers, diagnostics);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/QualifierTypes.h b/src/3rdparty/angle/src/compiler/translator/QualifierTypes.h
new file mode 100644
index 0000000000..10bdeed89d
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/QualifierTypes.h
@@ -0,0 +1,191 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
+#define COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
+
+#include "common/angleutils.h"
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Types.h"
+
+namespace sh
+{
+class TDiagnostics;
+
+TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation,
+ TDiagnostics *diagnostics);
+
+enum TQualifierType
+{
+ QtInvariant,
+ QtInterpolation,
+ QtLayout,
+ QtStorage,
+ QtPrecision,
+ QtMemory
+};
+
+class TQualifierWrapperBase : angle::NonCopyable
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TQualifierWrapperBase(const TSourceLoc &line) : mLine(line) {}
+ virtual ~TQualifierWrapperBase(){};
+ virtual TQualifierType getType() const = 0;
+ virtual TString getQualifierString() const = 0;
+ virtual unsigned int getRank() const = 0;
+ const TSourceLoc &getLine() const { return mLine; }
+ private:
+ TSourceLoc mLine;
+};
+
+class TInvariantQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TInvariantQualifierWrapper(const TSourceLoc &line) : TQualifierWrapperBase(line) {}
+ ~TInvariantQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtInvariant; }
+ TString getQualifierString() const { return "invariant"; }
+ unsigned int getRank() const;
+};
+
+class TInterpolationQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TInterpolationQualifierWrapper(TQualifier interpolationQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mInterpolationQualifier(interpolationQualifier)
+ {
+ }
+ ~TInterpolationQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtInterpolation; }
+ TString getQualifierString() const { return sh::getQualifierString(mInterpolationQualifier); }
+ TQualifier getQualifier() const { return mInterpolationQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TQualifier mInterpolationQualifier;
+};
+
+class TLayoutQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TLayoutQualifierWrapper(TLayoutQualifier layoutQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mLayoutQualifier(layoutQualifier)
+ {
+ }
+ ~TLayoutQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtLayout; }
+ TString getQualifierString() const { return "layout"; }
+ const TLayoutQualifier &getQualifier() const { return mLayoutQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TLayoutQualifier mLayoutQualifier;
+};
+
+class TStorageQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TStorageQualifierWrapper(TQualifier storageQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mStorageQualifier(storageQualifier)
+ {
+ }
+ ~TStorageQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtStorage; }
+ TString getQualifierString() const { return sh::getQualifierString(mStorageQualifier); }
+ TQualifier getQualifier() const { return mStorageQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TQualifier mStorageQualifier;
+};
+
+class TPrecisionQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TPrecisionQualifierWrapper(TPrecision precisionQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mPrecisionQualifier(precisionQualifier)
+ {
+ }
+ ~TPrecisionQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtPrecision; }
+ TString getQualifierString() const { return sh::getPrecisionString(mPrecisionQualifier); }
+ TPrecision getQualifier() const { return mPrecisionQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TPrecision mPrecisionQualifier;
+};
+
+class TMemoryQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TMemoryQualifierWrapper(TQualifier memoryQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mMemoryQualifier(memoryQualifier)
+ {
+ }
+ ~TMemoryQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtMemory; }
+ TString getQualifierString() const { return sh::getQualifierString(mMemoryQualifier); }
+ TQualifier getQualifier() const { return mMemoryQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TQualifier mMemoryQualifier;
+};
+
+// TTypeQualifier tightly covers type_qualifier from the grammar
+struct TTypeQualifier
+{
+ // initializes all of the qualifiers and sets the scope
+ TTypeQualifier(TQualifier scope, const TSourceLoc &loc);
+
+ TLayoutQualifier layoutQualifier;
+ TMemoryQualifier memoryQualifier;
+ TPrecision precision;
+ TQualifier qualifier;
+ bool invariant;
+ TSourceLoc line;
+};
+
+// TTypeQualifierBuilder contains all of the qualifiers when type_qualifier gets parsed.
+// It is to be used to validate the qualifier sequence and build a TTypeQualifier from it.
+class TTypeQualifierBuilder : angle::NonCopyable
+{
+ public:
+ using QualifierSequence = TVector<const TQualifierWrapperBase *>;
+
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TTypeQualifierBuilder(const TStorageQualifierWrapper *scope, int shaderVersion);
+ // Adds the passed qualifier to the end of the sequence.
+ void appendQualifier(const TQualifierWrapperBase *qualifier);
+ // Checks for the order of qualification and repeating qualifiers.
+ bool checkSequenceIsValid(TDiagnostics *diagnostics) const;
+ // Goes over the qualifier sequence and parses it to form a type qualifier for a function
+ // parameter.
+ // The returned object is initialized even if the parsing fails.
+ TTypeQualifier getParameterTypeQualifier(TDiagnostics *diagnostics) const;
+ // Goes over the qualifier sequence and parses it to form a type qualifier for a variable.
+ // The returned object is initialized even if the parsing fails.
+ TTypeQualifier getVariableTypeQualifier(TDiagnostics *diagnostics) const;
+
+ private:
+ QualifierSequence mQualifiers;
+ int mShaderVersion;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp
index 14e88b749a..5233a29f19 100644
--- a/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp
@@ -3,20 +3,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// During parsing, all constant expressions are folded to constant union nodes. The expressions that have been
-// folded may have had precision qualifiers, which should affect the precision of the consuming operation.
-// If the folded constant union nodes are written to output as such they won't have any precision qualifiers,
-// and their effect on the precision of the consuming operation is lost.
+// During parsing, all constant expressions are folded to constant union nodes. The expressions that
+// have been folded may have had precision qualifiers, which should affect the precision of the
+// consuming operation. If the folded constant union nodes are written to output as such they won't
+// have any precision qualifiers, and their effect on the precision of the consuming operation is
+// lost.
//
-// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants and hoists
-// the constants outside the containing expression as precision qualified named variables in case that is
-// required for correct precision propagation.
+// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants
+// and hoists the constants outside the containing expression as precision qualified named variables
+// in case that is required for correct precision propagation.
//
#include "compiler/translator/RecordConstantPrecision.h"
#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
namespace
{
@@ -24,7 +28,7 @@ namespace
class RecordConstantPrecisionTraverser : public TIntermTraverser
{
public:
- RecordConstantPrecisionTraverser();
+ RecordConstantPrecisionTraverser(TSymbolTable *symbolTable);
void visitConstantUnion(TIntermConstantUnion *node) override;
@@ -37,14 +41,18 @@ class RecordConstantPrecisionTraverser : public TIntermTraverser
bool mFoundHigherPrecisionConstant;
};
-RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser()
- : TIntermTraverser(true, false, true),
- mFoundHigherPrecisionConstant(false)
+RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable), mFoundHigherPrecisionConstant(false)
{
}
bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TIntermTyped *operand)
{
+ if (getParentNode()->getAsCaseNode() || getParentNode()->getAsBlock())
+ {
+ return false;
+ }
+
const TIntermBinary *parentAsBinary = getParentNode()->getAsBinaryNode();
if (parentAsBinary != nullptr)
{
@@ -52,15 +60,15 @@ bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TI
// its precision has no effect.
switch (parentAsBinary->getOp())
{
- case EOpInitialize:
- case EOpAssign:
- case EOpIndexDirect:
- case EOpIndexDirectStruct:
- case EOpIndexDirectInterfaceBlock:
- case EOpIndexIndirect:
- return false;
- default:
- break;
+ case EOpInitialize:
+ case EOpAssign:
+ case EOpIndexDirect:
+ case EOpIndexDirectStruct:
+ case EOpIndexDirectInterfaceBlock:
+ case EOpIndexIndirect:
+ return false;
+ default:
+ break;
}
TIntermTyped *otherOperand = parentAsBinary->getRight();
@@ -68,9 +76,10 @@ bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TI
{
otherOperand = parentAsBinary->getLeft();
}
- // If the precision of the other child is at least as high as the precision of the constant, the precision of
- // the constant has no effect.
- if (otherOperand->getAsConstantUnion() == nullptr && otherOperand->getPrecision() >= operand->getPrecision())
+ // If the precision of the other child is at least as high as the precision of the constant,
+ // the precision of the constant has no effect.
+ if (otherOperand->getAsConstantUnion() == nullptr &&
+ otherOperand->getPrecision() >= operand->getPrecision())
{
return false;
}
@@ -92,14 +101,15 @@ bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TI
{
return false;
}
- // If the precision of operands does affect the result, but the precision of any of the other children
- // has a precision that's at least as high as the precision of the constant, the precision of the constant
- // has no effect.
+ // If the precision of operands does affect the result, but the precision of any of the
+ // other children has a precision that's at least as high as the precision of the constant,
+ // the precision of the constant has no effect.
TIntermSequence *parameters = parentAsAggregate->getSequence();
for (TIntermNode *parameter : *parameters)
{
const TIntermTyped *typedParameter = parameter->getAsTyped();
- if (parameter != operand && typedParameter != nullptr && parameter->getAsConstantUnion() == nullptr &&
+ if (parameter != operand && typedParameter != nullptr &&
+ parameter->getAsConstantUnion() == nullptr &&
typedParameter->getPrecision() >= operand->getPrecision())
{
return false;
@@ -114,37 +124,36 @@ void RecordConstantPrecisionTraverser::visitConstantUnion(TIntermConstantUnion *
if (mFoundHigherPrecisionConstant)
return;
- // If the constant has lowp or undefined precision, it can't increase the precision of consuming operations.
+ // If the constant has lowp or undefined precision, it can't increase the precision of consuming
+ // operations.
if (node->getPrecision() < EbpMedium)
return;
- // It's possible the node has no effect on the precision of the consuming expression, depending on the
- // consuming expression, and the precision of the other parameters of the expression.
+ // It's possible the node has no effect on the precision of the consuming expression, depending
+ // on the consuming expression, and the precision of the other parameters of the expression.
if (!operandAffectsParentOperationPrecision(node))
return;
- // Make the constant a precision-qualified named variable to make sure it affects the precision of the consuming
- // expression.
+ // Make the constant a precision-qualified named variable to make sure it affects the precision
+ // of the consuming expression.
TIntermSequence insertions;
insertions.push_back(createTempInitDeclaration(node, EvqConst));
insertStatementsInParentBlock(insertions);
- mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, createTempSymbol(node->getType()), false));
+ queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
mFoundHigherPrecisionConstant = true;
}
void RecordConstantPrecisionTraverser::nextIteration()
{
- nextTemporaryIndex();
+ nextTemporaryId();
mFoundHigherPrecisionConstant = false;
}
-} // namespace
+} // namespace
-void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex)
+void RecordConstantPrecision(TIntermNode *root, TSymbolTable *symbolTable)
{
- RecordConstantPrecisionTraverser traverser;
- ASSERT(temporaryIndex != nullptr);
- traverser.useTemporaryIndex(temporaryIndex);
+ RecordConstantPrecisionTraverser traverser(symbolTable);
// Iterate as necessary, and reset the traverser between iterations.
do
{
@@ -152,6 +161,7 @@ void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex)
root->traverse(&traverser);
if (traverser.foundHigherPrecisionConstant())
traverser.updateTree();
- }
- while (traverser.foundHigherPrecisionConstant());
+ } while (traverser.foundHigherPrecisionConstant());
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h
index 2cd401b418..f86c2a8693 100644
--- a/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h
+++ b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h
@@ -3,21 +3,26 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// During parsing, all constant expressions are folded to constant union nodes. The expressions that have been
-// folded may have had precision qualifiers, which should affect the precision of the consuming operation.
-// If the folded constant union nodes are written to output as such they won't have any precision qualifiers,
-// and their effect on the precision of the consuming operation is lost.
+// During parsing, all constant expressions are folded to constant union nodes. The expressions that
+// have been folded may have had precision qualifiers, which should affect the precision of the
+// consuming operation. If the folded constant union nodes are written to output as such they won't
+// have any precision qualifiers, and their effect on the precision of the consuming operation is
+// lost.
//
-// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants and hoists
-// the constants outside the containing expression as precision qualified named variables in case that is
-// required for correct precision propagation.
+// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants
+// and hoists the constants outside the containing expression as precision qualified named variables
+// in case that is required for correct precision propagation.
//
#ifndef COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_
#define COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_
+namespace sh
+{
class TIntermNode;
+class TSymbolTable;
-void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex);
+void RecordConstantPrecision(TIntermNode *root, TSymbolTable *symbolTable);
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_
+#endif // COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp
index 5e0db2ad26..55fb124a42 100644
--- a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp
@@ -7,6 +7,9 @@
#include "common/debug.h"
#include "compiler/translator/RegenerateStructNames.h"
+namespace sh
+{
+
void RegenerateStructNames::visitSymbol(TIntermSymbol *symbol)
{
ASSERT(symbol);
@@ -16,7 +19,7 @@ void RegenerateStructNames::visitSymbol(TIntermSymbol *symbol)
if (!userType)
return;
- if (mSymbolTable.findBuiltIn(userType->name(), mShaderVersion))
+ if (mSymbolTable->findBuiltIn(userType->name(), mShaderVersion))
{
// Built-in struct, do not touch it.
return;
@@ -53,30 +56,21 @@ void RegenerateStructNames::visitSymbol(TIntermSymbol *symbol)
return;
}
std::string id = Str(uniqueId);
- TString tmp = kPrefix + TString(id.c_str());
+ TString tmp = kPrefix + TString(id.c_str());
tmp += "_" + userType->name();
userType->setName(tmp);
}
-bool RegenerateStructNames::visitAggregate(Visit, TIntermAggregate *aggregate)
+bool RegenerateStructNames::visitBlock(Visit, TIntermBlock *block)
{
- ASSERT(aggregate);
- switch (aggregate->getOp())
+ ++mScopeDepth;
+ TIntermSequence &sequence = *(block->getSequence());
+ for (TIntermNode *node : sequence)
{
- case EOpSequence:
- ++mScopeDepth;
- {
- TIntermSequence &sequence = *(aggregate->getSequence());
- for (size_t ii = 0; ii < sequence.size(); ++ii)
- {
- TIntermNode *node = sequence[ii];
- ASSERT(node != NULL);
- node->traverse(this);
- }
- }
- --mScopeDepth;
- return false;
- default:
- return true;
+ node->traverse(this);
}
+ --mScopeDepth;
+ return false;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h
index 3b98e5d709..293720b6a4 100644
--- a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h
+++ b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h
@@ -7,27 +7,29 @@
#ifndef COMPILER_TRANSLATOR_REGENERATESTRUCTNAMES_H_
#define COMPILER_TRANSLATOR_REGENERATESTRUCTNAMES_H_
-#include "compiler/translator/Intermediate.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/SymbolTable.h"
#include <set>
+namespace sh
+{
+
class RegenerateStructNames : public TIntermTraverser
{
public:
- RegenerateStructNames(const TSymbolTable &symbolTable,
- int shaderVersion)
- : TIntermTraverser(true, false, false),
- mSymbolTable(symbolTable),
+ RegenerateStructNames(TSymbolTable *symbolTable, int shaderVersion)
+ : TIntermTraverser(true, false, false, symbolTable),
mShaderVersion(shaderVersion),
- mScopeDepth(0) {}
+ mScopeDepth(0)
+ {
+ }
protected:
void visitSymbol(TIntermSymbol *) override;
- bool visitAggregate(Visit, TIntermAggregate *) override;
+ bool visitBlock(Visit, TIntermBlock *block) override;
private:
- const TSymbolTable &mSymbolTable;
int mShaderVersion;
// Indicating the depth of the current scope.
@@ -38,4 +40,6 @@ class RegenerateStructNames : public TIntermTraverser
std::set<int> mDeclaredGlobalStructs;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_REGENERATESTRUCTNAMES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.cpp
new file mode 100644
index 0000000000..e9e6a17013
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.cpp
@@ -0,0 +1,83 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveArrayLengthMethod.cpp:
+// Fold array length expressions, including cases where the "this" node has side effects.
+// Example:
+// int i = (a = b).length();
+// int j = (func()).length();
+// becomes:
+// (a = b);
+// int i = <constant array length>;
+// func();
+// int j = <constant array length>;
+//
+// Must be run after SplitSequenceOperator, SimplifyLoopConditions and SeparateDeclarations steps
+// have been done to expressions containing calls of the array length method.
+//
+// Does nothing to length method calls done on runtime-sized arrays.
+
+#include "compiler/translator/RemoveArrayLengthMethod.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class RemoveArrayLengthTraverser : public TIntermTraverser
+{
+ public:
+ RemoveArrayLengthTraverser() : TIntermTraverser(true, false, false), mFoundArrayLength(false) {}
+
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+
+ void nextIteration() { mFoundArrayLength = false; }
+
+ bool foundArrayLength() const { return mFoundArrayLength; }
+
+ private:
+ bool mFoundArrayLength;
+};
+
+bool RemoveArrayLengthTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ // The only case where we leave array length() in place is for runtime-sized arrays.
+ if (node->getOp() == EOpArrayLength && !node->getOperand()->getType().isUnsizedArray())
+ {
+ mFoundArrayLength = true;
+ if (!node->getOperand()->hasSideEffects())
+ {
+ queueReplacement(node->fold(nullptr), OriginalNode::IS_DROPPED);
+ return false;
+ }
+ insertStatementInParentBlock(node->getOperand()->deepCopy());
+ TConstantUnion *constArray = new TConstantUnion[1];
+ constArray->setIConst(node->getOperand()->getOutermostArraySize());
+ queueReplacement(new TIntermConstantUnion(constArray, node->getType()),
+ OriginalNode::IS_DROPPED);
+ return false;
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+void RemoveArrayLengthMethod(TIntermBlock *root)
+{
+ RemoveArrayLengthTraverser traverser;
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundArrayLength())
+ traverser.updateTree();
+ } while (traverser.foundArrayLength());
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.h b/src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.h
new file mode 100644
index 0000000000..3b2c6df824
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveArrayLengthMethod.h:
+// Fold array length expressions, including cases where the "this" node has side effects.
+// Example:
+// int i = (a = b).length();
+// int j = (func()).length();
+// becomes:
+// (a = b);
+// int i = <constant array length>;
+// func();
+// int j = <constant array length>;
+//
+// Must be run after SplitSequenceOperator, SimplifyLoopConditions and SeparateDeclarations steps
+// have been done to expressions containing calls of the array length method.
+//
+// Does nothing to length method calls done on runtime-sized arrays.
+
+namespace sh
+{
+
+class TIntermBlock;
+
+void RemoveArrayLengthMethod(TIntermBlock *root);
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp
index 74814f22a7..7766c1abc6 100644
--- a/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp
@@ -9,14 +9,20 @@
#include "compiler/translator/RemoveDynamicIndexing.h"
+#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/SymbolTable.h"
+namespace sh
+{
+
namespace
{
-TName GetIndexFunctionName(const TType &type, bool write)
+std::string GetIndexFunctionName(const TType &type, bool write)
{
TInfoSinkBase nameSink;
nameSink << "dyn_index_";
@@ -49,31 +55,29 @@ TName GetIndexFunctionName(const TType &type, bool write)
}
nameSink << type.getNominalSize();
}
- TString nameString = TFunction::mangleName(nameSink.c_str());
- TName name(nameString);
- name.setInternal(true);
- return name;
+ return nameSink.str();
}
-TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier)
+TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier, TSymbolTable *symbolTable)
{
- TIntermSymbol *symbol = new TIntermSymbol(0, "base", type);
+ TIntermSymbol *symbol = new TIntermSymbol(symbolTable->nextUniqueId(), "base", type);
symbol->setInternal(true);
symbol->getTypePointer()->setQualifier(qualifier);
return symbol;
}
-TIntermSymbol *CreateIndexSymbol()
+TIntermSymbol *CreateIndexSymbol(TSymbolTable *symbolTable)
{
- TIntermSymbol *symbol = new TIntermSymbol(0, "index", TType(EbtInt, EbpHigh));
+ TIntermSymbol *symbol =
+ new TIntermSymbol(symbolTable->nextUniqueId(), "index", TType(EbtInt, EbpHigh));
symbol->setInternal(true);
symbol->getTypePointer()->setQualifier(EvqIn);
return symbol;
}
-TIntermSymbol *CreateValueSymbol(const TType &type)
+TIntermSymbol *CreateValueSymbol(const TType &type, TSymbolTable *symbolTable)
{
- TIntermSymbol *symbol = new TIntermSymbol(0, "value", type);
+ TIntermSymbol *symbol = new TIntermSymbol(symbolTable->nextUniqueId(), "value", type);
symbol->setInternal(true);
symbol->getTypePointer()->setQualifier(EvqIn);
return symbol;
@@ -86,38 +90,14 @@ TIntermConstantUnion *CreateIntConstantNode(int i)
return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
}
-TIntermBinary *CreateIndexDirectBaseSymbolNode(const TType &indexedType,
- const TType &fieldType,
- const int index,
- TQualifier baseQualifier)
-{
- TIntermBinary *indexNode = new TIntermBinary(EOpIndexDirect);
- indexNode->setType(fieldType);
- TIntermSymbol *baseSymbol = CreateBaseSymbol(indexedType, baseQualifier);
- indexNode->setLeft(baseSymbol);
- indexNode->setRight(CreateIntConstantNode(index));
- return indexNode;
-}
-
-TIntermBinary *CreateAssignValueSymbolNode(TIntermTyped *targetNode, const TType &assignedValueType)
-{
- TIntermBinary *assignNode = new TIntermBinary(EOpAssign);
- assignNode->setType(assignedValueType);
- assignNode->setLeft(targetNode);
- assignNode->setRight(CreateValueSymbol(assignedValueType));
- return assignNode;
-}
-
TIntermTyped *EnsureSignedInt(TIntermTyped *node)
{
if (node->getBasicType() == EbtInt)
return node;
- TIntermAggregate *convertedNode = new TIntermAggregate(EOpConstructInt);
- convertedNode->setType(TType(EbtInt));
- convertedNode->getSequence()->push_back(node);
- convertedNode->setPrecisionFromChildren();
- return convertedNode;
+ TIntermSequence *arguments = new TIntermSequence();
+ arguments->push_back(node);
+ return TIntermAggregate::CreateConstructor(TType(EbtInt), arguments);
}
TType GetFieldType(const TType &indexedType)
@@ -177,7 +157,10 @@ TType GetFieldType(const TType &indexedType)
// base[1] = value;
// }
// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
-TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write)
+TIntermFunctionDefinition *GetIndexFunctionDefinition(TType type,
+ bool write,
+ const TSymbolUniqueId &functionId,
+ TSymbolTable *symbolTable)
{
ASSERT(!type.isArray());
// Conservatively use highp here, even if the indexed type is not highp. That way the code can't
@@ -185,11 +168,9 @@ TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write)
// highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
// principle this code could be used with multiple backends.
type.setPrecision(EbpHigh);
- TIntermAggregate *indexingFunction = new TIntermAggregate(EOpFunction);
- indexingFunction->setNameObj(GetIndexFunctionName(type, write));
TType fieldType = GetFieldType(type);
- int numCases = 0;
+ int numCases = 0;
if (type.isMatrix())
{
numCases = type.getCols();
@@ -198,41 +179,43 @@ TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write)
{
numCases = type.getNominalSize();
}
- if (write)
- {
- indexingFunction->setType(TType(EbtVoid));
- }
- else
+
+ TType returnType(EbtVoid);
+ if (!write)
{
- indexingFunction->setType(fieldType);
+ returnType = fieldType;
}
- TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
- TQualifier baseQualifier = EvqInOut;
+ std::string functionName = GetIndexFunctionName(type, write);
+ TIntermFunctionPrototype *prototypeNode =
+ CreateInternalFunctionPrototypeNode(returnType, functionName.c_str(), functionId);
+
+ TQualifier baseQualifier = EvqInOut;
if (!write)
baseQualifier = EvqIn;
- TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier);
- paramsNode->getSequence()->push_back(baseParam);
- TIntermSymbol *indexParam = CreateIndexSymbol();
- paramsNode->getSequence()->push_back(indexParam);
+ TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier, symbolTable);
+ prototypeNode->getSequence()->push_back(baseParam);
+ TIntermSymbol *indexParam = CreateIndexSymbol(symbolTable);
+ prototypeNode->getSequence()->push_back(indexParam);
+ TIntermSymbol *valueParam = nullptr;
if (write)
{
- TIntermSymbol *valueParam = CreateValueSymbol(fieldType);
- paramsNode->getSequence()->push_back(valueParam);
+ valueParam = CreateValueSymbol(fieldType, symbolTable);
+ prototypeNode->getSequence()->push_back(valueParam);
}
- indexingFunction->getSequence()->push_back(paramsNode);
- TIntermAggregate *statementList = new TIntermAggregate(EOpSequence);
+ TIntermBlock *statementList = new TIntermBlock();
for (int i = 0; i < numCases; ++i)
{
TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
statementList->getSequence()->push_back(caseNode);
TIntermBinary *indexNode =
- CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier);
+ new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(i));
if (write)
{
- TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType);
+ TIntermBinary *assignNode =
+ new TIntermBinary(EOpAssign, indexNode, valueParam->deepCopy());
statementList->getSequence()->push_back(assignNode);
TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
statementList->getSequence()->push_back(returnNode);
@@ -250,32 +233,33 @@ TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write)
TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
statementList->getSequence()->push_back(breakNode);
- TIntermSwitch *switchNode = new TIntermSwitch(CreateIndexSymbol(), statementList);
+ TIntermSwitch *switchNode = new TIntermSwitch(indexParam->deepCopy(), statementList);
- TIntermAggregate *bodyNode = new TIntermAggregate(EOpSequence);
+ TIntermBlock *bodyNode = new TIntermBlock();
bodyNode->getSequence()->push_back(switchNode);
- TIntermBinary *cond = new TIntermBinary(EOpLessThan);
+ TIntermBinary *cond =
+ new TIntermBinary(EOpLessThan, indexParam->deepCopy(), CreateIntConstantNode(0));
cond->setType(TType(EbtBool, EbpUndefined));
- cond->setLeft(CreateIndexSymbol());
- cond->setRight(CreateIntConstantNode(0));
// Two blocks: one accesses (either reads or writes) the first element and returns,
// the other accesses the last element.
- TIntermAggregate *useFirstBlock = new TIntermAggregate(EOpSequence);
- TIntermAggregate *useLastBlock = new TIntermAggregate(EOpSequence);
+ TIntermBlock *useFirstBlock = new TIntermBlock();
+ TIntermBlock *useLastBlock = new TIntermBlock();
TIntermBinary *indexFirstNode =
- CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier);
+ new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(0));
TIntermBinary *indexLastNode =
- CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier);
+ new TIntermBinary(EOpIndexDirect, baseParam->deepCopy(), CreateIndexNode(numCases - 1));
if (write)
{
- TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType);
+ TIntermBinary *assignFirstNode =
+ new TIntermBinary(EOpAssign, indexFirstNode, valueParam->deepCopy());
useFirstBlock->getSequence()->push_back(assignFirstNode);
TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
useFirstBlock->getSequence()->push_back(returnNode);
- TIntermBinary *assignLastNode = CreateAssignValueSymbolNode(indexLastNode, fieldType);
+ TIntermBinary *assignLastNode =
+ new TIntermBinary(EOpAssign, indexLastNode, valueParam->deepCopy());
useLastBlock->getSequence()->push_back(assignLastNode);
}
else
@@ -286,19 +270,21 @@ TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write)
TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
useLastBlock->getSequence()->push_back(returnLastNode);
}
- TIntermSelection *ifNode = new TIntermSelection(cond, useFirstBlock, nullptr);
+ TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
bodyNode->getSequence()->push_back(ifNode);
bodyNode->getSequence()->push_back(useLastBlock);
- indexingFunction->getSequence()->push_back(bodyNode);
-
+ TIntermFunctionDefinition *indexingFunction =
+ new TIntermFunctionDefinition(prototypeNode, bodyNode);
return indexingFunction;
}
class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
{
public:
- RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, int shaderVersion);
+ RemoveDynamicIndexingTraverser(TSymbolTable *symbolTable,
+ int shaderVersion,
+ PerformanceDiagnostics *perfDiagnostics);
bool visitBinary(Visit visit, TIntermBinary *node) override;
@@ -309,10 +295,11 @@ class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
bool usedTreeInsertion() const { return mUsedTreeInsertion; }
protected:
- // Sets of types that are indexed. Note that these can not store multiple variants
- // of the same type with different precisions - only one precision gets stored.
- std::set<TType> mIndexedVecAndMatrixTypes;
- std::set<TType> mWrittenVecAndMatrixTypes;
+ // Maps of types that are indexed to the indexing function ids used for them. Note that these
+ // can not store multiple variants of the same type with different precisions - only one
+ // precision gets stored.
+ std::map<TType, TSymbolUniqueId *> mIndexedVecAndMatrixTypes;
+ std::map<TType, TSymbolUniqueId *> mWrittenVecAndMatrixTypes;
bool mUsedTreeInsertion;
@@ -321,62 +308,74 @@ class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
// V[j++][i]++.
// where V is an array of vectors, j++ will only be evaluated once.
bool mRemoveIndexSideEffectsInSubtree;
+
+ PerformanceDiagnostics *mPerfDiagnostics;
};
-RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable,
- int shaderVersion)
+RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ PerformanceDiagnostics *perfDiagnostics)
: TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
mUsedTreeInsertion(false),
- mRemoveIndexSideEffectsInSubtree(false)
+ mRemoveIndexSideEffectsInSubtree(false),
+ mPerfDiagnostics(perfDiagnostics)
{
}
void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
{
- TIntermAggregate *rootAgg = root->getAsAggregate();
- ASSERT(rootAgg != nullptr && rootAgg->getOp() == EOpSequence);
+ TIntermBlock *rootBlock = root->getAsBlock();
+ ASSERT(rootBlock != nullptr);
TIntermSequence insertions;
- for (TType type : mIndexedVecAndMatrixTypes)
+ for (auto &type : mIndexedVecAndMatrixTypes)
{
- insertions.push_back(GetIndexFunctionDefinition(type, false));
+ insertions.push_back(
+ GetIndexFunctionDefinition(type.first, false, *type.second, mSymbolTable));
}
- for (TType type : mWrittenVecAndMatrixTypes)
+ for (auto &type : mWrittenVecAndMatrixTypes)
{
- insertions.push_back(GetIndexFunctionDefinition(type, true));
+ insertions.push_back(
+ GetIndexFunctionDefinition(type.first, true, *type.second, mSymbolTable));
}
- mInsertions.push_back(NodeInsertMultipleEntry(rootAgg, 0, insertions, TIntermSequence()));
+ rootBlock->insertChildNodes(0, insertions);
}
// Create a call to dyn_index_*() based on an indirect indexing op node
TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
- TIntermTyped *indexedNode,
- TIntermTyped *index)
+ TIntermTyped *index,
+ const TSymbolUniqueId &functionId)
{
ASSERT(node->getOp() == EOpIndexIndirect);
- TIntermAggregate *indexingCall = new TIntermAggregate(EOpFunctionCall);
+ TIntermSequence *arguments = new TIntermSequence();
+ arguments->push_back(node->getLeft());
+ arguments->push_back(index);
+
+ TType fieldType = GetFieldType(node->getLeft()->getType());
+ std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), false);
+ TIntermAggregate *indexingCall =
+ CreateInternalFunctionCallNode(fieldType, functionName.c_str(), functionId, arguments);
indexingCall->setLine(node->getLine());
- indexingCall->setUserDefined();
- indexingCall->setNameObj(GetIndexFunctionName(indexedNode->getType(), false));
- indexingCall->getSequence()->push_back(indexedNode);
- indexingCall->getSequence()->push_back(index);
-
- TType fieldType = GetFieldType(indexedNode->getType());
- indexingCall->setType(fieldType);
+ indexingCall->getFunctionSymbolInfo()->setKnownToNotHaveSideEffects(true);
return indexingCall;
}
TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
TIntermTyped *index,
- TIntermTyped *writtenValue)
+ TIntermTyped *writtenValue,
+ const TSymbolUniqueId &functionId)
{
- // Deep copy the left node so that two pointers to the same node don't end up in the tree.
- TIntermNode *leftCopy = node->getLeft()->deepCopy();
- ASSERT(leftCopy != nullptr && leftCopy->getAsTyped() != nullptr);
+ ASSERT(node->getOp() == EOpIndexIndirect);
+ TIntermSequence *arguments = new TIntermSequence();
+ // Deep copy the child nodes so that two pointers to the same node don't end up in the tree.
+ arguments->push_back(node->getLeft()->deepCopy());
+ arguments->push_back(index->deepCopy());
+ arguments->push_back(writtenValue);
+
+ std::string functionName = GetIndexFunctionName(node->getLeft()->getType(), true);
TIntermAggregate *indexedWriteCall =
- CreateIndexFunctionCall(node, leftCopy->getAsTyped(), index);
- indexedWriteCall->setNameObj(GetIndexFunctionName(node->getLeft()->getType(), true));
- indexedWriteCall->setType(TType(EbtVoid));
- indexedWriteCall->getSequence()->push_back(writtenValue);
+ CreateInternalFunctionCallNode(TType(EbtVoid), functionName.c_str(), functionId, arguments);
+ indexedWriteCall->setLine(node->getLine());
return indexedWriteCall;
}
@@ -397,23 +396,40 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod
// Now v_expr[s0] can be safely executed several times without unintended side effects.
// Init the temp variable holding the index
- TIntermAggregate *initIndex = createTempInitDeclaration(node->getRight());
- TIntermSequence insertions;
- insertions.push_back(initIndex);
- insertStatementsInParentBlock(insertions);
+ TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
+ insertStatementInParentBlock(initIndex);
mUsedTreeInsertion = true;
// Replace the index with the temp variable
TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
- NodeUpdateEntry replaceIndex(node, node->getRight(), tempIndex, false);
- mReplacements.push_back(replaceIndex);
+ queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
}
- else if (!node->getLeft()->isArray() && node->getLeft()->getBasicType() != EbtStruct)
+ else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
{
+ mPerfDiagnostics->warning(node->getLine(),
+ "Performance: dynamic indexing of vectors and "
+ "matrices is emulated and can be slow.",
+ "[]");
bool write = isLValueRequiredHere();
- TType type = node->getLeft()->getType();
- mIndexedVecAndMatrixTypes.insert(type);
+#if defined(ANGLE_ENABLE_ASSERTS)
+ // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
+ // implemented checks in this traverser.
+ IntermNodePatternMatcher matcher(
+ IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
+ ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
+#endif
+
+ const TType &type = node->getLeft()->getType();
+ TSymbolUniqueId *indexingFunctionId = new TSymbolUniqueId(mSymbolTable);
+ if (mIndexedVecAndMatrixTypes.find(type) == mIndexedVecAndMatrixTypes.end())
+ {
+ mIndexedVecAndMatrixTypes[type] = indexingFunctionId;
+ }
+ else
+ {
+ indexingFunctionId = mIndexedVecAndMatrixTypes[type];
+ }
if (write)
{
@@ -432,10 +448,30 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod
mRemoveIndexSideEffectsInSubtree = true;
return true;
}
+
+ TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
+ if (leftBinary != nullptr &&
+ IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
+ {
+ // This is a case like:
+ // mat2 m;
+ // m[a][b]++;
+ // Process the child node m[a] first.
+ return true;
+ }
+
// TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
// only writes it and doesn't need the previous value. http://anglebug.com/1116
- mWrittenVecAndMatrixTypes.insert(type);
+ TSymbolUniqueId *indexedWriteFunctionId = new TSymbolUniqueId(mSymbolTable);
+ if (mWrittenVecAndMatrixTypes.find(type) == mWrittenVecAndMatrixTypes.end())
+ {
+ mWrittenVecAndMatrixTypes[type] = indexedWriteFunctionId;
+ }
+ else
+ {
+ indexedWriteFunctionId = mWrittenVecAndMatrixTypes[type];
+ }
TType fieldType = GetFieldType(type);
TIntermSequence insertionsBefore;
@@ -443,28 +479,26 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod
// Store the index in a temporary signed int variable.
TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
- TIntermAggregate *initIndex = createTempInitDeclaration(indexInitializer);
+ TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
initIndex->setLine(node->getLine());
insertionsBefore.push_back(initIndex);
- TIntermAggregate *indexingCall = CreateIndexFunctionCall(
- node, node->getLeft(), createTempSymbol(indexInitializer->getType()));
-
- // Create a node for referring to the index after the nextTemporaryIndex() call
+ // Create a node for referring to the index after the nextTemporaryId() call
// below.
TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
- nextTemporaryIndex(); // From now on, creating temporary symbols that refer to the
- // field value.
+ TIntermAggregate *indexingCall =
+ CreateIndexFunctionCall(node, tempIndex, *indexingFunctionId);
+
+ nextTemporaryId(); // From now on, creating temporary symbols that refer to the
+ // field value.
insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
- TIntermAggregate *indexedWriteCall =
- CreateIndexedWriteFunctionCall(node, tempIndex, createTempSymbol(fieldType));
+ TIntermAggregate *indexedWriteCall = CreateIndexedWriteFunctionCall(
+ node, tempIndex, createTempSymbol(fieldType), *indexedWriteFunctionId);
insertionsAfter.push_back(indexedWriteCall);
insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
- NodeUpdateEntry replaceIndex(getParentNode(), node, createTempSymbol(fieldType),
- false);
- mReplacements.push_back(replaceIndex);
+ queueReplacement(createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
mUsedTreeInsertion = true;
}
else
@@ -476,9 +510,8 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod
// If the index_expr is unsigned, we'll convert it to signed.
ASSERT(!mRemoveIndexSideEffectsInSubtree);
TIntermAggregate *indexingCall = CreateIndexFunctionCall(
- node, node->getLeft(), EnsureSignedInt(node->getRight()));
- NodeUpdateEntry replaceIndex(getParentNode(), node, indexingCall, false);
- mReplacements.push_back(replaceIndex);
+ node, EnsureSignedInt(node->getRight()), *indexingFunctionId);
+ queueReplacement(indexingCall, OriginalNode::IS_DROPPED);
}
}
}
@@ -489,25 +522,29 @@ void RemoveDynamicIndexingTraverser::nextIteration()
{
mUsedTreeInsertion = false;
mRemoveIndexSideEffectsInSubtree = false;
- nextTemporaryIndex();
+ nextTemporaryId();
}
} // namespace
void RemoveDynamicIndexing(TIntermNode *root,
- unsigned int *temporaryIndex,
- const TSymbolTable &symbolTable,
- int shaderVersion)
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ PerformanceDiagnostics *perfDiagnostics)
{
- RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
- ASSERT(temporaryIndex != nullptr);
- traverser.useTemporaryIndex(temporaryIndex);
+ RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion, perfDiagnostics);
do
{
traverser.nextIteration();
root->traverse(&traverser);
traverser.updateTree();
} while (traverser.usedTreeInsertion());
+ // TODO(oetuaho@nvidia.com): It might be nicer to add the helper definitions also in the middle
+ // of traversal. Now the tree ends up in an inconsistent state in the middle, since there are
+ // function call nodes with no corresponding definition nodes. This needs special handling in
+ // TIntermLValueTrackingTraverser, and creates intricacies that are not easily apparent from a
+ // superficial reading of the code.
traverser.insertHelperDefinitions(root);
- traverser.updateTree();
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h
index ae3a93c9b1..543cf569a6 100644
--- a/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h
@@ -10,12 +10,18 @@
#ifndef COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_
#define COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_
+namespace sh
+{
+
class TIntermNode;
class TSymbolTable;
+class PerformanceDiagnostics;
void RemoveDynamicIndexing(TIntermNode *root,
- unsigned int *temporaryIndex,
- const TSymbolTable &symbolTable,
- int shaderVersion);
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ PerformanceDiagnostics *perfDiagnostics);
+
+} // namespace sh
#endif // COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.cpp
new file mode 100644
index 0000000000..b39c912e9c
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.cpp
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveEmptySwitchStatements.cpp: Remove switch statements that have an empty statement list.
+
+#include "compiler/translator/RemoveEmptySwitchStatements.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class RemoveEmptySwitchStatementsTraverser : public TIntermTraverser
+{
+ public:
+ RemoveEmptySwitchStatementsTraverser() : TIntermTraverser(true, false, false) {}
+
+ bool visitSwitch(Visit visit, TIntermSwitch *node);
+};
+
+bool RemoveEmptySwitchStatementsTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ if (node->getStatementList()->getSequence()->empty())
+ {
+ // Just output the init statement.
+ if (node->getInit()->hasSideEffects())
+ {
+ queueReplacement(node->getInit(), OriginalNode::IS_DROPPED);
+ }
+ else
+ {
+ TIntermSequence emptyReplacement;
+ ASSERT(getParentNode()->getAsBlock());
+ mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(),
+ node, emptyReplacement));
+ }
+ return false; // Nothing inside the child nodes to traverse.
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+void RemoveEmptySwitchStatements(TIntermBlock *root)
+{
+ RemoveEmptySwitchStatementsTraverser traverser;
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.h b/src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.h
new file mode 100644
index 0000000000..1018a50d82
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveEmptySwitchStatements.h: Remove switch statements that have an empty statement list.
+
+#ifndef COMPILER_TRANSLATOR_REMOVEEMPTYSWITCHSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_REMOVEEMPTYSWITCHSTATEMENTS_H_
+
+namespace sh
+{
+class TIntermBlock;
+
+void RemoveEmptySwitchStatements(TIntermBlock *root);
+}
+
+#endif // COMPILER_TRANSLATOR_REMOVEEMPTYSWITCHSTATEMENTS_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp
new file mode 100644
index 0000000000..4b533dc7b5
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp
@@ -0,0 +1,43 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/RemoveInvariantDeclaration.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// An AST traverser that removes invariant declaration for input in fragment shader
+// when GLSL >= 4.20 and for output in vertex shader when GLSL < 4.2.
+class RemoveInvariantDeclarationTraverser : public TIntermTraverser
+{
+ public:
+ RemoveInvariantDeclarationTraverser() : TIntermTraverser(true, false, false) {}
+
+ private:
+ bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override
+ {
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, emptyReplacement));
+ return false;
+ }
+};
+
+} // anonymous namespace
+
+void RemoveInvariantDeclaration(TIntermNode *root)
+{
+ RemoveInvariantDeclarationTraverser traverser;
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.h b/src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.h
new file mode 100644
index 0000000000..cf9d4aa4c2
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_REMOVEINVARIANTDECLARATION_H_
+#define COMPILER_TRANSLATOR_REMOVEINVARIANTDECLARATION_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void RemoveInvariantDeclaration(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVEINVARIANTDECLARATION_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp
new file mode 100644
index 0000000000..b86d64d7a3
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp
@@ -0,0 +1,116 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveNoOpCasesFromEndOfSwitchStatements.cpp: Clean up cases from the end of a switch statement
+// that only contain no-ops.
+
+#include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+bool AreEmptyBlocks(TIntermSequence *statements, size_t i);
+
+bool IsEmptyBlock(TIntermNode *node)
+{
+ TIntermBlock *asBlock = node->getAsBlock();
+ if (asBlock)
+ {
+ if (asBlock->getSequence()->empty())
+ {
+ return true;
+ }
+ return AreEmptyBlocks(asBlock->getSequence(), 0u);
+ }
+ // Empty declarations should have already been pruned, otherwise they would need to be handled
+ // here. Note that declarations for struct types do contain a nameless child node.
+ ASSERT(node->getAsDeclarationNode() == nullptr ||
+ !node->getAsDeclarationNode()->getSequence()->empty());
+ // Pure literal statements should also already be pruned.
+ ASSERT(node->getAsConstantUnion() == nullptr);
+ return false;
+}
+
+// Return true if all statements in "statements" starting from index i consist only of empty blocks
+// and no-op statements. Returns true also if there are no statements.
+bool AreEmptyBlocks(TIntermSequence *statements, size_t i)
+{
+ for (; i < statements->size(); ++i)
+ {
+ if (!IsEmptyBlock(statements->at(i)))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void RemoveNoOpCasesFromEndOfStatementList(TIntermBlock *statementList, TSymbolTable *symbolTable)
+{
+ TIntermSequence *statements = statementList->getSequence();
+
+ bool foundDeadCase = false;
+ do
+ {
+ if (statements->empty())
+ {
+ return;
+ }
+
+ // Find the last case label.
+ size_t i = statements->size();
+ while (i > 0u && !(*statements)[i - 1]->getAsCaseNode())
+ {
+ --i;
+ }
+ // Now i is the index of the first statement following the last label inside the switch
+ // statement.
+ ASSERT(i > 0u);
+
+ foundDeadCase = AreEmptyBlocks(statements, i);
+ if (foundDeadCase)
+ {
+ statements->erase(statements->begin() + (i - 1u), statements->end());
+ }
+ } while (foundDeadCase);
+}
+
+class RemoveNoOpCasesFromEndOfSwitchTraverser : public TIntermTraverser
+{
+ public:
+ RemoveNoOpCasesFromEndOfSwitchTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable)
+ {
+ }
+
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override;
+};
+
+bool RemoveNoOpCasesFromEndOfSwitchTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ // Here we may mutate the statement list, but it's safe since traversal has not yet reached
+ // there.
+ RemoveNoOpCasesFromEndOfStatementList(node->getStatementList(), mSymbolTable);
+ // Handle also nested switch statements.
+ return true;
+}
+
+} // anonymous namespace
+
+void RemoveNoOpCasesFromEndOfSwitchStatements(TIntermBlock *root, TSymbolTable *symbolTable)
+{
+ RemoveNoOpCasesFromEndOfSwitchTraverser traverser(symbolTable);
+ root->traverse(&traverser);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h b/src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h
new file mode 100644
index 0000000000..ebd75eb774
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveNoOpCasesFromEndOfSwitchStatements.h: Clean up cases from the end of a switch statement
+// that only contain no-ops.
+
+#ifndef COMPILER_TRANSLATOR_REMOVENOOPCASESFROMENDOFSWITCHSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_REMOVENOOPCASESFROMENDOFSWITCHSTATEMENTS_H_
+
+namespace sh
+{
+class TIntermBlock;
+class TSymbolTable;
+
+void RemoveNoOpCasesFromEndOfSwitchStatements(TIntermBlock *root, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVENOOPCASESFROMENDOFSWITCHSTATEMENTS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp b/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp
index 6dbb48da9c..b2cdac55f0 100644
--- a/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp
@@ -11,7 +11,10 @@
#include "compiler/translator/RemovePow.h"
#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
namespace
{
@@ -43,8 +46,7 @@ class RemovePowTraverser : public TIntermTraverser
};
RemovePowTraverser::RemovePowTraverser()
- : TIntermTraverser(true, false, false),
- mNeedAnotherIteration(false)
+ : TIntermTraverser(true, false, false), mNeedAnotherIteration(false)
{
}
@@ -52,31 +54,20 @@ bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
if (IsProblematicPow(node))
{
- TInfoSink nullSink;
-
TIntermTyped *x = node->getSequence()->at(0)->getAsTyped();
TIntermTyped *y = node->getSequence()->at(1)->getAsTyped();
- TIntermUnary *log = new TIntermUnary(EOpLog2);
- log->setOperand(x);
+ TIntermUnary *log = new TIntermUnary(EOpLog2, x);
log->setLine(node->getLine());
- log->setType(x->getType());
- TIntermBinary *mul = new TIntermBinary(EOpMul);
- mul->setLeft(y);
- mul->setRight(log);
+ TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType());
+ TIntermBinary *mul = new TIntermBinary(op, y, log);
mul->setLine(node->getLine());
- bool valid = mul->promote(nullSink);
- UNUSED_ASSERTION_VARIABLE(valid);
- ASSERT(valid);
- TIntermUnary *exp = new TIntermUnary(EOpExp2);
- exp->setOperand(mul);
+ TIntermUnary *exp = new TIntermUnary(EOpExp2, mul);
exp->setLine(node->getLine());
- exp->setType(node->getType());
- NodeUpdateEntry replacePow(getParentNode(), node, exp, false);
- mReplacements.push_back(replacePow);
+ queueReplacement(exp, OriginalNode::IS_DROPPED);
// If the x parameter also needs to be replaced, we need to do that in another traversal,
// since it's parent node will change in a way that's not handled correctly by updateTree().
@@ -89,7 +80,7 @@ bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
return true;
}
-} // namespace
+} // namespace
void RemovePow(TIntermNode *root)
{
@@ -100,6 +91,7 @@ void RemovePow(TIntermNode *root)
traverser.nextIteration();
root->traverse(&traverser);
traverser.updateTree();
- }
- while (traverser.needAnotherIteration());
+ } while (traverser.needAnotherIteration());
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemovePow.h b/src/3rdparty/angle/src/compiler/translator/RemovePow.h
index 40f9d672b2..3cd84988a9 100644
--- a/src/3rdparty/angle/src/compiler/translator/RemovePow.h
+++ b/src/3rdparty/angle/src/compiler/translator/RemovePow.h
@@ -11,8 +11,11 @@
#ifndef COMPILER_TRANSLATOR_REMOVEPOW_H_
#define COMPILER_TRANSLATOR_REMOVEPOW_H_
+namespace sh
+{
class TIntermNode;
void RemovePow(TIntermNode *root);
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_REMOVEPOW_H_
+#endif // COMPILER_TRANSLATOR_REMOVEPOW_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp
index b278b53436..dea949f448 100644
--- a/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp
@@ -3,36 +3,91 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// RemoveSwitchFallThrough.cpp: Remove fall-through from switch statements.
+// Note that it is unsafe to do further AST transformations on the AST generated
+// by this function. It leaves duplicate nodes in the AST making replacements
+// unreliable.
#include "compiler/translator/RemoveSwitchFallThrough.h"
-TIntermAggregate *RemoveSwitchFallThrough::removeFallThrough(TIntermAggregate *statementList)
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class RemoveSwitchFallThroughTraverser : public TIntermTraverser
+{
+ public:
+ static TIntermBlock *removeFallThrough(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics);
+
+ private:
+ RemoveSwitchFallThroughTraverser(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics);
+
+ void visitSymbol(TIntermSymbol *node) override;
+ void visitConstantUnion(TIntermConstantUnion *node) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+ bool visitBinary(Visit, TIntermBinary *node) override;
+ bool visitUnary(Visit, TIntermUnary *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitSwizzle(Visit, TIntermSwizzle *node) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override;
+ bool visitSwitch(Visit, TIntermSwitch *node) override;
+ bool visitCase(Visit, TIntermCase *node) override;
+ bool visitAggregate(Visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit, TIntermBlock *node) override;
+ bool visitLoop(Visit, TIntermLoop *node) override;
+ bool visitBranch(Visit, TIntermBranch *node) override;
+
+ void outputSequence(TIntermSequence *sequence, size_t startIndex);
+ void handlePreviousCase();
+
+ TIntermBlock *mStatementList;
+ TIntermBlock *mStatementListOut;
+ bool mLastStatementWasBreak;
+ TIntermBlock *mPreviousCase;
+ std::vector<TIntermBlock *> mCasesSharingBreak;
+ PerformanceDiagnostics *mPerfDiagnostics;
+};
+
+TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(
+ TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics)
{
- RemoveSwitchFallThrough rm(statementList);
+ RemoveSwitchFallThroughTraverser rm(statementList, perfDiagnostics);
ASSERT(statementList);
statementList->traverse(&rm);
- bool lastStatementWasBreak = rm.mLastStatementWasBreak;
- rm.mLastStatementWasBreak = true;
- rm.handlePreviousCase();
- if (!lastStatementWasBreak)
+ ASSERT(rm.mPreviousCase || statementList->getSequence()->empty());
+ if (!rm.mLastStatementWasBreak && rm.mPreviousCase)
{
+ // Make sure that there's a branch at the end of the final case inside the switch statement.
+ // This also ensures that any cases that fall through to the final case will get the break.
TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
- rm.mStatementListOut->getSequence()->push_back(finalBreak);
+ rm.mPreviousCase->getSequence()->push_back(finalBreak);
+ rm.mLastStatementWasBreak = true;
}
+ rm.handlePreviousCase();
return rm.mStatementListOut;
}
-RemoveSwitchFallThrough::RemoveSwitchFallThrough(TIntermAggregate *statementList)
+RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(
+ TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics)
: TIntermTraverser(true, false, false),
mStatementList(statementList),
mLastStatementWasBreak(false),
- mPreviousCase(nullptr)
+ mPreviousCase(nullptr),
+ mPerfDiagnostics(perfDiagnostics)
{
- mStatementListOut = new TIntermAggregate();
- mStatementListOut->setOp(EOpSequence);
+ mStatementListOut = new TIntermBlock();
}
-void RemoveSwitchFallThrough::visitSymbol(TIntermSymbol *node)
+void RemoveSwitchFallThroughTraverser::visitSymbol(TIntermSymbol *node)
{
// Note that this assumes that switch statements which don't begin by a case statement
// have already been weeded out in validation.
@@ -40,36 +95,57 @@ void RemoveSwitchFallThrough::visitSymbol(TIntermSymbol *node)
mLastStatementWasBreak = false;
}
-void RemoveSwitchFallThrough::visitConstantUnion(TIntermConstantUnion *node)
+void RemoveSwitchFallThroughTraverser::visitConstantUnion(TIntermConstantUnion *node)
+{
+ // Conditions of case labels are not traversed, so this is a constant statement like "0;".
+ // These are no-ops so there's no need to add them back to the statement list. Should have
+ // already been pruned out of the AST, in fact.
+ UNREACHABLE();
+}
+
+bool RemoveSwitchFallThroughTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitBinary(Visit, TIntermBinary *node)
{
- // Conditions of case labels are not traversed, so this is some other constant
- // Could be just a statement like "0;"
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
+ return false;
}
-bool RemoveSwitchFallThrough::visitBinary(Visit, TIntermBinary *node)
+bool RemoveSwitchFallThroughTraverser::visitUnary(Visit, TIntermUnary *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
-bool RemoveSwitchFallThrough::visitUnary(Visit, TIntermUnary *node)
+bool RemoveSwitchFallThroughTraverser::visitTernary(Visit, TIntermTernary *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
-bool RemoveSwitchFallThrough::visitSelection(Visit, TIntermSelection *node)
+bool RemoveSwitchFallThroughTraverser::visitSwizzle(Visit, TIntermSwizzle *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
-bool RemoveSwitchFallThrough::visitSwitch(Visit, TIntermSwitch *node)
+bool RemoveSwitchFallThroughTraverser::visitIfElse(Visit, TIntermIfElse *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitSwitch(Visit, TIntermSwitch *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
@@ -77,7 +153,7 @@ bool RemoveSwitchFallThrough::visitSwitch(Visit, TIntermSwitch *node)
return false;
}
-void RemoveSwitchFallThrough::outputSequence(TIntermSequence *sequence, size_t startIndex)
+void RemoveSwitchFallThroughTraverser::outputSequence(TIntermSequence *sequence, size_t startIndex)
{
for (size_t i = startIndex; i < sequence->size(); ++i)
{
@@ -85,20 +161,16 @@ void RemoveSwitchFallThrough::outputSequence(TIntermSequence *sequence, size_t s
}
}
-void RemoveSwitchFallThrough::handlePreviousCase()
+void RemoveSwitchFallThroughTraverser::handlePreviousCase()
{
if (mPreviousCase)
mCasesSharingBreak.push_back(mPreviousCase);
if (mLastStatementWasBreak)
{
- bool labelsWithNoStatements = true;
for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
{
- if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
- {
- labelsWithNoStatements = false;
- }
- if (labelsWithNoStatements)
+ ASSERT(!mCasesSharingBreak.at(i)->getSequence()->empty());
+ if (mCasesSharingBreak.at(i)->getSequence()->size() == 1)
{
// Fall-through is allowed in case the label has no statements.
outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
@@ -106,52 +178,93 @@ void RemoveSwitchFallThrough::handlePreviousCase()
else
{
// Include all the statements that this case can fall through under the same label.
+ if (mCasesSharingBreak.size() > i + 1u)
+ {
+ mPerfDiagnostics->warning(mCasesSharingBreak.at(i)->getLine(),
+ "Performance: non-empty fall-through cases in "
+ "switch statements generate extra code.",
+ "switch");
+ }
for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
{
- size_t startIndex = j > i ? 1 : 0; // Add the label only from the first sequence.
+ size_t startIndex =
+ j > i ? 1 : 0; // Add the label only from the first sequence.
outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
-
}
}
}
mCasesSharingBreak.clear();
}
mLastStatementWasBreak = false;
- mPreviousCase = nullptr;
+ mPreviousCase = nullptr;
}
-bool RemoveSwitchFallThrough::visitCase(Visit, TIntermCase *node)
+bool RemoveSwitchFallThroughTraverser::visitCase(Visit, TIntermCase *node)
{
handlePreviousCase();
- mPreviousCase = new TIntermAggregate();
- mPreviousCase->setOp(EOpSequence);
+ mPreviousCase = new TIntermBlock();
mPreviousCase->getSequence()->push_back(node);
+ mPreviousCase->setLine(node->getLine());
// Don't traverse the condition of the case statement
return false;
}
-bool RemoveSwitchFallThrough::visitAggregate(Visit, TIntermAggregate *node)
+bool RemoveSwitchFallThroughTraverser::visitAggregate(Visit, TIntermAggregate *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool DoesBlockAlwaysBreak(TIntermBlock *node)
+{
+ if (node->getSequence()->empty())
+ {
+ return false;
+ }
+
+ TIntermBlock *lastStatementAsBlock = node->getSequence()->back()->getAsBlock();
+ if (lastStatementAsBlock)
+ {
+ return DoesBlockAlwaysBreak(lastStatementAsBlock);
+ }
+
+ TIntermBranch *lastStatementAsBranch = node->getSequence()->back()->getAsBranchNode();
+ return lastStatementAsBranch != nullptr;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitBlock(Visit, TIntermBlock *node)
{
if (node != mStatementList)
{
mPreviousCase->getSequence()->push_back(node);
- mLastStatementWasBreak = false;
+ mLastStatementWasBreak = DoesBlockAlwaysBreak(node);
return false;
}
return true;
}
-bool RemoveSwitchFallThrough::visitLoop(Visit, TIntermLoop *node)
+bool RemoveSwitchFallThroughTraverser::visitLoop(Visit, TIntermLoop *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
-bool RemoveSwitchFallThrough::visitBranch(Visit, TIntermBranch *node)
+bool RemoveSwitchFallThroughTraverser::visitBranch(Visit, TIntermBranch *node)
{
mPreviousCase->getSequence()->push_back(node);
// TODO: Verify that accepting return or continue statements here doesn't cause problems.
mLastStatementWasBreak = true;
return false;
}
+
+} // anonymous namespace
+
+TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics)
+{
+ return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList, perfDiagnostics);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.h b/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.h
index db8699327c..7a3b1963f2 100644
--- a/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.h
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveSwitchFallThrough.h
@@ -3,41 +3,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// RemoveSwitchFallThrough.h: Remove fall-through from switch statements.
+// Note that it is unsafe to do further AST transformations on the AST generated
+// by this function. It leaves duplicate nodes in the AST making replacements
+// unreliable.
#ifndef COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_
#define COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_
-#include "compiler/translator/IntermNode.h"
-
-class RemoveSwitchFallThrough : public TIntermTraverser
+namespace sh
{
- public:
- // When given a statementList from a switch AST node, return an updated
- // statementList that has fall-through removed.
- static TIntermAggregate *removeFallThrough(TIntermAggregate *statementList);
-
- private:
- RemoveSwitchFallThrough(TIntermAggregate *statementList);
- void visitSymbol(TIntermSymbol *node) override;
- void visitConstantUnion(TIntermConstantUnion *node) override;
- bool visitBinary(Visit, TIntermBinary *node) override;
- bool visitUnary(Visit, TIntermUnary *node) override;
- bool visitSelection(Visit visit, TIntermSelection *node) override;
- bool visitSwitch(Visit, TIntermSwitch *node) override;
- bool visitCase(Visit, TIntermCase *node) override;
- bool visitAggregate(Visit, TIntermAggregate *node) override;
- bool visitLoop(Visit, TIntermLoop *node) override;
- bool visitBranch(Visit, TIntermBranch *node) override;
+class TIntermBlock;
+class PerformanceDiagnostics;
- void outputSequence(TIntermSequence *sequence, size_t startIndex);
- void handlePreviousCase();
+// When given a statementList from a switch AST node, return an updated
+// statementList that has fall-through removed.
+TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics);
- TIntermAggregate *mStatementList;
- TIntermAggregate *mStatementListOut;
- bool mLastStatementWasBreak;
- TIntermAggregate *mPreviousCase;
- std::vector<TIntermAggregate *> mCasesSharingBreak;
-};
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_
+#endif // COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.cpp
new file mode 100644
index 0000000000..74b5e73f71
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.cpp
@@ -0,0 +1,358 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveUnreferencedVariables.cpp:
+// Drop variables that are declared but never referenced in the AST. This avoids adding unnecessary
+// initialization code for them. Also removes unreferenced struct types.
+//
+
+#include "compiler/translator/RemoveUnreferencedVariables.h"
+
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class CollectVariableRefCountsTraverser : public TIntermTraverser
+{
+ public:
+ CollectVariableRefCountsTraverser();
+
+ using RefCountMap = std::unordered_map<int, unsigned int>;
+ RefCountMap &getSymbolIdRefCounts() { return mSymbolIdRefCounts; }
+ RefCountMap &getStructIdRefCounts() { return mStructIdRefCounts; }
+
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) override;
+
+ private:
+ void incrementStructTypeRefCount(const TType &type);
+
+ RefCountMap mSymbolIdRefCounts;
+
+ // Structure reference counts are counted from symbols, constructors, function calls, function
+ // return values and from interface block and structure fields. We need to track both function
+ // calls and function return values since there's a compiler option not to prune unused
+ // functions. The type of a constant union may also be a struct, but statements that are just a
+ // constant union are always pruned, and if the constant union is used somehow it will get
+ // counted by something else.
+ RefCountMap mStructIdRefCounts;
+};
+
+CollectVariableRefCountsTraverser::CollectVariableRefCountsTraverser()
+ : TIntermTraverser(true, false, false)
+{
+}
+
+void CollectVariableRefCountsTraverser::incrementStructTypeRefCount(const TType &type)
+{
+ if (type.isInterfaceBlock())
+ {
+ const auto *block = type.getInterfaceBlock();
+ ASSERT(block);
+
+ // We can end up incrementing ref counts of struct types referenced from an interface block
+ // multiple times for the same block. This doesn't matter, because interface blocks can't be
+ // pruned so we'll never do the reverse operation.
+ for (const auto &field : block->fields())
+ {
+ ASSERT(!field->type()->isInterfaceBlock());
+ incrementStructTypeRefCount(*field->type());
+ }
+ return;
+ }
+
+ const auto *structure = type.getStruct();
+ if (structure != nullptr)
+ {
+ auto structIter = mStructIdRefCounts.find(structure->uniqueId());
+ if (structIter == mStructIdRefCounts.end())
+ {
+ mStructIdRefCounts[structure->uniqueId()] = 1u;
+
+ for (const auto &field : structure->fields())
+ {
+ incrementStructTypeRefCount(*field->type());
+ }
+
+ return;
+ }
+ ++(structIter->second);
+ }
+}
+
+void CollectVariableRefCountsTraverser::visitSymbol(TIntermSymbol *node)
+{
+ incrementStructTypeRefCount(node->getType());
+
+ auto iter = mSymbolIdRefCounts.find(node->getId());
+ if (iter == mSymbolIdRefCounts.end())
+ {
+ mSymbolIdRefCounts[node->getId()] = 1u;
+ return;
+ }
+ ++(iter->second);
+}
+
+bool CollectVariableRefCountsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ // This tracks struct references in both function calls and constructors.
+ incrementStructTypeRefCount(node->getType());
+ return true;
+}
+
+bool CollectVariableRefCountsTraverser::visitFunctionPrototype(Visit visit,
+ TIntermFunctionPrototype *node)
+{
+ incrementStructTypeRefCount(node->getType());
+ return true;
+}
+
+// Traverser that removes all unreferenced variables on one traversal.
+class RemoveUnreferencedVariablesTraverser : public TIntermTraverser
+{
+ public:
+ RemoveUnreferencedVariablesTraverser(
+ CollectVariableRefCountsTraverser::RefCountMap *symbolIdRefCounts,
+ CollectVariableRefCountsTraverser::RefCountMap *structIdRefCounts,
+ TSymbolTable *symbolTable);
+
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ // Traverse loop and block nodes in reverse order. Note that this traverser does not track
+ // parent block positions, so insertStatementInParentBlock is unusable!
+ void traverseBlock(TIntermBlock *block) override;
+ void traverseLoop(TIntermLoop *loop) override;
+
+ private:
+ void removeVariableDeclaration(TIntermDeclaration *node, TIntermTyped *declarator);
+ void decrementStructTypeRefCount(const TType &type);
+
+ CollectVariableRefCountsTraverser::RefCountMap *mSymbolIdRefCounts;
+ CollectVariableRefCountsTraverser::RefCountMap *mStructIdRefCounts;
+ bool mRemoveReferences;
+};
+
+RemoveUnreferencedVariablesTraverser::RemoveUnreferencedVariablesTraverser(
+ CollectVariableRefCountsTraverser::RefCountMap *symbolIdRefCounts,
+ CollectVariableRefCountsTraverser::RefCountMap *structIdRefCounts,
+ TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable),
+ mSymbolIdRefCounts(symbolIdRefCounts),
+ mStructIdRefCounts(structIdRefCounts),
+ mRemoveReferences(false)
+{
+}
+
+void RemoveUnreferencedVariablesTraverser::decrementStructTypeRefCount(const TType &type)
+{
+ auto *structure = type.getStruct();
+ if (structure != nullptr)
+ {
+ ASSERT(mStructIdRefCounts->find(structure->uniqueId()) != mStructIdRefCounts->end());
+ unsigned int structRefCount = --(*mStructIdRefCounts)[structure->uniqueId()];
+
+ if (structRefCount == 0)
+ {
+ for (const auto &field : structure->fields())
+ {
+ decrementStructTypeRefCount(*field->type());
+ }
+ }
+ }
+}
+
+void RemoveUnreferencedVariablesTraverser::removeVariableDeclaration(TIntermDeclaration *node,
+ TIntermTyped *declarator)
+{
+ if (declarator->getType().isStructSpecifier() && !declarator->getType().isNamelessStruct())
+ {
+ unsigned int structId = declarator->getType().getStruct()->uniqueId();
+ if ((*mStructIdRefCounts)[structId] > 1u)
+ {
+ // If this declaration declares a named struct type that is used elsewhere, we need to
+ // keep it. We can still change the declarator though so that it doesn't declare an
+ // unreferenced variable.
+
+ // Note that since we're not removing the entire declaration, the struct's reference
+ // count will end up being one less than the correct refcount. But since the struct
+ // declaration is kept, the incorrect refcount can't cause any other problems.
+
+ if (declarator->getAsSymbolNode() && declarator->getAsSymbolNode()->getSymbol().empty())
+ {
+ // Already an empty declaration - nothing to do.
+ return;
+ }
+ queueReplacementWithParent(node, declarator,
+ new TIntermSymbol(mSymbolTable->getEmptySymbolId(),
+ TString(""), declarator->getType()),
+ OriginalNode::IS_DROPPED);
+ return;
+ }
+ }
+
+ if (getParentNode()->getAsBlock())
+ {
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, emptyReplacement));
+ }
+ else
+ {
+ ASSERT(getParentNode()->getAsLoopNode());
+ queueReplacement(nullptr, OriginalNode::IS_DROPPED);
+ }
+}
+
+bool RemoveUnreferencedVariablesTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ if (visit == PreVisit)
+ {
+ // SeparateDeclarations should have already been run.
+ ASSERT(node->getSequence()->size() == 1u);
+
+ TIntermTyped *declarator = node->getSequence()->back()->getAsTyped();
+ ASSERT(declarator);
+
+ // We can only remove variables that are not a part of the shader interface.
+ TQualifier qualifier = declarator->getQualifier();
+ if (qualifier != EvqTemporary && qualifier != EvqGlobal)
+ {
+ return true;
+ }
+
+ bool canRemoveVariable = false;
+ TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
+ if (symbolNode != nullptr)
+ {
+ canRemoveVariable =
+ (*mSymbolIdRefCounts)[symbolNode->getId()] == 1u || symbolNode->getSymbol().empty();
+ }
+ TIntermBinary *initNode = declarator->getAsBinaryNode();
+ if (initNode != nullptr)
+ {
+ ASSERT(initNode->getLeft()->getAsSymbolNode());
+ int symbolId = initNode->getLeft()->getAsSymbolNode()->getId();
+ canRemoveVariable =
+ (*mSymbolIdRefCounts)[symbolId] == 1u && !initNode->getRight()->hasSideEffects();
+ }
+
+ if (canRemoveVariable)
+ {
+ removeVariableDeclaration(node, declarator);
+ mRemoveReferences = true;
+ }
+ return true;
+ }
+ ASSERT(visit == PostVisit);
+ mRemoveReferences = false;
+ return true;
+}
+
+void RemoveUnreferencedVariablesTraverser::visitSymbol(TIntermSymbol *node)
+{
+ if (mRemoveReferences)
+ {
+ ASSERT(mSymbolIdRefCounts->find(node->getId()) != mSymbolIdRefCounts->end());
+ --(*mSymbolIdRefCounts)[node->getId()];
+
+ decrementStructTypeRefCount(node->getType());
+ }
+}
+
+bool RemoveUnreferencedVariablesTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mRemoveReferences)
+ {
+ decrementStructTypeRefCount(node->getType());
+ }
+ return true;
+}
+
+void RemoveUnreferencedVariablesTraverser::traverseBlock(TIntermBlock *node)
+{
+ // We traverse blocks in reverse order. This way reference counts can be decremented when
+ // removing initializers, and variables that become unused when initializers are removed can be
+ // removed on the same traversal.
+
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
+ if (preVisit)
+ visit = visitBlock(PreVisit, node);
+
+ if (visit)
+ {
+ for (auto iter = sequence->rbegin(); iter != sequence->rend(); ++iter)
+ {
+ (*iter)->traverse(this);
+ if (visit && inVisit)
+ {
+ if ((iter + 1) != sequence->rend())
+ visit = visitBlock(InVisit, node);
+ }
+ }
+ }
+
+ if (visit && postVisit)
+ visitBlock(PostVisit, node);
+}
+
+void RemoveUnreferencedVariablesTraverser::traverseLoop(TIntermLoop *node)
+{
+ // We traverse loops in reverse order as well. The loop body gets traversed before the init
+ // node.
+
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitLoop(PreVisit, node);
+
+ if (visit)
+ {
+ // We don't need to traverse loop expressions or conditions since they can't be declarations
+ // in the AST (loops which have a declaration in their condition get transformed in the
+ // parsing stage).
+ ASSERT(node->getExpression() == nullptr ||
+ node->getExpression()->getAsDeclarationNode() == nullptr);
+ ASSERT(node->getCondition() == nullptr ||
+ node->getCondition()->getAsDeclarationNode() == nullptr);
+
+ if (node->getBody())
+ node->getBody()->traverse(this);
+
+ if (node->getInit())
+ node->getInit()->traverse(this);
+ }
+
+ if (visit && postVisit)
+ visitLoop(PostVisit, node);
+}
+
+} // namespace
+
+void RemoveUnreferencedVariables(TIntermBlock *root, TSymbolTable *symbolTable)
+{
+ CollectVariableRefCountsTraverser collector;
+ root->traverse(&collector);
+ RemoveUnreferencedVariablesTraverser traverser(&collector.getSymbolIdRefCounts(),
+ &collector.getStructIdRefCounts(), symbolTable);
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.h b/src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.h
new file mode 100644
index 0000000000..39c8327776
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveUnreferencedVariables.h:
+// Drop variables that are declared but never referenced in the AST. This avoids adding unnecessary
+// initialization code for them. Also removes unreferenced struct types.
+//
+
+#ifndef COMPILER_TRANSLATOR_REMOVEUNREFERENCEDVARIABLES_H_
+#define COMPILER_TRANSLATOR_REMOVEUNREFERENCEDVARIABLES_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+void RemoveUnreferencedVariables(TIntermBlock *root, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVEUNREFERENCEDVARIABLES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RenameFunction.h b/src/3rdparty/angle/src/compiler/translator/RenameFunction.h
deleted file mode 100644
index fd6a365fea..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/RenameFunction.h
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_RENAMEFUNCTION_H_
-#define COMPILER_TRANSLATOR_RENAMEFUNCTION_H_
-
-#include "compiler/translator/IntermNode.h"
-
-//
-// Renames a function, including its declaration and any calls to it.
-//
-class RenameFunction : public TIntermTraverser
-{
-public:
- RenameFunction(const TString& oldFunctionName, const TString& newFunctionName)
- : TIntermTraverser(true, false, false)
- , mOldFunctionName(oldFunctionName)
- , mNewFunctionName(newFunctionName) {}
-
- bool visitAggregate(Visit visit, TIntermAggregate *node) override
- {
- TOperator op = node->getOp();
- if ((op == EOpFunction || op == EOpFunctionCall) && node->getName() == mOldFunctionName)
- node->setName(mNewFunctionName);
- return true;
- }
-
-private:
- const TString mOldFunctionName;
- const TString mNewFunctionName;
-};
-
-#endif // COMPILER_TRANSLATOR_RENAMEFUNCTION_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp
index 8347447546..dc3fb7a74e 100644
--- a/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp
@@ -9,7 +9,10 @@
#include "compiler/translator/RewriteDoWhile.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
namespace
{
@@ -41,17 +44,15 @@ namespace
class DoWhileRewriter : public TIntermTraverser
{
public:
- DoWhileRewriter() : TIntermTraverser(true, false, false) {}
+ DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
+ {
+ }
- bool visitAggregate(Visit, TIntermAggregate *node) override
+ bool visitBlock(Visit, TIntermBlock *node) override
{
- // A well-formed AST can only have do-while in EOpSequence which represent lists of
- // statements. By doing a prefix traversal we are able to replace the do-while in the
- // sequence directly as the content of the do-while will be traversed later.
- if (node->getOp() != EOpSequence)
- {
- return true;
- }
+ // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal
+ // we are able to replace the do-while in the sequence directly as the content of the
+ // do-while will be traversed later.
TIntermSequence *statements = node->getSequence();
@@ -68,10 +69,13 @@ class DoWhileRewriter : public TIntermTraverser
continue;
}
+ // Found a loop to change.
+ nextTemporaryId();
+
TType boolType = TType(EbtBool);
// bool temp = false;
- TIntermAggregate *tempDeclaration = nullptr;
+ TIntermDeclaration *tempDeclaration = nullptr;
{
TConstantUnion *falseConstant = new TConstantUnion();
falseConstant->setBConst(false);
@@ -95,23 +99,22 @@ class DoWhileRewriter : public TIntermTraverser
// break;
// }
// }
- TIntermSelection *breakIf = nullptr;
+ TIntermIfElse *breakIf = nullptr;
{
TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
- TIntermAggregate *breakBlock = new TIntermAggregate(EOpSequence);
+ TIntermBlock *breakBlock = new TIntermBlock();
breakBlock->getSequence()->push_back(breakStatement);
- TIntermUnary *negatedCondition = new TIntermUnary(EOpLogicalNot);
- negatedCondition->setOperand(loop->getCondition());
+ TIntermUnary *negatedCondition =
+ new TIntermUnary(EOpLogicalNot, loop->getCondition());
- TIntermSelection *innerIf =
- new TIntermSelection(negatedCondition, breakBlock, nullptr);
+ TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
- TIntermAggregate *innerIfBlock = new TIntermAggregate(EOpSequence);
+ TIntermBlock *innerIfBlock = new TIntermBlock();
innerIfBlock->getSequence()->push_back(innerIf);
- breakIf = new TIntermSelection(createTempSymbol(boolType), innerIfBlock, nullptr);
+ breakIf = new TIntermIfElse(createTempSymbol(boolType), innerIfBlock, nullptr);
}
// Assemble the replacement loops, reusing the do-while loop's body and inserting our
@@ -122,14 +125,10 @@ class DoWhileRewriter : public TIntermTraverser
trueConstant->setBConst(true);
TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
- TIntermAggregate *body = nullptr;
- if (loop->getBody() != nullptr)
- {
- body = loop->getBody()->getAsAggregate();
- }
- else
+ TIntermBlock *body = loop->getBody();
+ if (body == nullptr)
{
- body = new TIntermAggregate(EOpSequence);
+ body = new TIntermBlock();
}
auto sequence = body->getSequence();
sequence->insert(sequence->begin(), assignTrue);
@@ -143,8 +142,6 @@ class DoWhileRewriter : public TIntermTraverser
replacement.push_back(newLoop);
node->replaceChildNodeWithMultiple(loop, replacement);
-
- nextTemporaryIndex();
}
return true;
}
@@ -152,12 +149,11 @@ class DoWhileRewriter : public TIntermTraverser
} // anonymous namespace
-void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex)
+void RewriteDoWhile(TIntermNode *root, TSymbolTable *symbolTable)
{
- ASSERT(temporaryIndex != 0);
-
- DoWhileRewriter rewriter;
- rewriter.useTemporaryIndex(temporaryIndex);
+ DoWhileRewriter rewriter(symbolTable);
root->traverse(&rewriter);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h
index f6ec1caf06..e83bfc4b56 100644
--- a/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h
@@ -10,7 +10,14 @@
#ifndef COMPILER_TRANSLATOR_REWRITEDOWHILE_H_
#define COMPILER_TRANSLATOR_REWRITEDOWHILE_H_
+namespace sh
+{
+
class TIntermNode;
-void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex);
+class TSymbolTable;
+
+void RewriteDoWhile(TIntermNode *root, TSymbolTable *symbolTable);
+
+} // namespace sh
#endif // COMPILER_TRANSLATOR_REWRITEDOWHILE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp
index 52ede17434..ed1bfad8a8 100644
--- a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp
@@ -8,6 +8,9 @@
//
#include "compiler/translator/RewriteElseBlocks.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
#include "compiler/translator/NodeSearch.h"
#include "compiler/translator/SymbolTable.h"
@@ -20,121 +23,98 @@ namespace
class ElseBlockRewriter : public TIntermTraverser
{
public:
- ElseBlockRewriter();
+ ElseBlockRewriter(TSymbolTable *symbolTable);
protected:
- bool visitAggregate(Visit visit, TIntermAggregate *aggregate) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *aggregate) override;
+ bool visitBlock(Visit visit, TIntermBlock *block) override;
private:
- const TType *mFunctionType;
+ TIntermNode *rewriteIfElse(TIntermIfElse *ifElse);
- TIntermNode *rewriteSelection(TIntermSelection *selection);
+ const TType *mFunctionType;
};
-TIntermUnary *MakeNewUnary(TOperator op, TIntermTyped *operand)
+ElseBlockRewriter::ElseBlockRewriter(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable), mFunctionType(nullptr)
{
- TIntermUnary *unary = new TIntermUnary(op, operand->getType());
- unary->setOperand(operand);
- return unary;
}
-ElseBlockRewriter::ElseBlockRewriter()
- : TIntermTraverser(true, false, true),
- mFunctionType(NULL)
-{}
+bool ElseBlockRewriter::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ // Store the current function context (see comment below)
+ mFunctionType = ((visit == PreVisit) ? &node->getFunctionPrototype()->getType() : nullptr);
+ return true;
+}
-bool ElseBlockRewriter::visitAggregate(Visit visit, TIntermAggregate *node)
+bool ElseBlockRewriter::visitBlock(Visit visit, TIntermBlock *node)
{
- switch (node->getOp())
+ if (visit == PostVisit)
{
- case EOpSequence:
- if (visit == PostVisit)
+ for (size_t statementIndex = 0; statementIndex != node->getSequence()->size();
+ statementIndex++)
{
- for (size_t statementIndex = 0; statementIndex != node->getSequence()->size(); statementIndex++)
+ TIntermNode *statement = (*node->getSequence())[statementIndex];
+ TIntermIfElse *ifElse = statement->getAsIfElseNode();
+ if (ifElse && ifElse->getFalseBlock() != nullptr)
{
- TIntermNode *statement = (*node->getSequence())[statementIndex];
- TIntermSelection *selection = statement->getAsSelectionNode();
- if (selection && selection->getFalseBlock() != nullptr)
- {
- // Check for if / else if
- TIntermSelection *elseIfBranch = selection->getFalseBlock()->getAsSelectionNode();
- if (elseIfBranch)
- {
- selection->replaceChildNode(elseIfBranch, rewriteSelection(elseIfBranch));
- delete elseIfBranch;
- }
-
- (*node->getSequence())[statementIndex] = rewriteSelection(selection);
- delete selection;
- }
+ (*node->getSequence())[statementIndex] = rewriteIfElse(ifElse);
}
}
- break;
-
- case EOpFunction:
- // Store the current function context (see comment below)
- mFunctionType = ((visit == PreVisit) ? &node->getType() : NULL);
- break;
-
- default: break;
}
-
return true;
}
-TIntermNode *ElseBlockRewriter::rewriteSelection(TIntermSelection *selection)
+TIntermNode *ElseBlockRewriter::rewriteIfElse(TIntermIfElse *ifElse)
{
- ASSERT(selection != nullptr);
+ ASSERT(ifElse != nullptr);
- nextTemporaryIndex();
+ nextTemporaryId();
- TIntermTyped *typedCondition = selection->getCondition()->getAsTyped();
- TIntermAggregate *storeCondition = createTempInitDeclaration(typedCondition);
+ TIntermDeclaration *storeCondition = createTempInitDeclaration(ifElse->getCondition());
- TIntermSelection *falseBlock = nullptr;
+ TIntermBlock *falseBlock = nullptr;
TType boolType(EbtBool, EbpUndefined, EvqTemporary);
- if (selection->getFalseBlock())
+ if (ifElse->getFalseBlock())
{
- TIntermAggregate *negatedElse = nullptr;
+ TIntermBlock *negatedElse = nullptr;
// crbug.com/346463
// D3D generates error messages claiming a function has no return value, when rewriting
// an if-else clause that returns something non-void in a function. By appending dummy
// returns (that are unreachable) we can silence this compile error.
if (mFunctionType && mFunctionType->getBasicType() != EbtVoid)
{
- TString typeString = mFunctionType->getStruct() ? mFunctionType->getStruct()->name() :
- mFunctionType->getBasicString();
- TString rawText = "return (" + typeString + ")0";
- TIntermRaw *returnNode = new TIntermRaw(*mFunctionType, rawText);
- negatedElse = new TIntermAggregate(EOpSequence);
- negatedElse->getSequence()->push_back(returnNode);
+ TIntermNode *returnNode = new TIntermBranch(EOpReturn, CreateZeroNode(*mFunctionType));
+ negatedElse = new TIntermBlock();
+ negatedElse->appendStatement(returnNode);
}
TIntermSymbol *conditionSymbolElse = createTempSymbol(boolType);
- TIntermUnary *negatedCondition = MakeNewUnary(EOpLogicalNot, conditionSymbolElse);
- falseBlock = new TIntermSelection(negatedCondition,
- selection->getFalseBlock(), negatedElse);
+ TIntermUnary *negatedCondition = new TIntermUnary(EOpLogicalNot, conditionSymbolElse);
+ TIntermIfElse *falseIfElse =
+ new TIntermIfElse(negatedCondition, ifElse->getFalseBlock(), negatedElse);
+ falseBlock = EnsureBlock(falseIfElse);
}
TIntermSymbol *conditionSymbolSel = createTempSymbol(boolType);
- TIntermSelection *newSelection = new TIntermSelection(conditionSymbolSel, selection->getTrueBlock(), falseBlock);
+ TIntermIfElse *newIfElse =
+ new TIntermIfElse(conditionSymbolSel, ifElse->getTrueBlock(), falseBlock);
- TIntermAggregate *block = new TIntermAggregate(EOpSequence);
+ TIntermBlock *block = new TIntermBlock();
block->getSequence()->push_back(storeCondition);
- block->getSequence()->push_back(newSelection);
+ block->getSequence()->push_back(newIfElse);
return block;
}
-}
+} // anonymous namespace
-void RewriteElseBlocks(TIntermNode *node, unsigned int *temporaryIndex)
+void RewriteElseBlocks(TIntermNode *node, TSymbolTable *symbolTable)
{
- ElseBlockRewriter rewriter;
- rewriter.useTemporaryIndex(temporaryIndex);
+ ElseBlockRewriter rewriter(symbolTable);
node->traverse(&rewriter);
}
-}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h
index 24a425e66d..2586b5405c 100644
--- a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h
@@ -10,13 +10,13 @@
#ifndef COMPILER_TRANSLATOR_REWRITEELSEBLOCKS_H_
#define COMPILER_TRANSLATOR_REWRITEELSEBLOCKS_H_
-#include "compiler/translator/IntermNode.h"
-
namespace sh
{
-void RewriteElseBlocks(TIntermNode *node, unsigned int *temporaryIndex);
+class TIntermNode;
+class TSymbolTable;
+void RewriteElseBlocks(TIntermNode *node, TSymbolTable *symbolTable);
}
-#endif // COMPILER_TRANSLATOR_REWRITEELSEBLOCKS_H_
+#endif // COMPILER_TRANSLATOR_REWRITEELSEBLOCKS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp b/src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp
new file mode 100644
index 0000000000..b602e0c923
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp
@@ -0,0 +1,154 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of texelFetchOffset translation issue workaround.
+// See header for more info.
+
+#include "compiler/translator/RewriteTexelFetchOffset.h"
+
+#include "common/angleutils.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root, const TSymbolTable &symbolTable, int shaderVersion);
+
+ private:
+ Traverser(const TSymbolTable &symbolTable, int shaderVersion);
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ void nextIteration();
+
+ const TSymbolTable *symbolTable;
+ const int shaderVersion;
+ bool mFound = false;
+};
+
+Traverser::Traverser(const TSymbolTable &symbolTable, int shaderVersion)
+ : TIntermTraverser(true, false, false), symbolTable(&symbolTable), shaderVersion(shaderVersion)
+{
+}
+
+// static
+void Traverser::Apply(TIntermNode *root, const TSymbolTable &symbolTable, int shaderVersion)
+{
+ Traverser traverser(symbolTable, shaderVersion);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ traverser.updateTree();
+ }
+ } while (traverser.mFound);
+}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+}
+
+bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Decide if the node represents the call of texelFetchOffset.
+ if (node->getOp() != EOpCallBuiltInFunction)
+ {
+ return true;
+ }
+
+ if (node->getFunctionSymbolInfo()->getName() != "texelFetchOffset")
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround.
+ const TIntermSequence *sequence = node->getSequence();
+ ASSERT(sequence->size() == 4u);
+
+ // Decide if the sampler is a 2DArray sampler. In that case position is ivec3 and offset is
+ // ivec2.
+ bool is2DArray = sequence->at(1)->getAsTyped()->getNominalSize() == 3 &&
+ sequence->at(3)->getAsTyped()->getNominalSize() == 2;
+
+ // Create new node that represents the call of function texelFetch.
+ // Its argument list will be: texelFetch(sampler, Position+offset, lod).
+
+ TIntermSequence *texelFetchArguments = new TIntermSequence();
+
+ // sampler
+ texelFetchArguments->push_back(sequence->at(0));
+
+ // Position
+ TIntermTyped *texCoordNode = sequence->at(1)->getAsTyped();
+ ASSERT(texCoordNode);
+
+ // offset
+ TIntermTyped *offsetNode = nullptr;
+ ASSERT(sequence->at(3)->getAsTyped());
+ if (is2DArray)
+ {
+ // For 2DArray samplers, Position is ivec3 and offset is ivec2;
+ // So offset must be converted into an ivec3 before being added to Position.
+ TIntermSequence *constructOffsetIvecArguments = new TIntermSequence();
+ constructOffsetIvecArguments->push_back(sequence->at(3)->getAsTyped());
+
+ TIntermTyped *zeroNode = CreateZeroNode(TType(EbtInt));
+ constructOffsetIvecArguments->push_back(zeroNode);
+
+ offsetNode = TIntermAggregate::CreateConstructor(texCoordNode->getType(),
+ constructOffsetIvecArguments);
+ offsetNode->setLine(texCoordNode->getLine());
+ }
+ else
+ {
+ offsetNode = sequence->at(3)->getAsTyped();
+ }
+
+ // Position+offset
+ TIntermBinary *add = new TIntermBinary(EOpAdd, texCoordNode, offsetNode);
+ add->setLine(texCoordNode->getLine());
+ texelFetchArguments->push_back(add);
+
+ // lod
+ texelFetchArguments->push_back(sequence->at(2));
+
+ ASSERT(texelFetchArguments->size() == 3u);
+
+ TIntermTyped *texelFetchNode = CreateBuiltInFunctionCallNode("texelFetch", texelFetchArguments,
+ *symbolTable, shaderVersion);
+ texelFetchNode->setLine(node->getLine());
+
+ // Replace the old node by this new node.
+ queueReplacement(texelFetchNode, OriginalNode::IS_DROPPED);
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+void RewriteTexelFetchOffset(TIntermNode *root, const TSymbolTable &symbolTable, int shaderVersion)
+{
+ // texelFetchOffset is only valid in GLSL 3.0 and later.
+ if (shaderVersion < 300)
+ return;
+
+ Traverser::Apply(root, symbolTable, shaderVersion);
+}
+
+} // namespace sh \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.h b/src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.h
new file mode 100644
index 0000000000..694d709f8e
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around an issue on the translation
+// from texelFetchOffset into HLSL function Load on INTEL drivers. It
+// works by translating texelFetchOffset into texelFetch:
+//
+// - From: texelFetchOffset(sampler, Position, lod, offset)
+// - To: texelFetch(sampler, Position+offset, lod)
+//
+// See http://anglebug.com/1469
+
+#ifndef COMPILER_TRANSLATOR_REWRITE_TEXELFETCHOFFSET_H_
+#define COMPILER_TRANSLATOR_REWRITE_TEXELFETCHOFFSET_H_
+
+class TIntermNode;
+class TSymbolTable;
+
+namespace sh
+{
+
+void RewriteTexelFetchOffset(TIntermNode *root, const TSymbolTable &symbolTable, int shaderVersion);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REWRITE_TEXELFETCHOFFSET_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.cpp b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.cpp
new file mode 100644
index 0000000000..696a49536c
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.cpp
@@ -0,0 +1,94 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/RewriteUnaryMinusOperatorFloat.h"
+
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root);
+
+ private:
+ Traverser();
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ void nextIteration();
+
+ bool mFound = false;
+};
+
+// static
+void Traverser::Apply(TIntermNode *root)
+{
+ Traverser traverser;
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ traverser.updateTree();
+ }
+ } while (traverser.mFound);
+}
+
+Traverser::Traverser() : TIntermTraverser(true, false, false)
+{
+}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+}
+
+bool Traverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Detect if the current operator is unary minus operator.
+ if (node->getOp() != EOpNegative)
+ {
+ return true;
+ }
+
+ // Detect if the current operand is a float variable.
+ TIntermTyped *fValue = node->getOperand();
+ if (!fValue->getType().isScalarFloat())
+ {
+ return true;
+ }
+
+ // 0.0 - float
+ TIntermTyped *zero = CreateZeroNode(fValue->getType());
+ zero->setLine(fValue->getLine());
+ TIntermBinary *sub = new TIntermBinary(EOpSub, zero, fValue);
+ sub->setLine(fValue->getLine());
+
+ queueReplacement(sub, OriginalNode::IS_DROPPED);
+
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+void RewriteUnaryMinusOperatorFloat(TIntermNode *root)
+{
+ Traverser::Apply(root);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.h b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.h
new file mode 100644
index 0000000000..ccbfbcbd9e
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Rewrite "-float" to "0.0 - float" to work around unary minus operator on float issue on Intel Mac
+// OSX 10.11.
+
+#ifndef COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORFLOAT_H_
+#define COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORFLOAT_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void RewriteUnaryMinusOperatorFloat(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORFLOAT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp
new file mode 100644
index 0000000000..fe2ef948b4
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp
@@ -0,0 +1,112 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of evaluating unary integer variable bug workaround.
+// See header for more info.
+
+#include "compiler/translator/RewriteUnaryMinusOperatorInt.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root);
+
+ private:
+ Traverser();
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ void nextIteration();
+
+ bool mFound = false;
+};
+
+// static
+void Traverser::Apply(TIntermNode *root)
+{
+ Traverser traverser;
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ traverser.updateTree();
+ }
+ } while (traverser.mFound);
+}
+
+Traverser::Traverser() : TIntermTraverser(true, false, false)
+{
+}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+}
+
+bool Traverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Decide if the current unary operator is unary minus.
+ if (node->getOp() != EOpNegative)
+ {
+ return true;
+ }
+
+ // Decide if the current operand is an integer variable.
+ TIntermTyped *opr = node->getOperand();
+ if (!opr->getType().isScalarInt())
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround: -(int) -> ~(int) + 1.
+ // ~(int)
+ TIntermUnary *bitwiseNot = new TIntermUnary(EOpBitwiseNot, opr);
+ bitwiseNot->setLine(opr->getLine());
+
+ // Constant 1 (or 1u)
+ TConstantUnion *one = new TConstantUnion();
+ if (opr->getType().getBasicType() == EbtInt)
+ {
+ one->setIConst(1);
+ }
+ else
+ {
+ one->setUConst(1u);
+ }
+ TIntermConstantUnion *oneNode = new TIntermConstantUnion(one, opr->getType());
+ oneNode->getTypePointer()->setQualifier(EvqConst);
+ oneNode->setLine(opr->getLine());
+
+ // ~(int) + 1
+ TIntermBinary *add = new TIntermBinary(EOpAdd, bitwiseNot, oneNode);
+ add->setLine(opr->getLine());
+
+ queueReplacement(add, OriginalNode::IS_DROPPED);
+
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+void RewriteUnaryMinusOperatorInt(TIntermNode *root)
+{
+ Traverser::Apply(root);
+}
+
+} // namespace sh \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h
new file mode 100644
index 0000000000..50f0c442a7
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around a bug on evaluating unary
+// integer variable on Intel D3D driver. It works by rewriting -(int) to
+// ~(int) + 1 when evaluating unary integer variables.
+
+#ifndef COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORINT_H_
+#define COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORINT_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void RewriteUnaryMinusOperatorInt(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORINT_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.cpp b/src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.cpp
new file mode 100644
index 0000000000..3c4209c539
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.cpp
@@ -0,0 +1,112 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RunAtTheEndOfShader.cpp: Add code to be run at the end of the shader. In case main() contains a
+// return statement, this is done by replacing the main() function with another function that calls
+// the old main, like this:
+//
+// void main() { body }
+// =>
+// void main0() { body }
+// void main()
+// {
+// main0();
+// codeToRun
+// }
+//
+// This way the code will get run even if the return statement inside main is executed.
+//
+
+#include "compiler/translator/RunAtTheEndOfShader.h"
+
+#include "compiler/translator/FindMain.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class ContainsReturnTraverser : public TIntermTraverser
+{
+ public:
+ ContainsReturnTraverser() : TIntermTraverser(true, false, false), mContainsReturn(false) {}
+
+ bool visitBranch(Visit visit, TIntermBranch *node) override
+ {
+ if (node->getFlowOp() == EOpReturn)
+ {
+ mContainsReturn = true;
+ }
+ return false;
+ }
+
+ bool containsReturn() { return mContainsReturn; }
+
+ private:
+ bool mContainsReturn;
+};
+
+bool ContainsReturn(TIntermNode *node)
+{
+ ContainsReturnTraverser traverser;
+ node->traverse(&traverser);
+ return traverser.containsReturn();
+}
+
+void WrapMainAndAppend(TIntermBlock *root,
+ TIntermFunctionDefinition *main,
+ TIntermNode *codeToRun,
+ TSymbolTable *symbolTable)
+{
+ // Replace main() with main0() with the same body.
+ TSymbolUniqueId oldMainId(symbolTable);
+ std::stringstream oldMainName;
+ oldMainName << "main" << oldMainId.get();
+ TIntermFunctionDefinition *oldMain = CreateInternalFunctionDefinitionNode(
+ TType(EbtVoid), oldMainName.str().c_str(), main->getBody(), oldMainId);
+
+ bool replaced = root->replaceChildNode(main, oldMain);
+ ASSERT(replaced);
+
+ // void main()
+ TIntermFunctionPrototype *newMainProto = new TIntermFunctionPrototype(
+ TType(EbtVoid), main->getFunctionPrototype()->getFunctionSymbolInfo()->getId());
+ newMainProto->getFunctionSymbolInfo()->setName("main");
+
+ // {
+ // main0();
+ // codeToRun
+ // }
+ TIntermBlock *newMainBody = new TIntermBlock();
+ TIntermAggregate *oldMainCall = CreateInternalFunctionCallNode(
+ TType(EbtVoid), oldMainName.str().c_str(), oldMainId, new TIntermSequence());
+ newMainBody->appendStatement(oldMainCall);
+ newMainBody->appendStatement(codeToRun);
+
+ // Add the new main() to the root node.
+ TIntermFunctionDefinition *newMain = new TIntermFunctionDefinition(newMainProto, newMainBody);
+ root->appendStatement(newMain);
+}
+
+} // anonymous namespace
+
+void RunAtTheEndOfShader(TIntermBlock *root, TIntermNode *codeToRun, TSymbolTable *symbolTable)
+{
+ TIntermFunctionDefinition *main = FindMain(root);
+ if (!ContainsReturn(main))
+ {
+ main->getBody()->appendStatement(codeToRun);
+ return;
+ }
+
+ WrapMainAndAppend(root, main, codeToRun, symbolTable);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.h b/src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.h
new file mode 100644
index 0000000000..ed94c28dae
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RunAtTheEndOfShader.h: Add code to be run at the end of the shader.
+//
+
+#ifndef COMPILER_TRANSLATOR_RUNATTHEENDOFSHADER_H_
+#define COMPILER_TRANSLATOR_RUNATTHEENDOFSHADER_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TIntermNode;
+class TSymbolTable;
+
+void RunAtTheEndOfShader(TIntermBlock *root, TIntermNode *codeToRun, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_RUNATTHEENDOFSHADER_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
index 775c5d8710..746c16e2e6 100644
--- a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
@@ -3,6 +3,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// Scalarize vector and matrix constructor args, so that vectors built from components don't have
+// matrix arguments, and matrices built from components don't have vector arguments. This avoids
+// driver bugs around vector and matrix constructors.
+//
#include "common/debug.h"
#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
@@ -11,6 +15,11 @@
#include "angle_gl.h"
#include "common/angleutils.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
namespace
{
@@ -37,140 +46,93 @@ bool ContainsVectorNode(const TIntermSequence &sequence)
return false;
}
-TIntermConstantUnion *ConstructIndexNode(int index)
+TIntermBinary *ConstructVectorIndexBinaryNode(TIntermSymbol *symbolNode, int index)
{
- TConstantUnion *u = new TConstantUnion[1];
- u[0].setIConst(index);
-
- TType type(EbtInt, EbpUndefined, EvqConst, 1);
- TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
- return node;
+ return new TIntermBinary(EOpIndexDirect, symbolNode, CreateIndexNode(index));
}
-TIntermBinary *ConstructVectorIndexBinaryNode(TIntermSymbol *symbolNode, int index)
+TIntermBinary *ConstructMatrixIndexBinaryNode(TIntermSymbol *symbolNode, int colIndex, int rowIndex)
{
- TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
- binary->setLeft(symbolNode);
- TIntermConstantUnion *indexNode = ConstructIndexNode(index);
- binary->setRight(indexNode);
- return binary;
+ TIntermBinary *colVectorNode = ConstructVectorIndexBinaryNode(symbolNode, colIndex);
+
+ return new TIntermBinary(EOpIndexDirect, colVectorNode, CreateIndexNode(rowIndex));
}
-TIntermBinary *ConstructMatrixIndexBinaryNode(
- TIntermSymbol *symbolNode, int colIndex, int rowIndex)
+class ScalarizeArgsTraverser : public TIntermTraverser
{
- TIntermBinary *colVectorNode =
- ConstructVectorIndexBinaryNode(symbolNode, colIndex);
+ public:
+ ScalarizeArgsTraverser(sh::GLenum shaderType,
+ bool fragmentPrecisionHigh,
+ TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable),
+ mShaderType(shaderType),
+ mFragmentPrecisionHigh(fragmentPrecisionHigh)
+ {
+ }
- TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
- binary->setLeft(colVectorNode);
- TIntermConstantUnion *rowIndexNode = ConstructIndexNode(rowIndex);
- binary->setRight(rowIndexNode);
- return binary;
-}
+ protected:
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit visit, TIntermBlock *node) override;
-} // namespace anonymous
+ private:
+ void scalarizeArgs(TIntermAggregate *aggregate, bool scalarizeVector, bool scalarizeMatrix);
+
+ // If we have the following code:
+ // mat4 m(0);
+ // vec4 v(1, m);
+ // We will rewrite to:
+ // mat4 m(0);
+ // mat4 s0 = m;
+ // vec4 v(1, s0[0][0], s0[0][1], s0[0][2]);
+ // This function is to create nodes for "mat4 s0 = m;" and insert it to the code sequence. This
+ // way the possible side effects of the constructor argument will only be evaluated once.
+ void createTempVariable(TIntermTyped *original);
-bool ScalarizeVecAndMatConstructorArgs::visitAggregate(Visit visit, TIntermAggregate *node)
+ std::vector<TIntermSequence> mBlockStack;
+
+ sh::GLenum mShaderType;
+ bool mFragmentPrecisionHigh;
+};
+
+bool ScalarizeArgsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
- if (visit == PreVisit)
+ if (visit == PreVisit && node->getOp() == EOpConstruct)
{
- switch (node->getOp())
- {
- case EOpSequence:
- mSequenceStack.push_back(TIntermSequence());
- {
- for (TIntermSequence::const_iterator iter = node->getSequence()->begin();
- iter != node->getSequence()->end(); ++iter)
- {
- TIntermNode *child = *iter;
- ASSERT(child != NULL);
- child->traverse(this);
- mSequenceStack.back().push_back(child);
- }
- }
- if (mSequenceStack.back().size() > node->getSequence()->size())
- {
- node->getSequence()->clear();
- *(node->getSequence()) = mSequenceStack.back();
- }
- mSequenceStack.pop_back();
- return false;
- case EOpConstructVec2:
- case EOpConstructVec3:
- case EOpConstructVec4:
- case EOpConstructBVec2:
- case EOpConstructBVec3:
- case EOpConstructBVec4:
- case EOpConstructIVec2:
- case EOpConstructIVec3:
- case EOpConstructIVec4:
- if (ContainsMatrixNode(*(node->getSequence())))
- scalarizeArgs(node, false, true);
- break;
- case EOpConstructMat2:
- case EOpConstructMat2x3:
- case EOpConstructMat2x4:
- case EOpConstructMat3x2:
- case EOpConstructMat3:
- case EOpConstructMat3x4:
- case EOpConstructMat4x2:
- case EOpConstructMat4x3:
- case EOpConstructMat4:
- if (ContainsVectorNode(*(node->getSequence())))
- scalarizeArgs(node, true, false);
- break;
- default:
- break;
- }
+ if (node->getType().isVector() && ContainsMatrixNode(*(node->getSequence())))
+ scalarizeArgs(node, false, true);
+ else if (node->getType().isMatrix() && ContainsVectorNode(*(node->getSequence())))
+ scalarizeArgs(node, true, false);
}
return true;
}
-void ScalarizeVecAndMatConstructorArgs::scalarizeArgs(
- TIntermAggregate *aggregate, bool scalarizeVector, bool scalarizeMatrix)
+bool ScalarizeArgsTraverser::visitBlock(Visit visit, TIntermBlock *node)
{
- ASSERT(aggregate);
- int size = 0;
- switch (aggregate->getOp())
+ mBlockStack.push_back(TIntermSequence());
+ {
+ for (TIntermNode *child : *node->getSequence())
+ {
+ ASSERT(child != nullptr);
+ child->traverse(this);
+ mBlockStack.back().push_back(child);
+ }
+ }
+ if (mBlockStack.back().size() > node->getSequence()->size())
{
- case EOpConstructVec2:
- case EOpConstructBVec2:
- case EOpConstructIVec2:
- size = 2;
- break;
- case EOpConstructVec3:
- case EOpConstructBVec3:
- case EOpConstructIVec3:
- size = 3;
- break;
- case EOpConstructVec4:
- case EOpConstructBVec4:
- case EOpConstructIVec4:
- case EOpConstructMat2:
- size = 4;
- break;
- case EOpConstructMat2x3:
- case EOpConstructMat3x2:
- size = 6;
- break;
- case EOpConstructMat2x4:
- case EOpConstructMat4x2:
- size = 8;
- break;
- case EOpConstructMat3:
- size = 9;
- break;
- case EOpConstructMat3x4:
- case EOpConstructMat4x3:
- size = 12;
- break;
- case EOpConstructMat4:
- size = 16;
- break;
- default:
- break;
+ node->getSequence()->clear();
+ *(node->getSequence()) = mBlockStack.back();
}
+ mBlockStack.pop_back();
+ return false;
+}
+
+void ScalarizeArgsTraverser::scalarizeArgs(TIntermAggregate *aggregate,
+ bool scalarizeVector,
+ bool scalarizeMatrix)
+{
+ ASSERT(aggregate);
+ ASSERT(!aggregate->isArray());
+ int size = static_cast<int>(aggregate->getType().getObjectSize());
TIntermSequence *sequence = aggregate->getSequence();
TIntermSequence original(*sequence);
sequence->clear();
@@ -179,12 +141,10 @@ void ScalarizeVecAndMatConstructorArgs::scalarizeArgs(
ASSERT(size > 0);
TIntermTyped *node = original[ii]->getAsTyped();
ASSERT(node);
- TString varName = createTempVariable(node);
+ createTempVariable(node);
if (node->isScalar())
{
- TIntermSymbol *symbolNode =
- new TIntermSymbol(-1, varName, node->getType());
- sequence->push_back(symbolNode);
+ sequence->push_back(createTempSymbol(node->getType()));
size--;
}
else if (node->isVector())
@@ -195,17 +155,14 @@ void ScalarizeVecAndMatConstructorArgs::scalarizeArgs(
size -= repeat;
for (int index = 0; index < repeat; ++index)
{
- TIntermSymbol *symbolNode =
- new TIntermSymbol(-1, varName, node->getType());
- TIntermBinary *newNode = ConstructVectorIndexBinaryNode(
- symbolNode, index);
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
+ TIntermBinary *newNode = ConstructVectorIndexBinaryNode(symbolNode, index);
sequence->push_back(newNode);
}
}
else
{
- TIntermSymbol *symbolNode =
- new TIntermSymbol(-1, varName, node->getType());
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
sequence->push_back(symbolNode);
size -= node->getNominalSize();
}
@@ -220,10 +177,9 @@ void ScalarizeVecAndMatConstructorArgs::scalarizeArgs(
size -= repeat;
while (repeat > 0)
{
- TIntermSymbol *symbolNode =
- new TIntermSymbol(-1, varName, node->getType());
- TIntermBinary *newNode = ConstructMatrixIndexBinaryNode(
- symbolNode, colIndex, rowIndex);
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
+ TIntermBinary *newNode =
+ ConstructMatrixIndexBinaryNode(symbolNode, colIndex, rowIndex);
sequence->push_back(newNode);
rowIndex++;
if (rowIndex >= node->getRows())
@@ -236,8 +192,7 @@ void ScalarizeVecAndMatConstructorArgs::scalarizeArgs(
}
else
{
- TIntermSymbol *symbolNode =
- new TIntermSymbol(-1, varName, node->getType());
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
sequence->push_back(symbolNode);
size -= node->getCols() * node->getRows();
}
@@ -245,51 +200,39 @@ void ScalarizeVecAndMatConstructorArgs::scalarizeArgs(
}
}
-TString ScalarizeVecAndMatConstructorArgs::createTempVariable(TIntermTyped *original)
+void ScalarizeArgsTraverser::createTempVariable(TIntermTyped *original)
{
- TString tempVarName = "_webgl_tmp_";
- if (original->isScalar())
- {
- tempVarName += "scalar_";
- }
- else if (original->isVector())
- {
- tempVarName += "vec_";
- }
- else
- {
- ASSERT(original->isMatrix());
- tempVarName += "mat_";
- }
- tempVarName += Str(mTempVarCount).c_str();
- mTempVarCount++;
-
ASSERT(original);
- TType type = original->getType();
- type.setQualifier(EvqTemporary);
+ nextTemporaryId();
+ TIntermDeclaration *decl = createTempInitDeclaration(original);
- if (mShaderType == GL_FRAGMENT_SHADER &&
- type.getBasicType() == EbtFloat &&
+ TType type = original->getType();
+ if (mShaderType == GL_FRAGMENT_SHADER && type.getBasicType() == EbtFloat &&
type.getPrecision() == EbpUndefined)
{
// We use the highest available precision for the temporary variable
// to avoid computing the actual precision using the rules defined
// in GLSL ES 1.0 Section 4.5.2.
- type.setPrecision(mFragmentPrecisionHigh ? EbpHigh : EbpMedium);
+ TIntermBinary *init = decl->getSequence()->at(0)->getAsBinaryNode();
+ init->getTypePointer()->setPrecision(mFragmentPrecisionHigh ? EbpHigh : EbpMedium);
+ init->getLeft()->getTypePointer()->setPrecision(mFragmentPrecisionHigh ? EbpHigh
+ : EbpMedium);
}
- TIntermBinary *init = new TIntermBinary(EOpInitialize);
- TIntermSymbol *symbolNode = new TIntermSymbol(-1, tempVarName, type);
- init->setLeft(symbolNode);
- init->setRight(original);
- init->setType(type);
-
- TIntermAggregate *decl = new TIntermAggregate(EOpDeclaration);
- decl->getSequence()->push_back(init);
-
- ASSERT(mSequenceStack.size() > 0);
- TIntermSequence &sequence = mSequenceStack.back();
+ ASSERT(mBlockStack.size() > 0);
+ TIntermSequence &sequence = mBlockStack.back();
sequence.push_back(decl);
+}
+
+} // namespace anonymous
- return tempVarName;
+void ScalarizeVecAndMatConstructorArgs(TIntermBlock *root,
+ sh::GLenum shaderType,
+ bool fragmentPrecisionHigh,
+ TSymbolTable *symbolTable)
+{
+ ScalarizeArgsTraverser scalarizer(shaderType, fragmentPrecisionHigh, symbolTable);
+ root->traverse(&scalarizer);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h
index d7553be23b..b8f782d1ec 100644
--- a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h
+++ b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h
@@ -3,46 +3,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// Scalarize vector and matrix constructor args, so that vectors built from components don't have
+// matrix arguments, and matrices built from components don't have vector arguments. This avoids
+// driver bugs around vector and matrix constructors.
+//
#ifndef COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_
#define COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_
-#include "compiler/translator/IntermNode.h"
+#include "GLSLANG/ShaderLang.h"
-class ScalarizeVecAndMatConstructorArgs : public TIntermTraverser
+namespace sh
{
- public:
- ScalarizeVecAndMatConstructorArgs(sh::GLenum shaderType,
- bool fragmentPrecisionHigh)
- : TIntermTraverser(true, false, false),
- mTempVarCount(0),
- mShaderType(shaderType),
- mFragmentPrecisionHigh(fragmentPrecisionHigh) {}
-
- protected:
- bool visitAggregate(Visit visit, TIntermAggregate *node) override;
-
- private:
- void scalarizeArgs(TIntermAggregate *aggregate,
- bool scalarizeVector, bool scalarizeMatrix);
-
- // If we have the following code:
- // mat4 m(0);
- // vec4 v(1, m);
- // We will rewrite to:
- // mat4 m(0);
- // mat4 _webgl_tmp_mat_0 = m;
- // vec4 v(1, _webgl_tmp_mat_0[0][0], _webgl_tmp_mat_0[0][1], _webgl_tmp_mat_0[0][2]);
- // This function is to create nodes for "mat4 _webgl_tmp_mat_0 = m;" and insert it to
- // the code sequence.
- // Return the temporary variable name.
- TString createTempVariable(TIntermTyped *original);
-
- std::vector<TIntermSequence> mSequenceStack;
- int mTempVarCount;
-
- sh::GLenum mShaderType;
- bool mFragmentPrecisionHigh;
-};
+class TIntermBlock;
+class TSymbolTable;
+
+void ScalarizeVecAndMatConstructorArgs(TIntermBlock *root,
+ sh::GLenum shaderType,
+ bool fragmentPrecisionHigh,
+ TSymbolTable *symbolTable);
+} // namespace sh
#endif // COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp
index cccd4d3ff0..34c644d028 100644
--- a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp
@@ -13,8 +13,7 @@
namespace sh
{
SearchSymbol::SearchSymbol(const TString &symbol)
- : TIntermTraverser(true, false, false),
- mSymbol(symbol)
+ : TIntermTraverser(true, false, false), mSymbol(symbol)
{
match = false;
}
diff --git a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h
index 1e5e1700d1..b8379e041f 100644
--- a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h
+++ b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h
@@ -9,7 +9,7 @@
#ifndef COMPILER_TRANSLATOR_SEARCHSYMBOL_H_
#define COMPILER_TRANSLATOR_SEARCHSYMBOL_H_
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/ParseContext.h"
namespace sh
@@ -30,4 +30,4 @@ class SearchSymbol : public TIntermTraverser
};
}
-#endif // COMPILER_TRANSLATOR_SEARCHSYMBOL_H_
+#endif // COMPILER_TRANSLATOR_SEARCHSYMBOL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp
index de9050cd80..fe25823e38 100644
--- a/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp
@@ -3,7 +3,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// The SeparateArrayInitialization function splits each array initialization into a declaration and an assignment.
+// The SeparateArrayInitialization function splits each array initialization into a declaration and
+// an assignment.
// Example:
// type[n] a = initializer;
// will effectively become
@@ -20,6 +21,9 @@
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/OutputHLSL.h"
+namespace sh
+{
+
namespace
{
@@ -27,9 +31,10 @@ class SeparateArrayInitTraverser : private TIntermTraverser
{
public:
static void apply(TIntermNode *root);
+
private:
SeparateArrayInitTraverser();
- bool visitAggregate(Visit, TIntermAggregate *node) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
};
void SeparateArrayInitTraverser::apply(TIntermNode *root)
@@ -39,54 +44,49 @@ void SeparateArrayInitTraverser::apply(TIntermNode *root)
separateInit.updateTree();
}
-SeparateArrayInitTraverser::SeparateArrayInitTraverser()
- : TIntermTraverser(true, false, false)
+SeparateArrayInitTraverser::SeparateArrayInitTraverser() : TIntermTraverser(true, false, false)
{
}
-bool SeparateArrayInitTraverser::visitAggregate(Visit, TIntermAggregate *node)
+bool SeparateArrayInitTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
{
- if (node->getOp() == EOpDeclaration)
+ TIntermSequence *sequence = node->getSequence();
+ TIntermBinary *initNode = sequence->back()->getAsBinaryNode();
+ if (initNode != nullptr && initNode->getOp() == EOpInitialize)
{
- TIntermSequence *sequence = node->getSequence();
- TIntermBinary *initNode = sequence->back()->getAsBinaryNode();
- if (initNode != nullptr && initNode->getOp() == EOpInitialize)
+ TIntermTyped *initializer = initNode->getRight();
+ if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer))
{
- TIntermTyped *initializer = initNode->getRight();
- if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer))
- {
- // We rely on that array declarations have been isolated to single declarations.
- ASSERT(sequence->size() == 1);
- TIntermTyped *symbol = initNode->getLeft();
- TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
- ASSERT(parentAgg != nullptr);
-
- TIntermSequence replacements;
-
- TIntermAggregate *replacementDeclaration = new TIntermAggregate;
- replacementDeclaration->setOp(EOpDeclaration);
- replacementDeclaration->getSequence()->push_back(symbol);
- replacementDeclaration->setLine(symbol->getLine());
- replacements.push_back(replacementDeclaration);
-
- TIntermBinary *replacementAssignment = new TIntermBinary(EOpAssign);
- replacementAssignment->setLeft(symbol);
- replacementAssignment->setRight(initializer);
- replacementAssignment->setType(initializer->getType());
- replacementAssignment->setLine(symbol->getLine());
- replacements.push_back(replacementAssignment);
-
- mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacements));
- }
+ // We rely on that array declarations have been isolated to single declarations.
+ ASSERT(sequence->size() == 1);
+ TIntermTyped *symbol = initNode->getLeft();
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ ASSERT(parentBlock != nullptr);
+
+ TIntermSequence replacements;
+
+ TIntermDeclaration *replacementDeclaration = new TIntermDeclaration();
+ replacementDeclaration->appendDeclarator(symbol);
+ replacementDeclaration->setLine(symbol->getLine());
+ replacements.push_back(replacementDeclaration);
+
+ TIntermBinary *replacementAssignment =
+ new TIntermBinary(EOpAssign, symbol, initializer);
+ replacementAssignment->setLine(symbol->getLine());
+ replacements.push_back(replacementAssignment);
+
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
}
- return false;
}
- return true;
+ return false;
}
-} // namespace
+} // namespace
void SeparateArrayInitialization(TIntermNode *root)
{
SeparateArrayInitTraverser::apply(root);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h
index d16357a3af..3a9bb55dd1 100644
--- a/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h
+++ b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h
@@ -3,7 +3,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// The SeparateArrayInitialization function splits each array initialization into a declaration and an assignment.
+// The SeparateArrayInitialization function splits each array initialization into a declaration and
+// an assignment.
// Example:
// type[n] a = initializer;
// will effectively become
@@ -18,8 +19,11 @@
#ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
#define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
+namespace sh
+{
class TIntermNode;
void SeparateArrayInitialization(TIntermNode *root);
+} // namespace sh
#endif // COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp
index d33747f85b..9a066075c0 100644
--- a/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp
@@ -5,8 +5,8 @@
//
// The SeparateDeclarations function processes declarations, so that in the end each declaration
// contains only one declarator.
-// This is useful as an intermediate step when initialization needs to be separated from declaration,
-// or when things need to be unfolded out of the initializer.
+// This is useful as an intermediate step when initialization needs to be separated from
+// declaration, or when things need to be unfolded out of the initializer.
// Example:
// int a[1] = int[1](1), b[1] = int[1](2);
// gets transformed when run through this class into the AST equivalent of:
@@ -15,7 +15,10 @@
#include "compiler/translator/SeparateDeclarations.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
namespace
{
@@ -24,9 +27,10 @@ class SeparateDeclarationsTraverser : private TIntermTraverser
{
public:
static void apply(TIntermNode *root);
+
private:
SeparateDeclarationsTraverser();
- bool visitAggregate(Visit, TIntermAggregate *node) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
};
void SeparateDeclarationsTraverser::apply(TIntermNode *root)
@@ -41,37 +45,35 @@ SeparateDeclarationsTraverser::SeparateDeclarationsTraverser()
{
}
-bool SeparateDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node)
+bool SeparateDeclarationsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
{
- if (node->getOp() == EOpDeclaration)
+ TIntermSequence *sequence = node->getSequence();
+ if (sequence->size() > 1)
{
- TIntermSequence *sequence = node->getSequence();
- if (sequence->size() > 1)
- {
- TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
- ASSERT(parentAgg != nullptr);
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ ASSERT(parentBlock != nullptr);
- TIntermSequence replacementDeclarations;
- for (size_t ii = 0; ii < sequence->size(); ++ii)
- {
- TIntermAggregate *replacementDeclaration = new TIntermAggregate;
-
- replacementDeclaration->setOp(EOpDeclaration);
- replacementDeclaration->getSequence()->push_back(sequence->at(ii));
- replacementDeclaration->setLine(sequence->at(ii)->getLine());
- replacementDeclarations.push_back(replacementDeclaration);
- }
+ TIntermSequence replacementDeclarations;
+ for (size_t ii = 0; ii < sequence->size(); ++ii)
+ {
+ TIntermDeclaration *replacementDeclaration = new TIntermDeclaration();
- mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacementDeclarations));
+ replacementDeclaration->appendDeclarator(sequence->at(ii)->getAsTyped());
+ replacementDeclaration->setLine(sequence->at(ii)->getLine());
+ replacementDeclarations.push_back(replacementDeclaration);
}
- return false;
+
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentBlock, node, replacementDeclarations));
}
- return true;
+ return false;
}
-} // namespace
+} // namespace
void SeparateDeclarations(TIntermNode *root)
{
SeparateDeclarationsTraverser::apply(root);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h
index 77913ab8bb..8142faea1c 100644
--- a/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h
+++ b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h
@@ -5,8 +5,8 @@
//
// The SeparateDeclarations function processes declarations, so that in the end each declaration
// contains only one declarator.
-// This is useful as an intermediate step when initialization needs to be separated from declaration,
-// or when things need to be unfolded out of the initializer.
+// This is useful as an intermediate step when initialization needs to be separated from
+// declaration, or when things need to be unfolded out of the initializer.
// Example:
// int a[1] = int[1](1), b[1] = int[1](2);
// gets transformed when run through this class into the AST equivalent of:
@@ -16,8 +16,11 @@
#ifndef COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
#define COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
+namespace sh
+{
class TIntermNode;
void SeparateDeclarations(TIntermNode *root);
+} // namespace sh
#endif // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
index e8e1a21d9c..01d627937c 100644
--- a/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
@@ -3,15 +3,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names from more complex
-// expressions, assigning them to a temporary variable a#.
+// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names
+// from more complex expressions, assigning them to a temporary variable a#.
// Examples where a, b and c are all arrays:
// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];
#include "compiler/translator/SeparateExpressionsReturningArrays.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
namespace
{
@@ -20,7 +24,7 @@ namespace
class SeparateExpressionsTraverser : public TIntermTraverser
{
public:
- SeparateExpressionsTraverser();
+ SeparateExpressionsTraverser(TSymbolTable *symbolTable);
bool visitBinary(Visit visit, TIntermBinary *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
@@ -29,14 +33,17 @@ class SeparateExpressionsTraverser : public TIntermTraverser
bool foundArrayExpression() const { return mFoundArrayExpression; }
protected:
- // Marked to true once an operation that needs to be hoisted out of the expression has been found.
- // After that, no more AST updates are performed on that traversal.
+ // Marked to true once an operation that needs to be hoisted out of the expression has been
+ // found. After that, no more AST updates are performed on that traversal.
bool mFoundArrayExpression;
+
+ IntermNodePatternMatcher mPatternToSeparateMatcher;
};
-SeparateExpressionsTraverser::SeparateExpressionsTraverser()
- : TIntermTraverser(true, false, false),
- mFoundArrayExpression(false)
+SeparateExpressionsTraverser::SeparateExpressionsTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable),
+ mFoundArrayExpression(false),
+ mPatternToSeparateMatcher(IntermNodePatternMatcher::kExpressionReturningArray)
{
}
@@ -45,27 +52,7 @@ SeparateExpressionsTraverser::SeparateExpressionsTraverser()
// and also needs to be replaced in its original location by a different node.
TIntermBinary *CopyAssignmentNode(TIntermBinary *node)
{
- TIntermBinary *copyNode = new TIntermBinary(node->getOp());
- copyNode->setLeft(node->getLeft());
- copyNode->setRight(node->getRight());
- copyNode->setType(node->getType());
- return copyNode;
-}
-
-// Performs a shallow copy of a constructor/function call node.
-TIntermAggregate *CopyAggregateNode(TIntermAggregate *node)
-{
- TIntermAggregate *copyNode = new TIntermAggregate(node->getOp());
- TIntermSequence *copySeq = copyNode->getSequence();
- copySeq->insert(copySeq->begin(), node->getSequence()->begin(), node->getSequence()->end());
- copyNode->setType(node->getType());
- copyNode->setFunctionId(node->getFunctionId());
- if (node->isUserDefined())
- {
- copyNode->setUserDefined();
- }
- copyNode->setNameObj(node->getNameObj());
- return copyNode;
+ return new TIntermBinary(node->getOp(), node->getLeft(), node->getRight());
}
bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
@@ -73,90 +60,59 @@ bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
if (mFoundArrayExpression)
return false;
- // Early return if the expression is not an array or if we're not inside a complex expression.
- if (!node->getType().isArray() || parentNodeIsBlock())
+ // Return if the expression is not an array or if we're not inside a complex expression.
+ if (!mPatternToSeparateMatcher.match(node, getParentNode()))
return true;
- switch (node->getOp())
- {
- case EOpAssign:
- {
- mFoundArrayExpression = true;
-
- TIntermSequence insertions;
- insertions.push_back(CopyAssignmentNode(node));
- // TODO(oetuaho): In some cases it would be more optimal to not add the temporary node, but just use the
- // original target of the assignment. Care must be taken so that this doesn't happen when the same array
- // symbol is a target of assignment more than once in one expression.
- insertions.push_back(createTempInitDeclaration(node->getLeft()));
- insertStatementsInParentBlock(insertions);
-
- NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false);
- mReplacements.push_back(replaceVariable);
- }
- return false;
- default:
- return true;
- }
+ ASSERT(node->getOp() == EOpAssign);
+
+ mFoundArrayExpression = true;
+
+ TIntermSequence insertions;
+ insertions.push_back(CopyAssignmentNode(node));
+ // TODO(oetuaho): In some cases it would be more optimal to not add the temporary node, but just
+ // use the original target of the assignment. Care must be taken so that this doesn't happen
+ // when the same array symbol is a target of assignment more than once in one expression.
+ insertions.push_back(createTempInitDeclaration(node->getLeft()));
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
+
+ return false;
}
bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
if (mFoundArrayExpression)
- return false; // No need to traverse further
-
- if (getParentNode() != nullptr)
- {
- TIntermBinary *parentBinary = getParentNode()->getAsBinaryNode();
- bool parentIsAssignment = (parentBinary != nullptr &&
- (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize));
-
- if (!node->getType().isArray() || parentNodeIsBlock() || parentIsAssignment)
- return true;
-
- if (node->isConstructor())
- {
- mFoundArrayExpression = true;
+ return false; // No need to traverse further
- TIntermSequence insertions;
- insertions.push_back(createTempInitDeclaration(CopyAggregateNode(node)));
- insertStatementsInParentBlock(insertions);
+ if (!mPatternToSeparateMatcher.match(node, getParentNode()))
+ return true;
- NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false);
- mReplacements.push_back(replaceVariable);
+ ASSERT(node->isConstructor() || node->getOp() == EOpCallFunctionInAST);
- return false;
- }
- else if (node->getOp() == EOpFunctionCall)
- {
- mFoundArrayExpression = true;
+ mFoundArrayExpression = true;
- TIntermSequence insertions;
- insertions.push_back(createTempInitDeclaration(CopyAggregateNode(node)));
- insertStatementsInParentBlock(insertions);
+ TIntermSequence insertions;
+ insertions.push_back(createTempInitDeclaration(node->shallowCopy()));
+ insertStatementsInParentBlock(insertions);
- NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false);
- mReplacements.push_back(replaceVariable);
+ queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
- return false;
- }
- }
- return true;
+ return false;
}
void SeparateExpressionsTraverser::nextIteration()
{
mFoundArrayExpression = false;
- nextTemporaryIndex();
+ nextTemporaryId();
}
-} // namespace
+} // namespace
-void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex)
+void SeparateExpressionsReturningArrays(TIntermNode *root, TSymbolTable *symbolTable)
{
- SeparateExpressionsTraverser traverser;
- ASSERT(temporaryIndex != nullptr);
- traverser.useTemporaryIndex(temporaryIndex);
+ SeparateExpressionsTraverser traverser(symbolTable);
// Separate one expression at a time, and reset the traverser between iterations.
do
{
@@ -164,6 +120,7 @@ void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *tempora
root->traverse(&traverser);
if (traverser.foundArrayExpression())
traverser.updateTree();
- }
- while (traverser.foundArrayExpression());
+ } while (traverser.foundArrayExpression());
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h
index b178ebb3ec..f8eb438748 100644
--- a/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h
+++ b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h
@@ -3,8 +3,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names from more complex
-// expressions, assigning them to a temporary variable a#.
+// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names
+// from more complex expressions, assigning them to a temporary variable a#.
// Examples where a, b and c are all arrays:
// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];
@@ -12,8 +12,12 @@
#ifndef COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
#define COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
+namespace sh
+{
class TIntermNode;
+class TSymbolTable;
-void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex);
+void SeparateExpressionsReturningArrays(TIntermNode *root, TSymbolTable *symbolTable);
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
+#endif // COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Severity.h b/src/3rdparty/angle/src/compiler/translator/Severity.h
new file mode 100644
index 0000000000..47808a16a7
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/Severity.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_SEVERITY_H_
+#define COMPILER_TRANSLATOR_SEVERITY_H_
+
+namespace sh
+{
+
+// Severity is used to classify info log messages.
+enum Severity
+{
+ SH_WARNING,
+ SH_ERROR
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SEVERITY_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp b/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp
index e257f93e48..eeb13f2ec0 100644
--- a/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp
@@ -16,10 +16,13 @@
#include "compiler/translator/length_limits.h"
#ifdef ANGLE_ENABLE_HLSL
#include "compiler/translator/TranslatorHLSL.h"
-#endif // ANGLE_ENABLE_HLSL
+#endif // ANGLE_ENABLE_HLSL
#include "compiler/translator/VariablePacker.h"
#include "angle_gl.h"
+namespace sh
+{
+
namespace
{
@@ -34,70 +37,109 @@ template <typename VarT>
const std::vector<VarT> *GetVariableList(const TCompiler *compiler);
template <>
-const std::vector<sh::Uniform> *GetVariableList(const TCompiler *compiler)
+const std::vector<Uniform> *GetVariableList(const TCompiler *compiler)
{
return &compiler->getUniforms();
}
template <>
-const std::vector<sh::Varying> *GetVariableList(const TCompiler *compiler)
+const std::vector<Varying> *GetVariableList(const TCompiler *compiler)
{
- return &compiler->getVaryings();
+ switch (compiler->getShaderType())
+ {
+ case GL_VERTEX_SHADER:
+ return &compiler->getOutputVaryings();
+ case GL_FRAGMENT_SHADER:
+ return &compiler->getInputVaryings();
+ case GL_COMPUTE_SHADER:
+ ASSERT(compiler->getOutputVaryings().empty() && compiler->getInputVaryings().empty());
+ return &compiler->getOutputVaryings();
+ // Since geometry shaders have both input and output varyings, we shouldn't call GetVaryings
+ // on a geometry shader.
+ default:
+ return nullptr;
+ }
}
template <>
-const std::vector<sh::Attribute> *GetVariableList(const TCompiler *compiler)
+const std::vector<Attribute> *GetVariableList(const TCompiler *compiler)
{
return &compiler->getAttributes();
}
template <>
-const std::vector<sh::OutputVariable> *GetVariableList(const TCompiler *compiler)
+const std::vector<OutputVariable> *GetVariableList(const TCompiler *compiler)
{
return &compiler->getOutputVariables();
}
template <>
-const std::vector<sh::InterfaceBlock> *GetVariableList(const TCompiler *compiler)
+const std::vector<InterfaceBlock> *GetVariableList(const TCompiler *compiler)
{
return &compiler->getInterfaceBlocks();
}
-template <typename VarT>
-const std::vector<VarT> *GetShaderVariables(const ShHandle handle)
+TCompiler *GetCompilerFromHandle(ShHandle handle)
{
if (!handle)
{
- return NULL;
+ return nullptr;
}
- TShHandleBase* base = static_cast<TShHandleBase*>(handle);
- TCompiler* compiler = base->getAsCompiler();
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ return base->getAsCompiler();
+}
+
+template <typename VarT>
+const std::vector<VarT> *GetShaderVariables(const ShHandle handle)
+{
+ TCompiler *compiler = GetCompilerFromHandle(handle);
if (!compiler)
{
- return NULL;
+ return nullptr;
}
return GetVariableList<VarT>(compiler);
}
-TCompiler *GetCompilerFromHandle(ShHandle handle)
-{
- if (!handle)
- return NULL;
- TShHandleBase *base = static_cast<TShHandleBase *>(handle);
- return base->getAsCompiler();
-}
-
#ifdef ANGLE_ENABLE_HLSL
TranslatorHLSL *GetTranslatorHLSLFromHandle(ShHandle handle)
{
if (!handle)
- return NULL;
+ return nullptr;
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
return base->getAsTranslatorHLSL();
}
-#endif // ANGLE_ENABLE_HLSL
+#endif // ANGLE_ENABLE_HLSL
+
+GLenum GetGeometryShaderPrimitiveTypeEnum(sh::TLayoutPrimitiveType primitiveType)
+{
+ switch (primitiveType)
+ {
+ case EptPoints:
+ return GL_POINTS;
+ case EptLines:
+ return GL_LINES;
+ case EptLinesAdjacency:
+ return GL_LINES_ADJACENCY_EXT;
+ case EptTriangles:
+ return GL_TRIANGLES;
+ case EptTrianglesAdjacency:
+ return GL_TRIANGLES_ADJACENCY_EXT;
+
+ case EptLineStrip:
+ return GL_LINE_STRIP;
+ case EptTriangleStrip:
+ return GL_TRIANGLE_STRIP;
+
+ case EptUndefined:
+ return GL_INVALID_VALUE;
+
+ default:
+ UNREACHABLE();
+ return GL_INVALID_VALUE;
+ }
+}
} // anonymous namespace
@@ -105,7 +147,7 @@ TranslatorHLSL *GetTranslatorHLSLFromHandle(ShHandle handle)
// Driver must call this first, once, before doing any other compiler operations.
// Subsequent calls to this function are no-op.
//
-bool ShInitialize()
+bool Initialize()
{
if (!isInitialized)
{
@@ -117,7 +159,7 @@ bool ShInitialize()
//
// Cleanup symbol tables
//
-bool ShFinalize()
+bool Finalize()
{
if (isInitialized)
{
@@ -130,33 +172,38 @@ bool ShFinalize()
//
// Initialize built-in resources with minimum expected values.
//
-void ShInitBuiltInResources(ShBuiltInResources* resources)
+void InitBuiltInResources(ShBuiltInResources *resources)
{
// Make comparable.
memset(resources, 0, sizeof(*resources));
// Constants.
- resources->MaxVertexAttribs = 8;
- resources->MaxVertexUniformVectors = 128;
- resources->MaxVaryingVectors = 8;
- resources->MaxVertexTextureImageUnits = 0;
+ resources->MaxVertexAttribs = 8;
+ resources->MaxVertexUniformVectors = 128;
+ resources->MaxVaryingVectors = 8;
+ resources->MaxVertexTextureImageUnits = 0;
resources->MaxCombinedTextureImageUnits = 8;
- resources->MaxTextureImageUnits = 8;
- resources->MaxFragmentUniformVectors = 16;
- resources->MaxDrawBuffers = 1;
+ resources->MaxTextureImageUnits = 8;
+ resources->MaxFragmentUniformVectors = 16;
+ resources->MaxDrawBuffers = 1;
// Extensions.
- resources->OES_standard_derivatives = 0;
- resources->OES_EGL_image_external = 0;
- resources->ARB_texture_rectangle = 0;
- resources->EXT_blend_func_extended = 0;
- resources->EXT_draw_buffers = 0;
- resources->EXT_frag_depth = 0;
- resources->EXT_shader_texture_lod = 0;
- resources->WEBGL_debug_shader_precision = 0;
- resources->EXT_shader_framebuffer_fetch = 0;
- resources->NV_shader_framebuffer_fetch = 0;
- resources->ARM_shader_framebuffer_fetch = 0;
+ resources->OES_standard_derivatives = 0;
+ resources->OES_EGL_image_external = 0;
+ resources->OES_EGL_image_external_essl3 = 0;
+ resources->NV_EGL_stream_consumer_external = 0;
+ resources->ARB_texture_rectangle = 0;
+ resources->EXT_blend_func_extended = 0;
+ resources->EXT_draw_buffers = 0;
+ resources->EXT_frag_depth = 0;
+ resources->EXT_shader_texture_lod = 0;
+ resources->WEBGL_debug_shader_precision = 0;
+ resources->EXT_shader_framebuffer_fetch = 0;
+ resources->NV_shader_framebuffer_fetch = 0;
+ resources->ARM_shader_framebuffer_fetch = 0;
+ resources->OVR_multiview = 0;
+ resources->EXT_YUV_target = 0;
+ resources->OES_geometry_shader = 0;
resources->NV_draw_buffers = 0;
@@ -164,63 +211,125 @@ void ShInitBuiltInResources(ShBuiltInResources* resources)
resources->FragmentPrecisionHigh = 0;
// GLSL ES 3.0 constants.
- resources->MaxVertexOutputVectors = 16;
+ resources->MaxVertexOutputVectors = 16;
resources->MaxFragmentInputVectors = 15;
- resources->MinProgramTexelOffset = -8;
- resources->MaxProgramTexelOffset = 7;
+ resources->MinProgramTexelOffset = -8;
+ resources->MaxProgramTexelOffset = 7;
// Extensions constants.
resources->MaxDualSourceDrawBuffers = 0;
+ resources->MaxViewsOVR = 4;
+
// Disable name hashing by default.
- resources->HashFunction = NULL;
+ resources->HashFunction = nullptr;
resources->ArrayIndexClampingStrategy = SH_CLAMP_WITH_CLAMP_INTRINSIC;
resources->MaxExpressionComplexity = 256;
- resources->MaxCallStackDepth = 256;
+ resources->MaxCallStackDepth = 256;
+ resources->MaxFunctionParameters = 1024;
+
+ // ES 3.1 Revision 4, 7.2 Built-in Constants
+
+ // ES 3.1, Revision 4, 8.13 Texture minification
+ // "The value of MIN_PROGRAM_TEXTURE_GATHER_OFFSET must be less than or equal to the value of
+ // MIN_PROGRAM_TEXEL_OFFSET. The value of MAX_PROGRAM_TEXTURE_GATHER_OFFSET must be greater than
+ // or equal to the value of MAX_PROGRAM_TEXEL_OFFSET"
+ resources->MinProgramTextureGatherOffset = -8;
+ resources->MaxProgramTextureGatherOffset = 7;
+
+ resources->MaxImageUnits = 4;
+ resources->MaxVertexImageUniforms = 0;
+ resources->MaxFragmentImageUniforms = 0;
+ resources->MaxComputeImageUniforms = 4;
+ resources->MaxCombinedImageUniforms = 4;
+
+ resources->MaxUniformLocations = 1024;
+
+ resources->MaxCombinedShaderOutputResources = 4;
+
+ resources->MaxComputeWorkGroupCount[0] = 65535;
+ resources->MaxComputeWorkGroupCount[1] = 65535;
+ resources->MaxComputeWorkGroupCount[2] = 65535;
+ resources->MaxComputeWorkGroupSize[0] = 128;
+ resources->MaxComputeWorkGroupSize[1] = 128;
+ resources->MaxComputeWorkGroupSize[2] = 64;
+ resources->MaxComputeUniformComponents = 512;
+ resources->MaxComputeTextureImageUnits = 16;
+
+ resources->MaxComputeAtomicCounters = 8;
+ resources->MaxComputeAtomicCounterBuffers = 1;
+
+ resources->MaxVertexAtomicCounters = 0;
+ resources->MaxFragmentAtomicCounters = 0;
+ resources->MaxCombinedAtomicCounters = 8;
+ resources->MaxAtomicCounterBindings = 1;
+
+ resources->MaxVertexAtomicCounterBuffers = 0;
+ resources->MaxFragmentAtomicCounterBuffers = 0;
+ resources->MaxCombinedAtomicCounterBuffers = 1;
+ resources->MaxAtomicCounterBufferSize = 32;
+
+ resources->MaxUniformBufferBindings = 32;
+ resources->MaxShaderStorageBufferBindings = 4;
+
+ resources->MaxGeometryUniformComponents = 1024;
+ resources->MaxGeometryUniformBlocks = 12;
+ resources->MaxGeometryInputComponents = 64;
+ resources->MaxGeometryOutputComponents = 64;
+ resources->MaxGeometryOutputVertices = 256;
+ resources->MaxGeometryTotalOutputComponents = 1024;
+ resources->MaxGeometryTextureImageUnits = 16;
+ resources->MaxGeometryAtomicCounterBuffers = 0;
+ resources->MaxGeometryAtomicCounters = 0;
+ resources->MaxGeometryShaderStorageBlocks = 0;
+ resources->MaxGeometryShaderInvocations = 32;
+ resources->MaxGeometryImageUniforms = 0;
}
//
// Driver calls these to create and destroy compiler objects.
//
-ShHandle ShConstructCompiler(sh::GLenum type, ShShaderSpec spec,
- ShShaderOutput output,
- const ShBuiltInResources* resources)
+ShHandle ConstructCompiler(sh::GLenum type,
+ ShShaderSpec spec,
+ ShShaderOutput output,
+ const ShBuiltInResources *resources)
{
- TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec, output));
+ TShHandleBase *base = static_cast<TShHandleBase *>(ConstructCompiler(type, spec, output));
if (base == nullptr)
{
return 0;
}
- TCompiler* compiler = base->getAsCompiler();
+ TCompiler *compiler = base->getAsCompiler();
if (compiler == nullptr)
{
return 0;
}
// Generate built-in symbol table.
- if (!compiler->Init(*resources)) {
- ShDestruct(base);
+ if (!compiler->Init(*resources))
+ {
+ Destruct(base);
return 0;
}
- return reinterpret_cast<void*>(base);
+ return reinterpret_cast<void *>(base);
}
-void ShDestruct(ShHandle handle)
+void Destruct(ShHandle handle)
{
if (handle == 0)
return;
- TShHandleBase* base = static_cast<TShHandleBase*>(handle);
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
if (base->getAsCompiler())
DeleteCompiler(base->getAsCompiler());
}
-const std::string &ShGetBuiltInResourcesString(const ShHandle handle)
+const std::string &GetBuiltInResourcesString(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
@@ -234,11 +343,10 @@ const std::string &ShGetBuiltInResourcesString(const ShHandle handle)
// Return: The return value of ShCompile is really boolean, indicating
// success or failure.
//
-bool ShCompile(
- const ShHandle handle,
- const char *const shaderStrings[],
- size_t numStrings,
- int compileOptions)
+bool Compile(const ShHandle handle,
+ const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
@@ -246,23 +354,23 @@ bool ShCompile(
return compiler->compile(shaderStrings, numStrings, compileOptions);
}
-void ShClearResults(const ShHandle handle)
+void ClearResults(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
compiler->clearResults();
}
-int ShGetShaderVersion(const ShHandle handle)
+int GetShaderVersion(const ShHandle handle)
{
- TCompiler* compiler = GetCompilerFromHandle(handle);
+ TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
return compiler->getShaderVersion();
}
-ShShaderOutput ShGetShaderOutputType(const ShHandle handle)
+ShShaderOutput GetShaderOutputType(const ShHandle handle)
{
- TCompiler* compiler = GetCompilerFromHandle(handle);
+ TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
return compiler->getOutputType();
}
@@ -270,7 +378,7 @@ ShShaderOutput ShGetShaderOutputType(const ShHandle handle)
//
// Return any compiler log of messages for the application.
//
-const std::string &ShGetInfoLog(const ShHandle handle)
+const std::string &GetInfoLog(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
@@ -282,7 +390,7 @@ const std::string &ShGetInfoLog(const ShHandle handle)
//
// Return any object code.
//
-const std::string &ShGetObjectCode(const ShHandle handle)
+const std::string &GetObjectCode(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
@@ -291,58 +399,107 @@ const std::string &ShGetObjectCode(const ShHandle handle)
return infoSink.obj.str();
}
-const std::map<std::string, std::string> *ShGetNameHashingMap(
- const ShHandle handle)
+const std::map<std::string, std::string> *GetNameHashingMap(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
return &(compiler->getNameMap());
}
-const std::vector<sh::Uniform> *ShGetUniforms(const ShHandle handle)
+const std::vector<Uniform> *GetUniforms(const ShHandle handle)
{
- return GetShaderVariables<sh::Uniform>(handle);
+ return GetShaderVariables<Uniform>(handle);
}
-const std::vector<sh::Varying> *ShGetVaryings(const ShHandle handle)
+const std::vector<Varying> *GetInputVaryings(const ShHandle handle)
{
- return GetShaderVariables<sh::Varying>(handle);
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ if (compiler == nullptr)
+ {
+ return nullptr;
+ }
+ return &compiler->getInputVaryings();
}
-const std::vector<sh::Attribute> *ShGetAttributes(const ShHandle handle)
+const std::vector<Varying> *GetOutputVaryings(const ShHandle handle)
{
- return GetShaderVariables<sh::Attribute>(handle);
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ if (compiler == nullptr)
+ {
+ return nullptr;
+ }
+ return &compiler->getOutputVaryings();
}
-const std::vector<sh::OutputVariable> *ShGetOutputVariables(const ShHandle handle)
+const std::vector<Varying> *GetVaryings(const ShHandle handle)
{
- return GetShaderVariables<sh::OutputVariable>(handle);
+ return GetShaderVariables<Varying>(handle);
}
-const std::vector<sh::InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handle)
+const std::vector<Attribute> *GetAttributes(const ShHandle handle)
{
- return GetShaderVariables<sh::InterfaceBlock>(handle);
+ return GetShaderVariables<Attribute>(handle);
}
-bool ShCheckVariablesWithinPackingLimits(
- int maxVectors, ShVariableInfo *varInfoArray, size_t varInfoArraySize)
+const std::vector<OutputVariable> *GetOutputVariables(const ShHandle handle)
{
- if (varInfoArraySize == 0)
- return true;
- ASSERT(varInfoArray);
- std::vector<sh::ShaderVariable> variables;
- for (size_t ii = 0; ii < varInfoArraySize; ++ii)
- {
- sh::ShaderVariable var(varInfoArray[ii].type, varInfoArray[ii].size);
- variables.push_back(var);
- }
- VariablePacker packer;
- return packer.CheckVariablesWithinPackingLimits(maxVectors, variables);
+ return GetShaderVariables<OutputVariable>(handle);
}
-bool ShGetInterfaceBlockRegister(const ShHandle handle,
- const std::string &interfaceBlockName,
- unsigned int *indexOut)
+const std::vector<InterfaceBlock> *GetInterfaceBlocks(const ShHandle handle)
+{
+ return GetShaderVariables<InterfaceBlock>(handle);
+}
+
+const std::vector<InterfaceBlock> *GetUniformBlocks(const ShHandle handle)
+{
+ ASSERT(handle);
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return &compiler->getUniformBlocks();
+}
+
+const std::vector<InterfaceBlock> *GetShaderStorageBlocks(const ShHandle handle)
+{
+ ASSERT(handle);
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return &compiler->getShaderStorageBlocks();
+}
+
+WorkGroupSize GetComputeShaderLocalGroupSize(const ShHandle handle)
+{
+ ASSERT(handle);
+
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return compiler->getComputeShaderLocalSize();
+}
+
+int GetVertexShaderNumViews(const ShHandle handle)
+{
+ ASSERT(handle);
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return compiler->getNumViews();
+}
+
+bool CheckVariablesWithinPackingLimits(int maxVectors, const std::vector<ShaderVariable> &variables)
+{
+ return CheckVariablesInPackingLimits(maxVectors, variables);
+}
+
+bool GetUniformBlockRegister(const ShHandle handle,
+ const std::string &uniformBlockName,
+ unsigned int *indexOut)
{
#ifdef ANGLE_ENABLE_HLSL
ASSERT(indexOut);
@@ -350,35 +507,72 @@ bool ShGetInterfaceBlockRegister(const ShHandle handle,
TranslatorHLSL *translator = GetTranslatorHLSLFromHandle(handle);
ASSERT(translator);
- if (!translator->hasInterfaceBlock(interfaceBlockName))
+ if (!translator->hasUniformBlock(uniformBlockName))
{
return false;
}
- *indexOut = translator->getInterfaceBlockRegister(interfaceBlockName);
+ *indexOut = translator->getUniformBlockRegister(uniformBlockName);
return true;
#else
return false;
-#endif // ANGLE_ENABLE_HLSL
+#endif // ANGLE_ENABLE_HLSL
}
-bool ShGetUniformRegister(const ShHandle handle,
- const std::string &uniformName,
- unsigned int *indexOut)
+const std::map<std::string, unsigned int> *GetUniformRegisterMap(const ShHandle handle)
{
#ifdef ANGLE_ENABLE_HLSL
- ASSERT(indexOut);
TranslatorHLSL *translator = GetTranslatorHLSLFromHandle(handle);
ASSERT(translator);
- if (!translator->hasUniform(uniformName))
- {
- return false;
- }
-
- *indexOut = translator->getUniformRegister(uniformName);
- return true;
+ return translator->getUniformRegisterMap();
#else
- return false;
-#endif // ANGLE_ENABLE_HLSL
+ return nullptr;
+#endif // ANGLE_ENABLE_HLSL
+}
+
+GLenum GetGeometryShaderInputPrimitiveType(const ShHandle handle)
+{
+ ASSERT(handle);
+
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return GetGeometryShaderPrimitiveTypeEnum(compiler->getGeometryShaderInputPrimitiveType());
+}
+
+GLenum GetGeometryShaderOutputPrimitiveType(const ShHandle handle)
+{
+ ASSERT(handle);
+
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return GetGeometryShaderPrimitiveTypeEnum(compiler->getGeometryShaderOutputPrimitiveType());
}
+
+int GetGeometryShaderInvocations(const ShHandle handle)
+{
+ ASSERT(handle);
+
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return compiler->getGeometryShaderInvocations();
+}
+
+int GetGeometryShaderMaxVertices(const ShHandle handle)
+{
+ ASSERT(handle);
+
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return compiler->getGeometryShaderMaxVertices();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp b/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp
index 8f931b9bdd..4ab574e935 100644
--- a/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp
@@ -10,6 +10,7 @@
#include <GLSLANG/ShaderLang.h>
#include "common/debug.h"
+#include "common/utilities.h"
namespace sh
{
@@ -21,7 +22,6 @@ InterpolationType GetNonAuxiliaryInterpolationType(InterpolationType interpolati
{
return (interpolation == INTERPOLATION_CENTROID ? INTERPOLATION_SMOOTH : interpolation);
}
-
}
// The ES 3.0 spec is not clear on this point, but the ES 3.1 spec, and discussion
// on Khronos.org, clarifies that a smooth/flat mismatch produces a link error,
@@ -32,55 +32,58 @@ bool InterpolationTypesMatch(InterpolationType a, InterpolationType b)
}
ShaderVariable::ShaderVariable()
- : type(0),
- precision(0),
- arraySize(0),
- staticUse(false)
-{}
+ : type(0), precision(0), flattenedOffsetInParentArrays(0), staticUse(false)
+{
+}
+
+ShaderVariable::ShaderVariable(GLenum typeIn)
+ : type(typeIn), precision(0), flattenedOffsetInParentArrays(0), staticUse(false)
+{
+}
ShaderVariable::ShaderVariable(GLenum typeIn, unsigned int arraySizeIn)
- : type(typeIn),
- precision(0),
- arraySize(arraySizeIn),
- staticUse(false)
-{}
+ : type(typeIn), precision(0), flattenedOffsetInParentArrays(0), staticUse(false)
+{
+ ASSERT(arraySizeIn != 0);
+ arraySizes.push_back(arraySizeIn);
+}
ShaderVariable::~ShaderVariable()
-{}
+{
+}
ShaderVariable::ShaderVariable(const ShaderVariable &other)
: type(other.type),
precision(other.precision),
name(other.name),
mappedName(other.mappedName),
- arraySize(other.arraySize),
+ arraySizes(other.arraySizes),
+ flattenedOffsetInParentArrays(other.flattenedOffsetInParentArrays),
staticUse(other.staticUse),
fields(other.fields),
structName(other.structName)
-{}
+{
+}
ShaderVariable &ShaderVariable::operator=(const ShaderVariable &other)
{
- type = other.type;
- precision = other.precision;
- name = other.name;
+ type = other.type;
+ precision = other.precision;
+ name = other.name;
mappedName = other.mappedName;
- arraySize = other.arraySize;
- staticUse = other.staticUse;
- fields = other.fields;
+ arraySizes = other.arraySizes;
+ staticUse = other.staticUse;
+ flattenedOffsetInParentArrays = other.flattenedOffsetInParentArrays;
+ fields = other.fields;
structName = other.structName;
return *this;
}
bool ShaderVariable::operator==(const ShaderVariable &other) const
{
- if (type != other.type ||
- precision != other.precision ||
- name != other.name ||
- mappedName != other.mappedName ||
- arraySize != other.arraySize ||
- staticUse != other.staticUse ||
- fields.size() != other.fields.size() ||
+ if (type != other.type || precision != other.precision || name != other.name ||
+ mappedName != other.mappedName || arraySizes != other.arraySizes ||
+ staticUse != other.staticUse || fields.size() != other.fields.size() ||
structName != other.structName)
{
return false;
@@ -93,9 +96,52 @@ bool ShaderVariable::operator==(const ShaderVariable &other) const
return true;
}
-bool ShaderVariable::findInfoByMappedName(
- const std::string &mappedFullName,
- const ShaderVariable **leafVar, std::string *originalFullName) const
+void ShaderVariable::setArraySize(unsigned int size)
+{
+ arraySizes.clear();
+ if (size != 0)
+ {
+ arraySizes.push_back(size);
+ }
+}
+
+unsigned int ShaderVariable::getArraySizeProduct() const
+{
+ return gl::ArraySizeProduct(arraySizes);
+}
+
+void ShaderVariable::indexIntoArray(unsigned int arrayIndex)
+{
+ ASSERT(isArray());
+ flattenedOffsetInParentArrays =
+ arrayIndex + getOutermostArraySize() * flattenedOffsetInParentArrays;
+ arraySizes.pop_back();
+}
+
+unsigned int ShaderVariable::getNestedArraySize(unsigned int arrayNestingIndex) const
+{
+ ASSERT(arraySizes.size() > arrayNestingIndex);
+ return arraySizes[arraySizes.size() - 1u - arrayNestingIndex];
+}
+
+unsigned int ShaderVariable::getBasicTypeElementCount() const
+{
+ // GLES 3.1 Nov 2016 section 7.3.1.1 page 77 specifies that a separate entry should be generated
+ // for each array element when dealing with an array of arrays or an array of structs.
+ ASSERT(!isArrayOfArrays());
+ ASSERT(!isStruct() || !isArray());
+
+ // GLES 3.1 Nov 2016 page 82.
+ if (isArray())
+ {
+ return getOutermostArraySize();
+ }
+ return 1u;
+}
+
+bool ShaderVariable::findInfoByMappedName(const std::string &mappedFullName,
+ const ShaderVariable **leafVar,
+ std::string *originalFullName) const
{
ASSERT(leafVar && originalFullName);
// There are three cases:
@@ -110,7 +156,7 @@ bool ShaderVariable::findInfoByMappedName(
if (mappedFullName != this->mappedName)
return false;
*originalFullName = this->name;
- *leafVar = this;
+ *leafVar = this;
return true;
}
else
@@ -131,13 +177,13 @@ bool ShaderVariable::findInfoByMappedName(
if (closePos + 1 == mappedFullName.size())
{
*originalFullName = originalName;
- *leafVar = this;
+ *leafVar = this;
return true;
}
else
{
// In the form of 'a[0].b', so after ']', '.' is expected.
- if (mappedFullName[closePos + 1] != '.')
+ if (mappedFullName[closePos + 1] != '.')
return false;
remaining = mappedFullName.substr(closePos + 2); // Skip "]."
}
@@ -149,14 +195,13 @@ bool ShaderVariable::findInfoByMappedName(
}
for (size_t ii = 0; ii < this->fields.size(); ++ii)
{
- const ShaderVariable *fieldVar = NULL;
+ const ShaderVariable *fieldVar = nullptr;
std::string originalFieldName;
- bool found = fields[ii].findInfoByMappedName(
- remaining, &fieldVar, &originalFieldName);
+ bool found = fields[ii].findInfoByMappedName(remaining, &fieldVar, &originalFieldName);
if (found)
{
*originalFullName = originalName + "." + originalFieldName;
- *leafVar = fieldVar;
+ *leafVar = fieldVar;
return true;
}
}
@@ -164,24 +209,33 @@ bool ShaderVariable::findInfoByMappedName(
}
}
-bool ShaderVariable::isSameVariableAtLinkTime(
- const ShaderVariable &other, bool matchPrecision) const
+bool ShaderVariable::isBuiltIn() const
+{
+ return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
+}
+
+bool ShaderVariable::isSameVariableAtLinkTime(const ShaderVariable &other,
+ bool matchPrecision,
+ bool matchName) const
{
if (type != other.type)
return false;
if (matchPrecision && precision != other.precision)
return false;
- if (name != other.name)
+ if (matchName && name != other.name)
return false;
- ASSERT(mappedName == other.mappedName);
- if (arraySize != other.arraySize)
+ ASSERT(!matchName || mappedName == other.mappedName);
+ if (arraySizes != other.arraySizes)
return false;
if (fields.size() != other.fields.size())
return false;
+
+ // [OpenGL ES 3.1 SPEC Chapter 7.4.1]
+ // Variables declared as structures are considered to match in type if and only if structure
+ // members match in name, type, qualification, and declaration order.
for (size_t ii = 0; ii < fields.size(); ++ii)
{
- if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii],
- matchPrecision))
+ if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii], matchPrecision, true))
{
return false;
}
@@ -191,53 +245,75 @@ bool ShaderVariable::isSameVariableAtLinkTime(
return true;
}
-Uniform::Uniform()
-{}
+Uniform::Uniform() : binding(-1), offset(-1)
+{
+}
Uniform::~Uniform()
-{}
+{
+}
Uniform::Uniform(const Uniform &other)
- : ShaderVariable(other)
-{}
+ : VariableWithLocation(other), binding(other.binding), offset(other.offset)
+{
+}
Uniform &Uniform::operator=(const Uniform &other)
{
- ShaderVariable::operator=(other);
+ VariableWithLocation::operator=(other);
+ binding = other.binding;
+ offset = other.offset;
return *this;
}
bool Uniform::operator==(const Uniform &other) const
{
- return ShaderVariable::operator==(other);
+ return VariableWithLocation::operator==(other) && binding == other.binding &&
+ offset == other.offset;
}
bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
{
- return ShaderVariable::isSameVariableAtLinkTime(other, true);
+ // Enforce a consistent match.
+ // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=16261
+ if (binding != -1 && other.binding != -1 && binding != other.binding)
+ {
+ return false;
+ }
+ if (location != -1 && other.location != -1 && location != other.location)
+ {
+ return false;
+ }
+ if (offset != other.offset)
+ {
+ return false;
+ }
+ return VariableWithLocation::isSameVariableAtLinkTime(other, true, true);
}
-InterfaceVariable::InterfaceVariable() : location(-1)
-{}
+VariableWithLocation::VariableWithLocation() : location(-1)
+{
+}
-InterfaceVariable::~InterfaceVariable()
-{}
+VariableWithLocation::~VariableWithLocation()
+{
+}
-InterfaceVariable::InterfaceVariable(const InterfaceVariable &other)
+VariableWithLocation::VariableWithLocation(const VariableWithLocation &other)
: ShaderVariable(other), location(other.location)
-{}
+{
+}
-InterfaceVariable &InterfaceVariable::operator=(const InterfaceVariable &other)
+VariableWithLocation &VariableWithLocation::operator=(const VariableWithLocation &other)
{
ShaderVariable::operator=(other);
- location = other.location;
+ location = other.location;
return *this;
}
-bool InterfaceVariable::operator==(const InterfaceVariable &other) const
+bool VariableWithLocation::operator==(const VariableWithLocation &other) const
{
- return (ShaderVariable::operator==(other) &&
- location == other.location);
+ return (ShaderVariable::operator==(other) && location == other.location);
}
Attribute::Attribute()
@@ -248,19 +324,19 @@ Attribute::~Attribute()
{
}
-Attribute::Attribute(const Attribute &other) : InterfaceVariable(other)
+Attribute::Attribute(const Attribute &other) : VariableWithLocation(other)
{
}
Attribute &Attribute::operator=(const Attribute &other)
{
- InterfaceVariable::operator=(other);
+ VariableWithLocation::operator=(other);
return *this;
}
bool Attribute::operator==(const Attribute &other) const
{
- return InterfaceVariable::operator==(other);
+ return VariableWithLocation::operator==(other);
}
OutputVariable::OutputVariable()
@@ -271,79 +347,79 @@ OutputVariable::~OutputVariable()
{
}
-OutputVariable::OutputVariable(const OutputVariable &other) : InterfaceVariable(other)
+OutputVariable::OutputVariable(const OutputVariable &other) : VariableWithLocation(other)
{
}
OutputVariable &OutputVariable::operator=(const OutputVariable &other)
{
- InterfaceVariable::operator=(other);
+ VariableWithLocation::operator=(other);
return *this;
}
bool OutputVariable::operator==(const OutputVariable &other) const
{
- return InterfaceVariable::operator==(other);
+ return VariableWithLocation::operator==(other);
}
-InterfaceBlockField::InterfaceBlockField()
- : isRowMajorLayout(false)
-{}
+InterfaceBlockField::InterfaceBlockField() : isRowMajorLayout(false)
+{
+}
InterfaceBlockField::~InterfaceBlockField()
-{}
+{
+}
InterfaceBlockField::InterfaceBlockField(const InterfaceBlockField &other)
- : ShaderVariable(other),
- isRowMajorLayout(other.isRowMajorLayout)
-{}
+ : ShaderVariable(other), isRowMajorLayout(other.isRowMajorLayout)
+{
+}
InterfaceBlockField &InterfaceBlockField::operator=(const InterfaceBlockField &other)
{
ShaderVariable::operator=(other);
- isRowMajorLayout = other.isRowMajorLayout;
+ isRowMajorLayout = other.isRowMajorLayout;
return *this;
}
bool InterfaceBlockField::operator==(const InterfaceBlockField &other) const
{
- return (ShaderVariable::operator==(other) &&
- isRowMajorLayout == other.isRowMajorLayout);
+ return (ShaderVariable::operator==(other) && isRowMajorLayout == other.isRowMajorLayout);
}
bool InterfaceBlockField::isSameInterfaceBlockFieldAtLinkTime(
const InterfaceBlockField &other) const
{
- return (ShaderVariable::isSameVariableAtLinkTime(other, true) &&
+ return (ShaderVariable::isSameVariableAtLinkTime(other, true, true) &&
isRowMajorLayout == other.isRowMajorLayout);
}
-Varying::Varying()
- : interpolation(INTERPOLATION_SMOOTH),
- isInvariant(false)
-{}
+Varying::Varying() : interpolation(INTERPOLATION_SMOOTH), isInvariant(false)
+{
+}
Varying::~Varying()
-{}
+{
+}
Varying::Varying(const Varying &other)
- : ShaderVariable(other),
+ : VariableWithLocation(other),
interpolation(other.interpolation),
isInvariant(other.isInvariant)
-{}
+{
+}
Varying &Varying::operator=(const Varying &other)
{
- ShaderVariable::operator=(other);
- interpolation = other.interpolation;
- isInvariant = other.isInvariant;
+ VariableWithLocation::operator=(other);
+ interpolation = other.interpolation;
+ isInvariant = other.isInvariant;
return *this;
}
bool Varying::operator==(const Varying &other) const
{
- return (ShaderVariable::operator==(other) &&
- interpolation == other.interpolation &&
+ return (VariableWithLocation::operator==(other) && interpolation == other.interpolation &&
isInvariant == other.isInvariant);
}
@@ -354,20 +430,26 @@ bool Varying::isSameVaryingAtLinkTime(const Varying &other) const
bool Varying::isSameVaryingAtLinkTime(const Varying &other, int shaderVersion) const
{
- return (ShaderVariable::isSameVariableAtLinkTime(other, false) &&
- interpolation == other.interpolation &&
- (shaderVersion >= 300 || isInvariant == other.isInvariant));
+ return (ShaderVariable::isSameVariableAtLinkTime(other, false, false) &&
+ InterpolationTypesMatch(interpolation, other.interpolation) &&
+ (shaderVersion >= 300 || isInvariant == other.isInvariant) &&
+ (location == other.location) &&
+ (name == other.name || (shaderVersion >= 310 && location >= 0)));
}
InterfaceBlock::InterfaceBlock()
: arraySize(0),
layout(BLOCKLAYOUT_PACKED),
isRowMajorLayout(false),
- staticUse(false)
-{}
+ binding(-1),
+ staticUse(false),
+ blockType(BlockType::BLOCK_UNIFORM)
+{
+}
InterfaceBlock::~InterfaceBlock()
-{}
+{
+}
InterfaceBlock::InterfaceBlock(const InterfaceBlock &other)
: name(other.name),
@@ -376,20 +458,25 @@ InterfaceBlock::InterfaceBlock(const InterfaceBlock &other)
arraySize(other.arraySize),
layout(other.layout),
isRowMajorLayout(other.isRowMajorLayout),
+ binding(other.binding),
staticUse(other.staticUse),
+ blockType(other.blockType),
fields(other.fields)
-{}
+{
+}
InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other)
{
- name = other.name;
- mappedName = other.mappedName;
- instanceName = other.instanceName;
- arraySize = other.arraySize;
- layout = other.layout;
+ name = other.name;
+ mappedName = other.mappedName;
+ instanceName = other.instanceName;
+ arraySize = other.arraySize;
+ layout = other.layout;
isRowMajorLayout = other.isRowMajorLayout;
- staticUse = other.staticUse;
- fields = other.fields;
+ binding = other.binding;
+ staticUse = other.staticUse;
+ blockType = other.blockType;
+ fields = other.fields;
return *this;
}
@@ -398,4 +485,102 @@ std::string InterfaceBlock::fieldPrefix() const
return instanceName.empty() ? "" : name;
}
+std::string InterfaceBlock::fieldMappedPrefix() const
+{
+ return instanceName.empty() ? "" : mappedName;
+}
+
+bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) const
+{
+ if (name != other.name || mappedName != other.mappedName || arraySize != other.arraySize ||
+ layout != other.layout || isRowMajorLayout != other.isRowMajorLayout ||
+ binding != other.binding || blockType != other.blockType ||
+ fields.size() != other.fields.size())
+ {
+ return false;
+ }
+
+ for (size_t fieldIndex = 0; fieldIndex < fields.size(); ++fieldIndex)
+ {
+ if (!fields[fieldIndex].isSameInterfaceBlockFieldAtLinkTime(other.fields[fieldIndex]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool InterfaceBlock::isBuiltIn() const
+{
+ return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
+}
+
+void WorkGroupSize::fill(int fillValue)
+{
+ localSizeQualifiers[0] = fillValue;
+ localSizeQualifiers[1] = fillValue;
+ localSizeQualifiers[2] = fillValue;
+}
+
+void WorkGroupSize::setLocalSize(int localSizeX, int localSizeY, int localSizeZ)
+{
+ localSizeQualifiers[0] = localSizeX;
+ localSizeQualifiers[1] = localSizeY;
+ localSizeQualifiers[2] = localSizeZ;
+}
+
+// check that if one of them is less than 1, then all of them are.
+// Or if one is positive, then all of them are positive.
+bool WorkGroupSize::isLocalSizeValid() const
+{
+ return (
+ (localSizeQualifiers[0] < 1 && localSizeQualifiers[1] < 1 && localSizeQualifiers[2] < 1) ||
+ (localSizeQualifiers[0] > 0 && localSizeQualifiers[1] > 0 && localSizeQualifiers[2] > 0));
+}
+
+bool WorkGroupSize::isAnyValueSet() const
+{
+ return localSizeQualifiers[0] > 0 || localSizeQualifiers[1] > 0 || localSizeQualifiers[2] > 0;
+}
+
+bool WorkGroupSize::isDeclared() const
+{
+ bool localSizeDeclared = localSizeQualifiers[0] > 0;
+ ASSERT(isLocalSizeValid());
+ return localSizeDeclared;
+}
+
+bool WorkGroupSize::isWorkGroupSizeMatching(const WorkGroupSize &right) const
+{
+ for (size_t i = 0u; i < size(); ++i)
+ {
+ bool result = (localSizeQualifiers[i] == right.localSizeQualifiers[i] ||
+ (localSizeQualifiers[i] == 1 && right.localSizeQualifiers[i] == -1) ||
+ (localSizeQualifiers[i] == -1 && right.localSizeQualifiers[i] == 1));
+ if (!result)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+int &WorkGroupSize::operator[](size_t index)
+{
+ ASSERT(index < size());
+ return localSizeQualifiers[index];
+}
+
+int WorkGroupSize::operator[](size_t index) const
+{
+ ASSERT(index < size());
+ return localSizeQualifiers[index];
+}
+
+size_t WorkGroupSize::size() const
+{
+ return 3u;
+}
+
} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.cpp b/src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.cpp
deleted file mode 100644
index ac5eb67070..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/SimplifyArrayAssignment.h"
-
-bool SimplifyArrayAssignment::visitBinary(Visit visit, TIntermBinary *node)
-{
- switch (node->getOp())
- {
- case EOpAssign:
- {
- TIntermNode *parent = getParentNode();
- if (node->getLeft()->isArray() && parent != nullptr)
- {
- TIntermAggregate *parentAgg = parent->getAsAggregate();
- if (parentAgg != nullptr && parentAgg->getOp() == EOpSequence)
- {
- // This case is fine, the result of the assignment is not used.
- break;
- }
-
- // The result of the assignment needs to be stored into a temporary variable,
- // the assignment needs to be replaced with a reference to the temporary variable,
- // and the temporary variable needs to finally be assigned to the target variable.
-
- // This also needs to interact correctly with unfolding short circuiting operators.
- UNIMPLEMENTED();
- }
- }
- break;
- default:
- break;
- }
- return true;
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.h b/src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.h
deleted file mode 100644
index 247eb88d72..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.h
+++ /dev/null
@@ -1,25 +0,0 @@
-//
-// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// SimplifyArrayAssignment is an AST traverser to replace statements where
-// the return value of array assignment is used with statements where
-// the return value of array assignment is not used.
-//
-
-#ifndef COMPILER_TRANSLATOR_SIMPLIFYARRAYASSIGNMENT_H_
-#define COMPILER_TRANSLATOR_SIMPLIFYARRAYASSIGNMENT_H_
-
-#include "common/angleutils.h"
-#include "compiler/translator/IntermNode.h"
-
-class SimplifyArrayAssignment : public TIntermTraverser
-{
- public:
- SimplifyArrayAssignment() { }
-
- virtual bool visitBinary(Visit visit, TIntermBinary *node);
-};
-
-#endif // COMPILER_TRANSLATOR_SIMPLIFYARRAYASSIGNMENT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.cpp b/src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.cpp
new file mode 100644
index 0000000000..9704046839
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.cpp
@@ -0,0 +1,300 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SimplifyLoopConditions is an AST traverser that converts loop conditions and loop expressions
+// to regular statements inside the loop. This way further transformations that generate statements
+// from loop conditions and loop expressions work correctly.
+//
+
+#include "compiler/translator/SimplifyLoopConditions.h"
+
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class SimplifyLoopConditionsTraverser : public TLValueTrackingTraverser
+{
+ public:
+ SimplifyLoopConditionsTraverser(unsigned int conditionsToSimplifyMask,
+ TSymbolTable *symbolTable,
+ int shaderVersion);
+
+ void traverseLoop(TIntermLoop *node) override;
+
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+
+ bool foundLoopToChange() const { return mFoundLoopToChange; }
+
+ protected:
+ // Marked to true once an operation that needs to be hoisted out of a loop expression has been
+ // found.
+ bool mFoundLoopToChange;
+ bool mInsideLoopInitConditionOrExpression;
+ IntermNodePatternMatcher mConditionsToSimplify;
+};
+
+SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
+ unsigned int conditionsToSimplifyMask,
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+ : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
+ mFoundLoopToChange(false),
+ mInsideLoopInitConditionOrExpression(false),
+ mConditionsToSimplify(conditionsToSimplifyMask)
+{
+}
+
+// If we're inside a loop initialization, condition, or expression, we check for expressions that
+// should be moved out of the loop condition or expression. If one is found, the loop is
+// transformed.
+// If we're not inside loop initialization, condition, or expression, we only need to traverse nodes
+// that may contain loops.
+
+bool SimplifyLoopConditionsTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (!mInsideLoopInitConditionOrExpression)
+ return false;
+
+ if (mFoundLoopToChange)
+ return false; // Already decided to change this loop.
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node);
+ return !mFoundLoopToChange;
+}
+
+bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (!mInsideLoopInitConditionOrExpression)
+ return false;
+
+ if (mFoundLoopToChange)
+ return false; // Already decided to change this loop.
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
+ return !mFoundLoopToChange;
+}
+
+bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (!mInsideLoopInitConditionOrExpression)
+ return false;
+
+ if (mFoundLoopToChange)
+ return false; // Already decided to change this loop.
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
+ return !mFoundLoopToChange;
+}
+
+bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ if (!mInsideLoopInitConditionOrExpression)
+ return false;
+
+ if (mFoundLoopToChange)
+ return false; // Already decided to change this loop.
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node);
+ return !mFoundLoopToChange;
+}
+
+bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ if (!mInsideLoopInitConditionOrExpression)
+ return false;
+
+ if (mFoundLoopToChange)
+ return false; // Already decided to change this loop.
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node);
+ return !mFoundLoopToChange;
+}
+
+void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
+{
+ // Mark that we're inside a loop condition or expression, and determine if the loop needs to be
+ // transformed.
+
+ ScopedNodeInTraversalPath addToPath(this, node);
+
+ mInsideLoopInitConditionOrExpression = true;
+ mFoundLoopToChange = false;
+
+ if (!mFoundLoopToChange && node->getInit())
+ {
+ node->getInit()->traverse(this);
+ }
+
+ if (!mFoundLoopToChange && node->getCondition())
+ {
+ node->getCondition()->traverse(this);
+ }
+
+ if (!mFoundLoopToChange && node->getExpression())
+ {
+ node->getExpression()->traverse(this);
+ }
+
+ mInsideLoopInitConditionOrExpression = false;
+
+ if (mFoundLoopToChange)
+ {
+ nextTemporaryId();
+
+ // Replace the loop condition with a boolean variable that's updated on each iteration.
+ TLoopType loopType = node->getType();
+ if (loopType == ELoopWhile)
+ {
+ // Transform:
+ // while (expr) { body; }
+ // into
+ // bool s0 = expr;
+ // while (s0) { { body; } s0 = expr; }
+ TIntermSequence tempInitSeq;
+ tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
+ insertStatementsInParentBlock(tempInitSeq);
+
+ TIntermBlock *newBody = new TIntermBlock();
+ if (node->getBody())
+ {
+ newBody->getSequence()->push_back(node->getBody());
+ }
+ newBody->getSequence()->push_back(
+ createTempAssignment(node->getCondition()->deepCopy()));
+
+ // Can't use queueReplacement to replace old body, since it may have been nullptr.
+ // It's safe to do the replacements in place here - the new body will still be
+ // traversed, but that won't create any problems.
+ node->setBody(newBody);
+ node->setCondition(createTempSymbol(node->getCondition()->getType()));
+ }
+ else if (loopType == ELoopDoWhile)
+ {
+ // Transform:
+ // do {
+ // body;
+ // } while (expr);
+ // into
+ // bool s0 = true;
+ // do {
+ // { body; }
+ // s0 = expr;
+ // } while (s0);
+ TIntermSequence tempInitSeq;
+ tempInitSeq.push_back(createTempInitDeclaration(CreateBoolNode(true)));
+ insertStatementsInParentBlock(tempInitSeq);
+
+ TIntermBlock *newBody = new TIntermBlock();
+ if (node->getBody())
+ {
+ newBody->getSequence()->push_back(node->getBody());
+ }
+ newBody->getSequence()->push_back(
+ createTempAssignment(node->getCondition()->deepCopy()));
+
+ // Can't use queueReplacement to replace old body, since it may have been nullptr.
+ // It's safe to do the replacements in place here - the new body will still be
+ // traversed, but that won't create any problems.
+ node->setBody(newBody);
+ node->setCondition(createTempSymbol(node->getCondition()->getType()));
+ }
+ else if (loopType == ELoopFor)
+ {
+ // Move the loop condition inside the loop.
+ // Transform:
+ // for (init; expr; exprB) { body; }
+ // into
+ // {
+ // init;
+ // bool s0 = expr;
+ // while (s0) {
+ // { body; }
+ // exprB;
+ // s0 = expr;
+ // }
+ // }
+ TIntermBlock *loopScope = new TIntermBlock();
+ TIntermSequence *loopScopeSequence = loopScope->getSequence();
+
+ // Insert "init;"
+ if (node->getInit())
+ {
+ loopScopeSequence->push_back(node->getInit());
+ }
+
+ // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
+ TIntermTyped *conditionInitializer = nullptr;
+ if (node->getCondition())
+ {
+ conditionInitializer = node->getCondition()->deepCopy();
+ }
+ else
+ {
+ conditionInitializer = CreateBoolNode(true);
+ }
+ loopScopeSequence->push_back(createTempInitDeclaration(conditionInitializer));
+
+ // Insert "{ body; }" in the while loop
+ TIntermBlock *whileLoopBody = new TIntermBlock();
+ if (node->getBody())
+ {
+ whileLoopBody->getSequence()->push_back(node->getBody());
+ }
+ // Insert "exprB;" in the while loop
+ if (node->getExpression())
+ {
+ whileLoopBody->getSequence()->push_back(node->getExpression());
+ }
+ // Insert "s0 = expr;" in the while loop
+ if (node->getCondition())
+ {
+ whileLoopBody->getSequence()->push_back(
+ createTempAssignment(node->getCondition()->deepCopy()));
+ }
+
+ // Create "while(s0) { whileLoopBody }"
+ TIntermLoop *whileLoop = new TIntermLoop(
+ ELoopWhile, nullptr, createTempSymbol(conditionInitializer->getType()), nullptr,
+ whileLoopBody);
+ loopScope->getSequence()->push_back(whileLoop);
+ queueReplacement(loopScope, OriginalNode::IS_DROPPED);
+
+ // After this the old body node will be traversed and loops inside it may be
+ // transformed. This is fine, since the old body node will still be in the AST after the
+ // transformation that's queued here, and transforming loops inside it doesn't need to
+ // know the exact post-transform path to it.
+ }
+ }
+
+ mFoundLoopToChange = false;
+
+ // We traverse the body of the loop even if the loop is transformed.
+ if (node->getBody())
+ node->getBody()->traverse(this);
+}
+
+} // namespace
+
+void SimplifyLoopConditions(TIntermNode *root,
+ unsigned int conditionsToSimplifyMask,
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+{
+ SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.h b/src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.h
new file mode 100644
index 0000000000..d8f95cd2c8
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SimplifyLoopConditions is an AST traverser that converts loop conditions and loop expressions
+// to regular statements inside the loop. This way further transformations that generate statements
+// from loop conditions and loop expressions work correctly.
+//
+
+#ifndef COMPILER_TRANSLATOR_SIMPLIFYLOOPCONDITIONS_H_
+#define COMPILER_TRANSLATOR_SIMPLIFYLOOPCONDITIONS_H_
+
+namespace sh
+{
+class TIntermNode;
+class TSymbolTable;
+
+void SimplifyLoopConditions(TIntermNode *root,
+ unsigned int conditionsToSimplify,
+ TSymbolTable *symbolTable,
+ int shaderVersion);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SIMPLIFYLOOPCONDITIONS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.cpp b/src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.cpp
new file mode 100644
index 0000000000..5df3154560
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.cpp
@@ -0,0 +1,171 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SplitSequenceOperator is an AST traverser that detects sequence operator expressions that
+// go through further AST transformations that generate statements, and splits them so that
+// possible side effects of earlier parts of the sequence operator expression are guaranteed to be
+// evaluated before the latter parts of the sequence operator expression are evaluated.
+//
+
+#include "compiler/translator/SplitSequenceOperator.h"
+
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class SplitSequenceOperatorTraverser : public TLValueTrackingTraverser
+{
+ public:
+ SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
+ TSymbolTable *symbolTable,
+ int shaderVersion);
+
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+
+ void nextIteration();
+ bool foundExpressionToSplit() const { return mFoundExpressionToSplit; }
+
+ protected:
+ // Marked to true once an operation that needs to be hoisted out of the expression has been
+ // found. After that, no more AST updates are performed on that traversal.
+ bool mFoundExpressionToSplit;
+ int mInsideSequenceOperator;
+
+ IntermNodePatternMatcher mPatternToSplitMatcher;
+};
+
+SplitSequenceOperatorTraverser::SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+ : TLValueTrackingTraverser(true, false, true, symbolTable, shaderVersion),
+ mFoundExpressionToSplit(false),
+ mInsideSequenceOperator(0),
+ mPatternToSplitMatcher(patternsToSplitMask)
+{
+}
+
+void SplitSequenceOperatorTraverser::nextIteration()
+{
+ mFoundExpressionToSplit = false;
+ mInsideSequenceOperator = 0;
+ nextTemporaryId();
+}
+
+bool SplitSequenceOperatorTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFoundExpressionToSplit)
+ return false;
+
+ if (mInsideSequenceOperator > 0 && visit == PreVisit)
+ {
+ // Detect expressions that need to be simplified
+ mFoundExpressionToSplit = mPatternToSplitMatcher.match(node, getParentNode());
+ return !mFoundExpressionToSplit;
+ }
+
+ return true;
+}
+
+bool SplitSequenceOperatorTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (mFoundExpressionToSplit)
+ return false;
+
+ if (mInsideSequenceOperator > 0 && visit == PreVisit)
+ {
+ // Detect expressions that need to be simplified
+ mFoundExpressionToSplit = mPatternToSplitMatcher.match(node);
+ return !mFoundExpressionToSplit;
+ }
+
+ return true;
+}
+
+bool SplitSequenceOperatorTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (node->getOp() == EOpComma)
+ {
+ if (visit == PreVisit)
+ {
+ if (mFoundExpressionToSplit)
+ {
+ return false;
+ }
+ mInsideSequenceOperator++;
+ }
+ else if (visit == PostVisit)
+ {
+ // Split sequence operators starting from the outermost one to preserve correct
+ // execution order.
+ if (mFoundExpressionToSplit && mInsideSequenceOperator == 1)
+ {
+ // Move the left side operand into a separate statement in the parent block.
+ TIntermSequence insertions;
+ insertions.push_back(node->getLeft());
+ insertStatementsInParentBlock(insertions);
+ // Replace the comma node with its right side operand.
+ queueReplacement(node->getRight(), OriginalNode::IS_DROPPED);
+ }
+ mInsideSequenceOperator--;
+ }
+ return true;
+ }
+
+ if (mFoundExpressionToSplit)
+ return false;
+
+ if (mInsideSequenceOperator > 0 && visit == PreVisit)
+ {
+ // Detect expressions that need to be simplified
+ mFoundExpressionToSplit =
+ mPatternToSplitMatcher.match(node, getParentNode(), isLValueRequiredHere());
+ return !mFoundExpressionToSplit;
+ }
+
+ return true;
+}
+
+bool SplitSequenceOperatorTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ if (mFoundExpressionToSplit)
+ return false;
+
+ if (mInsideSequenceOperator > 0 && visit == PreVisit)
+ {
+ // Detect expressions that need to be simplified
+ mFoundExpressionToSplit = mPatternToSplitMatcher.match(node);
+ return !mFoundExpressionToSplit;
+ }
+
+ return true;
+}
+
+} // namespace
+
+void SplitSequenceOperator(TIntermNode *root,
+ int patternsToSplitMask,
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+{
+ SplitSequenceOperatorTraverser traverser(patternsToSplitMask, symbolTable, shaderVersion);
+ // Separate one expression at a time, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundExpressionToSplit())
+ traverser.updateTree();
+ } while (traverser.foundExpressionToSplit());
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.h b/src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.h
new file mode 100644
index 0000000000..8f1a766775
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SplitSequenceOperator is an AST traverser that detects sequence operator expressions that
+// go through further AST transformations that generate statements, and splits them so that
+// possible side effects of earlier parts of the sequence operator expression are guaranteed to be
+// evaluated before the latter parts of the sequence operator expression are evaluated.
+//
+
+#ifndef COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
+#define COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
+
+namespace sh
+{
+
+class TIntermNode;
+class TSymbolTable;
+
+void SplitSequenceOperator(TIntermNode *root,
+ int patternsToSplitMask,
+ TSymbolTable *symbolTable,
+ int shaderVersion);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/StructureHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/StructureHLSL.cpp
index fd1a29c1cd..61a9431ceb 100644
--- a/src/3rdparty/angle/src/compiler/translator/StructureHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/StructureHLSL.cpp
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
// StructureHLSL.cpp:
-// Definitions of methods for HLSL translation of GLSL structures.
+// HLSL translation of GLSL constructors and structures.
//
#include "compiler/translator/StructureHLSL.h"
@@ -17,23 +17,94 @@
namespace sh
{
+namespace
+{
+
+TString Define(const TStructure &structure,
+ bool useHLSLRowMajorPacking,
+ bool useStd140Packing,
+ Std140PaddingHelper *padHelper)
+{
+ const TFieldList &fields = structure.fields();
+ const bool isNameless = (structure.name() == "");
+ const TString &structName =
+ QualifiedStructNameString(structure, useHLSLRowMajorPacking, useStd140Packing);
+ const TString declareString = (isNameless ? "struct" : "struct " + structName);
+
+ TString string;
+ string += declareString +
+ "\n"
+ "{\n";
+
+ for (const TField *field : fields)
+ {
+ const TType &fieldType = *field->type();
+ if (!IsSampler(fieldType.getBasicType()))
+ {
+ const TStructure *fieldStruct = fieldType.getStruct();
+ const TString &fieldTypeString =
+ fieldStruct ? QualifiedStructNameString(*fieldStruct, useHLSLRowMajorPacking,
+ useStd140Packing)
+ : TypeString(fieldType);
+
+ if (padHelper)
+ {
+ string += padHelper->prePaddingString(fieldType);
+ }
+
+ string += " " + fieldTypeString + " " + DecorateField(field->name(), structure) +
+ ArrayString(fieldType) + ";\n";
+
+ if (padHelper)
+ {
+ string += padHelper->postPaddingString(fieldType, useHLSLRowMajorPacking);
+ }
+ }
+ }
+
+ // Nameless structs do not finish with a semicolon and newline, to leave room for an instance
+ // variable
+ string += (isNameless ? "} " : "};\n");
+
+ return string;
+}
+
+TString WriteParameterList(const std::vector<TType> &parameters)
+{
+ TString parameterList;
+ for (size_t parameter = 0u; parameter < parameters.size(); parameter++)
+ {
+ const TType &paramType = parameters[parameter];
+
+ parameterList += TypeString(paramType) + " x" + str(parameter) + ArrayString(paramType);
+
+ if (parameter < parameters.size() - 1u)
+ {
+ parameterList += ", ";
+ }
+ }
+ return parameterList;
+}
+
+} // anonymous namespace
+
Std140PaddingHelper::Std140PaddingHelper(const std::map<TString, int> &structElementIndexes,
unsigned *uniqueCounter)
- : mPaddingCounter(uniqueCounter),
- mElementIndex(0),
- mStructElementIndexes(&structElementIndexes)
-{}
+ : mPaddingCounter(uniqueCounter), mElementIndex(0), mStructElementIndexes(&structElementIndexes)
+{
+}
Std140PaddingHelper::Std140PaddingHelper(const Std140PaddingHelper &other)
: mPaddingCounter(other.mPaddingCounter),
mElementIndex(other.mElementIndex),
mStructElementIndexes(other.mStructElementIndexes)
-{}
+{
+}
Std140PaddingHelper &Std140PaddingHelper::operator=(const Std140PaddingHelper &other)
{
- mPaddingCounter = other.mPaddingCounter;
- mElementIndex = other.mElementIndex;
+ mPaddingCounter = other.mPaddingCounter;
+ mElementIndex = other.mElementIndex;
mStructElementIndexes = other.mStructElementIndexes;
return *this;
}
@@ -53,7 +124,7 @@ int Std140PaddingHelper::prePadding(const TType &type)
return 0;
}
- const GLenum glType = GLVariableType(type);
+ const GLenum glType = GLVariableType(type);
const int numComponents = gl::VariableComponentCount(glType);
if (numComponents >= 4)
@@ -70,9 +141,9 @@ int Std140PaddingHelper::prePadding(const TType &type)
return 0;
}
- const int alignment = numComponents == 3 ? 4 : numComponents;
+ const int alignment = numComponents == 3 ? 4 : numComponents;
const int paddingOffset = (mElementIndex % alignment);
- const int paddingCount = (paddingOffset != 0 ? (alignment - paddingOffset) : 0);
+ const int paddingCount = (paddingOffset != 0 ? (alignment - paddingOffset) : 0);
mElementIndex += paddingCount;
mElementIndex += numComponents;
@@ -102,25 +173,26 @@ TString Std140PaddingHelper::postPaddingString(const TType &type, bool useHLSLRo
return "";
}
- int numComponents = 0;
- TStructure *structure = type.getStruct();
+ int numComponents = 0;
+ const TStructure *structure = type.getStruct();
if (type.isMatrix())
{
- // This method can also be called from structureString, which does not use layout qualifiers.
+ // 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);
+ const GLenum glType = GLVariableType(type);
+ numComponents = gl::MatrixComponentCount(glType, isRowMajorMatrix);
}
else if (structure)
{
- const TString &structName = QualifiedStructNameString(*structure,
- useHLSLRowMajorPacking, true);
+ const TString &structName =
+ QualifiedStructNameString(*structure, useHLSLRowMajorPacking, true);
numComponents = mStructElementIndexes->find(structName)->second;
if (numComponents == 0)
@@ -131,7 +203,7 @@ TString Std140PaddingHelper::postPaddingString(const TType &type, bool useHLSLRo
else
{
const GLenum glType = GLVariableType(type);
- numComponents = gl::VariableComponentCount(glType);
+ numComponents = gl::VariableComponentCount(glType);
}
TString padding;
@@ -142,178 +214,183 @@ TString Std140PaddingHelper::postPaddingString(const TType &type, bool useHLSLRo
return padding;
}
-StructureHLSL::StructureHLSL()
- : mUniquePaddingCounter(0)
-{}
+StructureHLSL::StructureHLSL() : mUniquePaddingCounter(0)
+{
+}
Std140PaddingHelper StructureHLSL::getPaddingHelper()
{
return Std140PaddingHelper(mStd140StructElementIndexes, &mUniquePaddingCounter);
}
-TString StructureHLSL::defineQualified(const TStructure &structure, bool useHLSLRowMajorPacking, bool useStd140Packing)
+TString StructureHLSL::defineQualified(const TStructure &structure,
+ bool useHLSLRowMajorPacking,
+ bool useStd140Packing)
{
if (useStd140Packing)
{
Std140PaddingHelper padHelper = getPaddingHelper();
- return define(structure, useHLSLRowMajorPacking, useStd140Packing, &padHelper);
+ return Define(structure, useHLSLRowMajorPacking, useStd140Packing, &padHelper);
}
else
{
- return define(structure, useHLSLRowMajorPacking, useStd140Packing, NULL);
+ return Define(structure, useHLSLRowMajorPacking, useStd140Packing, nullptr);
}
}
TString StructureHLSL::defineNameless(const TStructure &structure)
{
- return define(structure, false, false, NULL);
+ return Define(structure, false, false, nullptr);
}
-TString StructureHLSL::define(const TStructure &structure, bool useHLSLRowMajorPacking,
- bool useStd140Packing, Std140PaddingHelper *padHelper)
+StructureHLSL::DefinedStructs::iterator StructureHLSL::defineVariants(const TStructure &structure,
+ const TString &name)
{
- const TFieldList &fields = structure.fields();
- const bool isNameless = (structure.name() == "");
- const TString &structName = QualifiedStructNameString(structure, useHLSLRowMajorPacking,
- useStd140Packing);
- const TString declareString = (isNameless ? "struct" : "struct " + structName);
+ ASSERT(mDefinedStructs.find(name) == mDefinedStructs.end());
- TString string;
- string += declareString + "\n"
- "{\n";
-
- for (unsigned int i = 0; i < fields.size(); i++)
+ for (const TField *field : structure.fields())
{
- const TField &field = *fields[i];
- const TType &fieldType = *field.type();
- const TStructure *fieldStruct = fieldType.getStruct();
- const TString &fieldTypeString = fieldStruct ?
- QualifiedStructNameString(*fieldStruct, useHLSLRowMajorPacking,
- useStd140Packing) :
- TypeString(fieldType);
-
- if (padHelper)
+ const TType *fieldType = field->type();
+ if (fieldType->getBasicType() == EbtStruct)
{
- string += padHelper->prePaddingString(fieldType);
- }
-
- string += " " + fieldTypeString + " " + DecorateField(field.name(), structure) + ArrayString(fieldType) + ";\n";
-
- if (padHelper)
- {
- string += padHelper->postPaddingString(fieldType, useHLSLRowMajorPacking);
+ ensureStructDefined(*fieldType->getStruct());
}
}
- // Nameless structs do not finish with a semicolon and newline, to leave room for an instance variable
- string += (isNameless ? "} " : "};\n");
-
- return string;
+ DefinedStructs::iterator addedStruct =
+ mDefinedStructs.insert(std::make_pair(name, new TStructProperties())).first;
+ // Add element index
+ storeStd140ElementIndex(structure, false);
+ storeStd140ElementIndex(structure, true);
+
+ const TString &structString = defineQualified(structure, false, false);
+
+ ASSERT(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);
+ return addedStruct;
}
-void StructureHLSL::addConstructor(const TType &type, const TString &name, const TIntermSequence *parameters)
+void StructureHLSL::ensureStructDefined(const TStructure &structure)
{
+ const TString name = StructNameString(structure);
if (name == "")
{
- return; // Nameless structures don't have constructors
+ return; // Nameless structures are not defined
}
-
- if (type.getStruct() && mStructNames.find(name) != mStructNames.end())
+ if (mDefinedStructs.find(name) == mDefinedStructs.end())
{
- return; // Already added
+ defineVariants(structure, name);
}
+}
- 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);
- }
+TString StructureHLSL::addStructConstructor(const TStructure &structure)
+{
+ const TString name = StructNameString(structure);
- const TFieldList &fields = structure->fields();
- for (unsigned int i = 0; i < fields.size(); i++)
- {
- ctorParameters.push_back(*fields[i]->type());
- }
- }
- else if (parameters)
+ if (name == "")
{
- for (TIntermSequence::const_iterator parameter = parameters->begin(); parameter != parameters->end(); parameter++)
- {
- ctorParameters.push_back((*parameter)->getAsTyped()->getType());
- }
+ return TString(); // Nameless structures don't have constructors
}
- else UNREACHABLE();
-
- TString constructor;
- if (ctorType.getStruct())
+ auto definedStruct = mDefinedStructs.find(name);
+ if (definedStruct == mDefinedStructs.end())
{
- constructor += name + " " + name + "_ctor(";
+ definedStruct = defineVariants(structure, name);
}
- else // Built-in type
+ const TString constructorFunctionName = TString(name) + "_ctor";
+ TString *constructor = &definedStruct->second->constructor;
+ if (!constructor->empty())
{
- constructor += TypeString(ctorType) + " " + name + "(";
+ return constructorFunctionName; // Already added
}
+ *constructor += name + " " + constructorFunctionName + "(";
- for (unsigned int parameter = 0; parameter < ctorParameters.size(); parameter++)
+ std::vector<TType> ctorParameters;
+ const TFieldList &fields = structure.fields();
+ for (const TField *field : fields)
{
- const TType &paramType = ctorParameters[parameter];
-
- constructor += TypeString(paramType) + " x" + str(parameter) + ArrayString(paramType);
-
- if (parameter < ctorParameters.size() - 1)
+ const TType *fieldType = field->type();
+ if (!IsSampler(fieldType->getBasicType()))
{
- constructor += ", ";
+ ctorParameters.push_back(*fieldType);
}
}
+ // Structs that have sampler members should not have constructor calls, and otherwise structs
+ // are guaranteed to be non-empty by the grammar. Structs can't contain empty declarations
+ // either.
+ ASSERT(!ctorParameters.empty());
+
+ *constructor += WriteParameterList(ctorParameters);
- constructor += ")\n"
- "{\n";
+ *constructor +=
+ ")\n"
+ "{\n"
+ " " +
+ name + " structure = { ";
- if (ctorType.getStruct())
+ for (size_t parameterIndex = 0u; parameterIndex < ctorParameters.size(); ++parameterIndex)
{
- constructor += " " + name + " structure = {";
+ *constructor += "x" + str(parameterIndex);
+ if (parameterIndex < ctorParameters.size() - 1u)
+ {
+ *constructor += ", ";
+ }
}
- else
+ *constructor +=
+ "};\n"
+ " return structure;\n"
+ "}\n";
+
+ return constructorFunctionName;
+}
+
+TString StructureHLSL::addBuiltInConstructor(const TType &type, const TIntermSequence *parameters)
+{
+ ASSERT(!type.isArray());
+ ASSERT(type.getStruct() == nullptr);
+ ASSERT(parameters);
+
+ TType ctorType = type;
+ ctorType.setPrecision(EbpHigh);
+ ctorType.setQualifier(EvqTemporary);
+
+ const TString constructorFunctionName =
+ TString(type.getBuiltInTypeNameString()) + "_ctor" + DisambiguateFunctionName(parameters);
+ TString constructor = TypeString(ctorType) + " " + constructorFunctionName + "(";
+
+ std::vector<TType> ctorParameters;
+ for (auto parameter : *parameters)
{
- constructor += " return " + TypeString(ctorType) + "(";
+ const TType &paramType = parameter->getAsTyped()->getType();
+ ASSERT(!paramType.isArray());
+ ctorParameters.push_back(paramType);
}
+ constructor += WriteParameterList(ctorParameters);
+
+ constructor +=
+ ")\n"
+ "{\n"
+ " return " +
+ TypeString(ctorType) + "(";
if (ctorType.isMatrix() && ctorParameters.size() == 1)
{
- int rows = ctorType.getRows();
- int cols = ctorType.getCols();
+ int rows = ctorType.getRows();
+ int cols = ctorType.getCols();
const TType &parameter = ctorParameters[0];
if (parameter.isScalar())
@@ -355,7 +432,8 @@ void StructureHLSL::addConstructor(const TType &type, const TString &name, const
}
else
{
- ASSERT(rows == 2 && cols == 2 && parameter.isVector() && parameter.getNominalSize() == 4);
+ ASSERT(rows == 2 && cols == 2 && parameter.isVector() &&
+ parameter.getNominalSize() == 4);
constructor += "x0";
}
@@ -367,20 +445,13 @@ void StructureHLSL::addConstructor(const TType &type, const TString &name, const
while (remainingComponents > 0)
{
- const TType &parameter = ctorParameters[parameterIndex];
+ const TType &parameter = ctorParameters[parameterIndex];
const size_t parameterSize = parameter.getObjectSize();
- bool moreParameters = parameterIndex + 1 < ctorParameters.size();
+ 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())
+ if (parameter.isScalar())
{
remainingComponents -= parameter.getObjectSize();
}
@@ -395,16 +466,26 @@ void StructureHLSL::addConstructor(const TType &type, const TString &name, const
{
switch (remainingComponents)
{
- case 1: constructor += ".x"; break;
- case 2: constructor += ".xy"; break;
- case 3: constructor += ".xyz"; break;
- case 4: constructor += ".xyzw"; break;
- default: UNREACHABLE();
+ 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
+ UNREACHABLE();
}
else if (parameter.isMatrix())
{
@@ -417,10 +498,17 @@ void StructureHLSL::addConstructor(const TType &type, const TString &name, const
{
switch (remainingComponents)
{
- case 1: constructor += ".x"; break;
- case 2: constructor += ".xy"; break;
- case 3: constructor += ".xyz"; break;
- default: UNREACHABLE();
+ case 1:
+ constructor += ".x";
+ break;
+ case 2:
+ constructor += ".xy";
+ break;
+ case 3:
+ constructor += ".xyz";
+ break;
+ default:
+ UNREACHABLE();
}
remainingComponents = 0;
@@ -438,7 +526,10 @@ void StructureHLSL::addConstructor(const TType &type, const TString &name, const
column++;
}
}
- else UNREACHABLE();
+ else
+ {
+ UNREACHABLE();
+ }
if (moreParameters)
{
@@ -452,53 +543,52 @@ void StructureHLSL::addConstructor(const TType &type, const TString &name, const
}
}
- if (ctorType.getStruct())
- {
- constructor += "};\n"
- " return structure;\n"
- "}\n";
- }
- else
- {
- constructor += ");\n"
- "}\n";
- }
+ constructor +=
+ ");\n"
+ "}\n";
- mConstructors.insert(constructor);
+ mBuiltInConstructors.insert(constructor);
+
+ return constructorFunctionName;
}
std::string StructureHLSL::structsHeader() const
{
TInfoSinkBase out;
- for (size_t structIndex = 0; structIndex < mStructDeclarations.size(); structIndex++)
+ for (auto &declaration : mStructDeclarations)
+ {
+ out << declaration;
+ }
+
+ for (auto &structure : mDefinedStructs)
{
- out << mStructDeclarations[structIndex];
+ out << structure.second->constructor;
}
- for (Constructors::const_iterator constructor = mConstructors.begin();
- constructor != mConstructors.end();
- constructor++)
+ for (auto &constructor : mBuiltInConstructors)
{
- out << *constructor;
+ out << constructor;
}
return out.str();
}
-void StructureHLSL::storeStd140ElementIndex(const TStructure &structure, bool useHLSLRowMajorPacking)
+void StructureHLSL::storeStd140ElementIndex(const TStructure &structure,
+ bool useHLSLRowMajorPacking)
{
Std140PaddingHelper padHelper = getPaddingHelper();
- const TFieldList &fields = structure.fields();
+ const TFieldList &fields = structure.fields();
- for (unsigned int i = 0; i < fields.size(); i++)
+ for (const TField *field : fields)
{
- padHelper.prePadding(*fields[i]->type());
+ padHelper.prePadding(*field->type());
}
- // Add remaining element index to the global map, for use with nested structs in standard layouts
+ // Add remaining element index to the global map, for use with nested structs in standard
+ // layouts
const TString &structName = QualifiedStructNameString(structure, useHLSLRowMajorPacking, true);
mStd140StructElementIndexes[structName] = padHelper.elementIndex();
}
-}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/StructureHLSL.h b/src/3rdparty/angle/src/compiler/translator/StructureHLSL.h
index cffe2a41ae..daced8f8d1 100644
--- a/src/3rdparty/angle/src/compiler/translator/StructureHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/StructureHLSL.h
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
// StructureHLSL.h:
-// Interfaces of methods for HLSL translation of GLSL structures.
+// HLSL translation of GLSL constructors and structures.
//
#ifndef COMPILER_TRANSLATOR_STRUCTUREHLSL_H_
@@ -49,11 +49,14 @@ class StructureHLSL : angle::NonCopyable
public:
StructureHLSL();
- void addConstructor(const TType &type, const TString &name, const TIntermSequence *parameters);
- std::string structsHeader() const;
+ // Returns the name of the constructor function.
+ TString addStructConstructor(const TStructure &structure);
+ TString addBuiltInConstructor(const TType &type, const TIntermSequence *parameters);
- TString defineQualified(const TStructure &structure, bool useHLSLRowMajorPacking, bool useStd140Packing);
static TString defineNameless(const TStructure &structure);
+ void ensureStructDefined(const TStructure &structure);
+
+ std::string structsHeader() const;
Std140PaddingHelper getPaddingHelper();
@@ -62,20 +65,34 @@ class StructureHLSL : angle::NonCopyable
std::map<TString, int> mStd140StructElementIndexes;
- typedef std::set<TString> StructNames;
- StructNames mStructNames;
+ struct TStructProperties : public angle::NonCopyable
+ {
+ POOL_ALLOCATOR_NEW_DELETE();
+
+ TStructProperties() {}
- typedef std::set<TString> Constructors;
- Constructors mConstructors;
+ // Constructor is an empty string in case the struct doesn't have a constructor yet.
+ TString constructor;
+ };
+ // Map from struct name to struct properties.
+ typedef std::map<TString, TStructProperties *> DefinedStructs;
+ DefinedStructs mDefinedStructs;
+
+ // Struct declarations need to be kept in a vector instead of having them inside mDefinedStructs
+ // since maintaining the original order is necessary for nested structs.
typedef std::vector<TString> StructDeclarations;
StructDeclarations mStructDeclarations;
+ typedef std::set<TString> BuiltInConstructors;
+ BuiltInConstructors mBuiltInConstructors;
+
void storeStd140ElementIndex(const TStructure &structure, bool useHLSLRowMajorPacking);
- static TString define(const TStructure &structure, bool useHLSLRowMajorPacking,
- bool useStd140Packing, Std140PaddingHelper *padHelper);
+ TString defineQualified(const TStructure &structure,
+ bool useHLSLRowMajorPacking,
+ bool useStd140Packing);
+ DefinedStructs::iterator defineVariants(const TStructure &structure, const TString &name);
};
-
}
-#endif // COMPILER_TRANSLATOR_STRUCTUREHLSL_H_
+#endif // COMPILER_TRANSLATOR_STRUCTUREHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp b/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp
index dc8f8e3b6b..6c38461469 100644
--- a/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp
@@ -3,45 +3,87 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-
-//
-// Symbol table for parsing. Most functionaliy and main ideas
-// are documented in the header file.
+// Symbol table for parsing. The design principles and most of the functionality are documented in
+// the header file.
//
#if defined(_MSC_VER)
-#pragma warning(disable: 4718)
+#pragma warning(disable : 4718)
#endif
#include "compiler/translator/SymbolTable.h"
+
#include "compiler/translator/Cache.h"
+#include "compiler/translator/IntermNode.h"
#include <stdio.h>
#include <algorithm>
-int TSymbolTable::uniqueIdCounter = 0;
+namespace sh
+{
+
+namespace
+{
+
+static const char kFunctionMangledNameSeparator = '(';
+
+} // anonymous namespace
+
+TSymbol::TSymbol(TSymbolTable *symbolTable, const TString *n)
+ : uniqueId(symbolTable->nextUniqueId()), name(n), extension(TExtension::UNDEFINED)
+{
+}
//
// Functions have buried pointers to delete.
//
TFunction::~TFunction()
{
+ clearParameters();
+}
+
+void TFunction::clearParameters()
+{
for (TParamList::iterator i = parameters.begin(); i != parameters.end(); ++i)
delete (*i).type;
+ parameters.clear();
+ mangledName = nullptr;
+}
+
+void TFunction::swapParameters(const TFunction &parametersSource)
+{
+ clearParameters();
+ for (auto parameter : parametersSource.parameters)
+ {
+ addParameter(parameter);
+ }
}
const TString *TFunction::buildMangledName() const
{
- std::string newName = mangleName(getName()).c_str();
+ std::string newName = getName().c_str();
+ newName += kFunctionMangledNameSeparator;
for (const auto &p : parameters)
{
- newName += p.type->getMangledName().c_str();
+ newName += p.type->getMangledName();
}
-
return NewPoolTString(newName.c_str());
}
+const TString &TFunction::GetMangledNameFromCall(const TString &functionName,
+ const TIntermSequence &arguments)
+{
+ std::string newName = functionName.c_str();
+ newName += kFunctionMangledNameSeparator;
+
+ for (TIntermNode *argument : arguments)
+ {
+ newName += argument->getAsTyped()->getType().getMangledName();
+ }
+ return *NewPoolTString(newName.c_str());
+}
+
//
// Symbol table levels are a map of pointers to symbols that have to be deleted.
//
@@ -53,8 +95,6 @@ TSymbolTableLevel::~TSymbolTableLevel()
bool TSymbolTableLevel::insert(TSymbol *symbol)
{
- symbol->setUniqueId(TSymbolTable::nextUniqueId());
-
// returning true means symbol was added to the table
tInsertResult result = level.insert(tLevelPair(symbol->getMangledName(), symbol));
@@ -63,8 +103,6 @@ bool TSymbolTableLevel::insert(TSymbol *symbol)
bool TSymbolTableLevel::insertUnmangled(TFunction *function)
{
- function->setUniqueId(TSymbolTable::nextUniqueId());
-
// returning true means symbol was added to the table
tInsertResult result = level.insert(tLevelPair(function->getName(), function));
@@ -80,22 +118,27 @@ TSymbol *TSymbolTableLevel::find(const TString &name) const
return (*it).second;
}
-TSymbol *TSymbolTable::find(const TString &name, int shaderVersion,
- bool *builtIn, bool *sameScope) const
+TSymbol *TSymbolTable::find(const TString &name,
+ int shaderVersion,
+ bool *builtIn,
+ bool *sameScope) const
{
int level = currentLevel();
TSymbol *symbol;
do
{
- if (level == ESSL3_BUILTINS && shaderVersion != 300)
+ if (level == GLSL_BUILTINS)
+ level--;
+ if (level == ESSL3_1_BUILTINS && shaderVersion != 310)
+ level--;
+ if (level == ESSL3_BUILTINS && shaderVersion < 300)
level--;
if (level == ESSL1_BUILTINS && shaderVersion != 100)
level--;
symbol = table[level]->find(name);
- }
- while (symbol == 0 && --level >= 0);
+ } while (symbol == 0 && --level >= 0);
if (builtIn)
*builtIn = (level <= LAST_BUILTIN_LEVEL);
@@ -105,12 +148,28 @@ TSymbol *TSymbolTable::find(const TString &name, int shaderVersion,
return symbol;
}
-TSymbol *TSymbolTable::findBuiltIn(
- const TString &name, int shaderVersion) const
+TSymbol *TSymbolTable::findGlobal(const TString &name) const
+{
+ ASSERT(table.size() > GLOBAL_LEVEL);
+ return table[GLOBAL_LEVEL]->find(name);
+}
+
+TSymbol *TSymbolTable::findBuiltIn(const TString &name, int shaderVersion) const
+{
+ return findBuiltIn(name, shaderVersion, false);
+}
+
+TSymbol *TSymbolTable::findBuiltIn(const TString &name,
+ int shaderVersion,
+ bool includeGLSLBuiltins) const
{
for (int level = LAST_BUILTIN_LEVEL; level >= 0; level--)
{
- if (level == ESSL3_BUILTINS && shaderVersion != 300)
+ if (level == GLSL_BUILTINS && !includeGLSLBuiltins)
+ level--;
+ if (level == ESSL3_1_BUILTINS && shaderVersion != 310)
+ level--;
+ if (level == ESSL3_BUILTINS && shaderVersion < 300)
level--;
if (level == ESSL1_BUILTINS && shaderVersion != 100)
level--;
@@ -121,7 +180,7 @@ TSymbol *TSymbolTable::findBuiltIn(
return symbol;
}
- return 0;
+ return nullptr;
}
TSymbolTable::~TSymbolTable()
@@ -135,7 +194,8 @@ bool IsGenType(const TType *type)
if (type)
{
TBasicType basicType = type->getBasicType();
- return basicType == EbtGenType || basicType == EbtGenIType || basicType == EbtGenUType || basicType == EbtGenBType;
+ return basicType == EbtGenType || basicType == EbtGenIType || basicType == EbtGenUType ||
+ basicType == EbtGenBType;
}
return false;
@@ -146,7 +206,8 @@ bool IsVecType(const TType *type)
if (type)
{
TBasicType basicType = type->getBasicType();
- return basicType == EbtVec || basicType == EbtIVec || basicType == EbtUVec || basicType == EbtBVec;
+ return basicType == EbtVec || basicType == EbtIVec || basicType == EbtUVec ||
+ basicType == EbtBVec;
}
return false;
@@ -163,13 +224,19 @@ const TType *SpecificType(const TType *type, int size)
ASSERT(!IsVecType(type));
- switch(type->getBasicType())
+ switch (type->getBasicType())
{
- case EbtGenType: return TCache::getType(EbtFloat, static_cast<unsigned char>(size));
- case EbtGenIType: return TCache::getType(EbtInt, static_cast<unsigned char>(size));
- case EbtGenUType: return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
- case EbtGenBType: return TCache::getType(EbtBool, static_cast<unsigned char>(size));
- default: return type;
+ case EbtGenType:
+ return TCache::getType(EbtFloat, type->getQualifier(),
+ static_cast<unsigned char>(size));
+ case EbtGenIType:
+ return TCache::getType(EbtInt, type->getQualifier(), static_cast<unsigned char>(size));
+ case EbtGenUType:
+ return TCache::getType(EbtUInt, type->getQualifier(), static_cast<unsigned char>(size));
+ case EbtGenBType:
+ return TCache::getType(EbtBool, type->getQualifier(), static_cast<unsigned char>(size));
+ default:
+ return type;
}
}
@@ -184,65 +251,232 @@ const TType *VectorType(const TType *type, int size)
ASSERT(!IsGenType(type));
- switch(type->getBasicType())
+ switch (type->getBasicType())
+ {
+ case EbtVec:
+ return TCache::getType(EbtFloat, static_cast<unsigned char>(size));
+ case EbtIVec:
+ return TCache::getType(EbtInt, static_cast<unsigned char>(size));
+ case EbtUVec:
+ return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
+ case EbtBVec:
+ return TCache::getType(EbtBool, static_cast<unsigned char>(size));
+ default:
+ return type;
+ }
+}
+
+TVariable *TSymbolTable::declareVariable(const TString *name, const TType &type)
+{
+ return insertVariable(currentLevel(), name, type);
+}
+
+TVariable *TSymbolTable::declareStructType(TStructure *str)
+{
+ return insertStructType(currentLevel(), str);
+}
+
+TInterfaceBlockName *TSymbolTable::declareInterfaceBlockName(const TString *name)
+{
+ TInterfaceBlockName *blockNameSymbol = new TInterfaceBlockName(this, name);
+ if (insert(currentLevel(), blockNameSymbol))
+ {
+ return blockNameSymbol;
+ }
+ return nullptr;
+}
+
+TInterfaceBlockName *TSymbolTable::insertInterfaceBlockNameExt(ESymbolLevel level,
+ TExtension ext,
+ const TString *name)
+{
+ TInterfaceBlockName *blockNameSymbol = new TInterfaceBlockName(this, name);
+ if (insert(level, ext, blockNameSymbol))
+ {
+ return blockNameSymbol;
+ }
+ return nullptr;
+}
+
+TVariable *TSymbolTable::insertVariable(ESymbolLevel level, const char *name, const TType &type)
+{
+ return insertVariable(level, NewPoolTString(name), type);
+}
+
+TVariable *TSymbolTable::insertVariable(ESymbolLevel level, const TString *name, const TType &type)
+{
+ TVariable *var = new TVariable(this, name, type);
+ if (insert(level, var))
+ {
+ // Do lazy initialization for struct types, so we allocate to the current scope.
+ if (var->getType().getBasicType() == EbtStruct)
+ {
+ var->getType().realize();
+ }
+ return var;
+ }
+ return nullptr;
+}
+
+TVariable *TSymbolTable::insertVariableExt(ESymbolLevel level,
+ TExtension ext,
+ const char *name,
+ const TType &type)
+{
+ TVariable *var = new TVariable(this, NewPoolTString(name), type);
+ if (insert(level, ext, var))
+ {
+ if (var->getType().getBasicType() == EbtStruct)
+ {
+ var->getType().realize();
+ }
+ return var;
+ }
+ return nullptr;
+}
+
+TVariable *TSymbolTable::insertStructType(ESymbolLevel level, TStructure *str)
+{
+ TVariable *var = new TVariable(this, &str->name(), TType(str), true);
+ if (insert(level, var))
{
- case EbtVec: return TCache::getType(EbtFloat, static_cast<unsigned char>(size));
- case EbtIVec: return TCache::getType(EbtInt, static_cast<unsigned char>(size));
- case EbtUVec: return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
- case EbtBVec: return TCache::getType(EbtBool, static_cast<unsigned char>(size));
- default: return type;
+ var->getType().realize();
+ return var;
}
+ return nullptr;
}
-void TSymbolTable::insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, const TType *rvalue, const char *name,
- const TType *ptype1, const TType *ptype2, const TType *ptype3, const TType *ptype4, const TType *ptype5)
+void TSymbolTable::insertBuiltIn(ESymbolLevel level,
+ TOperator op,
+ TExtension ext,
+ const TType *rvalue,
+ const char *name,
+ const TType *ptype1,
+ const TType *ptype2,
+ const TType *ptype3,
+ const TType *ptype4,
+ const TType *ptype5)
{
if (ptype1->getBasicType() == EbtGSampler2D)
{
+ insertUnmangledBuiltInName(name, level);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler2D), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler2D), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler2D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
+ TCache::getType(EbtSampler2D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name,
+ TCache::getType(EbtISampler2D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name,
+ TCache::getType(EbtUSampler2D), ptype2, ptype3, ptype4, ptype5);
}
else if (ptype1->getBasicType() == EbtGSampler3D)
{
+ insertUnmangledBuiltInName(name, level);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler3D), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler3D), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler3D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
+ TCache::getType(EbtSampler3D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name,
+ TCache::getType(EbtISampler3D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name,
+ TCache::getType(EbtUSampler3D), ptype2, ptype3, ptype4, ptype5);
}
else if (ptype1->getBasicType() == EbtGSamplerCube)
{
+ insertUnmangledBuiltInName(name, level);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSamplerCube), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISamplerCube), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSamplerCube), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
+ TCache::getType(EbtSamplerCube), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name,
+ TCache::getType(EbtISamplerCube), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name,
+ TCache::getType(EbtUSamplerCube), ptype2, ptype3, ptype4, ptype5);
}
else if (ptype1->getBasicType() == EbtGSampler2DArray)
{
+ insertUnmangledBuiltInName(name, level);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler2DArray), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler2DArray), ptype2, ptype3, ptype4, ptype5);
- insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler2DArray), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
+ TCache::getType(EbtSampler2DArray), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name,
+ TCache::getType(EbtISampler2DArray), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name,
+ TCache::getType(EbtUSampler2DArray), ptype2, ptype3, ptype4, ptype5);
}
- else if (IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3))
+ else if (ptype1->getBasicType() == EbtGSampler2DMS)
{
- ASSERT(!ptype4 && !ptype5);
- insertBuiltIn(level, op, ext, SpecificType(rvalue, 1), name, SpecificType(ptype1, 1), SpecificType(ptype2, 1), SpecificType(ptype3, 1));
- insertBuiltIn(level, op, ext, SpecificType(rvalue, 2), name, SpecificType(ptype1, 2), SpecificType(ptype2, 2), SpecificType(ptype3, 2));
- insertBuiltIn(level, op, ext, SpecificType(rvalue, 3), name, SpecificType(ptype1, 3), SpecificType(ptype2, 3), SpecificType(ptype3, 3));
- insertBuiltIn(level, op, ext, SpecificType(rvalue, 4), name, SpecificType(ptype1, 4), SpecificType(ptype2, 4), SpecificType(ptype3, 4));
+ insertUnmangledBuiltInName(name, level);
+ bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
+ TCache::getType(EbtSampler2DMS), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name,
+ TCache::getType(EbtISampler2DMS), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name,
+ TCache::getType(EbtUSampler2DMS), ptype2, ptype3, ptype4, ptype5);
+ }
+ else if (IsGImage(ptype1->getBasicType()))
+ {
+ insertUnmangledBuiltInName(name, level);
+
+ const TType *floatType = TCache::getType(EbtFloat, 4);
+ const TType *intType = TCache::getType(EbtInt, 4);
+ const TType *unsignedType = TCache::getType(EbtUInt, 4);
+
+ const TType *floatImage =
+ TCache::getType(convertGImageToFloatImage(ptype1->getBasicType()));
+ const TType *intImage = TCache::getType(convertGImageToIntImage(ptype1->getBasicType()));
+ const TType *unsignedImage =
+ TCache::getType(convertGImageToUnsignedImage(ptype1->getBasicType()));
+
+ // GLSL ES 3.10, Revision 4, 8.12 Image Functions
+ if (rvalue->getBasicType() == EbtGVec4)
+ {
+ // imageLoad
+ insertBuiltIn(level, floatType, name, floatImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, intType, name, intImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, unsignedType, name, unsignedImage, ptype2, ptype3, ptype4, ptype5);
+ }
+ else if (rvalue->getBasicType() == EbtVoid)
+ {
+ // imageStore
+ insertBuiltIn(level, rvalue, name, floatImage, ptype2, floatType, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, intImage, ptype2, intType, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, unsignedImage, ptype2, unsignedType, ptype4, ptype5);
+ }
+ else
+ {
+ // imageSize
+ insertBuiltIn(level, rvalue, name, floatImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, intImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, unsignedImage, ptype2, ptype3, ptype4, ptype5);
+ }
+ }
+ else if (IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3) ||
+ IsGenType(ptype4))
+ {
+ ASSERT(!ptype5);
+ insertUnmangledBuiltInName(name, level);
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 1), name, SpecificType(ptype1, 1),
+ SpecificType(ptype2, 1), SpecificType(ptype3, 1), SpecificType(ptype4, 1));
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 2), name, SpecificType(ptype1, 2),
+ SpecificType(ptype2, 2), SpecificType(ptype3, 2), SpecificType(ptype4, 2));
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 3), name, SpecificType(ptype1, 3),
+ SpecificType(ptype2, 3), SpecificType(ptype3, 3), SpecificType(ptype4, 3));
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 4), name, SpecificType(ptype1, 4),
+ SpecificType(ptype2, 4), SpecificType(ptype3, 4), SpecificType(ptype4, 4));
}
else if (IsVecType(rvalue) || IsVecType(ptype1) || IsVecType(ptype2) || IsVecType(ptype3))
{
ASSERT(!ptype4 && !ptype5);
- insertBuiltIn(level, op, ext, VectorType(rvalue, 2), name, VectorType(ptype1, 2), VectorType(ptype2, 2), VectorType(ptype3, 2));
- insertBuiltIn(level, op, ext, VectorType(rvalue, 3), name, VectorType(ptype1, 3), VectorType(ptype2, 3), VectorType(ptype3, 3));
- insertBuiltIn(level, op, ext, VectorType(rvalue, 4), name, VectorType(ptype1, 4), VectorType(ptype2, 4), VectorType(ptype3, 4));
+ insertUnmangledBuiltInName(name, level);
+ insertBuiltIn(level, op, ext, VectorType(rvalue, 2), name, VectorType(ptype1, 2),
+ VectorType(ptype2, 2), VectorType(ptype3, 2));
+ insertBuiltIn(level, op, ext, VectorType(rvalue, 3), name, VectorType(ptype1, 3),
+ VectorType(ptype2, 3), VectorType(ptype3, 3));
+ insertBuiltIn(level, op, ext, VectorType(rvalue, 4), name, VectorType(ptype1, 4),
+ VectorType(ptype2, 4), VectorType(ptype3, 4));
}
else
{
- TFunction *function = new TFunction(NewPoolTString(name), rvalue, op, ext);
+ TFunction *function = new TFunction(this, NewPoolTString(name), rvalue, op, ext);
function->addParameter(TConstParameter(ptype1));
@@ -266,10 +500,61 @@ void TSymbolTable::insertBuiltIn(ESymbolLevel level, TOperator op, const char *e
function->addParameter(TConstParameter(ptype5));
}
+ ASSERT(hasUnmangledBuiltInAtLevel(name, level));
insert(level, function);
}
}
+void TSymbolTable::insertBuiltInOp(ESymbolLevel level,
+ TOperator op,
+ const TType *rvalue,
+ const TType *ptype1,
+ const TType *ptype2,
+ const TType *ptype3,
+ const TType *ptype4,
+ const TType *ptype5)
+{
+ const char *name = GetOperatorString(op);
+ ASSERT(strlen(name) > 0);
+ insertUnmangledBuiltInName(name, level);
+ insertBuiltIn(level, op, TExtension::UNDEFINED, rvalue, name, ptype1, ptype2, ptype3, ptype4,
+ ptype5);
+}
+
+void TSymbolTable::insertBuiltInOp(ESymbolLevel level,
+ TOperator op,
+ TExtension ext,
+ const TType *rvalue,
+ const TType *ptype1,
+ const TType *ptype2,
+ const TType *ptype3,
+ const TType *ptype4,
+ const TType *ptype5)
+{
+ const char *name = GetOperatorString(op);
+ insertUnmangledBuiltInName(name, level);
+ insertBuiltIn(level, op, ext, rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
+}
+
+void TSymbolTable::insertBuiltInFunctionNoParameters(ESymbolLevel level,
+ TOperator op,
+ const TType *rvalue,
+ const char *name)
+{
+ insertUnmangledBuiltInName(name, level);
+ insert(level, new TFunction(this, NewPoolTString(name), rvalue, op));
+}
+
+void TSymbolTable::insertBuiltInFunctionNoParametersExt(ESymbolLevel level,
+ TExtension ext,
+ TOperator op,
+ const TType *rvalue,
+ const char *name)
+{
+ insertUnmangledBuiltInName(name, level);
+ insert(level, new TFunction(this, NewPoolTString(name), rvalue, op, ext));
+}
+
TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const
{
if (!SupportsPrecision(type))
@@ -279,7 +564,7 @@ TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const
TBasicType baseType = (type == EbtUInt) ? EbtInt : type;
int level = static_cast<int>(precisionStack.size()) - 1;
- assert(level >= 0); // Just to be safe. Should not happen.
+ assert(level >= 0); // Just to be safe. Should not happen.
// If we dont find anything we return this. Some types don't have predefined default precision.
TPrecision prec = EbpUndefined;
while (level >= 0)
@@ -294,3 +579,44 @@ TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const
}
return prec;
}
+
+void TSymbolTable::insertUnmangledBuiltInName(const char *name, ESymbolLevel level)
+{
+ ASSERT(level >= 0 && level < static_cast<ESymbolLevel>(table.size()));
+ table[level]->insertUnmangledBuiltInName(std::string(name));
+}
+
+bool TSymbolTable::hasUnmangledBuiltInAtLevel(const char *name, ESymbolLevel level)
+{
+ ASSERT(level >= 0 && level < static_cast<ESymbolLevel>(table.size()));
+ return table[level]->hasUnmangledBuiltIn(std::string(name));
+}
+
+bool TSymbolTable::hasUnmangledBuiltInForShaderVersion(const char *name, int shaderVersion)
+{
+ ASSERT(static_cast<ESymbolLevel>(table.size()) > LAST_BUILTIN_LEVEL);
+
+ for (int level = LAST_BUILTIN_LEVEL; level >= 0; --level)
+ {
+ if (level == ESSL3_1_BUILTINS && shaderVersion != 310)
+ {
+ --level;
+ }
+ if (level == ESSL3_BUILTINS && shaderVersion < 300)
+ {
+ --level;
+ }
+ if (level == ESSL1_BUILTINS && shaderVersion != 100)
+ {
+ --level;
+ }
+
+ if (table[level]->hasUnmangledBuiltIn(name))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SymbolTable.h b/src/3rdparty/angle/src/compiler/translator/SymbolTable.h
index 2706149e3c..5d792ec188 100644
--- a/src/3rdparty/angle/src/compiler/translator/SymbolTable.h
+++ b/src/3rdparty/angle/src/compiler/translator/SymbolTable.h
@@ -18,7 +18,7 @@
// so that symbol table lookups are never ambiguous. This allows
// a simpler symbol table structure.
//
-// * Pushing and popping of scope, so symbol table will really be a stack
+// * Pushing and popping of scope, so symbol table will really be a stack
// of symbol tables. Searched from the top, with new inserts going into
// the top.
//
@@ -30,111 +30,78 @@
// are tracked in the intermediate representation, not the symbol table.
//
+#include <array>
#include <assert.h>
#include <set>
#include "common/angleutils.h"
+#include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolUniqueId.h"
+
+namespace sh
+{
// Symbol base class. (Can build functions or variables out of these...)
class TSymbol : angle::NonCopyable
{
public:
POOL_ALLOCATOR_NEW_DELETE();
- TSymbol(const TString *n)
- : uniqueId(0),
- name(n)
- {
- }
+ TSymbol(TSymbolTable *symbolTable, const TString *n);
+
virtual ~TSymbol()
{
// don't delete name, it's from the pool
}
- const TString &getName() const
- {
- return *name;
- }
- virtual const TString &getMangledName() const
- {
- return getName();
- }
- virtual bool isFunction() const
- {
- return false;
- }
- virtual bool isVariable() const
- {
- return false;
- }
- void setUniqueId(int id)
- {
- uniqueId = id;
- }
- int getUniqueId() const
- {
- return uniqueId;
- }
- void relateToExtension(const TString &ext)
- {
- extension = ext;
- }
- const TString &getExtension() const
- {
- return extension;
- }
+ const TString &getName() const { return *name; }
+ virtual const TString &getMangledName() const { return getName(); }
+ virtual bool isFunction() const { return false; }
+ virtual bool isVariable() const { return false; }
+ const TSymbolUniqueId &getUniqueId() const { return uniqueId; }
+ void relateToExtension(TExtension ext) { extension = ext; }
+ TExtension getExtension() const { return extension; }
private:
- int uniqueId; // For real comparing during code generation
+ const TSymbolUniqueId uniqueId;
const TString *name;
- TString extension;
+ TExtension extension;
};
-// Variable class, meaning a symbol that's not a function.
-//
-// There could be a separate class heirarchy for Constant variables;
-// Only one of int, bool, or float, (or none) is correct for
-// any particular use, but it's easy to do this way, and doesn't
-// seem worth having separate classes, and "getConst" can't simply return
-// different values for different types polymorphically, so this is
-// just simple and pragmatic.
+// Variable, meaning a symbol that's not a function.
+//
+// May store the value of a constant variable of any type (float, int, bool or struct).
class TVariable : public TSymbol
{
public:
- TVariable(const TString *name, const TType &t, bool uT = false)
- : TSymbol(name),
- type(t),
- userType(uT),
- unionArray(0)
- {
- }
~TVariable() override {}
bool isVariable() const override { return true; }
- TType &getType()
- {
- return type;
- }
- const TType &getType() const
- {
- return type;
- }
- bool isUserType() const
- {
- return userType;
- }
- void setQualifier(TQualifier qualifier)
- {
- type.setQualifier(qualifier);
- }
+ TType &getType() { return type; }
+ const TType &getType() const { return type; }
+ bool isUserType() const { return userType; }
+ void setQualifier(TQualifier qualifier) { type.setQualifier(qualifier); }
const TConstantUnion *getConstPointer() const { return unionArray; }
void shareConstPointer(const TConstantUnion *constArray) { unionArray = constArray; }
private:
+ friend class TSymbolTable;
+
+ TVariable(TSymbolTable *symbolTable,
+ const TString *name,
+ const TType &t,
+ bool isUserTypeDefinition = false)
+ : TSymbol(symbolTable, name), type(t), userType(isUserTypeDefinition), unionArray(0)
+ {
+ }
+
TType type;
+
+ // Set to true if this represents a struct type, as opposed to a variable.
bool userType;
+
// we are assuming that Pool Allocator will free the memory
// allocated to unionArray when this object is destroyed.
const TConstantUnion *unionArray;
@@ -143,34 +110,18 @@ class TVariable : public TSymbol
// Immutable version of TParameter.
struct TConstParameter
{
- TConstParameter()
- : name(nullptr),
- type(nullptr)
- {
- }
- explicit TConstParameter(const TString *n)
- : name(n),
- type(nullptr)
- {
- }
- explicit TConstParameter(const TType *t)
- : name(nullptr),
- type(t)
- {
- }
- TConstParameter(const TString *n, const TType *t)
- : name(n),
- type(t)
- {
- }
+ TConstParameter() : name(nullptr), type(nullptr) {}
+ explicit TConstParameter(const TString *n) : name(n), type(nullptr) {}
+ explicit TConstParameter(const TType *t) : name(nullptr), type(t) {}
+ TConstParameter(const TString *n, const TType *t) : name(n), type(t) {}
// Both constructor arguments must be const.
- TConstParameter(TString *n, TType *t) = delete;
+ TConstParameter(TString *n, TType *t) = delete;
TConstParameter(const TString *n, TType *t) = delete;
TConstParameter(TString *n, const TType *t) = delete;
- const TString *name;
- const TType *type;
+ const TString *const name;
+ const TType *const type;
};
// The function sub-class of symbols and the parser will need to
@@ -183,25 +134,26 @@ struct TParameter
TConstParameter turnToConst()
{
const TString *constName = name;
- const TType *constType = type;
- name = nullptr;
- type = nullptr;
+ const TType *constType = type;
+ name = nullptr;
+ type = nullptr;
return TConstParameter(constName, constType);
}
- TString *name;
+ const TString *name;
TType *type;
};
-// The function sub-class of a symbol.
+// The function sub-class of a symbol.
class TFunction : public TSymbol
{
public:
- TFunction(const TString *name,
+ TFunction(TSymbolTable *symbolTable,
+ const TString *name,
const TType *retType,
- TOperator tOp = EOpNull,
- const char *ext = "")
- : TSymbol(name),
+ TOperator tOp = EOpNull,
+ TExtension ext = TExtension::UNDEFINED)
+ : TSymbol(symbolTable, name),
returnType(retType),
mangledName(nullptr),
op(tOp),
@@ -213,21 +165,14 @@ class TFunction : public TSymbol
~TFunction() override;
bool isFunction() const override { return true; }
- static TString mangleName(const TString &name)
- {
- return name + '(';
- }
- static TString unmangleName(const TString &mangledName)
- {
- return TString(mangledName.c_str(), mangledName.find_first_of('('));
- }
-
void addParameter(const TConstParameter &p)
{
parameters.push_back(p);
mangledName = nullptr;
}
+ void swapParameters(const TFunction &parametersSource);
+
const TString &getMangledName() const override
{
if (mangledName == nullptr)
@@ -236,31 +181,25 @@ class TFunction : public TSymbol
}
return *mangledName;
}
- const TType &getReturnType() const
- {
- return *returnType;
- }
- TOperator getBuiltInOp() const
- {
- return op;
- }
+ static const TString &GetMangledNameFromCall(const TString &functionName,
+ const TIntermSequence &arguments);
+
+ const TType &getReturnType() const { return *returnType; }
+
+ TOperator getBuiltInOp() const { return op; }
void setDefined() { defined = true; }
bool isDefined() { return defined; }
void setHasPrototypeDeclaration() { mHasPrototypeDeclaration = true; }
bool hasPrototypeDeclaration() const { return mHasPrototypeDeclaration; }
- size_t getParamCount() const
- {
- return parameters.size();
- }
- const TConstParameter &getParam(size_t i) const
- {
- return parameters[i];
- }
+ size_t getParamCount() const { return parameters.size(); }
+ const TConstParameter &getParam(size_t i) const { return parameters[i]; }
private:
+ void clearParameters();
+
const TString *buildMangledName() const;
typedef TVector<TConstParameter> TParamList;
@@ -276,12 +215,11 @@ class TFunction : public TSymbol
class TInterfaceBlockName : public TSymbol
{
public:
- TInterfaceBlockName(const TString *name)
- : TSymbol(name)
- {
- }
+ virtual ~TInterfaceBlockName() {}
- virtual ~TInterfaceBlockName()
+ private:
+ friend class TSymbolTable;
+ TInterfaceBlockName(TSymbolTable *symbolTable, const TString *name) : TSymbol(symbolTable, name)
{
}
};
@@ -289,14 +227,12 @@ class TInterfaceBlockName : public TSymbol
class TSymbolTableLevel
{
public:
- typedef TMap<TString, TSymbol *> tLevel;
+ typedef TUnorderedMap<TString, TSymbol *> tLevel;
typedef tLevel::const_iterator const_iterator;
typedef const tLevel::value_type tLevelPair;
typedef std::pair<tLevel::iterator, bool> tInsertResult;
- TSymbolTableLevel()
- {
- }
+ TSymbolTableLevel() : mGlobalInvariant(false) {}
~TSymbolTableLevel();
bool insert(TSymbol *symbol);
@@ -306,25 +242,52 @@ class TSymbolTableLevel
TSymbol *find(const TString &name) const;
+ void addInvariantVarying(const std::string &name) { mInvariantVaryings.insert(name); }
+
+ bool isVaryingInvariant(const std::string &name)
+ {
+ return (mGlobalInvariant || mInvariantVaryings.count(name) > 0);
+ }
+
+ void setGlobalInvariant(bool invariant) { mGlobalInvariant = invariant; }
+
+ void insertUnmangledBuiltInName(const std::string &name)
+ {
+ mUnmangledBuiltInNames.insert(name);
+ }
+
+ bool hasUnmangledBuiltIn(const std::string &name)
+ {
+ return mUnmangledBuiltInNames.count(name) > 0;
+ }
+
protected:
tLevel level;
+ std::set<std::string> mInvariantVaryings;
+ bool mGlobalInvariant;
+
+ private:
+ std::set<std::string> mUnmangledBuiltInNames;
};
// Define ESymbolLevel as int rather than an enum since level can go
// above GLOBAL_LEVEL and cause atBuiltInLevel() to fail if the
// compiler optimizes the >= of the last element to ==.
typedef int ESymbolLevel;
-const int COMMON_BUILTINS = 0;
-const int ESSL1_BUILTINS = 1;
-const int ESSL3_BUILTINS = 2;
-const int LAST_BUILTIN_LEVEL = ESSL3_BUILTINS;
-const int GLOBAL_LEVEL = 3;
+const int COMMON_BUILTINS = 0;
+const int ESSL1_BUILTINS = 1;
+const int ESSL3_BUILTINS = 2;
+const int ESSL3_1_BUILTINS = 3;
+// GLSL_BUILTINS are desktop GLSL builtins that don't exist in ESSL but are used to implement
+// features in ANGLE's GLSL backend. They're not visible to the parser.
+const int GLSL_BUILTINS = 4;
+const int LAST_BUILTIN_LEVEL = GLSL_BUILTINS;
+const int GLOBAL_LEVEL = 5;
class TSymbolTable : angle::NonCopyable
{
public:
- TSymbolTable()
- : mGlobalInvariant(false)
+ TSymbolTable() : mUniqueIdCounter(0), mEmptySymbolId(this)
{
// The symbol table cannot be used until push() is called, but
// the lack of an initial call to push() can be used to detect
@@ -336,18 +299,9 @@ class TSymbolTable : angle::NonCopyable
// When the symbol table is initialized with the built-ins, there should
// 'push' calls, so that built-ins are at level 0 and the shader
// globals are at level 1.
- bool isEmpty() const
- {
- return table.empty();
- }
- bool atBuiltInLevel() const
- {
- return currentLevel() <= LAST_BUILTIN_LEVEL;
- }
- bool atGlobalLevel() const
- {
- return currentLevel() <= GLOBAL_LEVEL;
- }
+ bool isEmpty() const { return table.empty(); }
+ bool atBuiltInLevel() const { return currentLevel() <= LAST_BUILTIN_LEVEL; }
+ bool atGlobalLevel() const { return currentLevel() == GLOBAL_LEVEL; }
void push()
{
table.push_back(new TSymbolTableLevel);
@@ -363,87 +317,159 @@ class TSymbolTable : angle::NonCopyable
precisionStack.pop_back();
}
- bool declare(TSymbol *symbol)
- {
- return insert(currentLevel(), symbol);
- }
-
- bool insert(ESymbolLevel level, TSymbol *symbol)
- {
- return table[level]->insert(symbol);
- }
-
- bool insert(ESymbolLevel level, const char *ext, TSymbol *symbol)
- {
- symbol->relateToExtension(ext);
- return table[level]->insert(symbol);
- }
-
- bool insertConstInt(ESymbolLevel level, const char *name, int value)
+ // The declare* entry points are used when parsing and declare symbols at the current scope.
+ // They return the created symbol in case the declaration was successful, and nullptr if the
+ // declaration failed due to redefinition.
+ TVariable *declareVariable(const TString *name, const TType &type);
+ TVariable *declareStructType(TStructure *str);
+ TInterfaceBlockName *declareInterfaceBlockName(const TString *name);
+
+ // The insert* entry points are used when initializing the symbol table with built-ins.
+ // They return the created symbol in case the declaration was successful, and nullptr if the
+ // declaration failed due to redefinition.
+ TVariable *insertVariable(ESymbolLevel level, const char *name, const TType &type);
+ TVariable *insertVariableExt(ESymbolLevel level,
+ TExtension ext,
+ const char *name,
+ const TType &type);
+ TVariable *insertStructType(ESymbolLevel level, TStructure *str);
+ TInterfaceBlockName *insertInterfaceBlockNameExt(ESymbolLevel level,
+ TExtension ext,
+ const TString *name);
+
+ bool insertConstInt(ESymbolLevel level, const char *name, int value, TPrecision precision)
{
- TVariable *constant = new TVariable(
- NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1));
+ TVariable *constant =
+ new TVariable(this, NewPoolTString(name), TType(EbtInt, precision, EvqConst, 1));
TConstantUnion *unionArray = new TConstantUnion[1];
unionArray[0].setIConst(value);
constant->shareConstPointer(unionArray);
return insert(level, constant);
}
- bool insertConstIntExt(ESymbolLevel level, const char *ext, const char *name, int value)
+ bool insertConstIntExt(ESymbolLevel level,
+ TExtension ext,
+ const char *name,
+ int value,
+ TPrecision precision)
{
TVariable *constant =
- new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1));
+ new TVariable(this, NewPoolTString(name), TType(EbtInt, precision, EvqConst, 1));
TConstantUnion *unionArray = new TConstantUnion[1];
unionArray[0].setIConst(value);
constant->shareConstPointer(unionArray);
return insert(level, ext, constant);
}
- void insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, const TType *rvalue, const char *name,
- const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0);
-
- void insertBuiltIn(ESymbolLevel level, const TType *rvalue, const char *name,
- const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0)
+ bool insertConstIvec3(ESymbolLevel level,
+ const char *name,
+ const std::array<int, 3> &values,
+ TPrecision precision)
{
- insertBuiltIn(level, EOpNull, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
- }
+ TVariable *constantIvec3 =
+ new TVariable(this, NewPoolTString(name), TType(EbtInt, precision, EvqConst, 3));
- void insertBuiltIn(ESymbolLevel level, const char *ext, const TType *rvalue, const char *name,
- const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0)
- {
+ TConstantUnion *unionArray = new TConstantUnion[3];
+ for (size_t index = 0u; index < 3u; ++index)
+ {
+ unionArray[index].setIConst(values[index]);
+ }
+ constantIvec3->shareConstPointer(unionArray);
+
+ return insert(level, constantIvec3);
+ }
+
+ void insertBuiltIn(ESymbolLevel level,
+ TOperator op,
+ TExtension ext,
+ const TType *rvalue,
+ const char *name,
+ const TType *ptype1,
+ const TType *ptype2 = 0,
+ const TType *ptype3 = 0,
+ const TType *ptype4 = 0,
+ const TType *ptype5 = 0);
+
+ void insertBuiltIn(ESymbolLevel level,
+ const TType *rvalue,
+ const char *name,
+ const TType *ptype1,
+ const TType *ptype2 = 0,
+ const TType *ptype3 = 0,
+ const TType *ptype4 = 0,
+ const TType *ptype5 = 0)
+ {
+ insertUnmangledBuiltInName(name, level);
+ insertBuiltIn(level, EOpNull, TExtension::UNDEFINED, rvalue, name, ptype1, ptype2, ptype3,
+ ptype4, ptype5);
+ }
+
+ void insertBuiltIn(ESymbolLevel level,
+ TExtension ext,
+ const TType *rvalue,
+ const char *name,
+ const TType *ptype1,
+ const TType *ptype2 = 0,
+ const TType *ptype3 = 0,
+ const TType *ptype4 = 0,
+ const TType *ptype5 = 0)
+ {
+ insertUnmangledBuiltInName(name, level);
insertBuiltIn(level, EOpNull, ext, rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
}
- void insertBuiltIn(ESymbolLevel level, TOperator op, const TType *rvalue, const char *name,
- const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0)
- {
- insertBuiltIn(level, op, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
- }
+ void insertBuiltInOp(ESymbolLevel level,
+ TOperator op,
+ const TType *rvalue,
+ const TType *ptype1,
+ const TType *ptype2 = 0,
+ const TType *ptype3 = 0,
+ const TType *ptype4 = 0,
+ const TType *ptype5 = 0);
+
+ void insertBuiltInOp(ESymbolLevel level,
+ TOperator op,
+ TExtension ext,
+ const TType *rvalue,
+ const TType *ptype1,
+ const TType *ptype2 = 0,
+ const TType *ptype3 = 0,
+ const TType *ptype4 = 0,
+ const TType *ptype5 = 0);
+
+ void insertBuiltInFunctionNoParameters(ESymbolLevel level,
+ TOperator op,
+ const TType *rvalue,
+ const char *name);
+
+ void insertBuiltInFunctionNoParametersExt(ESymbolLevel level,
+ TExtension ext,
+ TOperator op,
+ const TType *rvalue,
+ const char *name);
+
+ TSymbol *find(const TString &name,
+ int shaderVersion,
+ bool *builtIn = nullptr,
+ bool *sameScope = nullptr) const;
+
+ TSymbol *findGlobal(const TString &name) const;
- TSymbol *find(const TString &name, int shaderVersion,
- bool *builtIn = NULL, bool *sameScope = NULL) const;
TSymbol *findBuiltIn(const TString &name, int shaderVersion) const;
-
+
+ TSymbol *findBuiltIn(const TString &name, int shaderVersion, bool includeGLSLBuiltins) const;
+
TSymbolTableLevel *getOuterLevel()
{
assert(currentLevel() >= 1);
return table[currentLevel() - 1];
}
- void dump(TInfoSink &infoSink) const;
-
- bool setDefaultPrecision(const TPublicType &type, TPrecision prec)
+ void setDefaultPrecision(TBasicType type, TPrecision prec)
{
- if (!SupportsPrecision(type.type))
- return false;
- if (type.type == EbtUInt)
- return false; // ESSL 3.00.4 section 4.5.4
- if (type.isAggregate())
- return false; // Not allowed to set for aggregate types
int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1;
// Uses map operator [], overwrites the current value
- (*precisionStack[indexOfLastElement])[type.type] = prec;
- return true;
+ (*precisionStack[indexOfLastElement])[type] = prec;
}
// Searches down the precisionStack for a precision qualifier
@@ -454,7 +480,8 @@ class TSymbolTable : angle::NonCopyable
// "invariant varying_name;".
void addInvariantVarying(const std::string &originalName)
{
- mInvariantVaryings.insert(originalName);
+ ASSERT(atGlobalLevel());
+ table[currentLevel()]->addInvariantVarying(originalName);
}
// If this returns false, the varying could still be invariant
// if it is set as invariant during the varying variable
@@ -462,32 +489,57 @@ class TSymbolTable : angle::NonCopyable
// variable's type, not here.
bool isVaryingInvariant(const std::string &originalName) const
{
- return (mGlobalInvariant ||
- mInvariantVaryings.count(originalName) > 0);
+ ASSERT(atGlobalLevel());
+ return table[currentLevel()]->isVaryingInvariant(originalName);
}
- void setGlobalInvariant() { mGlobalInvariant = true; }
- bool getGlobalInvariant() const { return mGlobalInvariant; }
-
- static int nextUniqueId()
+ void setGlobalInvariant(bool invariant)
{
- return ++uniqueIdCounter;
+ ASSERT(atGlobalLevel());
+ table[currentLevel()]->setGlobalInvariant(invariant);
}
+ const TSymbolUniqueId nextUniqueId() { return TSymbolUniqueId(this); }
+
+ // The empty symbol id is shared between all empty string ("") symbols. They are used in the
+ // AST for unused function parameters and struct type declarations that don't declare a
+ // variable, for example.
+ const TSymbolUniqueId &getEmptySymbolId() { return mEmptySymbolId; }
+
+ // Checks whether there is a built-in accessible by a shader with the specified version.
+ bool hasUnmangledBuiltInForShaderVersion(const char *name, int shaderVersion);
+
private:
- ESymbolLevel currentLevel() const
+ friend class TSymbolUniqueId;
+ int nextUniqueIdValue() { return ++mUniqueIdCounter; }
+
+ ESymbolLevel currentLevel() const { return static_cast<ESymbolLevel>(table.size() - 1); }
+
+ TVariable *insertVariable(ESymbolLevel level, const TString *name, const TType &type);
+
+ bool insert(ESymbolLevel level, TSymbol *symbol) { return table[level]->insert(symbol); }
+
+ bool insert(ESymbolLevel level, TExtension ext, TSymbol *symbol)
{
- return static_cast<ESymbolLevel>(table.size() - 1);
+ symbol->relateToExtension(ext);
+ return table[level]->insert(symbol);
}
+ // Used to insert unmangled functions to check redeclaration of built-ins in ESSL 3.00 and
+ // above.
+ void insertUnmangledBuiltInName(const char *name, ESymbolLevel level);
+
+ bool hasUnmangledBuiltInAtLevel(const char *name, ESymbolLevel level);
+
std::vector<TSymbolTableLevel *> table;
typedef TMap<TBasicType, TPrecision> PrecisionStackLevel;
- std::vector< PrecisionStackLevel *> precisionStack;
+ std::vector<PrecisionStackLevel *> precisionStack;
- std::set<std::string> mInvariantVaryings;
- bool mGlobalInvariant;
+ int mUniqueIdCounter;
- static int uniqueIdCounter;
+ const TSymbolUniqueId mEmptySymbolId;
};
-#endif // COMPILER_TRANSLATOR_SYMBOLTABLE_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SYMBOLTABLE_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.cpp b/src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.cpp
new file mode 100644
index 0000000000..2a68ae3e70
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.cpp
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SymbolUniqueId.cpp: Encapsulates a unique id for a symbol.
+
+#include "compiler/translator/SymbolUniqueId.h"
+
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+TSymbolUniqueId::TSymbolUniqueId(TSymbolTable *symbolTable) : mId(symbolTable->nextUniqueIdValue())
+{
+}
+
+TSymbolUniqueId::TSymbolUniqueId(const TSymbol &symbol) : mId(symbol.getUniqueId().get())
+{
+}
+
+int TSymbolUniqueId::get() const
+{
+ return mId;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.h b/src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.h
new file mode 100644
index 0000000000..4bd5604246
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SymbolUniqueId.h: Encapsulates a unique id for a symbol.
+
+#ifndef COMPILER_TRANSLATOR_SYMBOLUNIQUEID_H_
+#define COMPILER_TRANSLATOR_SYMBOLUNIQUEID_H_
+
+#include "compiler/translator/Common.h"
+
+namespace sh
+{
+
+class TSymbolTable;
+class TSymbol;
+
+class TSymbolUniqueId
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ explicit TSymbolUniqueId(TSymbolTable *symbolTable);
+ explicit TSymbolUniqueId(const TSymbol &symbol);
+ TSymbolUniqueId(const TSymbolUniqueId &) = default;
+ TSymbolUniqueId &operator=(const TSymbolUniqueId &) = default;
+
+ int get() const;
+
+ private:
+ int mId;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SYMBOLUNIQUEID_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.cpp
new file mode 100644
index 0000000000..d2b65a6c56
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.cpp
@@ -0,0 +1,1322 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL
+// output. Some of the implementations are straightforward and just call the HLSL equivalent of the
+// ESSL texture function, others do more work to emulate ESSL texture sampling or size query
+// behavior.
+//
+
+#include "compiler/translator/TextureFunctionHLSL.h"
+
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void OutputIntTexCoordWrap(TInfoSinkBase &out,
+ const char *wrapMode,
+ const char *size,
+ const TString &texCoord,
+ const TString &texCoordOffset,
+ const char *texCoordOutName)
+{
+ // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim
+ // but rather use equivalent formulas that map better to HLSL.
+ out << "int " << texCoordOutName << ";\n";
+ out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset
+ << ") / " << size << ";\n";
+
+ // CLAMP_TO_EDGE
+ out << "if (" << wrapMode << " == 1)\n";
+ out << "{\n";
+ out << " " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName
+ << "Offset)), 0, int(" << size << ") - 1);\n";
+ out << "}\n";
+
+ // MIRRORED_REPEAT
+ out << "else if (" << wrapMode << " == 3)\n";
+ out << "{\n";
+ out << " float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName
+ << "Offset) * 0.5) * 2.0 - 1.0);\n";
+ out << " " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n";
+ out << "}\n";
+
+ // REPEAT
+ out << "else\n";
+ out << "{\n";
+ out << " " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName
+ << "Offset)));\n";
+ out << "}\n";
+}
+
+void OutputIntTexCoordWraps(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ TString *texCoordX,
+ TString *texCoordY,
+ TString *texCoordZ)
+{
+ // Convert from normalized floating-point to integer
+ out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n";
+ if (textureFunction.offset)
+ {
+ OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "offset.x", "tix");
+ }
+ else
+ {
+ OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "0", "tix");
+ }
+ *texCoordX = "tix";
+ out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n";
+ if (textureFunction.offset)
+ {
+ OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "offset.y", "tiy");
+ }
+ else
+ {
+ OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "0", "tiy");
+ }
+ *texCoordY = "tiy";
+
+ if (IsSamplerArray(textureFunction.sampler))
+ {
+ *texCoordZ = "int(max(0, min(layers - 1, floor(0.5 + t.z))))";
+ }
+ else if (!IsSamplerCube(textureFunction.sampler) && !IsSampler2D(textureFunction.sampler))
+ {
+ out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n";
+ if (textureFunction.offset)
+ {
+ OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "offset.z", "tiz");
+ }
+ else
+ {
+ OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "0", "tiz");
+ }
+ *texCoordZ = "tiz";
+ }
+}
+
+void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const TString &textureReference,
+ const TString &samplerReference)
+{
+ out << textureReference;
+ if (IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ out << ".Load(";
+ return;
+ }
+
+ if (IsShadowSampler(textureFunction.sampler))
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << ".SampleCmp(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ out << ".SampleCmpLevelZero(";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ out << ".Sample(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << ".SampleBias(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << ".SampleLevel(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ out << ".SampleGrad(";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ out << samplerReference << ", ";
+}
+
+const char *GetSamplerCoordinateTypeString(
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ int hlslCoords)
+{
+ if (IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ switch (hlslCoords)
+ {
+ case 2:
+ if (textureFunction.sampler == EbtSampler2DMS ||
+ textureFunction.sampler == EbtISampler2DMS ||
+ textureFunction.sampler == EbtUSampler2DMS)
+ return "int2";
+ else
+ return "int3";
+ case 3:
+ return "int4";
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ switch (hlslCoords)
+ {
+ case 2:
+ return "float2";
+ case 3:
+ return "float3";
+ case 4:
+ return "float4";
+ default:
+ UNREACHABLE();
+ }
+ }
+ return "";
+}
+
+int GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction &textureFunction,
+ ShShaderOutput outputType)
+{
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ int hlslCoords = 2;
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtSamplerExternalOES:
+ case EbtSampler2DMS:
+ hlslCoords = 2;
+ break;
+ case EbtSamplerCube:
+ hlslCoords = 3;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ return hlslCoords;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ return 4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ return 2;
+ case EbtSampler2DMS:
+ return 2;
+ case EbtSampler3D:
+ return 3;
+ case EbtSamplerCube:
+ return 3;
+ case EbtSampler2DArray:
+ return 3;
+ case EbtSamplerExternalOES:
+ return 2;
+ case EbtISampler2D:
+ return 2;
+ case EbtISampler2DMS:
+ return 2;
+ case EbtISampler3D:
+ return 3;
+ case EbtISamplerCube:
+ return 3;
+ case EbtISampler2DArray:
+ return 3;
+ case EbtUSampler2D:
+ return 2;
+ case EbtUSampler2DMS:
+ return 2;
+ case EbtUSampler3D:
+ return 3;
+ case EbtUSamplerCube:
+ return 3;
+ case EbtUSampler2DArray:
+ return 3;
+ case EbtSampler2DShadow:
+ return 2;
+ case EbtSamplerCubeShadow:
+ return 3;
+ case EbtSampler2DArrayShadow:
+ return 3;
+ default:
+ UNREACHABLE();
+ }
+ }
+ return 0;
+}
+
+void OutputTextureFunctionArgumentList(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType)
+{
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtSamplerExternalOES:
+ out << "sampler2D s";
+ break;
+ case EbtSamplerCube:
+ out << "samplerCUBE s";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ out << TextureString(textureFunction.sampler) << " x, "
+ << SamplerString(textureFunction.sampler) << " s";
+ }
+ else
+ {
+ ASSERT(outputType == SH_HLSL_4_1_OUTPUT);
+ // A bug in the D3D compiler causes some nested sampling operations to fail.
+ // See http://anglebug.com/1923
+ // TODO(jmadill): Reinstate the const keyword when possible.
+ out << /*"const"*/ "uint samplerIndex";
+ }
+ }
+
+ if (textureFunction.method ==
+ TextureFunctionHLSL::TextureFunction::FETCH) // Integer coordinates
+ {
+ switch (textureFunction.coords)
+ {
+ case 2:
+ out << ", int2 t";
+ break;
+ case 3:
+ out << ", int3 t";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else // Floating-point coordinates (except textureSize)
+ {
+ switch (textureFunction.coords)
+ {
+ case 0:
+ break; // textureSize(gSampler2DMS sampler)
+ case 1:
+ out << ", int lod";
+ break; // textureSize()
+ case 2:
+ out << ", float2 t";
+ break;
+ case 3:
+ out << ", float3 t";
+ break;
+ case 4:
+ out << ", float4 t";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ case EbtSamplerExternalOES:
+ out << ", float2 ddx, float2 ddy";
+ break;
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCubeShadow:
+ out << ", float3 ddx, float3 ddy";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ break; // Comes after the offset parameter
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << ", float lod";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ break; // Comes after the offset parameter
+ case TextureFunctionHLSL::TextureFunction::SIZE:
+ break;
+ case TextureFunctionHLSL::TextureFunction::FETCH:
+ if (textureFunction.sampler == EbtSampler2DMS ||
+ textureFunction.sampler == EbtISampler2DMS ||
+ textureFunction.sampler == EbtUSampler2DMS)
+ out << ", int index";
+ else
+ out << ", int mip";
+ break;
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (textureFunction.offset)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ out << ", int3 offset";
+ break;
+ case EbtSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2D:
+ case EbtISampler2DArray:
+ case EbtUSampler2D:
+ case EbtUSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ case EbtSampler2DMS:
+ case EbtISampler2DMS:
+ case EbtUSampler2DMS:
+ case EbtSamplerExternalOES:
+ out << ", int2 offset";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << ", float bias";
+ }
+}
+
+void GetTextureReference(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType,
+ TString *textureReference,
+ TString *samplerReference)
+{
+ if (outputType == SH_HLSL_4_1_OUTPUT)
+ {
+ TString suffix = TextureGroupSuffix(textureFunction.sampler);
+ if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D)
+ {
+ *textureReference = TString("textures") + suffix + "[samplerIndex]";
+ *samplerReference = TString("samplers") + suffix + "[samplerIndex]";
+ }
+ else
+ {
+ out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix
+ << ";\n";
+ *textureReference = TString("textures") + suffix + "[textureIndex]";
+ out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" << suffix
+ << ";\n";
+ *samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]";
+ }
+ }
+ else
+ {
+ *textureReference = "x";
+ *samplerReference = "s";
+ }
+}
+
+void OutputTextureSizeFunctionBody(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const TString &textureReference,
+ bool getDimensionsIgnoresBaseLevel)
+{
+ if (IsSampler2DMS(textureFunction.sampler))
+ {
+ out << " uint width; uint height; uint samples;\n"
+ << " " << textureReference << ".GetDimensions(width, height, samples);\n";
+ }
+ else
+ {
+ if (getDimensionsIgnoresBaseLevel)
+ {
+ out << " int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
+ }
+ else
+ {
+ out << " int baseLevel = 0;\n";
+ }
+
+ if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) ||
+ (IsIntegerSampler(textureFunction.sampler) && IsSamplerCube(textureFunction.sampler)))
+ {
+ // "depth" stores either the number of layers in an array texture or 3D depth
+ out << " uint width; uint height; uint depth; uint numberOfLevels;\n"
+ << " " << textureReference
+ << ".GetDimensions(baseLevel, width, height, depth, numberOfLevels);\n"
+ << " width = max(width >> lod, 1);\n"
+ << " height = max(height >> lod, 1);\n";
+
+ if (!IsSamplerArray(textureFunction.sampler))
+ {
+ out << " depth = max(depth >> lod, 1);\n";
+ }
+ }
+ else if (IsSampler2D(textureFunction.sampler) || IsSamplerCube(textureFunction.sampler))
+ {
+ out << " uint width; uint height; uint numberOfLevels;\n"
+ << " " << textureReference
+ << ".GetDimensions(baseLevel, width, height, numberOfLevels);\n"
+ << " width = max(width >> lod, 1);\n"
+ << " height = max(height >> lod, 1);\n";
+ }
+ else
+ UNREACHABLE();
+ }
+
+ if (strcmp(textureFunction.getReturnType(), "int3") == 0)
+ {
+ out << " return int3(width, height, depth);\n";
+ }
+ else
+ {
+ out << " return int2(width, height);\n";
+ }
+}
+
+void ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction &textureFunction,
+ TString *texCoordX,
+ TString *texCoordY,
+ TString *texCoordZ)
+{
+ if (textureFunction.proj)
+ {
+ TString proj("");
+ switch (textureFunction.coords)
+ {
+ case 3:
+ proj = " / t.z";
+ break;
+ case 4:
+ proj = " / t.w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ *texCoordX = "(" + *texCoordX + proj + ")";
+ *texCoordY = "(" + *texCoordY + proj + ")";
+ *texCoordZ = "(" + *texCoordZ + proj + ")";
+ }
+}
+
+void OutputIntegerTextureSampleFunctionComputations(
+ TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType,
+ const TString &textureReference,
+ TString *texCoordX,
+ TString *texCoordY,
+ TString *texCoordZ)
+{
+ if (!IsIntegerSampler(textureFunction.sampler))
+ {
+ return;
+ }
+ if (IsSamplerCube(textureFunction.sampler))
+ {
+ out << " float width; float height; float layers; float levels;\n";
+
+ out << " uint mip = 0;\n";
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, layers, levels);\n";
+
+ out << " bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n";
+ out << " bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n";
+ out << " bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n";
+ out << " bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || "
+ "(zMajor && t.z < 0.0f);\n";
+
+ // FACE_POSITIVE_X = 000b
+ // FACE_NEGATIVE_X = 001b
+ // FACE_POSITIVE_Y = 010b
+ // FACE_NEGATIVE_Y = 011b
+ // FACE_POSITIVE_Z = 100b
+ // FACE_NEGATIVE_Z = 101b
+ out << " int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
+
+ out << " float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
+ out << " float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
+ out << " float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
+
+ out << " t.x = (u * 0.5f / m) + 0.5f;\n";
+ out << " t.y = (v * 0.5f / m) + 0.5f;\n";
+
+ // Mip level computation.
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT)
+ {
+ out << " float2 tSized = float2(t.x * width, t.y * height);\n"
+ " float2 dx = ddx(tSized);\n"
+ " float2 dy = ddy(tSized);\n"
+ " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial
+ // derivatives of P are assumed to be in the coordinate system used before
+ // texture coordinates are projected onto the appropriate cube face."
+ // ddx[0] and ddy[0] are the derivatives of t.x passed into the function
+ // ddx[1] and ddy[1] are the derivatives of t.y passed into the function
+ // ddx[2] and ddy[2] are the derivatives of t.z passed into the function
+ // Determine the derivatives of u, v and m
+ out << " float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] "
+ ": ddx[0]);\n"
+ " float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] "
+ ": ddy[0]);\n"
+ " float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n"
+ " float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n"
+ " float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n"
+ " float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n";
+ // Now determine the derivatives of the face coordinates, using the
+ // derivatives calculated above.
+ // d / dx (u(x) * 0.5 / m(x) + 0.5)
+ // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2
+ out << " float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n"
+ " float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n"
+ " float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n"
+ " float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n"
+ " float2 sizeVec = float2(width, height);\n"
+ " float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n"
+ " float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n";
+ // Optimization: instead of: log2(max(length(faceddx), length(faceddy)))
+ // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2
+ out << " float lengthfaceddx2 = dot(faceddx, faceddx);\n"
+ " float lengthfaceddy2 = dot(faceddy, faceddy);\n"
+ " float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * 0.5f;\n";
+ }
+ out << " mip = uint(min(max(round(lod), 0), levels - 1));\n"
+ << " " << textureReference
+ << ".GetDimensions(mip, width, height, layers, levels);\n";
+ }
+
+ // Convert from normalized floating-point to integer
+ *texCoordX = "int(floor(width * frac(" + *texCoordX + ")))";
+ *texCoordY = "int(floor(height * frac(" + *texCoordY + ")))";
+ *texCoordZ = "face";
+ }
+ else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ if (IsSampler2D(textureFunction.sampler))
+ {
+ if (IsSamplerArray(textureFunction.sampler))
+ {
+ out << " float width; float height; float layers; float levels;\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
+ {
+ out << " uint mip = 0;\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << " uint mip = bias;\n";
+ }
+ else
+ {
+
+ out << " " << textureReference
+ << ".GetDimensions(0, width, height, layers, levels);\n";
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " float2 tSized = float2(t.x * width, t.y * height);\n"
+ " float dx = length(ddx(tSized));\n"
+ " float dy = length(ddy(tSized));\n"
+ " float lod = log2(max(dx, dy));\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " lod += bias;\n";
+ }
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ out << " float2 sizeVec = float2(width, height);\n"
+ " float2 sizeDdx = ddx * sizeVec;\n"
+ " float2 sizeDdy = ddy * sizeVec;\n"
+ " float lod = log2(max(dot(sizeDdx, sizeDdx), "
+ "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
+ }
+
+ out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+ }
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, layers, levels);\n";
+ }
+ else
+ {
+ out << " float width; float height; float levels;\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
+ {
+ out << " uint mip = 0;\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << " uint mip = bias;\n";
+ }
+ else
+ {
+ out << " " << textureReference
+ << ".GetDimensions(0, width, height, levels);\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " float2 tSized = float2(t.x * width, t.y * height);\n"
+ " float dx = length(ddx(tSized));\n"
+ " float dy = length(ddy(tSized));\n"
+ " float lod = log2(max(dx, dy));\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " lod += bias;\n";
+ }
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ out << " float2 sizeVec = float2(width, height);\n"
+ " float2 sizeDdx = ddx * sizeVec;\n"
+ " float2 sizeDdy = ddy * sizeVec;\n"
+ " float lod = log2(max(dot(sizeDdx, sizeDdx), "
+ "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
+ }
+
+ out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+ }
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, levels);\n";
+ }
+ }
+ else if (IsSampler3D(textureFunction.sampler))
+ {
+ out << " float width; float height; float depth; float levels;\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
+ {
+ out << " uint mip = 0;\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << " uint mip = bias;\n";
+ }
+ else
+ {
+ out << " " << textureReference
+ << ".GetDimensions(0, width, height, depth, levels);\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n"
+ " float dx = length(ddx(tSized));\n"
+ " float dy = length(ddy(tSized));\n"
+ " float lod = log2(max(dx, dy));\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " lod += bias;\n";
+ }
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ out << " float3 sizeVec = float3(width, height, depth);\n"
+ " float3 sizeDdx = ddx * sizeVec;\n"
+ " float3 sizeDdy = ddy * sizeVec;\n"
+ " float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, "
+ "sizeDdy))) * 0.5f;\n";
+ }
+
+ out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+ }
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, depth, levels);\n";
+ }
+ else
+ UNREACHABLE();
+
+ OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ);
+ }
+}
+
+void OutputTextureSampleFunctionReturnStatement(
+ TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType,
+ const TString &textureReference,
+ const TString &samplerReference,
+ const TString &texCoordX,
+ const TString &texCoordY,
+ const TString &texCoordZ)
+{
+ out << " return ";
+
+ // HLSL intrinsic
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtSamplerExternalOES:
+ out << "tex2D";
+ break;
+ case EbtSamplerCube:
+ out << "texCUBE";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ out << "(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << "bias(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << "lod(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ out << "lod(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << "lod(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ out << "grad(" << samplerReference << ", ";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ OutputHLSL4SampleFunctionPrefix(out, textureFunction, textureReference, samplerReference);
+ }
+ else
+ UNREACHABLE();
+
+ const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType);
+
+ out << GetSamplerCoordinateTypeString(textureFunction, hlslCoords) << "(" << texCoordX << ", "
+ << texCoordY;
+
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ if (hlslCoords >= 3)
+ {
+ if (textureFunction.coords < 3)
+ {
+ out << ", 0";
+ }
+ else
+ {
+ out << ", " << texCoordZ;
+ }
+ }
+
+ if (hlslCoords == 4)
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << ", bias";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << ", lod";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ out << ", 0";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << ", bias";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ out << ")";
+ }
+ else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ if (hlslCoords >= 3)
+ {
+ ASSERT(!IsIntegerSampler(textureFunction.sampler) ||
+ !IsSamplerCube(textureFunction.sampler) || texCoordZ == "face");
+ out << ", " << texCoordZ;
+ }
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ if (IsIntegerSampler(textureFunction.sampler))
+ {
+ out << ", mip)";
+ }
+ else if (IsShadowSampler(textureFunction.sampler))
+ {
+ // Compare value
+ if (textureFunction.proj)
+ {
+ // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
+ // The resulting third component of P' in the shadow forms is used as
+ // Dref
+ out << "), " << texCoordZ;
+ }
+ else
+ {
+ switch (textureFunction.coords)
+ {
+ case 3:
+ out << "), t.z";
+ break;
+ case 4:
+ out << "), t.w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+ else
+ {
+ out << "), ddx, ddy";
+ }
+ }
+ else if (IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ if (textureFunction.sampler == EbtSampler2DMS ||
+ textureFunction.sampler == EbtISampler2DMS ||
+ textureFunction.sampler == EbtUSampler2DMS)
+ out << "), index";
+ else
+ out << ", mip)";
+ }
+ else if (IsShadowSampler(textureFunction.sampler))
+ {
+ // Compare value
+ if (textureFunction.proj)
+ {
+ // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
+ // The resulting third component of P' in the shadow forms is used as Dref
+ out << "), " << texCoordZ;
+ }
+ else
+ {
+ switch (textureFunction.coords)
+ {
+ case 3:
+ out << "), t.z";
+ break;
+ case 4:
+ out << "), t.w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+ else
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ out << ")";
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << "), bias";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << "), lod";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ out << "), 0";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << "), bias";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (textureFunction.offset &&
+ (!IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH))
+ {
+ out << ", offset";
+ }
+ }
+ else
+ UNREACHABLE();
+
+ out << ");\n"; // Close the sample function call and return statement
+}
+
+} // Anonymous namespace
+
+TString TextureFunctionHLSL::TextureFunction::name() const
+{
+ TString name = "gl_texture";
+
+ // We need to include full the sampler type in the function name to make the signature unique
+ // on D3D11, where samplers are passed to texture functions as indices.
+ name += TextureTypeSuffix(this->sampler);
+
+ if (proj)
+ {
+ name += "Proj";
+ }
+
+ if (offset)
+ {
+ name += "Offset";
+ }
+
+ switch (method)
+ {
+ case IMPLICIT:
+ break;
+ case BIAS:
+ break; // Extra parameter makes the signature unique
+ case LOD:
+ name += "Lod";
+ break;
+ case LOD0:
+ name += "Lod0";
+ break;
+ case LOD0BIAS:
+ name += "Lod0";
+ break; // Extra parameter makes the signature unique
+ case SIZE:
+ name += "Size";
+ break;
+ case FETCH:
+ name += "Fetch";
+ break;
+ case GRAD:
+ name += "Grad";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return name;
+}
+
+const char *TextureFunctionHLSL::TextureFunction::getReturnType() const
+{
+ if (method == TextureFunction::SIZE)
+ {
+ switch (sampler)
+ {
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DShadow:
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCubeShadow:
+ case EbtSamplerExternalOES:
+ case EbtSampler2DMS:
+ case EbtISampler2DMS:
+ case EbtUSampler2DMS:
+ return "int2";
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DArrayShadow:
+ return "int3";
+ default:
+ UNREACHABLE();
+ }
+ }
+ else // Sampling function
+ {
+ switch (sampler)
+ {
+ case EbtSampler2D:
+ case EbtSampler2DMS:
+ case EbtSampler3D:
+ case EbtSamplerCube:
+ case EbtSampler2DArray:
+ case EbtSamplerExternalOES:
+ return "float4";
+ case EbtISampler2D:
+ case EbtISampler2DMS:
+ case EbtISampler3D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ return "int4";
+ case EbtUSampler2D:
+ case EbtUSampler2DMS:
+ case EbtUSampler3D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ return "uint4";
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ return "float";
+ default:
+ UNREACHABLE();
+ }
+ }
+ return "";
+}
+
+bool TextureFunctionHLSL::TextureFunction::operator<(const TextureFunction &rhs) const
+{
+ return std::tie(sampler, coords, proj, offset, method) <
+ std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method);
+}
+
+TString TextureFunctionHLSL::useTextureFunction(const TString &name,
+ TBasicType samplerType,
+ int coords,
+ size_t argumentCount,
+ bool lod0,
+ sh::GLenum shaderType)
+{
+ TextureFunction textureFunction;
+ textureFunction.sampler = samplerType;
+ textureFunction.coords = coords;
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.proj = false;
+ textureFunction.offset = false;
+
+ if (name == "texture2D" || name == "textureCube" || name == "texture")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ }
+ else if (name == "texture2DProj" || name == "textureProj")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.proj = true;
+ }
+ else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" ||
+ name == "texture2DLodEXT" || name == "textureCubeLodEXT")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ }
+ else if (name == "texture2DProjLod" || name == "textureProjLod" ||
+ name == "texture2DProjLodEXT")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ textureFunction.proj = true;
+ }
+ else if (name == "textureSize")
+ {
+ textureFunction.method = TextureFunction::SIZE;
+ }
+ else if (name == "textureOffset")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureProjOffset")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.offset = true;
+ textureFunction.proj = true;
+ }
+ else if (name == "textureLodOffset")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureProjLodOffset")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ textureFunction.proj = true;
+ textureFunction.offset = true;
+ }
+ else if (name == "texelFetch")
+ {
+ textureFunction.method = TextureFunction::FETCH;
+ }
+ else if (name == "texelFetchOffset")
+ {
+ textureFunction.method = TextureFunction::FETCH;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureGrad" || name == "texture2DGradEXT")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ }
+ else if (name == "textureGradOffset")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" ||
+ name == "textureCubeGradEXT")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ textureFunction.proj = true;
+ }
+ else if (name == "textureProjGradOffset")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ textureFunction.proj = true;
+ textureFunction.offset = true;
+ }
+ else
+ UNREACHABLE();
+
+ if (textureFunction.method ==
+ TextureFunction::IMPLICIT) // Could require lod 0 or have a bias argument
+ {
+ size_t mandatoryArgumentCount = 2; // All functions have sampler and coordinate arguments
+
+ if (textureFunction.offset)
+ {
+ mandatoryArgumentCount++;
+ }
+
+ bool bias = (argumentCount > mandatoryArgumentCount); // Bias argument is optional
+
+ if (lod0 || shaderType == GL_VERTEX_SHADER)
+ {
+ if (bias)
+ {
+ textureFunction.method = TextureFunction::LOD0BIAS;
+ }
+ else
+ {
+ textureFunction.method = TextureFunction::LOD0;
+ }
+ }
+ else if (bias)
+ {
+ textureFunction.method = TextureFunction::BIAS;
+ }
+ }
+
+ mUsesTexture.insert(textureFunction);
+ return textureFunction.name();
+}
+
+void TextureFunctionHLSL::textureFunctionHeader(TInfoSinkBase &out,
+ const ShShaderOutput outputType,
+ bool getDimensionsIgnoresBaseLevel)
+{
+ for (const TextureFunction &textureFunction : mUsesTexture)
+ {
+ // Function header
+ out << textureFunction.getReturnType() << " " << textureFunction.name() << "(";
+
+ OutputTextureFunctionArgumentList(out, textureFunction, outputType);
+
+ out << ")\n"
+ "{\n";
+
+ // In some cases we use a variable to store the texture/sampler objects, but to work around
+ // a D3D11 compiler bug related to discard inside a loop that is conditional on texture
+ // sampling we need to call the function directly on references to the texture and sampler
+ // arrays. The bug was found using dEQP-GLES3.functional.shaders.discard*loop_texture*
+ // tests.
+ TString textureReference;
+ TString samplerReference;
+ GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference);
+
+ if (textureFunction.method == TextureFunction::SIZE)
+ {
+ OutputTextureSizeFunctionBody(out, textureFunction, textureReference,
+ getDimensionsIgnoresBaseLevel);
+ }
+ else
+ {
+ TString texCoordX("t.x");
+ TString texCoordY("t.y");
+ TString texCoordZ("t.z");
+ ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ);
+ OutputIntegerTextureSampleFunctionComputations(out, textureFunction, outputType,
+ textureReference, &texCoordX, &texCoordY,
+ &texCoordZ);
+ OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType,
+ textureReference, samplerReference,
+ texCoordX, texCoordY, texCoordZ);
+ }
+
+ out << "}\n"
+ "\n";
+ }
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.h b/src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.h
new file mode 100644
index 0000000000..68bf8c0898
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.h
@@ -0,0 +1,76 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL
+// output. Some of the implementations are straightforward and just call the HLSL equivalent of the
+// ESSL texture function, others do more work to emulate ESSL texture sampling or size query
+// behavior.
+//
+
+#ifndef COMPILER_TRANSLATOR_TEXTUREFUNCTIONHLSL_H_
+#define COMPILER_TRANSLATOR_TEXTUREFUNCTIONHLSL_H_
+
+#include <set>
+
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Common.h"
+#include "compiler/translator/InfoSink.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+
+class TextureFunctionHLSL final : angle::NonCopyable
+{
+ public:
+ struct TextureFunction
+ {
+ // See ESSL 3.00.6 section 8.8 for reference about what the different methods below do.
+ enum Method
+ {
+ IMPLICIT, // Mipmap LOD determined implicitly (standard lookup)
+ BIAS,
+ LOD,
+ LOD0,
+ LOD0BIAS,
+ SIZE, // textureSize()
+ FETCH,
+ GRAD
+ };
+
+ TString name() const;
+
+ bool operator<(const TextureFunction &rhs) const;
+
+ const char *getReturnType() const;
+
+ TBasicType sampler;
+ int coords;
+ bool proj;
+ bool offset;
+ Method method;
+ };
+
+ // Returns the name of the texture function implementation to call.
+ // The name that's passed in is the name of the GLSL texture function that it should implement.
+ TString useTextureFunction(const TString &name,
+ TBasicType samplerType,
+ int coords,
+ size_t argumentCount,
+ bool lod0,
+ sh::GLenum shaderType);
+
+ void textureFunctionHeader(TInfoSinkBase &out,
+ const ShShaderOutput outputType,
+ bool getDimensionsIgnoresBaseLevel);
+
+ private:
+ typedef std::set<TextureFunction> TextureFunctionSet;
+ TextureFunctionSet mUsesTexture;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TEXTUREFUNCTIONHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp
index 76d006fd11..23c967f944 100644
--- a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp
@@ -12,21 +12,28 @@
#include "compiler/translator/OutputESSL.h"
#include "angle_gl.h"
+namespace sh
+{
+
TranslatorESSL::TranslatorESSL(sh::GLenum type, ShShaderSpec spec)
: TCompiler(type, spec, SH_ESSL_OUTPUT)
{
}
-void TranslatorESSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions)
+void TranslatorESSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions)
{
- if (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS)
+ if (compileOptions & SH_EMULATE_ATAN2_FLOAT_FUNCTION)
{
- InitBuiltInFunctionEmulatorForGLSLWorkarounds(emu, getShaderType());
+ InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(emu);
}
}
-void TranslatorESSL::translate(TIntermNode *root, int) {
- TInfoSinkBase& sink = getInfoSink().obj;
+void TranslatorESSL::translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics * /*perfDiagnostics*/)
+{
+ TInfoSinkBase &sink = getInfoSink().obj;
int shaderVer = getShaderVersion();
if (shaderVer > 100)
@@ -34,69 +41,144 @@ void TranslatorESSL::translate(TIntermNode *root, int) {
sink << "#version " << shaderVer << " es\n";
}
- writePragma();
-
// Write built-in extension behaviors.
- writeExtensionBehavior();
+ writeExtensionBehavior(compileOptions);
- bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
+ // Write pragmas after extensions because some drivers consider pragmas
+ // like non-preprocessor tokens.
+ writePragma(compileOptions);
+
+ bool precisionEmulation =
+ getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
if (precisionEmulation)
{
- EmulatePrecision emulatePrecision(getSymbolTable(), shaderVer);
+ EmulatePrecision emulatePrecision(&getSymbolTable(), shaderVer);
root->traverse(&emulatePrecision);
emulatePrecision.updateTree();
- emulatePrecision.writeEmulationHelpers(sink, SH_ESSL_OUTPUT);
+ emulatePrecision.writeEmulationHelpers(sink, shaderVer, SH_ESSL_OUTPUT);
}
- RecordConstantPrecision(root, getTemporaryIndex());
+ RecordConstantPrecision(root, &getSymbolTable());
// Write emulated built-in functions if needed.
- if (!getBuiltInFunctionEmulator().IsOutputEmpty())
+ if (!getBuiltInFunctionEmulator().isOutputEmpty())
{
sink << "// BEGIN: Generated code for built-in function emulation\n\n";
if (getShaderType() == GL_FRAGMENT_SHADER)
{
sink << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
- << "#define webgl_emu_precision highp\n"
+ << "#define emu_precision highp\n"
<< "#else\n"
- << "#define webgl_emu_precision mediump\n"
+ << "#define emu_precision mediump\n"
<< "#endif\n\n";
}
else
{
- sink << "#define webgl_emu_precision highp\n";
+ sink << "#define emu_precision highp\n";
}
- getBuiltInFunctionEmulator().OutputEmulatedFunctions(sink);
+ getBuiltInFunctionEmulator().outputEmulatedFunctions(sink);
sink << "// END: Generated code for built-in function emulation\n\n";
}
// Write array bounds clamping emulation if needed.
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+ if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ {
+ const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
+ sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+ << ", local_size_z=" << localSize[2] << ") in;\n";
+ }
+
+ if (getShaderType() == GL_GEOMETRY_SHADER_OES)
+ {
+ WriteGeometryShaderLayoutQualifiers(
+ sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
+ getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices());
+ }
+
// Write translated shader.
TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
- getSymbolTable(), shaderVer, precisionEmulation);
+ &getSymbolTable(), getShaderType(), shaderVer, precisionEmulation,
+ compileOptions);
+
+ if (compileOptions & SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM)
+ {
+ TName uniformName(TString("ViewID_OVR"));
+ uniformName.setInternal(true);
+ sink << "highp uniform int " << outputESSL.hashName(uniformName) << ";\n";
+ }
+
root->traverse(&outputESSL);
}
-void TranslatorESSL::writeExtensionBehavior() {
- TInfoSinkBase& sink = getInfoSink().obj;
- const TExtensionBehavior& extBehavior = getExtensionBehavior();
- for (TExtensionBehavior::const_iterator iter = extBehavior.begin();
- iter != extBehavior.end(); ++iter) {
- if (iter->second != EBhUndefined) {
- if (getResources().NV_shader_framebuffer_fetch && iter->first == "GL_EXT_shader_framebuffer_fetch") {
+bool TranslatorESSL::shouldFlattenPragmaStdglInvariantAll()
+{
+ // Not necessary when translating to ESSL.
+ return false;
+}
+
+void TranslatorESSL::writeExtensionBehavior(ShCompileOptions compileOptions)
+{
+ TInfoSinkBase &sink = getInfoSink().obj;
+ const TExtensionBehavior &extBehavior = getExtensionBehavior();
+ const bool isMultiviewExtEmulated =
+ (compileOptions &
+ (SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM | SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
+ SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER)) != 0u;
+ for (TExtensionBehavior::const_iterator iter = extBehavior.begin(); iter != extBehavior.end();
+ ++iter)
+ {
+ if (iter->second != EBhUndefined)
+ {
+ const bool isMultiview = (iter->first == TExtension::OVR_multiview);
+ if (getResources().NV_shader_framebuffer_fetch &&
+ iter->first == TExtension::EXT_shader_framebuffer_fetch)
+ {
sink << "#extension GL_NV_shader_framebuffer_fetch : "
- << getBehaviorString(iter->second) << "\n";
- } else if (getResources().NV_draw_buffers && iter->first == "GL_EXT_draw_buffers") {
- sink << "#extension GL_NV_draw_buffers : "
- << getBehaviorString(iter->second) << "\n";
- } else {
- sink << "#extension " << iter->first << " : "
- << getBehaviorString(iter->second) << "\n";
+ << GetBehaviorString(iter->second) << "\n";
+ }
+ else if (getResources().NV_draw_buffers && iter->first == TExtension::EXT_draw_buffers)
+ {
+ sink << "#extension GL_NV_draw_buffers : " << GetBehaviorString(iter->second)
+ << "\n";
+ }
+ else if (isMultiview && isMultiviewExtEmulated)
+ {
+ if (getShaderType() == GL_VERTEX_SHADER &&
+ (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u)
+ {
+ // Emit the NV_viewport_array2 extension in a vertex shader if the
+ // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the
+ // OVR_multiview(2) extension is requested.
+ sink << "#extension GL_NV_viewport_array2 : require\n";
+ }
+ }
+ else if (iter->first == TExtension::OES_geometry_shader)
+ {
+ sink << "#ifdef GL_OES_geometry_shader\n"
+ << "#extension GL_OES_geometry_shader : " << GetBehaviorString(iter->second)
+ << "\n"
+ << "#elif defined GL_EXT_geometry_shader\n"
+ << "#extension GL_EXT_geometry_shader : " << GetBehaviorString(iter->second)
+ << "\n";
+ if (iter->second == EBhRequire)
+ {
+ sink << "#else\n"
+ << "#error \"No geometry shader extensions available.\" // Only generate "
+ "this if the extension is \"required\"\n";
+ }
+ sink << "#endif\n";
+ }
+ else
+ {
+ sink << "#extension " << GetExtensionNameString(iter->first) << " : "
+ << GetBehaviorString(iter->second) << "\n";
}
}
}
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h
index 2cc61074d4..24dc738513 100644
--- a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h
@@ -9,18 +9,27 @@
#include "compiler/translator/Compiler.h"
+namespace sh
+{
+
class TranslatorESSL : public TCompiler
{
public:
TranslatorESSL(sh::GLenum type, ShShaderSpec spec);
protected:
- void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) override;
+ void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions) override;
- void translate(TIntermNode *root, int compileOptions) override;
+ void translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics *perfDiagnostics) override;
+ bool shouldFlattenPragmaStdglInvariantAll() override;
private:
- void writeExtensionBehavior();
+ void writeExtensionBehavior(ShCompileOptions compileOptions);
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_TRANSLATORESSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp
index 05e9eb310b..a14e69e5d5 100644
--- a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp
@@ -11,53 +11,116 @@
#include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/ExtensionGLSL.h"
#include "compiler/translator/OutputGLSL.h"
+#include "compiler/translator/RewriteTexelFetchOffset.h"
+#include "compiler/translator/RewriteUnaryMinusOperatorFloat.h"
#include "compiler/translator/VersionGLSL.h"
-TranslatorGLSL::TranslatorGLSL(sh::GLenum type,
- ShShaderSpec spec,
- ShShaderOutput output)
- : TCompiler(type, spec, output) {
+namespace sh
+{
+
+TranslatorGLSL::TranslatorGLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
+ : TCompiler(type, spec, output)
+{
}
-void TranslatorGLSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions)
+void TranslatorGLSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions)
{
- if (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS)
+ if (compileOptions & SH_EMULATE_ABS_INT_FUNCTION)
+ {
+ InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(emu, getShaderType());
+ }
+
+ if (compileOptions & SH_EMULATE_ISNAN_FLOAT_FUNCTION)
{
- InitBuiltInFunctionEmulatorForGLSLWorkarounds(emu, getShaderType());
+ InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(emu, getShaderVersion());
+ }
+
+ if (compileOptions & SH_EMULATE_ATAN2_FLOAT_FUNCTION)
+ {
+ InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(emu);
}
int targetGLSLVersion = ShaderOutputTypeToGLSLVersion(getOutputType());
InitBuiltInFunctionEmulatorForGLSLMissingFunctions(emu, getShaderType(), targetGLSLVersion);
}
-void TranslatorGLSL::translate(TIntermNode *root, int compileOptions)
+void TranslatorGLSL::translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics * /*perfDiagnostics*/)
{
- TInfoSinkBase& sink = getInfoSink().obj;
+ TInfoSinkBase &sink = getInfoSink().obj;
// Write GLSL version.
writeVersion(root);
- writePragma();
-
// Write extension behaviour as needed
- writeExtensionBehavior(root);
+ writeExtensionBehavior(root, compileOptions);
+
+ // Write pragmas after extensions because some drivers consider pragmas
+ // like non-preprocessor tokens.
+ writePragma(compileOptions);
+
+ // If flattening the global invariant pragma, write invariant declarations for built-in
+ // variables. It should be harmless to do this twice in the case that the shader also explicitly
+ // did this. However, it's important to emit invariant qualifiers only for those built-in
+ // variables that are actually used, to avoid affecting the behavior of the shader.
+ if ((compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) != 0 &&
+ getPragma().stdgl.invariantAll &&
+ !sh::RemoveInvariant(getShaderType(), getShaderVersion(), getOutputType(), compileOptions))
+ {
+ ASSERT(wereVariablesCollected());
+
+ switch (getShaderType())
+ {
+ case GL_VERTEX_SHADER:
+ sink << "invariant gl_Position;\n";
- bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
+ // gl_PointSize should be declared invariant in both ESSL 1.00 and 3.00 fragment
+ // shaders if it's statically referenced.
+ conditionallyOutputInvariantDeclaration("gl_PointSize");
+ break;
+ case GL_FRAGMENT_SHADER:
+ // The preprocessor will reject this pragma if it's used in ESSL 3.00 fragment
+ // shaders, so we can use simple logic to determine whether to declare these
+ // variables invariant.
+ conditionallyOutputInvariantDeclaration("gl_FragCoord");
+ conditionallyOutputInvariantDeclaration("gl_PointCoord");
+ break;
+ default:
+ // Currently not reached, but leave this in for future expansion.
+ ASSERT(false);
+ break;
+ }
+ }
+
+ if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0)
+ {
+ sh::RewriteTexelFetchOffset(root, getSymbolTable(), getShaderVersion());
+ }
+
+ if ((compileOptions & SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR) != 0)
+ {
+ sh::RewriteUnaryMinusOperatorFloat(root);
+ }
+
+ bool precisionEmulation =
+ getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
if (precisionEmulation)
{
- EmulatePrecision emulatePrecision(getSymbolTable(), getShaderVersion());
+ EmulatePrecision emulatePrecision(&getSymbolTable(), getShaderVersion());
root->traverse(&emulatePrecision);
emulatePrecision.updateTree();
- emulatePrecision.writeEmulationHelpers(sink, getOutputType());
+ emulatePrecision.writeEmulationHelpers(sink, getShaderVersion(), getOutputType());
}
// Write emulated built-in functions if needed.
- if (!getBuiltInFunctionEmulator().IsOutputEmpty())
+ if (!getBuiltInFunctionEmulator().isOutputEmpty())
{
sink << "// BEGIN: Generated code for built-in function emulation\n\n";
- sink << "#define webgl_emu_precision\n\n";
- getBuiltInFunctionEmulator().OutputEmulatedFunctions(sink);
+ sink << "#define emu_precision\n\n";
+ getBuiltInFunctionEmulator().outputEmulatedFunctions(sink);
sink << "// END: Generated code for built-in function emulation\n\n";
}
@@ -69,7 +132,7 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions)
if (getShaderType() == GL_FRAGMENT_SHADER)
{
const bool mayHaveESSL1SecondaryOutputs =
- IsExtensionEnabled(getExtensionBehavior(), "GL_EXT_blend_func_extended") &&
+ IsExtensionEnabled(getExtensionBehavior(), TExtension::EXT_blend_func_extended) &&
getShaderVersion() == 100;
const bool declareGLFragmentOutputs = IsGLSL130OrNewer(getOutputType());
@@ -132,17 +195,48 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions)
}
}
+ if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ {
+ const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
+ sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+ << ", local_size_z=" << localSize[2] << ") in;\n";
+ }
+
+ if (getShaderType() == GL_GEOMETRY_SHADER_OES)
+ {
+ WriteGeometryShaderLayoutQualifiers(
+ sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
+ getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices());
+ }
+
// Write translated shader.
- TOutputGLSL outputGLSL(sink,
- getArrayIndexClampingStrategy(),
- getHashFunction(),
- getNameMap(),
- getSymbolTable(),
- getShaderVersion(),
- getOutputType());
+ TOutputGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
+ &getSymbolTable(), getShaderType(), getShaderVersion(), getOutputType(),
+ compileOptions);
+
+ if (compileOptions & SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM)
+ {
+ TName uniformName(TString("ViewID_OVR"));
+ uniformName.setInternal(true);
+ sink << "uniform int " << outputGLSL.hashName(uniformName) << ";\n";
+ }
+
root->traverse(&outputGLSL);
}
+bool TranslatorGLSL::shouldFlattenPragmaStdglInvariantAll()
+{
+ // Required when outputting to any GLSL version greater than 1.20, but since ANGLE doesn't
+ // translate to that version, return true for the next higher version.
+ return IsGLSL130OrNewer(getOutputType());
+}
+
+bool TranslatorGLSL::shouldCollectVariables(ShCompileOptions compileOptions)
+{
+ return (compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) ||
+ TCompiler::shouldCollectVariables(compileOptions);
+}
+
void TranslatorGLSL::writeVersion(TIntermNode *root)
{
TVersionGLSL versionGLSL(getShaderType(), getPragma(), getOutputType());
@@ -152,15 +246,15 @@ void TranslatorGLSL::writeVersion(TIntermNode *root)
// If there is no version directive in the shader, 110 is implied.
if (version > 110)
{
- TInfoSinkBase& sink = getInfoSink().obj;
+ TInfoSinkBase &sink = getInfoSink().obj;
sink << "#version " << version << "\n";
}
}
-void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root)
+void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root, ShCompileOptions compileOptions)
{
- TInfoSinkBase& sink = getInfoSink().obj;
- const TExtensionBehavior& extBehavior = getExtensionBehavior();
+ TInfoSinkBase &sink = getInfoSink().obj;
+ const TExtensionBehavior &extBehavior = getExtensionBehavior();
for (const auto &iter : extBehavior)
{
if (iter.second == EBhUndefined)
@@ -168,21 +262,58 @@ void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root)
continue;
}
- // For GLSL output, we don't need to emit most extensions explicitly,
- // but some we need to translate.
- if (iter.first == "GL_EXT_shader_texture_lod")
+ if (getOutputType() == SH_GLSL_COMPATIBILITY_OUTPUT)
+ {
+ // For GLSL output, we don't need to emit most extensions explicitly,
+ // but some we need to translate in GL compatibility profile.
+ if (iter.first == TExtension::EXT_shader_texture_lod)
+ {
+ sink << "#extension GL_ARB_shader_texture_lod : " << GetBehaviorString(iter.second)
+ << "\n";
+ }
+
+ if (iter.first == TExtension::EXT_draw_buffers)
+ {
+ sink << "#extension GL_ARB_draw_buffers : " << GetBehaviorString(iter.second)
+ << "\n";
+ }
+
+ if (iter.first == TExtension::OES_geometry_shader)
+ {
+ sink << "#extension GL_ARB_geometry_shader4 : " << GetBehaviorString(iter.second)
+ << "\n";
+ }
+ }
+
+ const bool isMultiview = (iter.first == TExtension::OVR_multiview);
+ if (isMultiview && getShaderType() == GL_VERTEX_SHADER &&
+ (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u)
{
- sink << "#extension GL_ARB_shader_texture_lod : " << getBehaviorString(iter.second)
- << "\n";
+ // Emit the NV_viewport_array2 extension in a vertex shader if the
+ // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the OVR_multiview(2)
+ // extension is requested.
+ sink << "#extension GL_NV_viewport_array2 : require\n";
}
}
// GLSL ES 3 explicit location qualifiers need to use an extension before GLSL 330
- if (getShaderVersion() >= 300 && getOutputType() < SH_GLSL_330_CORE_OUTPUT)
+ if (getShaderVersion() >= 300 && getOutputType() < SH_GLSL_330_CORE_OUTPUT &&
+ getShaderType() != GL_COMPUTE_SHADER)
{
sink << "#extension GL_ARB_explicit_attrib_location : require\n";
}
+ // Need to enable gpu_shader5 to have index constant sampler array indexing
+ if (getOutputType() != SH_ESSL_OUTPUT && getOutputType() < SH_GLSL_400_CORE_OUTPUT &&
+ getShaderVersion() == 100)
+ {
+ // Don't use "require" on to avoid breaking WebGL 1 on drivers that silently
+ // support index constant sampler array indexing, but don't have the extension or
+ // on drivers that don't have the extension at all as it would break WebGL 1 for
+ // some users.
+ sink << "#extension GL_ARB_gpu_shader5 : enable\n";
+ }
+
TExtensionGLSL extensionGLSL(getOutputType());
root->traverse(&extensionGLSL);
@@ -195,3 +326,14 @@ void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root)
sink << "#extension " << ext << " : require\n";
}
}
+
+void TranslatorGLSL::conditionallyOutputInvariantDeclaration(const char *builtinVaryingName)
+{
+ if (isVaryingDefined(builtinVaryingName))
+ {
+ TInfoSinkBase &sink = getInfoSink().obj;
+ sink << "invariant " << builtinVaryingName << ";\n";
+ }
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h
index 4f07b21980..982d0e5ddc 100644
--- a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h
@@ -9,19 +9,30 @@
#include "compiler/translator/Compiler.h"
+namespace sh
+{
+
class TranslatorGLSL : public TCompiler
{
public:
TranslatorGLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
protected:
- void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) override;
+ void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions) override;
- void translate(TIntermNode *root, int compileOptions) override;
+ void translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics *perfDiagnostics) override;
+ bool shouldFlattenPragmaStdglInvariantAll() override;
+ bool shouldCollectVariables(ShCompileOptions compileOptions) override;
private:
void writeVersion(TIntermNode *root);
- void writeExtensionBehavior(TIntermNode *root);
+ void writeExtensionBehavior(TIntermNode *root, ShCompileOptions compileOptions);
+ void conditionallyOutputInvariantDeclaration(const char *builtinVaryingName);
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_TRANSLATORGLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp
index c5d18d21bf..091a649cfc 100644
--- a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp
@@ -6,79 +6,156 @@
#include "compiler/translator/TranslatorHLSL.h"
+#include "compiler/translator/AddDefaultReturnStatements.h"
#include "compiler/translator/ArrayReturnValueToOutParameter.h"
+#include "compiler/translator/BreakVariableAliasingInInnerLoops.h"
+#include "compiler/translator/EmulatePrecision.h"
+#include "compiler/translator/ExpandIntegerPowExpressions.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
#include "compiler/translator/OutputHLSL.h"
#include "compiler/translator/RemoveDynamicIndexing.h"
+#include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h"
#include "compiler/translator/RewriteElseBlocks.h"
+#include "compiler/translator/RewriteTexelFetchOffset.h"
+#include "compiler/translator/RewriteUnaryMinusOperatorInt.h"
#include "compiler/translator/SeparateArrayInitialization.h"
#include "compiler/translator/SeparateDeclarations.h"
#include "compiler/translator/SeparateExpressionsReturningArrays.h"
+#include "compiler/translator/SimplifyLoopConditions.h"
+#include "compiler/translator/SplitSequenceOperator.h"
#include "compiler/translator/UnfoldShortCircuitToIf.h"
+#include "compiler/translator/WrapSwitchStatementsInBlocks.h"
+
+namespace sh
+{
TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
: TCompiler(type, spec, output)
{
}
-void TranslatorHLSL::translate(TIntermNode *root, int compileOptions)
+void TranslatorHLSL::translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics *perfDiagnostics)
{
const ShBuiltInResources &resources = getResources();
- int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
+ int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
+
+ sh::AddDefaultReturnStatements(root);
- SeparateDeclarations(root);
+ // Note that SimplifyLoopConditions needs to be run before any other AST transformations that
+ // may need to generate new statements from loop conditions or loop expressions.
+ // Note that SeparateDeclarations has already been run in TCompiler::compileTreeImpl().
+ SimplifyLoopConditions(root,
+ IntermNodePatternMatcher::kExpressionReturningArray |
+ IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
+ IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
+ &getSymbolTable(), getShaderVersion());
+
+ SplitSequenceOperator(root,
+ IntermNodePatternMatcher::kExpressionReturningArray |
+ IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
+ IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
+ &getSymbolTable(), getShaderVersion());
// Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf.
- UnfoldShortCircuitToIf(root, getTemporaryIndex());
+ UnfoldShortCircuitToIf(root, &getSymbolTable());
- SeparateExpressionsReturningArrays(root, getTemporaryIndex());
+ SeparateExpressionsReturningArrays(root, &getSymbolTable());
// Note that SeparateDeclarations needs to be run before SeparateArrayInitialization.
SeparateArrayInitialization(root);
// HLSL doesn't support arrays as return values, we'll need to make functions that have an array
// as a return value to use an out parameter to transfer the array data instead.
- ArrayReturnValueToOutParameter(root, getTemporaryIndex());
+ ArrayReturnValueToOutParameter(root, &getSymbolTable());
if (!shouldRunLoopAndIndexingValidation(compileOptions))
{
// HLSL doesn't support dynamic indexing of vectors and matrices.
- RemoveDynamicIndexing(root, getTemporaryIndex(), getSymbolTable(), getShaderVersion());
+ RemoveDynamicIndexing(root, &getSymbolTable(), getShaderVersion(), perfDiagnostics);
}
// Work around D3D9 bug that would manifest in vertex shaders with selection blocks which
// use a vertex attribute as a condition, and some related computation in the else block.
if (getOutputType() == SH_HLSL_3_0_OUTPUT && getShaderType() == GL_VERTEX_SHADER)
{
- sh::RewriteElseBlocks(root, getTemporaryIndex());
+ sh::RewriteElseBlocks(root, &getSymbolTable());
+ }
+
+ // Work around an HLSL compiler frontend aliasing optimization bug.
+ // TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed
+ // in the next release of d3dcompiler.dll, it would be nice to detect the DLL
+ // version and only apply the workaround if it is too old.
+ sh::BreakVariableAliasingInInnerLoops(root);
+
+ // WrapSwitchStatementsInBlocks should be called after any AST transformations that might
+ // introduce variable declarations inside the main scope of any switch statement.
+ if (WrapSwitchStatementsInBlocks(root))
+ {
+ // The WrapSwitchStatementsInBlocks step might introduce new no-op cases to the end of
+ // switch statements, so make sure to clean up the AST.
+ RemoveNoOpCasesFromEndOfSwitchStatements(root, &getSymbolTable());
+ }
+
+ bool precisionEmulation =
+ getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
+
+ if (precisionEmulation)
+ {
+ EmulatePrecision emulatePrecision(&getSymbolTable(), getShaderVersion());
+ root->traverse(&emulatePrecision);
+ emulatePrecision.updateTree();
+ emulatePrecision.writeEmulationHelpers(getInfoSink().obj, getShaderVersion(),
+ getOutputType());
+ }
+
+ if ((compileOptions & SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS) != 0)
+ {
+ sh::ExpandIntegerPowExpressions(root, &getSymbolTable());
+ }
+
+ if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0)
+ {
+ sh::RewriteTexelFetchOffset(root, getSymbolTable(), getShaderVersion());
+ }
+
+ if (((compileOptions & SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR) != 0) &&
+ getShaderType() == GL_VERTEX_SHADER)
+ {
+ sh::RewriteUnaryMinusOperatorInt(root);
}
sh::OutputHLSL outputHLSL(getShaderType(), getShaderVersion(), getExtensionBehavior(),
- getSourcePath(), getOutputType(), numRenderTargets, getUniforms(), compileOptions);
+ getSourcePath(), getOutputType(), numRenderTargets, getUniforms(),
+ compileOptions, &getSymbolTable(), perfDiagnostics);
outputHLSL.output(root, getInfoSink().obj);
- mInterfaceBlockRegisterMap = outputHLSL.getInterfaceBlockRegisterMap();
- mUniformRegisterMap = outputHLSL.getUniformRegisterMap();
+ mUniformBlockRegisterMap = outputHLSL.getUniformBlockRegisterMap();
+ mUniformRegisterMap = outputHLSL.getUniformRegisterMap();
}
-bool TranslatorHLSL::hasInterfaceBlock(const std::string &interfaceBlockName) const
+bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll()
{
- return (mInterfaceBlockRegisterMap.count(interfaceBlockName) > 0);
+ // Not necessary when translating to HLSL.
+ return false;
}
-unsigned int TranslatorHLSL::getInterfaceBlockRegister(const std::string &interfaceBlockName) const
+bool TranslatorHLSL::hasUniformBlock(const std::string &uniformBlockName) const
{
- ASSERT(hasInterfaceBlock(interfaceBlockName));
- return mInterfaceBlockRegisterMap.find(interfaceBlockName)->second;
+ return (mUniformBlockRegisterMap.count(uniformBlockName) > 0);
}
-bool TranslatorHLSL::hasUniform(const std::string &uniformName) const
+unsigned int TranslatorHLSL::getUniformBlockRegister(const std::string &uniformBlockName) const
{
- return (mUniformRegisterMap.count(uniformName) > 0);
+ ASSERT(hasUniformBlock(uniformBlockName));
+ return mUniformBlockRegisterMap.find(uniformBlockName)->second;
}
-unsigned int TranslatorHLSL::getUniformRegister(const std::string &uniformName) const
+const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const
{
- ASSERT(hasUniform(uniformName));
- return mUniformRegisterMap.find(uniformName)->second;
+ return &mUniformRegisterMap;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h
index 907d816744..d7005a603c 100644
--- a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h
@@ -9,26 +9,33 @@
#include "compiler/translator/Compiler.h"
+namespace sh
+{
+
class TranslatorHLSL : public TCompiler
{
public:
TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
TranslatorHLSL *getAsTranslatorHLSL() override { return this; }
- bool hasInterfaceBlock(const std::string &interfaceBlockName) const;
- unsigned int getInterfaceBlockRegister(const std::string &interfaceBlockName) const;
+ bool hasUniformBlock(const std::string &interfaceBlockName) const;
+ unsigned int getUniformBlockRegister(const std::string &interfaceBlockName) const;
- bool hasUniform(const std::string &uniformName) const;
- unsigned int getUniformRegister(const std::string &uniformName) const;
+ const std::map<std::string, unsigned int> *getUniformRegisterMap() const;
protected:
- void translate(TIntermNode *root, int compileOptions) override;
+ void translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics *perfDiagnostics) override;
+ bool shouldFlattenPragmaStdglInvariantAll() override;
// collectVariables needs to be run always so registers can be assigned.
- bool shouldCollectVariables(int compileOptions) override { return true; }
+ bool shouldCollectVariables(ShCompileOptions compileOptions) override { return true; }
- std::map<std::string, unsigned int> mInterfaceBlockRegisterMap;
+ std::map<std::string, unsigned int> mUniformBlockRegisterMap;
std::map<std::string, unsigned int> mUniformRegisterMap;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_TRANSLATORHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.cpp b/src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.cpp
new file mode 100644
index 0000000000..0fe2a21f90
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.cpp
@@ -0,0 +1,173 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TranslatorVulkan:
+// A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl.
+// The shaders are then fed into glslang to spit out SPIR-V (libANGLE-side).
+// See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt
+//
+
+#include "compiler/translator/TranslatorVulkan.h"
+
+#include "angle_gl.h"
+#include "common/utilities.h"
+#include "compiler/translator/OutputVulkanGLSL.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+class DeclareDefaultUniformsTraverser : public TIntermTraverser
+{
+ public:
+ DeclareDefaultUniformsTraverser(TInfoSinkBase *sink,
+ ShHashFunction64 hashFunction,
+ NameMap *nameMap)
+ : TIntermTraverser(true, true, true),
+ mSink(sink),
+ mHashFunction(hashFunction),
+ mNameMap(nameMap),
+ mInDefaultUniform(false)
+ {
+ }
+
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
+ {
+ const TIntermSequence &sequence = *(node->getSequence());
+
+ // TODO(jmadill): Compound declarations.
+ ASSERT(sequence.size() == 1);
+
+ TIntermTyped *variable = sequence.front()->getAsTyped();
+ const TType &type = variable->getType();
+ bool isUniform = (type.getQualifier() == EvqUniform) && !IsOpaqueType(type.getBasicType());
+
+ if (visit == PreVisit)
+ {
+ if (isUniform)
+ {
+ (*mSink) << " " << GetTypeName(type, mHashFunction, mNameMap) << " ";
+ mInDefaultUniform = true;
+ }
+ }
+ else if (visit == InVisit)
+ {
+ mInDefaultUniform = isUniform;
+ }
+ else if (visit == PostVisit)
+ {
+ if (isUniform)
+ {
+ (*mSink) << ";\n";
+
+ // Remove the uniform declaration from the tree so it isn't parsed again.
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(
+ getParentNode()->getAsBlock(), node, emptyReplacement));
+ }
+
+ mInDefaultUniform = false;
+ }
+ return true;
+ }
+
+ void visitSymbol(TIntermSymbol *symbol) override
+ {
+ if (mInDefaultUniform)
+ {
+ const TName &name = symbol->getName();
+ ASSERT(name.getString().substr(0, 3) != "gl_");
+ (*mSink) << HashName(name, mHashFunction, mNameMap);
+ }
+ }
+
+ private:
+ TInfoSinkBase *mSink;
+ ShHashFunction64 mHashFunction;
+ NameMap *mNameMap;
+ bool mInDefaultUniform;
+};
+
+TranslatorVulkan::TranslatorVulkan(sh::GLenum type, ShShaderSpec spec)
+ : TCompiler(type, spec, SH_GLSL_450_CORE_OUTPUT)
+{
+}
+
+void TranslatorVulkan::translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics * /*perfDiagnostics*/)
+{
+ TInfoSinkBase &sink = getInfoSink().obj;
+
+ sink << "#version 450 core\n";
+
+ // Write out default uniforms into a uniform block assigned to a specific set/binding.
+ int defaultUniformCount = 0;
+ for (const auto &uniform : getUniforms())
+ {
+ if (!uniform.isBuiltIn() && uniform.staticUse && !gl::IsOpaqueType(uniform.type))
+ {
+ ++defaultUniformCount;
+ }
+ }
+
+ if (defaultUniformCount > 0)
+ {
+ sink << "\nlayout(@@ DEFAULT-UNIFORMS-SET-BINDING @@) uniform defaultUniforms\n{\n";
+
+ DeclareDefaultUniformsTraverser defaultTraverser(&sink, getHashFunction(), &getNameMap());
+ root->traverse(&defaultTraverser);
+ defaultTraverser.updateTree();
+
+ sink << "};\n";
+ }
+
+ // Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
+ // if it's core profile shaders and they are used.
+ if (getShaderType() == GL_FRAGMENT_SHADER)
+ {
+ bool hasGLFragColor = false;
+ bool hasGLFragData = false;
+
+ for (const auto &outputVar : outputVariables)
+ {
+ if (outputVar.name == "gl_FragColor")
+ {
+ ASSERT(!hasGLFragColor);
+ hasGLFragColor = true;
+ continue;
+ }
+ else if (outputVar.name == "gl_FragData")
+ {
+ ASSERT(!hasGLFragData);
+ hasGLFragData = true;
+ continue;
+ }
+ }
+ ASSERT(!(hasGLFragColor && hasGLFragData));
+ if (hasGLFragColor)
+ {
+ sink << "layout(location = 0) out vec4 webgl_FragColor;\n";
+ }
+ if (hasGLFragData)
+ {
+ sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
+ }
+ }
+
+ // Write translated shader.
+ TOutputVulkanGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(),
+ getNameMap(), &getSymbolTable(), getShaderType(),
+ getShaderVersion(), getOutputType(), compileOptions);
+ root->traverse(&outputGLSL);
+}
+
+bool TranslatorVulkan::shouldFlattenPragmaStdglInvariantAll()
+{
+ // Not necessary.
+ return false;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.h b/src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.h
new file mode 100644
index 0000000000..ef67b15ae1
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.h
@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TranslatorVulkan:
+// A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl.
+// The shaders are then fed into glslang to spit out SPIR-V (libANGLE-side).
+// See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt
+//
+
+#ifndef COMPILER_TRANSLATOR_TRANSLATORVULKAN_H_
+#define COMPILER_TRANSLATOR_TRANSLATORVULKAN_H_
+
+#include "compiler/translator/Compiler.h"
+
+namespace sh
+{
+
+class TranslatorVulkan : public TCompiler
+{
+ public:
+ TranslatorVulkan(sh::GLenum type, ShShaderSpec spec);
+
+ protected:
+ void translate(TIntermBlock *root,
+ ShCompileOptions compileOptions,
+ PerformanceDiagnostics *perfDiagnostics) override;
+ bool shouldFlattenPragmaStdglInvariantAll() override;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TRANSLATORVULKAN_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/Types.cpp b/src/3rdparty/angle/src/compiler/translator/Types.cpp
index 87fdfe0d54..530ffe3aeb 100644
--- a/src/3rdparty/angle/src/compiler/translator/Types.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/Types.cpp
@@ -5,58 +5,394 @@
//
#if defined(_MSC_VER)
-#pragma warning(disable: 4718)
+#pragma warning(disable : 4718)
#endif
#include "compiler/translator/Types.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
#include <algorithm>
#include <climits>
-const char* getBasicString(TBasicType t)
+namespace sh
+{
+
+const char *getBasicString(TBasicType t)
{
switch (t)
{
- case EbtVoid: return "void"; break;
- case EbtFloat: return "float"; break;
- case EbtInt: return "int"; break;
- case EbtUInt: return "uint"; break;
- case EbtBool: return "bool"; break;
- case EbtSampler2D: return "sampler2D"; break;
- case EbtSampler3D: return "sampler3D"; break;
- case EbtSamplerCube: return "samplerCube"; break;
- case EbtSamplerExternalOES: return "samplerExternalOES"; break;
- case EbtSampler2DRect: return "sampler2DRect"; break;
- case EbtSampler2DArray: return "sampler2DArray"; break;
- case EbtISampler2D: return "isampler2D"; break;
- case EbtISampler3D: return "isampler3D"; break;
- case EbtISamplerCube: return "isamplerCube"; break;
- case EbtISampler2DArray: return "isampler2DArray"; break;
- case EbtUSampler2D: return "usampler2D"; break;
- case EbtUSampler3D: return "usampler3D"; break;
- case EbtUSamplerCube: return "usamplerCube"; break;
- case EbtUSampler2DArray: return "usampler2DArray"; break;
- case EbtSampler2DShadow: return "sampler2DShadow"; break;
- case EbtSamplerCubeShadow: return "samplerCubeShadow"; break;
- case EbtSampler2DArrayShadow: return "sampler2DArrayShadow"; break;
- case EbtStruct: return "structure"; break;
- case EbtInterfaceBlock: return "interface block"; break;
- default: UNREACHABLE(); return "unknown type";
+ case EbtVoid:
+ return "void";
+ case EbtFloat:
+ return "float";
+ case EbtInt:
+ return "int";
+ case EbtUInt:
+ return "uint";
+ case EbtBool:
+ return "bool";
+ case EbtYuvCscStandardEXT:
+ return "yuvCscStandardEXT";
+ case EbtSampler2D:
+ return "sampler2D";
+ case EbtSampler3D:
+ return "sampler3D";
+ case EbtSamplerCube:
+ return "samplerCube";
+ case EbtSamplerExternalOES:
+ return "samplerExternalOES";
+ case EbtSamplerExternal2DY2YEXT:
+ return "__samplerExternal2DY2YEXT";
+ case EbtSampler2DRect:
+ return "sampler2DRect";
+ case EbtSampler2DArray:
+ return "sampler2DArray";
+ case EbtSampler2DMS:
+ return "sampler2DMS";
+ case EbtISampler2D:
+ return "isampler2D";
+ case EbtISampler3D:
+ return "isampler3D";
+ case EbtISamplerCube:
+ return "isamplerCube";
+ case EbtISampler2DArray:
+ return "isampler2DArray";
+ case EbtISampler2DMS:
+ return "isampler2DMS";
+ case EbtUSampler2D:
+ return "usampler2D";
+ case EbtUSampler3D:
+ return "usampler3D";
+ case EbtUSamplerCube:
+ return "usamplerCube";
+ case EbtUSampler2DArray:
+ return "usampler2DArray";
+ case EbtUSampler2DMS:
+ return "usampler2DMS";
+ case EbtSampler2DShadow:
+ return "sampler2DShadow";
+ case EbtSamplerCubeShadow:
+ return "samplerCubeShadow";
+ case EbtSampler2DArrayShadow:
+ return "sampler2DArrayShadow";
+ case EbtStruct:
+ return "structure";
+ case EbtInterfaceBlock:
+ return "interface block";
+ case EbtImage2D:
+ return "image2D";
+ case EbtIImage2D:
+ return "iimage2D";
+ case EbtUImage2D:
+ return "uimage2D";
+ case EbtImage3D:
+ return "image3D";
+ case EbtIImage3D:
+ return "iimage3D";
+ case EbtUImage3D:
+ return "uimage3D";
+ case EbtImage2DArray:
+ return "image2DArray";
+ case EbtIImage2DArray:
+ return "iimage2DArray";
+ case EbtUImage2DArray:
+ return "uimage2DArray";
+ case EbtImageCube:
+ return "imageCube";
+ case EbtIImageCube:
+ return "iimageCube";
+ case EbtUImageCube:
+ return "uimageCube";
+ case EbtAtomicCounter:
+ return "atomic_uint";
+ default:
+ UNREACHABLE();
+ return "unknown type";
}
}
+// TType implementation.
+TType::TType()
+ : type(EbtVoid),
+ precision(EbpUndefined),
+ qualifier(EvqGlobal),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::Create()),
+ layoutQualifier(TLayoutQualifier::Create()),
+ primarySize(0),
+ secondarySize(0),
+ mArraySizes(nullptr),
+ mInterfaceBlock(nullptr),
+ mStructure(nullptr),
+ mIsStructSpecifier(false),
+ mMangledName(nullptr)
+{
+}
+
+TType::TType(TBasicType t, unsigned char ps, unsigned char ss)
+ : type(t),
+ precision(EbpUndefined),
+ qualifier(EvqGlobal),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::Create()),
+ layoutQualifier(TLayoutQualifier::Create()),
+ primarySize(ps),
+ secondarySize(ss),
+ mArraySizes(nullptr),
+ mInterfaceBlock(nullptr),
+ mStructure(nullptr),
+ mIsStructSpecifier(false),
+ mMangledName(nullptr)
+{
+}
+
+TType::TType(TBasicType t, TPrecision p, TQualifier q, unsigned char ps, unsigned char ss)
+ : type(t),
+ precision(p),
+ qualifier(q),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::Create()),
+ layoutQualifier(TLayoutQualifier::Create()),
+ primarySize(ps),
+ secondarySize(ss),
+ mArraySizes(nullptr),
+ mInterfaceBlock(nullptr),
+ mStructure(nullptr),
+ mIsStructSpecifier(false),
+ mMangledName(nullptr)
+{
+}
+
TType::TType(const TPublicType &p)
- : type(p.type), precision(p.precision), qualifier(p.qualifier), invariant(p.invariant),
- layoutQualifier(p.layoutQualifier), primarySize(p.primarySize), secondarySize(p.secondarySize),
- array(p.array), arraySize(p.arraySize), interfaceBlock(0), structure(0)
+ : type(p.getBasicType()),
+ precision(p.precision),
+ qualifier(p.qualifier),
+ invariant(p.invariant),
+ memoryQualifier(p.memoryQualifier),
+ layoutQualifier(p.layoutQualifier),
+ primarySize(p.getPrimarySize()),
+ secondarySize(p.getSecondarySize()),
+ mArraySizes(nullptr),
+ mInterfaceBlock(nullptr),
+ mStructure(nullptr),
+ mIsStructSpecifier(false),
+ mMangledName(nullptr)
{
- if (p.userDef)
- structure = p.userDef->getStruct();
+ ASSERT(primarySize <= 4);
+ ASSERT(secondarySize <= 4);
+ if (p.isArray())
+ {
+ mArraySizes = new TVector<unsigned int>(*p.arraySizes);
+ }
+ if (p.getUserDef())
+ {
+ mStructure = p.getUserDef();
+ mIsStructSpecifier = p.isStructSpecifier();
+ }
}
-bool TStructure::equals(const TStructure &other) const
+TType::TType(TStructure *userDef)
+ : type(EbtStruct),
+ precision(EbpUndefined),
+ qualifier(EvqTemporary),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::Create()),
+ layoutQualifier(TLayoutQualifier::Create()),
+ primarySize(1),
+ secondarySize(1),
+ mArraySizes(nullptr),
+ mInterfaceBlock(nullptr),
+ mStructure(userDef),
+ mIsStructSpecifier(false),
+ mMangledName(nullptr)
{
- return (uniqueId() == other.uniqueId());
+}
+
+TType::TType(TInterfaceBlock *interfaceBlockIn,
+ TQualifier qualifierIn,
+ TLayoutQualifier layoutQualifierIn)
+ : type(EbtInterfaceBlock),
+ precision(EbpUndefined),
+ qualifier(qualifierIn),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::Create()),
+ layoutQualifier(layoutQualifierIn),
+ primarySize(1),
+ secondarySize(1),
+ mArraySizes(nullptr),
+ mInterfaceBlock(interfaceBlockIn),
+ mStructure(0),
+ mIsStructSpecifier(false),
+ mMangledName(nullptr)
+{
+}
+
+TType::TType(const TType &t)
+ : type(t.type),
+ precision(t.precision),
+ qualifier(t.qualifier),
+ invariant(t.invariant),
+ memoryQualifier(t.memoryQualifier),
+ layoutQualifier(t.layoutQualifier),
+ primarySize(t.primarySize),
+ secondarySize(t.secondarySize),
+ mArraySizes(t.mArraySizes ? new TVector<unsigned int>(*t.mArraySizes) : nullptr),
+ mInterfaceBlock(t.mInterfaceBlock),
+ mStructure(t.mStructure),
+ mIsStructSpecifier(t.mIsStructSpecifier),
+ mMangledName(t.mMangledName)
+{
+}
+
+TType &TType::operator=(const TType &t)
+{
+ type = t.type;
+ precision = t.precision;
+ qualifier = t.qualifier;
+ invariant = t.invariant;
+ memoryQualifier = t.memoryQualifier;
+ layoutQualifier = t.layoutQualifier;
+ primarySize = t.primarySize;
+ secondarySize = t.secondarySize;
+ mArraySizes = t.mArraySizes ? new TVector<unsigned int>(*t.mArraySizes) : nullptr;
+ mInterfaceBlock = t.mInterfaceBlock;
+ mStructure = t.mStructure;
+ mIsStructSpecifier = t.mIsStructSpecifier;
+ mMangledName = t.mMangledName;
+ return *this;
+}
+
+bool TType::canBeConstructed() const
+{
+ switch (type)
+ {
+ case EbtFloat:
+ case EbtInt:
+ case EbtUInt:
+ case EbtBool:
+ case EbtStruct:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const char *TType::getBuiltInTypeNameString() const
+{
+ if (isMatrix())
+ {
+ switch (getCols())
+ {
+ case 2:
+ switch (getRows())
+ {
+ case 2:
+ return "mat2";
+ case 3:
+ return "mat2x3";
+ case 4:
+ return "mat2x4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case 3:
+ switch (getRows())
+ {
+ case 2:
+ return "mat3x2";
+ case 3:
+ return "mat3";
+ case 4:
+ return "mat3x4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case 4:
+ switch (getRows())
+ {
+ case 2:
+ return "mat4x2";
+ case 3:
+ return "mat4x3";
+ case 4:
+ return "mat4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+ if (isVector())
+ {
+ switch (getBasicType())
+ {
+ case EbtFloat:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "vec2";
+ case 3:
+ return "vec3";
+ case 4:
+ return "vec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case EbtInt:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "ivec2";
+ case 3:
+ return "ivec3";
+ case 4:
+ return "ivec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case EbtBool:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "bvec2";
+ case 3:
+ return "bvec3";
+ case 4:
+ return "bvec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case EbtUInt:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "uvec2";
+ case 3:
+ return "uvec3";
+ case 4:
+ return "uvec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+ ASSERT(getBasicType() != EbtStruct);
+ ASSERT(getBasicType() != EbtInterfaceBlock);
+ return getBasicString();
}
TString TType::getCompleteString() const
@@ -69,8 +405,14 @@ TString TType::getCompleteString() const
stream << getQualifierString() << " ";
if (precision != EbpUndefined)
stream << getPrecisionString() << " ";
- if (array)
- stream << "array[" << getArraySize() << "] of ";
+ if (mArraySizes)
+ {
+ for (auto arraySizeIter = mArraySizes->rbegin(); arraySizeIter != mArraySizes->rend();
+ ++arraySizeIter)
+ {
+ stream << "array[" << (*arraySizeIter) << "] of ";
+ }
+ }
if (isMatrix())
stream << getCols() << "X" << getRows() << " matrix of ";
else if (isVector())
@@ -83,7 +425,7 @@ TString TType::getCompleteString() const
//
// Recursively generate mangled names.
//
-TString TType::buildMangledName() const
+const char *TType::buildMangledName() const
{
TString mangledName;
if (isMatrix())
@@ -93,78 +435,132 @@ TString TType::buildMangledName() const
switch (type)
{
- case EbtFloat:
- mangledName += 'f';
- break;
- case EbtInt:
- mangledName += 'i';
- break;
- case EbtUInt:
- mangledName += 'u';
- break;
- case EbtBool:
- mangledName += 'b';
- break;
- case EbtSampler2D:
- mangledName += "s2";
- break;
- case EbtSampler3D:
- mangledName += "s3";
- break;
- case EbtSamplerCube:
- mangledName += "sC";
- break;
- case EbtSampler2DArray:
- mangledName += "s2a";
- break;
- case EbtSamplerExternalOES:
- mangledName += "sext";
- break;
- case EbtSampler2DRect:
- mangledName += "s2r";
- break;
- case EbtISampler2D:
- mangledName += "is2";
- break;
- case EbtISampler3D:
- mangledName += "is3";
- break;
- case EbtISamplerCube:
- mangledName += "isC";
- break;
- case EbtISampler2DArray:
- mangledName += "is2a";
- break;
- case EbtUSampler2D:
- mangledName += "us2";
- break;
- case EbtUSampler3D:
- mangledName += "us3";
- break;
- case EbtUSamplerCube:
- mangledName += "usC";
- break;
- case EbtUSampler2DArray:
- mangledName += "us2a";
- break;
- case EbtSampler2DShadow:
- mangledName += "s2s";
- break;
- case EbtSamplerCubeShadow:
- mangledName += "sCs";
- break;
- case EbtSampler2DArrayShadow:
- mangledName += "s2as";
- break;
- case EbtStruct:
- mangledName += structure->mangledName();
- break;
- case EbtInterfaceBlock:
- mangledName += interfaceBlock->mangledName();
- break;
- default:
- // EbtVoid, EbtAddress and non types
- break;
+ case EbtFloat:
+ mangledName += 'f';
+ break;
+ case EbtInt:
+ mangledName += 'i';
+ break;
+ case EbtUInt:
+ mangledName += 'u';
+ break;
+ case EbtBool:
+ mangledName += 'b';
+ break;
+ case EbtYuvCscStandardEXT:
+ mangledName += "ycs";
+ break;
+ case EbtSampler2D:
+ mangledName += "s2";
+ break;
+ case EbtSampler3D:
+ mangledName += "s3";
+ break;
+ case EbtSamplerCube:
+ mangledName += "sC";
+ break;
+ case EbtSampler2DArray:
+ mangledName += "s2a";
+ break;
+ case EbtSamplerExternalOES:
+ mangledName += "sext";
+ break;
+ case EbtSamplerExternal2DY2YEXT:
+ mangledName += "sext2y2y";
+ break;
+ case EbtSampler2DRect:
+ mangledName += "s2r";
+ break;
+ case EbtSampler2DMS:
+ mangledName += "s2ms";
+ break;
+ case EbtISampler2D:
+ mangledName += "is2";
+ break;
+ case EbtISampler3D:
+ mangledName += "is3";
+ break;
+ case EbtISamplerCube:
+ mangledName += "isC";
+ break;
+ case EbtISampler2DArray:
+ mangledName += "is2a";
+ break;
+ case EbtISampler2DMS:
+ mangledName += "is2ms";
+ break;
+ case EbtUSampler2D:
+ mangledName += "us2";
+ break;
+ case EbtUSampler3D:
+ mangledName += "us3";
+ break;
+ case EbtUSamplerCube:
+ mangledName += "usC";
+ break;
+ case EbtUSampler2DArray:
+ mangledName += "us2a";
+ break;
+ case EbtUSampler2DMS:
+ mangledName += "us2ms";
+ break;
+ case EbtSampler2DShadow:
+ mangledName += "s2s";
+ break;
+ case EbtSamplerCubeShadow:
+ mangledName += "sCs";
+ break;
+ case EbtSampler2DArrayShadow:
+ mangledName += "s2as";
+ break;
+ case EbtImage2D:
+ mangledName += "im2";
+ break;
+ case EbtIImage2D:
+ mangledName += "iim2";
+ break;
+ case EbtUImage2D:
+ mangledName += "uim2";
+ break;
+ case EbtImage3D:
+ mangledName += "im3";
+ break;
+ case EbtIImage3D:
+ mangledName += "iim3";
+ break;
+ case EbtUImage3D:
+ mangledName += "uim3";
+ break;
+ case EbtImage2DArray:
+ mangledName += "im2a";
+ break;
+ case EbtIImage2DArray:
+ mangledName += "iim2a";
+ break;
+ case EbtUImage2DArray:
+ mangledName += "uim2a";
+ break;
+ case EbtImageCube:
+ mangledName += "imc";
+ break;
+ case EbtIImageCube:
+ mangledName += "iimc";
+ break;
+ case EbtUImageCube:
+ mangledName += "uimc";
+ break;
+ case EbtAtomicCounter:
+ mangledName += "ac";
+ break;
+ case EbtStruct:
+ mangledName += mStructure->mangledName();
+ break;
+ case EbtInterfaceBlock:
+ mangledName += mInterfaceBlock->mangledName();
+ break;
+ default:
+ // EbtVoid, EbtAddress and non types
+ break;
}
if (isMatrix())
@@ -178,15 +574,25 @@ TString TType::buildMangledName() const
mangledName += static_cast<char>('0' + getNominalSize());
}
- if (isArray())
+ if (mArraySizes)
{
- char buf[20];
- snprintf(buf, sizeof(buf), "%d", arraySize);
- mangledName += '[';
- mangledName += buf;
- mangledName += ']';
+ for (unsigned int arraySize : *mArraySizes)
+ {
+ char buf[20];
+ snprintf(buf, sizeof(buf), "%d", arraySize);
+ mangledName += '[';
+ mangledName += buf;
+ mangledName += ']';
+ }
}
- return mangledName;
+
+ mangledName += ';';
+
+ // Copy string contents into a pool-allocated buffer, so we never need to call delete.
+ size_t requiredSize = mangledName.size() + 1;
+ char *buffer = reinterpret_cast<char *>(GetGlobalPoolAllocator()->allocate(requiredSize));
+ memcpy(buffer, mangledName.c_str(), requiredSize);
+ return buffer;
}
size_t TType::getObjectSize() const
@@ -194,23 +600,265 @@ size_t TType::getObjectSize() const
size_t totalSize;
if (getBasicType() == EbtStruct)
- totalSize = structure->objectSize();
+ totalSize = mStructure->objectSize();
else
totalSize = primarySize * secondarySize;
- if (isArray())
+ if (totalSize == 0)
+ return 0;
+
+ if (mArraySizes)
{
- // TODO: getArraySize() returns an int, not a size_t
- size_t currentArraySize = getArraySize();
- if (currentArraySize > INT_MAX / totalSize)
- totalSize = INT_MAX;
- else
- totalSize *= currentArraySize;
+ for (size_t arraySize : *mArraySizes)
+ {
+ if (arraySize > INT_MAX / totalSize)
+ totalSize = INT_MAX;
+ else
+ totalSize *= arraySize;
+ }
}
return totalSize;
}
+int TType::getLocationCount() const
+{
+ int count = 1;
+
+ if (getBasicType() == EbtStruct)
+ {
+ count = mStructure->getLocationCount();
+ }
+
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (mArraySizes)
+ {
+ for (unsigned int arraySize : *mArraySizes)
+ {
+ if (arraySize > static_cast<unsigned int>(std::numeric_limits<int>::max() / count))
+ {
+ count = std::numeric_limits<int>::max();
+ }
+ else
+ {
+ count *= static_cast<int>(arraySize);
+ }
+ }
+ }
+
+ return count;
+}
+
+unsigned int TType::getArraySizeProduct() const
+{
+ if (!mArraySizes)
+ return 1u;
+
+ unsigned int product = 1u;
+
+ for (unsigned int arraySize : *mArraySizes)
+ {
+ product *= arraySize;
+ }
+ return product;
+}
+
+bool TType::isUnsizedArray() const
+{
+ if (!mArraySizes)
+ return false;
+
+ for (unsigned int arraySize : *mArraySizes)
+ {
+ if (arraySize == 0u)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TType::sameNonArrayType(const TType &right) const
+{
+ return (type == right.type && primarySize == right.primarySize &&
+ secondarySize == right.secondarySize && mStructure == right.mStructure);
+}
+
+bool TType::isElementTypeOf(const TType &arrayType) const
+{
+ if (!sameNonArrayType(arrayType))
+ {
+ return false;
+ }
+ if (arrayType.getNumArraySizes() != getNumArraySizes() + 1u)
+ {
+ return false;
+ }
+ if (isArray())
+ {
+ for (size_t i = 0; i < mArraySizes->size(); ++i)
+ {
+ if ((*mArraySizes)[i] != (*arrayType.mArraySizes)[i])
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void TType::sizeUnsizedArrays(const TVector<unsigned int> *newArraySizes)
+{
+ size_t newArraySizesSize = newArraySizes ? newArraySizes->size() : 0;
+ for (size_t i = 0u; i < getNumArraySizes(); ++i)
+ {
+ if ((*mArraySizes)[i] == 0)
+ {
+ if (i < newArraySizesSize)
+ {
+ ASSERT(newArraySizes != nullptr);
+ (*mArraySizes)[i] = (*newArraySizes)[i];
+ }
+ else
+ {
+ (*mArraySizes)[i] = 1u;
+ }
+ }
+ }
+ invalidateMangledName();
+}
+
+void TType::sizeOutermostUnsizedArray(unsigned int arraySize)
+{
+ ASSERT(isArray());
+ ASSERT(mArraySizes->back() == 0u);
+ mArraySizes->back() = arraySize;
+}
+
+void TType::setBasicType(TBasicType t)
+{
+ if (type != t)
+ {
+ type = t;
+ invalidateMangledName();
+ }
+}
+
+void TType::setPrimarySize(unsigned char ps)
+{
+ if (primarySize != ps)
+ {
+ ASSERT(ps <= 4);
+ primarySize = ps;
+ invalidateMangledName();
+ }
+}
+
+void TType::setSecondarySize(unsigned char ss)
+{
+ if (secondarySize != ss)
+ {
+ ASSERT(ss <= 4);
+ secondarySize = ss;
+ invalidateMangledName();
+ }
+}
+
+void TType::makeArray(unsigned int s)
+{
+ if (!mArraySizes)
+ mArraySizes = new TVector<unsigned int>();
+
+ mArraySizes->push_back(s);
+ invalidateMangledName();
+}
+
+void TType::makeArrays(const TVector<unsigned int> &sizes)
+{
+ if (!mArraySizes)
+ mArraySizes = new TVector<unsigned int>();
+
+ mArraySizes->insert(mArraySizes->end(), sizes.begin(), sizes.end());
+ invalidateMangledName();
+}
+
+void TType::setArraySize(size_t arrayDimension, unsigned int s)
+{
+ ASSERT(mArraySizes != nullptr);
+ ASSERT(arrayDimension < mArraySizes->size());
+ if (mArraySizes->at(arrayDimension) != s)
+ {
+ (*mArraySizes)[arrayDimension] = s;
+ invalidateMangledName();
+ }
+}
+
+void TType::toArrayElementType()
+{
+ ASSERT(mArraySizes != nullptr);
+ if (mArraySizes->size() > 0)
+ {
+ mArraySizes->pop_back();
+ invalidateMangledName();
+ }
+}
+
+void TType::setInterfaceBlock(TInterfaceBlock *interfaceBlockIn)
+{
+ if (mInterfaceBlock != interfaceBlockIn)
+ {
+ mInterfaceBlock = interfaceBlockIn;
+ invalidateMangledName();
+ }
+}
+
+void TType::setStruct(TStructure *s)
+{
+ if (mStructure != s)
+ {
+ mStructure = s;
+ invalidateMangledName();
+ }
+}
+
+const char *TType::getMangledName() const
+{
+ if (mMangledName == nullptr)
+ {
+ mMangledName = buildMangledName();
+ }
+
+ return mMangledName;
+}
+
+void TType::realize()
+{
+ getMangledName();
+}
+
+void TType::invalidateMangledName()
+{
+ mMangledName = nullptr;
+}
+
+// TStructure implementation.
+TStructure::TStructure(TSymbolTable *symbolTable, const TString *name, TFieldList *fields)
+ : TFieldListCollection(name, fields),
+ mDeepestNesting(0),
+ mUniqueId(symbolTable->nextUniqueId()),
+ mAtGlobalScope(false)
+{
+}
+
+bool TStructure::equals(const TStructure &other) const
+{
+ return (uniqueId() == other.uniqueId());
+}
+
bool TStructure::containsArrays() const
{
for (size_t i = 0; i < mFields->size(); ++i)
@@ -244,6 +892,66 @@ bool TStructure::containsSamplers() const
return false;
}
+void TType::createSamplerSymbols(const TString &namePrefix,
+ const TString &apiNamePrefix,
+ TVector<TIntermSymbol *> *outputSymbols,
+ TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames,
+ TSymbolTable *symbolTable) const
+{
+ if (isStructureContainingSamplers())
+ {
+ if (isArray())
+ {
+ TType elementType(*this);
+ elementType.toArrayElementType();
+ for (unsigned int arrayIndex = 0u; arrayIndex < getOutermostArraySize(); ++arrayIndex)
+ {
+ TStringStream elementName;
+ elementName << namePrefix << "_" << arrayIndex;
+ TStringStream elementApiName;
+ elementApiName << apiNamePrefix << "[" << arrayIndex << "]";
+ elementType.createSamplerSymbols(elementName.str(), elementApiName.str(),
+ outputSymbols, outputSymbolsToAPINames,
+ symbolTable);
+ }
+ }
+ else
+ {
+ mStructure->createSamplerSymbols(namePrefix, apiNamePrefix, outputSymbols,
+ outputSymbolsToAPINames, symbolTable);
+ }
+ return;
+ }
+
+ ASSERT(IsSampler(type));
+ TIntermSymbol *symbol = new TIntermSymbol(symbolTable->nextUniqueId(), namePrefix, *this);
+ outputSymbols->push_back(symbol);
+ if (outputSymbolsToAPINames)
+ {
+ (*outputSymbolsToAPINames)[symbol] = apiNamePrefix;
+ }
+}
+
+void TStructure::createSamplerSymbols(const TString &namePrefix,
+ const TString &apiNamePrefix,
+ TVector<TIntermSymbol *> *outputSymbols,
+ TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames,
+ TSymbolTable *symbolTable) const
+{
+ ASSERT(containsSamplers());
+ for (auto &field : *mFields)
+ {
+ const TType *fieldType = field->type();
+ if (IsSampler(fieldType->getBasicType()) || fieldType->isStructureContainingSamplers())
+ {
+ TString fieldName = namePrefix + "_" + field->name();
+ TString fieldApiName = apiNamePrefix + "." + field->name();
+ fieldType->createSamplerSymbols(fieldName, fieldApiName, outputSymbols,
+ outputSymbolsToAPINames, symbolTable);
+ }
+ }
+}
+
TString TFieldListCollection::buildMangledName(const TString &mangledNamePrefix) const
{
TString mangledName(mangledNamePrefix);
@@ -259,9 +967,9 @@ TString TFieldListCollection::buildMangledName(const TString &mangledNamePrefix)
size_t TFieldListCollection::calculateObjectSize() const
{
size_t size = 0;
- for (size_t i = 0; i < mFields->size(); ++i)
+ for (const TField *field : *mFields)
{
- size_t fieldSize = (*mFields)[i]->type()->getObjectSize();
+ size_t fieldSize = field->type()->getObjectSize();
if (fieldSize > INT_MAX - size)
size = INT_MAX;
else
@@ -270,6 +978,24 @@ size_t TFieldListCollection::calculateObjectSize() const
return size;
}
+int TFieldListCollection::getLocationCount() const
+{
+ int count = 0;
+ for (const TField *field : *mFields)
+ {
+ int fieldCount = field->type()->getLocationCount();
+ if (fieldCount > std::numeric_limits<int>::max() - count)
+ {
+ count = std::numeric_limits<int>::max();
+ }
+ else
+ {
+ count += fieldCount;
+ }
+ }
+ return count;
+}
+
int TStructure::calculateDeepestNesting() const
{
int maxNesting = 0;
@@ -277,3 +1003,70 @@ int TStructure::calculateDeepestNesting() const
maxNesting = std::max(maxNesting, (*mFields)[i]->type()->getDeepestStructNesting());
return 1 + maxNesting;
}
+
+// TPublicType implementation.
+void TPublicType::initialize(const TTypeSpecifierNonArray &typeSpecifier, TQualifier q)
+{
+ typeSpecifierNonArray = typeSpecifier;
+ layoutQualifier = TLayoutQualifier::Create();
+ memoryQualifier = TMemoryQualifier::Create();
+ qualifier = q;
+ invariant = false;
+ precision = EbpUndefined;
+ arraySizes = nullptr;
+}
+
+void TPublicType::initializeBasicType(TBasicType basicType)
+{
+ typeSpecifierNonArray.type = basicType;
+ typeSpecifierNonArray.primarySize = 1;
+ typeSpecifierNonArray.secondarySize = 1;
+ layoutQualifier = TLayoutQualifier::Create();
+ memoryQualifier = TMemoryQualifier::Create();
+ qualifier = EvqTemporary;
+ invariant = false;
+ precision = EbpUndefined;
+ arraySizes = nullptr;
+}
+
+bool TPublicType::isStructureContainingArrays() const
+{
+ if (!typeSpecifierNonArray.userDef)
+ {
+ return false;
+ }
+
+ return typeSpecifierNonArray.userDef->containsArrays();
+}
+
+bool TPublicType::isStructureContainingType(TBasicType t) const
+{
+ if (!typeSpecifierNonArray.userDef)
+ {
+ return false;
+ }
+
+ return typeSpecifierNonArray.userDef->containsType(t);
+}
+
+void TPublicType::setArraySizes(TVector<unsigned int> *sizes)
+{
+ arraySizes = sizes;
+}
+
+bool TPublicType::isArray() const
+{
+ return arraySizes && !arraySizes->empty();
+}
+
+void TPublicType::clearArrayness()
+{
+ arraySizes = nullptr;
+}
+
+bool TPublicType::isAggregate() const
+{
+ return isArray() || typeSpecifierNonArray.isMatrix() || typeSpecifierNonArray.isVector();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/Types.h b/src/3rdparty/angle/src/compiler/translator/Types.h
index c2968dceba..7dc84c5b1c 100644
--- a/src/3rdparty/angle/src/compiler/translator/Types.h
+++ b/src/3rdparty/angle/src/compiler/translator/Types.h
@@ -12,41 +12,33 @@
#include "compiler/translator/BaseTypes.h"
#include "compiler/translator/Common.h"
+#include "compiler/translator/SymbolUniqueId.h"
+
+namespace sh
+{
struct TPublicType;
class TType;
class TSymbol;
+class TIntermSymbol;
+class TSymbolTable;
class TField : angle::NonCopyable
{
public:
POOL_ALLOCATOR_NEW_DELETE();
TField(TType *type, TString *name, const TSourceLoc &line)
- : mType(type),
- mName(name),
- mLine(line)
+ : mType(type), mName(name), mLine(line)
{
}
// TODO(alokp): We should only return const type.
// Fix it by tweaking grammar.
- TType *type()
- {
- return mType;
- }
- const TType *type() const
- {
- return mType;
- }
+ TType *type() { return mType; }
+ const TType *type() const { return mType; }
- const TString &name() const
- {
- return *mName;
- }
- const TSourceLoc &line() const
- {
- return mLine;
- }
+ const TString &name() const { return *mName; }
+ const TSourceLoc &line() const { return mLine; }
private:
TType *mType;
@@ -58,33 +50,28 @@ typedef TVector<TField *> TFieldList;
inline TFieldList *NewPoolTFieldList()
{
void *memory = GetGlobalPoolAllocator()->allocate(sizeof(TFieldList));
- return new(memory) TFieldList;
+ return new (memory) TFieldList;
}
class TFieldListCollection : angle::NonCopyable
{
public:
- const TString &name() const
- {
- return *mName;
- }
- const TFieldList &fields() const
- {
- return *mFields;
- }
+ const TString &name() const { return *mName; }
+ const TFieldList &fields() const { return *mFields; }
size_t objectSize() const
{
if (mObjectSize == 0)
mObjectSize = calculateObjectSize();
return mObjectSize;
- };
+ }
+
+ // How many locations the field list consumes as a uniform.
+ int getLocationCount() const;
protected:
TFieldListCollection(const TString *name, TFieldList *fields)
- : mName(name),
- mFields(fields),
- mObjectSize(0)
+ : mName(name), mFields(fields), mObjectSize(0)
{
}
TString buildMangledName(const TString &mangledNamePrefix) const;
@@ -102,13 +89,7 @@ class TStructure : public TFieldListCollection
{
public:
POOL_ALLOCATOR_NEW_DELETE();
- TStructure(const TString *name, TFieldList *fields)
- : TFieldListCollection(name, fields),
- mDeepestNesting(0),
- mUniqueId(0),
- mAtGlobalScope(false)
- {
- }
+ TStructure(TSymbolTable *symbolTable, const TString *name, TFieldList *fields);
int deepestNesting() const
{
@@ -120,28 +101,19 @@ class TStructure : public TFieldListCollection
bool containsType(TBasicType t) const;
bool containsSamplers() const;
- bool equals(const TStructure &other) const;
+ void createSamplerSymbols(const TString &namePrefix,
+ const TString &apiNamePrefix,
+ TVector<TIntermSymbol *> *outputSymbols,
+ TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames,
+ TSymbolTable *symbolTable) const;
- void setUniqueId(int uniqueId)
- {
- mUniqueId = uniqueId;
- }
+ bool equals(const TStructure &other) const;
- int uniqueId() const
- {
- ASSERT(mUniqueId != 0);
- return mUniqueId;
- }
+ int uniqueId() const { return mUniqueId.get(); }
- void setAtGlobalScope(bool atGlobalScope)
- {
- mAtGlobalScope = atGlobalScope;
- }
+ void setAtGlobalScope(bool atGlobalScope) { mAtGlobalScope = atGlobalScope; }
- bool atGlobalScope() const
- {
- return mAtGlobalScope;
- }
+ bool atGlobalScope() const { return mAtGlobalScope; }
const TString &mangledName() const
{
@@ -158,13 +130,13 @@ class TStructure : public TFieldListCollection
void setName(const TString &name)
{
TString *mutableName = const_cast<TString *>(mName);
- *mutableName = name;
+ *mutableName = name;
}
int calculateDeepestNesting() const;
mutable int mDeepestNesting;
- int mUniqueId;
+ const TSymbolUniqueId mUniqueId;
bool mAtGlobalScope;
};
@@ -172,40 +144,23 @@ class TInterfaceBlock : public TFieldListCollection
{
public:
POOL_ALLOCATOR_NEW_DELETE();
- TInterfaceBlock(const TString *name, TFieldList *fields, const TString *instanceName,
- int arraySize, const TLayoutQualifier &layoutQualifier)
+ TInterfaceBlock(const TString *name,
+ TFieldList *fields,
+ const TString *instanceName,
+ const TLayoutQualifier &layoutQualifier)
: TFieldListCollection(name, fields),
mInstanceName(instanceName),
- mArraySize(arraySize),
mBlockStorage(layoutQualifier.blockStorage),
- mMatrixPacking(layoutQualifier.matrixPacking)
+ mMatrixPacking(layoutQualifier.matrixPacking),
+ mBinding(layoutQualifier.binding)
{
}
- const TString &instanceName() const
- {
- return *mInstanceName;
- }
- bool hasInstanceName() const
- {
- return mInstanceName != NULL;
- }
- bool isArray() const
- {
- return mArraySize > 0;
- }
- int arraySize() const
- {
- return mArraySize;
- }
- TLayoutBlockStorage blockStorage() const
- {
- return mBlockStorage;
- }
- TLayoutMatrixPacking matrixPacking() const
- {
- return mMatrixPacking;
- }
+ const TString &instanceName() const { return *mInstanceName; }
+ bool hasInstanceName() const { return mInstanceName != nullptr; }
+ TLayoutBlockStorage blockStorage() const { return mBlockStorage; }
+ TLayoutMatrixPacking matrixPacking() const { return mMatrixPacking; }
+ int blockBinding() const { return mBinding; }
const TString &mangledName() const
{
if (mMangledName.empty())
@@ -214,10 +169,10 @@ class TInterfaceBlock : public TFieldListCollection
}
private:
- const TString *mInstanceName; // for interface block instance names
- int mArraySize; // 0 if not an array
+ const TString *mInstanceName; // for interface block instance names
TLayoutBlockStorage mBlockStorage;
TLayoutMatrixPacking mMatrixPacking;
+ int mBinding;
};
//
@@ -227,101 +182,42 @@ class TType
{
public:
POOL_ALLOCATOR_NEW_DELETE();
- TType()
- : type(EbtVoid), precision(EbpUndefined), qualifier(EvqGlobal), invariant(false),
- layoutQualifier(TLayoutQualifier::create()),
- primarySize(0), secondarySize(0), array(false), arraySize(0),
- interfaceBlock(nullptr), structure(nullptr)
- {
- }
- TType(TBasicType t, unsigned char ps = 1, unsigned char ss = 1)
- : type(t), precision(EbpUndefined), qualifier(EvqGlobal), invariant(false),
- layoutQualifier(TLayoutQualifier::create()),
- primarySize(ps), secondarySize(ss), array(false), arraySize(0),
- interfaceBlock(0), structure(0)
- {
- }
- TType(TBasicType t, TPrecision p, TQualifier q = EvqTemporary,
- unsigned char ps = 1, unsigned char ss = 1, bool a = false)
- : type(t), precision(p), qualifier(q), invariant(false),
- layoutQualifier(TLayoutQualifier::create()),
- primarySize(ps), secondarySize(ss), array(a), arraySize(0),
- interfaceBlock(0), structure(0)
- {
- }
+ TType();
+ explicit TType(TBasicType t, unsigned char ps = 1, unsigned char ss = 1);
+ TType(TBasicType t,
+ TPrecision p,
+ TQualifier q = EvqTemporary,
+ unsigned char ps = 1,
+ unsigned char ss = 1);
explicit TType(const TPublicType &p);
- TType(TStructure *userDef, TPrecision p = EbpUndefined)
- : type(EbtStruct), precision(p), qualifier(EvqTemporary), invariant(false),
- layoutQualifier(TLayoutQualifier::create()),
- primarySize(1), secondarySize(1), array(false), arraySize(0),
- interfaceBlock(0), structure(userDef)
- {
- }
- TType(TInterfaceBlock *interfaceBlockIn, TQualifier qualifierIn,
- TLayoutQualifier layoutQualifierIn, int arraySizeIn)
- : type(EbtInterfaceBlock), precision(EbpUndefined), qualifier(qualifierIn),
- invariant(false), layoutQualifier(layoutQualifierIn),
- primarySize(1), secondarySize(1), array(arraySizeIn > 0), arraySize(arraySizeIn),
- interfaceBlock(interfaceBlockIn), structure(0)
- {
- }
+ explicit TType(TStructure *userDef);
+ TType(TInterfaceBlock *interfaceBlockIn,
+ TQualifier qualifierIn,
+ TLayoutQualifier layoutQualifierIn);
+ TType(const TType &t);
+ TType &operator=(const TType &t);
- TType(const TType &) = default;
- TType &operator=(const TType &) = default;
+ TBasicType getBasicType() const { return type; }
+ void setBasicType(TBasicType t);
- TBasicType getBasicType() const
- {
- return type;
- }
- void setBasicType(TBasicType t)
- {
- if (type != t)
- {
- type = t;
- invalidateMangledName();
- }
- }
+ TPrecision getPrecision() const { return precision; }
+ void setPrecision(TPrecision p) { precision = p; }
- TPrecision getPrecision() const
- {
- return precision;
- }
- void setPrecision(TPrecision p)
- {
- precision = p;
- }
+ TQualifier getQualifier() const { return qualifier; }
+ void setQualifier(TQualifier q) { qualifier = q; }
- TQualifier getQualifier() const
- {
- return qualifier;
- }
- void setQualifier(TQualifier q)
- {
- qualifier = q;
- }
+ bool isInvariant() const { return invariant; }
- bool isInvariant() const
- {
- return invariant;
- }
+ void setInvariant(bool i) { invariant = i; }
- TLayoutQualifier getLayoutQualifier() const
- {
- return layoutQualifier;
- }
- void setLayoutQualifier(TLayoutQualifier lq)
- {
- layoutQualifier = lq;
- }
+ TMemoryQualifier getMemoryQualifier() const { return memoryQualifier; }
+ void setMemoryQualifier(const TMemoryQualifier &mq) { memoryQualifier = mq; }
- int getNominalSize() const
- {
- return primarySize;
- }
- int getSecondarySize() const
- {
- return secondarySize;
- }
+ TLayoutQualifier getLayoutQualifier() const { return layoutQualifier; }
+ void setLayoutQualifier(TLayoutQualifier lq) { layoutQualifier = lq; }
+
+ int getNominalSize() const { return primarySize; }
+ int getSecondarySize() const { return secondarySize; }
int getCols() const
{
ASSERT(isMatrix());
@@ -332,139 +228,82 @@ class TType
ASSERT(isMatrix());
return secondarySize;
}
- void setPrimarySize(unsigned char ps)
- {
- if (primarySize != ps)
- {
- primarySize = ps;
- invalidateMangledName();
- }
- }
- void setSecondarySize(unsigned char ss)
- {
- if (secondarySize != ss)
- {
- secondarySize = ss;
- invalidateMangledName();
- }
- }
+ void setPrimarySize(unsigned char ps);
+ void setSecondarySize(unsigned char ss);
// Full size of single instance of type
size_t getObjectSize() const;
- bool isMatrix() const
- {
- return primarySize > 1 && secondarySize > 1;
- }
- bool isNonSquareMatrix() const
- {
- return isMatrix() && primarySize != secondarySize;
- }
- bool isArray() const
- {
- return array;
- }
- bool isUnsizedArray() const
- {
- return array && arraySize == 0;
- }
- int getArraySize() const
- {
- return arraySize;
- }
- void setArraySize(int s)
- {
- if (!array || arraySize != s)
- {
- array = true;
- arraySize = s;
- invalidateMangledName();
- }
- }
- void clearArrayness()
- {
- if (array)
- {
- array = false;
- arraySize = 0;
- invalidateMangledName();
- }
- }
+ // Get how many locations this type consumes as a uniform.
+ int getLocationCount() const;
- TInterfaceBlock *getInterfaceBlock() const
- {
- return interfaceBlock;
- }
- void setInterfaceBlock(TInterfaceBlock *interfaceBlockIn)
- {
- if (interfaceBlock != interfaceBlockIn)
- {
- interfaceBlock = interfaceBlockIn;
- invalidateMangledName();
- }
- }
- bool isInterfaceBlock() const
- {
- return type == EbtInterfaceBlock;
+ bool isMatrix() const { return primarySize > 1 && secondarySize > 1; }
+ bool isNonSquareMatrix() const { return isMatrix() && primarySize != secondarySize; }
+ bool isArray() const { return mArraySizes != nullptr && !mArraySizes->empty(); }
+ bool isArrayOfArrays() const { return isArray() && mArraySizes->size() > 1u; }
+ size_t getNumArraySizes() const { return isArray() ? mArraySizes->size() : 0; }
+ const TVector<unsigned int> *getArraySizes() const { return mArraySizes; }
+ unsigned int getArraySizeProduct() const;
+ bool isUnsizedArray() const;
+ unsigned int getOutermostArraySize() const {
+ ASSERT(isArray());
+ return mArraySizes->back();
}
+ void makeArray(unsigned int s);
- bool isVector() const
- {
- return primarySize > 1 && secondarySize == 1;
- }
+ // sizes contain new outermost array sizes.
+ void makeArrays(const TVector<unsigned int> &sizes);
+ // Here, the array dimension value 0 corresponds to the innermost array.
+ void setArraySize(size_t arrayDimension, unsigned int s);
+
+ // Will set unsized array sizes according to newArraySizes. In case there are more
+ // unsized arrays than there are sizes in newArraySizes, defaults to setting any
+ // remaining array sizes to 1.
+ void sizeUnsizedArrays(const TVector<unsigned int> *newArraySizes);
+
+ // Will size the outermost array according to arraySize.
+ void sizeOutermostUnsizedArray(unsigned int arraySize);
+
+ // Note that the array element type might still be an array type in GLSL ES version >= 3.10.
+ void toArrayElementType();
+
+ TInterfaceBlock *getInterfaceBlock() const { return mInterfaceBlock; }
+ void setInterfaceBlock(TInterfaceBlock *interfaceBlockIn);
+ bool isInterfaceBlock() const { return type == EbtInterfaceBlock; }
+
+ bool isVector() const { return primarySize > 1 && secondarySize == 1; }
bool isScalar() const
{
- return primarySize == 1 && secondarySize == 1 && !structure;
- }
- bool isScalarInt() const
- {
- return isScalar() && (type == EbtInt || type == EbtUInt);
+ return primarySize == 1 && secondarySize == 1 && !mStructure && !isArray();
}
+ bool isScalarFloat() const { return isScalar() && type == EbtFloat; }
+ bool isScalarInt() const { return isScalar() && (type == EbtInt || type == EbtUInt); }
- TStructure *getStruct() const
- {
- return structure;
- }
- void setStruct(TStructure *s)
- {
- if (structure != s)
- {
- structure = s;
- invalidateMangledName();
- }
- }
+ bool canBeConstructed() const;
- const TString &getMangledName() const
- {
- if (mangled.empty())
- {
- mangled = buildMangledName();
- mangled += ';';
- }
+ TStructure *getStruct() { return mStructure; }
+ const TStructure *getStruct() const { return mStructure; }
+ void setStruct(TStructure *s);
- return mangled;
- }
+ const char *getMangledName() const;
+
+ bool sameNonArrayType(const TType &right) const;
+
+ // Returns true if arrayType is an array made of this type.
+ bool isElementTypeOf(const TType &arrayType) const;
- bool sameElementType(const TType &right) const
- {
- return type == right.type &&
- primarySize == right.primarySize &&
- secondarySize == right.secondarySize &&
- structure == right.structure;
- }
bool operator==(const TType &right) const
{
- return type == right.type &&
- primarySize == right.primarySize &&
- secondarySize == right.secondarySize &&
- array == right.array && (!array || arraySize == right.arraySize) &&
- structure == right.structure;
+ size_t numArraySizesL = getNumArraySizes();
+ size_t numArraySizesR = right.getNumArraySizes();
+ bool arraySizesEqual = numArraySizesL == numArraySizesR &&
+ (numArraySizesL == 0 || *mArraySizes == *right.mArraySizes);
+ return type == right.type && primarySize == right.primarySize &&
+ secondarySize == right.secondarySize && arraySizesEqual &&
+ mStructure == right.mStructure;
// don't check the qualifier, it's not ever what's being sought after
}
- bool operator!=(const TType &right) const
- {
- return !operator==(right);
- }
+ bool operator!=(const TType &right) const { return !operator==(right); }
bool operator<(const TType &right) const
{
if (type != right.type)
@@ -473,28 +312,28 @@ class TType
return primarySize < right.primarySize;
if (secondarySize != right.secondarySize)
return secondarySize < right.secondarySize;
- if (array != right.array)
- return array < right.array;
- if (arraySize != right.arraySize)
- return arraySize < right.arraySize;
- if (structure != right.structure)
- return structure < right.structure;
+ size_t numArraySizesL = getNumArraySizes();
+ size_t numArraySizesR = right.getNumArraySizes();
+ if (numArraySizesL != numArraySizesR)
+ return numArraySizesL < numArraySizesR;
+ for (size_t i = 0; i < numArraySizesL; ++i)
+ {
+ if ((*mArraySizes)[i] != (*right.mArraySizes)[i])
+ return (*mArraySizes)[i] < (*right.mArraySizes)[i];
+ }
+ if (mStructure != right.mStructure)
+ return mStructure < right.mStructure;
return false;
}
- const char *getBasicString() const
- {
- return ::getBasicString(type);
- }
- const char *getPrecisionString() const
- {
- return ::getPrecisionString(precision);
- }
- const char *getQualifierString() const
- {
- return ::getQualifierString(qualifier);
- }
+ const char *getBasicString() const { return sh::getBasicString(type); }
+
+ const char *getPrecisionString() const { return sh::getPrecisionString(precision); }
+ const char *getQualifierString() const { return sh::getQualifierString(qualifier); }
+
+ const char *getBuiltInTypeNameString() const;
+
TString getCompleteString() const;
// If this type is a struct, returns the deepest struct nesting of
@@ -509,176 +348,162 @@ class TType
// For type "nesting2", this method would return 2 -- the number
// of structures through which indirection must occur to reach the
// deepest field (nesting2.field1.position).
- int getDeepestStructNesting() const
- {
- return structure ? structure->deepestNesting() : 0;
- }
+ int getDeepestStructNesting() const { return mStructure ? mStructure->deepestNesting() : 0; }
+
+ bool isNamelessStruct() const { return mStructure && mStructure->name() == ""; }
bool isStructureContainingArrays() const
{
- return structure ? structure->containsArrays() : false;
+ return mStructure ? mStructure->containsArrays() : false;
}
bool isStructureContainingType(TBasicType t) const
{
- return structure ? structure->containsType(t) : false;
+ return mStructure ? mStructure->containsType(t) : false;
}
bool isStructureContainingSamplers() const
{
- return structure ? structure->containsSamplers() : false;
+ return mStructure ? mStructure->containsSamplers() : false;
}
+ bool isStructSpecifier() const { return mIsStructSpecifier; }
+
+ void createSamplerSymbols(const TString &namePrefix,
+ const TString &apiNamePrefix,
+ TVector<TIntermSymbol *> *outputSymbols,
+ TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames,
+ TSymbolTable *symbolTable) const;
+
// Initializes all lazily-initialized members.
- void realize()
- {
- getMangledName();
- }
+ void realize();
private:
- void invalidateMangledName() { mangled = ""; }
- TString buildMangledName() const;
- size_t getStructSize() const;
+ void invalidateMangledName();
+ const char *buildMangledName() const;
TBasicType type;
TPrecision precision;
TQualifier qualifier;
bool invariant;
+ TMemoryQualifier memoryQualifier;
TLayoutQualifier layoutQualifier;
- unsigned char primarySize; // size of vector or cols matrix
- unsigned char secondarySize; // rows of a matrix
- bool array;
- int arraySize;
+ unsigned char primarySize; // size of vector or cols matrix
+ unsigned char secondarySize; // rows of a matrix
+
+ // Used to make an array type. Outermost array size is stored at the end of the vector. Having 0
+ // in this vector means an unsized array.
+ TVector<unsigned int> *mArraySizes;
- // 0 unless this is an interface block, or interface block member variable
- TInterfaceBlock *interfaceBlock;
+ // This is set only in the following two cases:
+ // 1) Represents an interface block.
+ // 2) Represents the member variable of an unnamed interface block.
+ // It's nullptr also for members of named interface blocks.
+ TInterfaceBlock *mInterfaceBlock;
// 0 unless this is a struct
- TStructure *structure;
+ TStructure *mStructure;
+ bool mIsStructSpecifier;
- mutable TString mangled;
+ mutable const char *mMangledName;
};
-//
-// This is a workaround for a problem with the yacc stack, It can't have
-// types that it thinks have non-trivial constructors. It should
-// just be used while recognizing the grammar, not anything else. Pointers
-// could be used, but also trying to avoid lots of memory management overhead.
-//
-// Not as bad as it looks, there is no actual assumption that the fields
-// match up or are name the same or anything like that.
-//
-struct TPublicType
+// TTypeSpecifierNonArray stores all of the necessary fields for type_specifier_nonarray from the
+// grammar
+struct TTypeSpecifierNonArray
{
TBasicType type;
- TLayoutQualifier layoutQualifier;
- TQualifier qualifier;
- bool invariant;
- TPrecision precision;
- unsigned char primarySize; // size of vector or cols of matrix
- unsigned char secondarySize; // rows of matrix
- bool array;
- int arraySize;
- TType *userDef;
+ unsigned char primarySize; // size of vector or cols of matrix
+ unsigned char secondarySize; // rows of matrix
+ TStructure *userDef;
TSourceLoc line;
// true if the type was defined by a struct specifier rather than a reference to a type name.
bool isStructSpecifier;
- void setBasic(TBasicType bt, TQualifier q, const TSourceLoc &ln)
+ void initialize(TBasicType aType, const TSourceLoc &aLine)
{
- type = bt;
- layoutQualifier = TLayoutQualifier::create();
- qualifier = q;
- invariant = false;
- precision = EbpUndefined;
- primarySize = 1;
- secondarySize = 1;
- array = false;
- arraySize = 0;
- userDef = 0;
- line = ln;
+ ASSERT(aType != EbtStruct);
+ type = aType;
+ primarySize = 1;
+ secondarySize = 1;
+ userDef = nullptr;
+ line = aLine;
isStructSpecifier = false;
}
- void setAggregate(unsigned char size)
+ void initializeStruct(TStructure *aUserDef, bool aIsStructSpecifier, const TSourceLoc &aLine)
{
- primarySize = size;
+ type = EbtStruct;
+ primarySize = 1;
+ secondarySize = 1;
+ userDef = aUserDef;
+ line = aLine;
+ isStructSpecifier = aIsStructSpecifier;
}
- void setMatrix(unsigned char c, unsigned char r)
- {
- ASSERT(c > 1 && r > 1 && c <= 4 && r <= 4);
- primarySize = c;
- secondarySize = r;
- }
+ void setAggregate(unsigned char size) { primarySize = size; }
- bool isUnsizedArray() const
- {
- return array && arraySize == 0;
- }
- void setArraySize(int s)
- {
- array = true;
- arraySize = s;
- }
- void clearArrayness()
+ void setMatrix(unsigned char columns, unsigned char rows)
{
- array = false;
- arraySize = 0;
+ ASSERT(columns > 1 && rows > 1 && columns <= 4 && rows <= 4);
+ primarySize = columns;
+ secondarySize = rows;
}
- bool isStructureContainingArrays() const
- {
- if (!userDef)
- {
- return false;
- }
+ bool isMatrix() const { return primarySize > 1 && secondarySize > 1; }
- return userDef->isStructureContainingArrays();
- }
+ bool isVector() const { return primarySize > 1 && secondarySize == 1; }
+};
- bool isStructureContainingType(TBasicType t) const
- {
- if (!userDef)
- {
- return false;
- }
+//
+// This is a workaround for a problem with the yacc stack, It can't have
+// types that it thinks have non-trivial constructors. It should
+// just be used while recognizing the grammar, not anything else. Pointers
+// could be used, but also trying to avoid lots of memory management overhead.
+//
+// Not as bad as it looks, there is no actual assumption that the fields
+// match up or are name the same or anything like that.
+//
+struct TPublicType
+{
+ // Must have a trivial default constructor since it is used in YYSTYPE.
+ TPublicType() = default;
- return userDef->isStructureContainingType(t);
- }
+ void initialize(const TTypeSpecifierNonArray &typeSpecifier, TQualifier q);
+ void initializeBasicType(TBasicType basicType);
- bool isMatrix() const
- {
- return primarySize > 1 && secondarySize > 1;
- }
+ TBasicType getBasicType() const { return typeSpecifierNonArray.type; }
+ void setBasicType(TBasicType basicType) { typeSpecifierNonArray.type = basicType; }
- bool isVector() const
- {
- return primarySize > 1 && secondarySize == 1;
- }
+ unsigned char getPrimarySize() const { return typeSpecifierNonArray.primarySize; }
+ unsigned char getSecondarySize() const { return typeSpecifierNonArray.secondarySize; }
- int getCols() const
- {
- ASSERT(isMatrix());
- return primarySize;
- }
+ TStructure *getUserDef() const { return typeSpecifierNonArray.userDef; }
+ const TSourceLoc &getLine() const { return typeSpecifierNonArray.line; }
- int getRows() const
- {
- ASSERT(isMatrix());
- return secondarySize;
- }
+ bool isStructSpecifier() const { return typeSpecifierNonArray.isStructSpecifier; }
- int getNominalSize() const
- {
- return primarySize;
- }
+ bool isStructureContainingArrays() const;
+ bool isStructureContainingType(TBasicType t) const;
+ void setArraySizes(TVector<unsigned int> *sizes);
+ bool isArray() const;
+ void clearArrayness();
+ bool isAggregate() const;
- bool isAggregate() const
- {
- return array || isMatrix() || isVector();
- }
+ TTypeSpecifierNonArray typeSpecifierNonArray;
+ TLayoutQualifier layoutQualifier;
+ TMemoryQualifier memoryQualifier;
+ TQualifier qualifier;
+ bool invariant;
+ TPrecision precision;
+
+ // Either nullptr or empty in case the type is not an array. The last element is the outermost
+ // array size. Note that due to bison restrictions, copies of the public type created by the
+ // copy constructor share the same arraySizes pointer.
+ const TVector<unsigned int> *arraySizes;
};
-#endif // COMPILER_TRANSLATOR_TYPES_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TYPES_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.cpp b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.cpp
deleted file mode 100644
index f79f9dd7fb..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-//
-// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// UnfoldShortCircuit is an AST traverser to output short-circuiting operators as if-else statements.
-// The results are assigned to s# temporaries, which are used by the main translator instead of
-// the original expression.
-//
-
-#include "compiler/translator/UnfoldShortCircuit.h"
-
-#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/OutputHLSL.h"
-#include "compiler/translator/UtilsHLSL.h"
-
-namespace sh
-{
-UnfoldShortCircuit::UnfoldShortCircuit(OutputHLSL *outputHLSL) : mOutputHLSL(outputHLSL)
-{
- mTemporaryIndex = 0;
-}
-
-void UnfoldShortCircuit::traverse(TIntermNode *node)
-{
- int rewindIndex = mTemporaryIndex;
- node->traverse(this);
- mTemporaryIndex = rewindIndex;
-}
-
-bool UnfoldShortCircuit::visitBinary(Visit visit, TIntermBinary *node)
-{
- TInfoSinkBase &out = mOutputHLSL->getInfoSink();
-
- // If our right node doesn't have side effects, we know we don't need to unfold this
- // expression: there will be no short-circuiting side effects to avoid
- // (note: unfolding doesn't depend on the left node -- it will always be evaluated)
- if (!node->getRight()->hasSideEffects())
- {
- return true;
- }
-
- switch (node->getOp())
- {
- case EOpLogicalOr:
- // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true; else s = y;",
- // and then further simplifies down to "bool s = x; if(!s) s = y;".
- {
- int i = mTemporaryIndex;
-
- out << "bool s" << i << ";\n";
-
- out << "{\n";
-
- mTemporaryIndex = i + 1;
- node->getLeft()->traverse(this);
- out << "s" << i << " = ";
- mTemporaryIndex = i + 1;
- node->getLeft()->traverse(mOutputHLSL);
- out << ";\n";
- out << "if (!s" << i << ")\n"
- "{\n";
- mTemporaryIndex = i + 1;
- node->getRight()->traverse(this);
- out << " s" << i << " = ";
- mTemporaryIndex = i + 1;
- node->getRight()->traverse(mOutputHLSL);
- out << ";\n"
- "}\n";
-
- out << "}\n";
-
- mTemporaryIndex = i + 1;
- }
- return false;
- case EOpLogicalAnd:
- // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y; else s = false;",
- // and then further simplifies down to "bool s = x; if(s) s = y;".
- {
- int i = mTemporaryIndex;
-
- out << "bool s" << i << ";\n";
-
- out << "{\n";
-
- mTemporaryIndex = i + 1;
- node->getLeft()->traverse(this);
- out << "s" << i << " = ";
- mTemporaryIndex = i + 1;
- node->getLeft()->traverse(mOutputHLSL);
- out << ";\n";
- out << "if (s" << i << ")\n"
- "{\n";
- mTemporaryIndex = i + 1;
- node->getRight()->traverse(this);
- out << " s" << i << " = ";
- mTemporaryIndex = i + 1;
- node->getRight()->traverse(mOutputHLSL);
- out << ";\n"
- "}\n";
-
- out << "}\n";
-
- mTemporaryIndex = i + 1;
- }
- return false;
- default:
- return true;
- }
-}
-
-bool UnfoldShortCircuit::visitSelection(Visit visit, TIntermSelection *node)
-{
- TInfoSinkBase &out = mOutputHLSL->getInfoSink();
-
- // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
- if (node->usesTernaryOperator())
- {
- int i = mTemporaryIndex;
-
- out << TypeString(node->getType()) << " s" << i << ";\n";
-
- out << "{\n";
-
- mTemporaryIndex = i + 1;
- node->getCondition()->traverse(this);
- out << "if (";
- mTemporaryIndex = i + 1;
- node->getCondition()->traverse(mOutputHLSL);
- out << ")\n"
- "{\n";
- mTemporaryIndex = i + 1;
- node->getTrueBlock()->traverse(this);
- out << " s" << i << " = ";
- mTemporaryIndex = i + 1;
- node->getTrueBlock()->traverse(mOutputHLSL);
- out << ";\n"
- "}\n"
- "else\n"
- "{\n";
- mTemporaryIndex = i + 1;
- node->getFalseBlock()->traverse(this);
- out << " s" << i << " = ";
- mTemporaryIndex = i + 1;
- node->getFalseBlock()->traverse(mOutputHLSL);
- out << ";\n"
- "}\n";
-
- out << "}\n";
-
- mTemporaryIndex = i + 1;
- }
-
- return false;
-}
-
-bool UnfoldShortCircuit::visitLoop(Visit visit, TIntermLoop *node)
-{
- int rewindIndex = mTemporaryIndex;
-
- if (node->getInit())
- {
- node->getInit()->traverse(this);
- }
-
- if (node->getCondition())
- {
- node->getCondition()->traverse(this);
- }
-
- if (node->getExpression())
- {
- node->getExpression()->traverse(this);
- }
-
- mTemporaryIndex = rewindIndex;
-
- return false;
-}
-
-int UnfoldShortCircuit::getNextTemporaryIndex()
-{
- return mTemporaryIndex++;
-}
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.h b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.h
deleted file mode 100644
index eaceb0a0b3..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// UnfoldShortCircuit is an AST traverser to output short-circuiting operators as if-else statements
-//
-
-#ifndef COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
-#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
-
-#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/ParseContext.h"
-
-namespace sh
-{
-class OutputHLSL;
-
-class UnfoldShortCircuit : public TIntermTraverser
-{
- public:
- UnfoldShortCircuit(OutputHLSL *outputHLSL);
-
- void traverse(TIntermNode *node);
- bool visitBinary(Visit visit, TIntermBinary*);
- bool visitSelection(Visit visit, TIntermSelection *node);
- bool visitLoop(Visit visit, TIntermLoop *node);
-
- int getNextTemporaryIndex();
-
- protected:
- OutputHLSL *const mOutputHLSL;
-
- int mTemporaryIndex;
-};
-}
-
-#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp
index e50bf202ef..4e4653bbe5 100644
--- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp
@@ -6,52 +6,54 @@
#include "compiler/translator/UnfoldShortCircuitAST.h"
+namespace sh
+{
+
namespace
{
// "x || y" is equivalent to "x ? true : y".
-TIntermSelection *UnfoldOR(TIntermTyped *x, TIntermTyped *y)
+TIntermTernary *UnfoldOR(TIntermTyped *x, TIntermTyped *y)
{
- const TType boolType(EbtBool, EbpUndefined);
TConstantUnion *u = new TConstantUnion;
u->setBConst(true);
- TIntermConstantUnion *trueNode = new TIntermConstantUnion(
- u, TType(EbtBool, EbpUndefined, EvqConst, 1));
- return new TIntermSelection(x, trueNode, y, boolType);
+ TIntermConstantUnion *trueNode =
+ new TIntermConstantUnion(u, TType(EbtBool, EbpUndefined, EvqConst, 1));
+ return new TIntermTernary(x, trueNode, y);
}
// "x && y" is equivalent to "x ? y : false".
-TIntermSelection *UnfoldAND(TIntermTyped *x, TIntermTyped *y)
+TIntermTernary *UnfoldAND(TIntermTyped *x, TIntermTyped *y)
{
- const TType boolType(EbtBool, EbpUndefined);
TConstantUnion *u = new TConstantUnion;
u->setBConst(false);
- TIntermConstantUnion *falseNode = new TIntermConstantUnion(
- u, TType(EbtBool, EbpUndefined, EvqConst, 1));
- return new TIntermSelection(x, y, falseNode, boolType);
+ TIntermConstantUnion *falseNode =
+ new TIntermConstantUnion(u, TType(EbtBool, EbpUndefined, EvqConst, 1));
+ return new TIntermTernary(x, y, falseNode);
}
} // namespace anonymous
bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node)
{
- TIntermSelection *replacement = NULL;
+ TIntermTernary *replacement = nullptr;
switch (node->getOp())
{
- case EOpLogicalOr:
- replacement = UnfoldOR(node->getLeft(), node->getRight());
- break;
- case EOpLogicalAnd:
- replacement = UnfoldAND(node->getLeft(), node->getRight());
- break;
- default:
- break;
+ case EOpLogicalOr:
+ replacement = UnfoldOR(node->getLeft(), node->getRight());
+ break;
+ case EOpLogicalAnd:
+ replacement = UnfoldAND(node->getLeft(), node->getRight());
+ break;
+ default:
+ break;
}
if (replacement)
{
- mReplacements.push_back(
- NodeUpdateEntry(getParentNode(), node, replacement, false));
+ queueReplacement(replacement, OriginalNode::IS_DROPPED);
}
return true;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h
index b92a4e9152..7f377e6f15 100644
--- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h
+++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h
@@ -11,7 +11,10 @@
#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUITAST_H_
#include "common/angleutils.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
// This traverser identifies all the short circuit binary nodes that need to
// be replaced, and creates the corresponding replacement nodes. However,
@@ -20,12 +23,11 @@
class UnfoldShortCircuitAST : public TIntermTraverser
{
public:
- UnfoldShortCircuitAST()
- : TIntermTraverser(true, false, false)
- {
- }
+ UnfoldShortCircuitAST() : TIntermTraverser(true, false, false) {}
bool visitBinary(Visit visit, TIntermBinary *) override;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUITAST_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp
index be23b524d7..774f1fc704 100644
--- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp
@@ -3,14 +3,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements.
+// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else
+// statements.
// The results are assigned to s# temporaries, which are used by the main translator instead of
// the original expression.
//
#include "compiler/translator/UnfoldShortCircuitToIf.h"
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
namespace
{
@@ -19,44 +24,26 @@ namespace
class UnfoldShortCircuitTraverser : public TIntermTraverser
{
public:
- UnfoldShortCircuitTraverser();
+ UnfoldShortCircuitTraverser(TSymbolTable *symbolTable);
bool visitBinary(Visit visit, TIntermBinary *node) override;
- bool visitAggregate(Visit visit, TIntermAggregate *node) override;
- bool visitSelection(Visit visit, TIntermSelection *node) override;
- bool visitLoop(Visit visit, TIntermLoop *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
void nextIteration();
bool foundShortCircuit() const { return mFoundShortCircuit; }
protected:
- // Check if the traversal is inside a loop condition or expression, in which case the unfolded
- // expression needs to be copied inside the loop. Returns true if the copying is done, in which
- // case no further unfolding should be done on the same traversal.
- // The parameters are the node that will be unfolded to multiple statements and so can't remain
- // inside a loop condition, and its parent.
- bool copyLoopConditionOrExpression(TIntermNode *parent, TIntermTyped *node);
-
// Marked to true once an operation that needs to be unfolded has been found.
// After that, no more unfolding is performed on that traversal.
bool mFoundShortCircuit;
- // Set to the loop node while a loop condition or expression is being traversed.
- TIntermLoop *mParentLoop;
- // Parent of the loop node while a loop condition or expression is being traversed.
- TIntermNode *mLoopParent;
-
- bool mInLoopCondition;
- bool mInLoopExpression;
+ IntermNodePatternMatcher mPatternToUnfoldMatcher;
};
-UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser()
- : TIntermTraverser(true, false, true),
+UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable),
mFoundShortCircuit(false),
- mParentLoop(nullptr),
- mLoopParent(nullptr),
- mInLoopCondition(false),
- mInLoopExpression(false)
+ mPatternToUnfoldMatcher(IntermNodePatternMatcher::kUnfoldedShortCircuitExpression)
{
}
@@ -64,19 +51,23 @@ bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
{
if (mFoundShortCircuit)
return false;
+
+ if (visit != PreVisit)
+ return true;
+
+ if (!mPatternToUnfoldMatcher.match(node, getParentNode()))
+ return true;
+
// If our right node doesn't have side effects, we know we don't need to unfold this
// expression: there will be no short-circuiting side effects to avoid
// (note: unfolding doesn't depend on the left node -- it will always be evaluated)
- if (!node->getRight()->hasSideEffects())
- {
- return true;
- }
+ ASSERT(node->getRight()->hasSideEffects());
+
+ mFoundShortCircuit = true;
switch (node->getOp())
{
- case EOpLogicalOr:
- mFoundShortCircuit = true;
- if (!copyLoopConditionOrExpression(getParentNode(), node))
+ case EOpLogicalOr:
{
// "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true;
// else s = y;",
@@ -88,24 +79,21 @@ bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
ASSERT(node->getLeft()->getType() == boolType);
insertions.push_back(createTempInitDeclaration(node->getLeft()));
- TIntermAggregate *assignRightBlock = new TIntermAggregate(EOpSequence);
+ TIntermBlock *assignRightBlock = new TIntermBlock();
ASSERT(node->getRight()->getType() == boolType);
assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
- TIntermUnary *notTempSymbol = new TIntermUnary(EOpLogicalNot, boolType);
- notTempSymbol->setOperand(createTempSymbol(boolType));
- TIntermSelection *ifNode = new TIntermSelection(notTempSymbol, assignRightBlock, nullptr);
+ TIntermUnary *notTempSymbol =
+ new TIntermUnary(EOpLogicalNot, createTempSymbol(boolType));
+ TIntermIfElse *ifNode = new TIntermIfElse(notTempSymbol, assignRightBlock, nullptr);
insertions.push_back(ifNode);
insertStatementsInParentBlock(insertions);
- NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(boolType), false);
- mReplacements.push_back(replaceVariable);
+ queueReplacement(createTempSymbol(boolType), OriginalNode::IS_DROPPED);
+ return false;
}
- return false;
- case EOpLogicalAnd:
- mFoundShortCircuit = true;
- if (!copyLoopConditionOrExpression(getParentNode(), node))
+ case EOpLogicalAnd:
{
// "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y;
// else s = false;",
@@ -116,246 +104,75 @@ bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
ASSERT(node->getLeft()->getType() == boolType);
insertions.push_back(createTempInitDeclaration(node->getLeft()));
- TIntermAggregate *assignRightBlock = new TIntermAggregate(EOpSequence);
+ TIntermBlock *assignRightBlock = new TIntermBlock();
ASSERT(node->getRight()->getType() == boolType);
assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
- TIntermSelection *ifNode = new TIntermSelection(createTempSymbol(boolType), assignRightBlock, nullptr);
+ TIntermIfElse *ifNode =
+ new TIntermIfElse(createTempSymbol(boolType), assignRightBlock, nullptr);
insertions.push_back(ifNode);
insertStatementsInParentBlock(insertions);
- NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(boolType), false);
- mReplacements.push_back(replaceVariable);
+ queueReplacement(createTempSymbol(boolType), OriginalNode::IS_DROPPED);
+ return false;
}
- return false;
- default:
- return true;
+ default:
+ UNREACHABLE();
+ return true;
}
}
-bool UnfoldShortCircuitTraverser::visitSelection(Visit visit, TIntermSelection *node)
+bool UnfoldShortCircuitTraverser::visitTernary(Visit visit, TIntermTernary *node)
{
if (mFoundShortCircuit)
return false;
- // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
- if (visit == PreVisit && node->usesTernaryOperator())
- {
- mFoundShortCircuit = true;
- if (!copyLoopConditionOrExpression(getParentNode(), node))
- {
- TIntermSequence insertions;
-
- TIntermSymbol *tempSymbol = createTempSymbol(node->getType());
- TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
- tempDeclaration->getSequence()->push_back(tempSymbol);
- insertions.push_back(tempDeclaration);
-
- TIntermAggregate *trueBlock = new TIntermAggregate(EOpSequence);
- TIntermBinary *trueAssignment =
- createTempAssignment(node->getTrueBlock()->getAsTyped());
- trueBlock->getSequence()->push_back(trueAssignment);
-
- TIntermAggregate *falseBlock = new TIntermAggregate(EOpSequence);
- TIntermBinary *falseAssignment =
- createTempAssignment(node->getFalseBlock()->getAsTyped());
- falseBlock->getSequence()->push_back(falseAssignment);
-
- TIntermSelection *ifNode =
- new TIntermSelection(node->getCondition()->getAsTyped(), trueBlock, falseBlock);
- insertions.push_back(ifNode);
-
- insertStatementsInParentBlock(insertions);
-
- TIntermSymbol *ternaryResult = createTempSymbol(node->getType());
- NodeUpdateEntry replaceVariable(getParentNode(), node, ternaryResult, false);
- mReplacements.push_back(replaceVariable);
- }
- return false;
- }
-
- return true;
-}
-
-bool UnfoldShortCircuitTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
-{
- if (visit == PreVisit && mFoundShortCircuit)
- return false; // No need to traverse further
+ if (visit != PreVisit)
+ return true;
- if (node->getOp() == EOpComma)
- {
- ASSERT(visit != PreVisit || !mFoundShortCircuit);
+ if (!mPatternToUnfoldMatcher.match(node))
+ return true;
- if (visit == PostVisit && mFoundShortCircuit)
- {
- // We can be sure that we arrived here because there was a short-circuiting operator
- // inside the sequence operator since we only start traversing the sequence operator in
- // case a short-circuiting operator has not been found so far.
- // We need to unfold the sequence (comma) operator, otherwise the evaluation order of
- // statements would be messed up by unfolded operations inside.
- // Don't do any other unfolding on this round of traversal.
- mReplacements.clear();
- mMultiReplacements.clear();
- mInsertions.clear();
-
- if (!copyLoopConditionOrExpression(getParentNode(), node))
- {
- TIntermSequence insertions;
- TIntermSequence *seq = node->getSequence();
-
- TIntermSequence::size_type i = 0;
- ASSERT(!seq->empty());
- while (i < seq->size() - 1)
- {
- TIntermTyped *child = (*seq)[i]->getAsTyped();
- insertions.push_back(child);
- ++i;
- }
-
- insertStatementsInParentBlock(insertions);
-
- NodeUpdateEntry replaceVariable(getParentNode(), node, (*seq)[i], false);
- mReplacements.push_back(replaceVariable);
- }
- }
- }
- return true;
-}
+ mFoundShortCircuit = true;
-bool UnfoldShortCircuitTraverser::visitLoop(Visit visit, TIntermLoop *node)
-{
- if (visit == PreVisit)
- {
- if (mFoundShortCircuit)
- return false; // No need to traverse further
-
- mLoopParent = getParentNode();
- mParentLoop = node;
- incrementDepth(node);
+ // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
+ TIntermSequence insertions;
- if (node->getInit())
- {
- node->getInit()->traverse(this);
- if (mFoundShortCircuit)
- {
- decrementDepth();
- return false;
- }
- }
+ TIntermDeclaration *tempDeclaration = createTempDeclaration(node->getType());
+ insertions.push_back(tempDeclaration);
- if (node->getCondition())
- {
- mInLoopCondition = true;
- node->getCondition()->traverse(this);
- mInLoopCondition = false;
-
- if (mFoundShortCircuit)
- {
- decrementDepth();
- return false;
- }
- }
+ TIntermBlock *trueBlock = new TIntermBlock();
+ TIntermBinary *trueAssignment = createTempAssignment(node->getTrueExpression());
+ trueBlock->getSequence()->push_back(trueAssignment);
- if (node->getExpression())
- {
- mInLoopExpression = true;
- node->getExpression()->traverse(this);
- mInLoopExpression = false;
-
- if (mFoundShortCircuit)
- {
- decrementDepth();
- return false;
- }
- }
+ TIntermBlock *falseBlock = new TIntermBlock();
+ TIntermBinary *falseAssignment = createTempAssignment(node->getFalseExpression());
+ falseBlock->getSequence()->push_back(falseAssignment);
- if (node->getBody())
- node->getBody()->traverse(this);
+ TIntermIfElse *ifNode =
+ new TIntermIfElse(node->getCondition()->getAsTyped(), trueBlock, falseBlock);
+ insertions.push_back(ifNode);
- decrementDepth();
- }
- return false;
-}
+ insertStatementsInParentBlock(insertions);
-bool UnfoldShortCircuitTraverser::copyLoopConditionOrExpression(TIntermNode *parent,
- TIntermTyped *node)
-{
- if (mInLoopCondition)
- {
- mReplacements.push_back(
- NodeUpdateEntry(parent, node, createTempSymbol(node->getType()), false));
- TIntermAggregate *body = mParentLoop->getBody();
- TIntermSequence empty;
- if (mParentLoop->getType() == ELoopDoWhile)
- {
- // Declare the temporary variable before the loop.
- TIntermSequence insertionsBeforeLoop;
- insertionsBeforeLoop.push_back(createTempDeclaration(node->getType()));
- insertStatementsInParentBlock(insertionsBeforeLoop);
-
- // Move a part of do-while loop condition to inside the loop.
- TIntermSequence insertionsInLoop;
- insertionsInLoop.push_back(createTempAssignment(node));
- mInsertions.push_back(NodeInsertMultipleEntry(body, body->getSequence()->size() - 1,
- empty, insertionsInLoop));
- }
- else
- {
- // The loop initializer expression and one copy of the part of the loop condition are
- // executed before the loop. They need to be in a new scope.
- TIntermAggregate *loopScope = new TIntermAggregate(EOpSequence);
-
- TIntermNode *initializer = mParentLoop->getInit();
- if (initializer != nullptr)
- {
- // Move the initializer to the newly created outer scope, so that condition can
- // depend on it.
- mReplacements.push_back(NodeUpdateEntry(mParentLoop, initializer, nullptr, false));
- loopScope->getSequence()->push_back(initializer);
- }
-
- loopScope->getSequence()->push_back(createTempInitDeclaration(node));
- loopScope->getSequence()->push_back(mParentLoop);
- mReplacements.push_back(NodeUpdateEntry(mLoopParent, mParentLoop, loopScope, true));
-
- // The second copy of the part of the loop condition is executed inside the loop.
- TIntermSequence insertionsInLoop;
- insertionsInLoop.push_back(createTempAssignment(node->deepCopy()));
- mInsertions.push_back(NodeInsertMultipleEntry(body, body->getSequence()->size() - 1,
- empty, insertionsInLoop));
- }
- return true;
- }
+ TIntermSymbol *ternaryResult = createTempSymbol(node->getType());
+ queueReplacement(ternaryResult, OriginalNode::IS_DROPPED);
- if (mInLoopExpression)
- {
- TIntermTyped *movedExpression = mParentLoop->getExpression();
- mReplacements.push_back(NodeUpdateEntry(mParentLoop, movedExpression, nullptr, false));
- TIntermAggregate *body = mParentLoop->getBody();
- TIntermSequence empty;
- TIntermSequence insertions;
- insertions.push_back(movedExpression);
- mInsertions.push_back(
- NodeInsertMultipleEntry(body, body->getSequence()->size() - 1, empty, insertions));
- return true;
- }
return false;
}
void UnfoldShortCircuitTraverser::nextIteration()
{
mFoundShortCircuit = false;
- nextTemporaryIndex();
+ nextTemporaryId();
}
-} // namespace
+} // namespace
-void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex)
+void UnfoldShortCircuitToIf(TIntermNode *root, TSymbolTable *symbolTable)
{
- UnfoldShortCircuitTraverser traverser;
- ASSERT(temporaryIndex != nullptr);
- traverser.useTemporaryIndex(temporaryIndex);
+ UnfoldShortCircuitTraverser traverser(symbolTable);
// Unfold one operator at a time, and reset the traverser between iterations.
do
{
@@ -363,6 +180,7 @@ void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex)
root->traverse(&traverser);
if (traverser.foundShortCircuit())
traverser.updateTree();
- }
- while (traverser.foundShortCircuit());
+ } while (traverser.foundShortCircuit());
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h
index 0fe37b7140..37dd83a8cf 100644
--- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h
+++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h
@@ -3,7 +3,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements.
+// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else
+// statements.
// The results are assigned to s# temporaries, which are used by the main translator instead of
// the original expression.
//
@@ -11,8 +12,14 @@
#ifndef COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
+namespace sh
+{
+
class TIntermNode;
+class TSymbolTable;
+
+void UnfoldShortCircuitToIf(TIntermNode *root, TSymbolTable *symbolTable);
-void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex);
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
+#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp
index 20961c44c1..9f18509438 100644
--- a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
// UniformHLSL.cpp:
-// Methods for GLSL to HLSL translation for uniforms and interface blocks.
+// Methods for GLSL to HLSL translation for uniforms and uniform blocks.
//
#include "compiler/translator/UniformHLSL.h"
@@ -18,6 +18,9 @@
namespace sh
{
+namespace
+{
+
static const char *UniformRegisterPrefix(const TType &type)
{
if (IsSampler(type.getBasicType()))
@@ -32,22 +35,23 @@ static const char *UniformRegisterPrefix(const TType &type)
static TString InterfaceBlockFieldTypeString(const TField &field, TLayoutBlockStorage blockStorage)
{
- const TType &fieldType = *field.type();
+ const TType &fieldType = *field.type();
const TLayoutMatrixPacking matrixPacking = fieldType.getLayoutQualifier().matrixPacking;
ASSERT(matrixPacking != EmpUnspecified);
- TStructure *structure = fieldType.getStruct();
+ const TStructure *structure = fieldType.getStruct();
if (fieldType.isMatrix())
{
// Use HLSL row-major packing for GLSL column-major matrices
- const TString &matrixPackString = (matrixPacking == EmpRowMajor ? "column_major" : "row_major");
+ const TString &matrixPackString =
+ (matrixPacking == EmpRowMajor ? "column_major" : "row_major");
return matrixPackString + " " + TypeString(fieldType);
}
else if (structure)
{
// Use HLSL row-major packing for GLSL column-major matrices
return QualifiedStructNameString(*structure, matrixPacking == EmpColumnMajor,
- blockStorage == EbsStd140);
+ blockStorage == EbsStd140);
}
else
{
@@ -60,23 +64,58 @@ static TString InterfaceBlockStructName(const TInterfaceBlock &interfaceBlock)
return DecoratePrivate(interfaceBlock.name()) + "_type";
}
-UniformHLSL::UniformHLSL(StructureHLSL *structureHLSL, ShShaderOutput outputType, const std::vector<Uniform> &uniforms)
+void OutputSamplerIndexArrayInitializer(TInfoSinkBase &out,
+ const TType &type,
+ unsigned int startIndex)
+{
+ out << "{";
+ TType elementType(type);
+ elementType.toArrayElementType();
+ for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i)
+ {
+ if (i > 0u)
+ {
+ out << ", ";
+ }
+ if (elementType.isArray())
+ {
+ OutputSamplerIndexArrayInitializer(out, elementType,
+ startIndex + i * elementType.getArraySizeProduct());
+ }
+ else
+ {
+ out << (startIndex + i);
+ }
+ }
+ out << "}";
+}
+
+} // anonymous namespace
+
+UniformHLSL::UniformHLSL(sh::GLenum shaderType,
+ StructureHLSL *structureHLSL,
+ ShShaderOutput outputType,
+ const std::vector<Uniform> &uniforms)
: mUniformRegister(0),
- mInterfaceBlockRegister(0),
- mSamplerRegister(0),
+ mUniformBlockRegister(0),
+ mTextureRegister(0),
+ mRWTextureRegister(0),
+ mSamplerCount(0),
+ mShaderType(shaderType),
mStructureHLSL(structureHLSL),
mOutputType(outputType),
mUniforms(uniforms)
-{}
+{
+}
void UniformHLSL::reserveUniformRegisters(unsigned int registerCount)
{
mUniformRegister = registerCount;
}
-void UniformHLSL::reserveInterfaceBlockRegisters(unsigned int registerCount)
+void UniformHLSL::reserveUniformBlockRegisters(unsigned int registerCount)
{
- mInterfaceBlockRegister = registerCount;
+ mUniformBlockRegister = registerCount;
}
const Uniform *UniformHLSL::findUniformByName(const TString &name) const
@@ -89,46 +128,78 @@ const Uniform *UniformHLSL::findUniformByName(const TString &name) const
}
}
- UNREACHABLE();
- return NULL;
+ return nullptr;
}
-unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type,
- const TString &name,
- unsigned int *registerCount)
+unsigned int UniformHLSL::assignUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount)
{
- unsigned int registerIndex = (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister);
-
+ unsigned int registerIndex;
const Uniform *uniform = findUniformByName(name);
ASSERT(uniform);
+ if (IsSampler(type.getBasicType()) ||
+ (IsImage(type.getBasicType()) && type.getMemoryQualifier().readonly))
+ {
+ registerIndex = mTextureRegister;
+ }
+ else if (IsImage(type.getBasicType()))
+ {
+ registerIndex = mRWTextureRegister;
+ }
+ else
+ {
+ registerIndex = mUniformRegister;
+ }
+
mUniformRegisterMap[uniform->name] = registerIndex;
- ASSERT(registerCount);
- *registerCount = HLSLVariableRegisterCount(*uniform, mOutputType);
+ unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType);
- if (gl::IsSamplerType(uniform->type))
+ if (IsSampler(type.getBasicType()) ||
+ (IsImage(type.getBasicType()) && type.getMemoryQualifier().readonly))
{
- mSamplerRegister += *registerCount;
+ mTextureRegister += registerCount;
+ }
+ else if (IsImage(type.getBasicType()))
+ {
+ mRWTextureRegister += registerCount;
}
else
{
- mUniformRegister += *registerCount;
+ mUniformRegister += registerCount;
+ }
+ if (outRegisterCount)
+ {
+ *outRegisterCount = registerCount;
}
-
return registerIndex;
}
-unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, const TString &name)
+unsigned int UniformHLSL::assignSamplerInStructUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount)
{
- unsigned int registerCount;
- return declareUniformAndAssignRegister(type, name, &registerCount);
+ // Sampler that is a field of a uniform structure.
+ ASSERT(IsSampler(type.getBasicType()));
+ unsigned int registerIndex = mTextureRegister;
+ mUniformRegisterMap[std::string(name.c_str())] = registerIndex;
+ unsigned int registerCount = type.isArray() ? type.getArraySizeProduct() : 1u;
+ mTextureRegister += registerCount;
+ if (outRegisterCount)
+ {
+ *outRegisterCount = registerCount;
+ }
+ return registerIndex;
}
-void UniformHLSL::outputHLSLSamplerUniformGroup(TInfoSinkBase &out,
- const HLSLTextureSamplerGroup textureGroup,
- const TVector<const TIntermSymbol *> &group,
- unsigned int *groupTextureRegisterIndex)
+void UniformHLSL::outputHLSLSamplerUniformGroup(
+ TInfoSinkBase &out,
+ const HLSLTextureGroup textureGroup,
+ const TVector<const TIntermSymbol *> &group,
+ const TMap<const TIntermSymbol *, TString> &samplerInStructSymbolsToAPINames,
+ unsigned int *groupTextureRegisterIndex)
{
if (group.empty())
{
@@ -140,24 +211,33 @@ void UniformHLSL::outputHLSLSamplerUniformGroup(TInfoSinkBase &out,
const TType &type = uniform->getType();
const TString &name = uniform->getSymbol();
unsigned int registerCount;
- unsigned int samplerArrayIndex =
- declareUniformAndAssignRegister(type, name, &registerCount);
+
+ // The uniform might be just a regular sampler or one extracted from a struct.
+ unsigned int samplerArrayIndex = 0u;
+ const Uniform *uniformByName = findUniformByName(name);
+ if (uniformByName)
+ {
+ samplerArrayIndex = assignUniformRegister(type, name, &registerCount);
+ }
+ else
+ {
+ ASSERT(samplerInStructSymbolsToAPINames.find(uniform) !=
+ samplerInStructSymbolsToAPINames.end());
+ samplerArrayIndex = assignSamplerInStructUniformRegister(
+ type, samplerInStructSymbolsToAPINames.at(uniform), &registerCount);
+ }
groupRegisterCount += registerCount;
+
if (type.isArray())
{
- out << "static const uint " << DecorateIfNeeded(uniform->getName()) << ArrayString(type)
- << " = {";
- for (int i = 0; i < type.getArraySize(); ++i)
- {
- if (i > 0)
- out << ", ";
- out << (samplerArrayIndex + i);
- }
- out << "};\n";
+ out << "static const uint " << DecorateVariableIfNeeded(uniform->getName())
+ << ArrayString(type) << " = ";
+ OutputSamplerIndexArrayInitializer(out, type, samplerArrayIndex);
+ out << ";\n";
}
else
{
- out << "static const uint " << DecorateIfNeeded(uniform->getName()) << " = "
+ out << "static const uint " << DecorateVariableIfNeeded(uniform->getName()) << " = "
<< samplerArrayIndex << ";\n";
}
}
@@ -179,9 +259,88 @@ void UniformHLSL::outputHLSLSamplerUniformGroup(TInfoSinkBase &out,
*groupTextureRegisterIndex += groupRegisterCount;
}
+void UniformHLSL::outputHLSL4_0_FL9_3Sampler(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex)
+{
+ out << "uniform " << SamplerString(type.getBasicType()) << " sampler_"
+ << DecorateVariableIfNeeded(name) << ArrayString(type) << " : register(s"
+ << str(registerIndex) << ");\n";
+ out << "uniform " << TextureString(type.getBasicType()) << " texture_"
+ << DecorateVariableIfNeeded(name) << ArrayString(type) << " : register(t"
+ << str(registerIndex) << ");\n";
+}
+
+void UniformHLSL::outputHLSL4_1_FL11Texture(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex)
+{
+ // TODO(xinghua.cao@intel.com): if image2D variable is bound on one layer of Texture3D or
+ // Texture2DArray. Translate this variable to HLSL Texture3D object or HLSL Texture2DArray
+ // object, or create a temporary Texture2D to save content of the layer and bind the
+ // temporary Texture2D to image2D variable.
+ out << "uniform "
+ << TextureString(type.getBasicType(), type.getLayoutQualifier().imageInternalFormat) << " "
+ << DecorateVariableIfNeeded(name) << ArrayString(type) << " : register(t"
+ << str(registerIndex) << ");\n";
+ return;
+}
+
+void UniformHLSL::outputHLSL4_1_FL11RWTexture(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex)
+{
+ // TODO(xinghua.cao@intel.com): if image2D variable is bound on one layer of Texture3D or
+ // Texture2DArray. Translate this variable to HLSL RWTexture3D object or HLSL RWTexture2DArray
+ // object, or create a temporary Texture2D to save content of the layer and bind the
+ // temporary Texture2D to image2D variable.
+ if (mShaderType == GL_COMPUTE_SHADER)
+ {
+ out << "uniform "
+ << RWTextureString(type.getBasicType(), type.getLayoutQualifier().imageInternalFormat)
+ << " " << DecorateVariableIfNeeded(name) << ArrayString(type) << " : register(u"
+ << str(registerIndex) << ");\n";
+ }
+ else
+ {
+ // TODO(xinghua.cao@intel.com): Support images in vertex shader and fragment shader,
+ // which are needed to sync binding value when linking program.
+ }
+ return;
+}
+
+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 &registerString =
+ TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")";
+
+ out << "uniform " << typeName << " ";
+
+ out << DecorateVariableIfNeeded(name);
+
+ out << ArrayString(type) << " : " << registerString << ";\n";
+}
+
void UniformHLSL::uniformsHeader(TInfoSinkBase &out,
ShShaderOutput outputType,
- const ReferencedSymbols &referencedUniforms)
+ const ReferencedSymbols &referencedUniforms,
+ TSymbolTable *symbolTable)
{
if (!referencedUniforms.empty())
{
@@ -190,45 +349,69 @@ void UniformHLSL::uniformsHeader(TInfoSinkBase &out,
// In the case of HLSL 4, sampler uniforms need to be grouped by type before the code is
// written. They are grouped based on the combination of the HLSL texture type and
// HLSL sampler type, enumerated in HLSLTextureSamplerGroup.
- TVector<TVector<const TIntermSymbol *>> groupedSamplerUniforms;
- groupedSamplerUniforms.resize(HLSL_TEXTURE_MAX + 1);
+ TVector<TVector<const TIntermSymbol *>> groupedSamplerUniforms(HLSL_TEXTURE_MAX + 1);
+ TMap<const TIntermSymbol *, TString> samplerInStructSymbolsToAPINames;
+ TVector<const TIntermSymbol *> imageUniformsHLSL41Output;
for (auto &uniformIt : referencedUniforms)
{
// Output regular uniforms. Group sampler uniforms by type.
const TIntermSymbol &uniform = *uniformIt.second;
- const TType &type = uniform.getType();
- const TString &name = uniform.getSymbol();
+ const TType &type = uniform.getType();
+ const TName &name = uniform.getName();
if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType()))
{
- HLSLTextureSamplerGroup group = TextureGroup(type.getBasicType());
+ HLSLTextureGroup group = TextureGroup(type.getBasicType());
groupedSamplerUniforms[group].push_back(&uniform);
}
else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType()))
{
- unsigned int registerIndex = declareUniformAndAssignRegister(type, name);
- out << "uniform " << SamplerString(type.getBasicType()) << " sampler_"
- << DecorateUniform(name, type) << ArrayString(type) << " : register(s"
- << str(registerIndex) << ");\n";
- out << "uniform " << TextureString(type.getBasicType()) << " texture_"
- << DecorateUniform(name, type) << ArrayString(type) << " : register(t"
- << str(registerIndex) << ");\n";
+ unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr);
+ outputHLSL4_0_FL9_3Sampler(out, type, name, registerIndex);
+ }
+ else if (outputType == SH_HLSL_4_1_OUTPUT && IsImage(type.getBasicType()))
+ {
+ imageUniformsHLSL41Output.push_back(&uniform);
}
else
{
- unsigned int registerIndex = declareUniformAndAssignRegister(type, name);
- 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 &registerString = TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")";
-
- out << "uniform " << typeName << " " << DecorateUniform(name, type) << ArrayString(type)
- << " : " << registerString << ";\n";
+ if (type.isStructureContainingSamplers())
+ {
+ TVector<TIntermSymbol *> samplerSymbols;
+ TMap<TIntermSymbol *, TString> symbolsToAPINames;
+ type.createSamplerSymbols("angle_" + name.getString(), name.getString(),
+ &samplerSymbols, &symbolsToAPINames, symbolTable);
+ for (TIntermSymbol *sampler : samplerSymbols)
+ {
+ const TType &samplerType = sampler->getType();
+
+ // Will use angle_ prefix instead of regular prefix.
+ sampler->setInternal(true);
+ const TName &samplerName = sampler->getName();
+
+ if (outputType == SH_HLSL_4_1_OUTPUT)
+ {
+ HLSLTextureGroup group = TextureGroup(samplerType.getBasicType());
+ groupedSamplerUniforms[group].push_back(sampler);
+ samplerInStructSymbolsToAPINames[sampler] = symbolsToAPINames[sampler];
+ }
+ else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ unsigned int registerIndex = assignSamplerInStructUniformRegister(
+ samplerType, symbolsToAPINames[sampler], nullptr);
+ outputHLSL4_0_FL9_3Sampler(out, samplerType, samplerName, registerIndex);
+ }
+ else
+ {
+ ASSERT(outputType == SH_HLSL_3_0_OUTPUT);
+ unsigned int registerIndex = assignSamplerInStructUniformRegister(
+ samplerType, symbolsToAPINames[sampler], nullptr);
+ outputUniform(out, samplerType, samplerName, registerIndex);
+ }
+ }
+ }
+ unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr);
+ outputUniform(out, type, name, registerIndex);
}
}
@@ -239,70 +422,114 @@ void UniformHLSL::uniformsHeader(TInfoSinkBase &out,
ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D);
for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId)
{
- outputHLSLSamplerUniformGroup(out, HLSLTextureSamplerGroup(groupId),
- groupedSamplerUniforms[groupId],
- &groupTextureRegisterIndex);
+ outputHLSLSamplerUniformGroup(
+ out, HLSLTextureGroup(groupId), groupedSamplerUniforms[groupId],
+ samplerInStructSymbolsToAPINames, &groupTextureRegisterIndex);
+ }
+ mSamplerCount = groupTextureRegisterIndex;
+
+ for (const TIntermSymbol *image : imageUniformsHLSL41Output)
+ {
+ const TType &type = image->getType();
+ const TName &name = image->getName();
+ unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr);
+ if (type.getMemoryQualifier().readonly)
+ {
+ outputHLSL4_1_FL11Texture(out, type, name, registerIndex);
+ }
+ else
+ {
+ outputHLSL4_1_FL11RWTexture(out, type, name, registerIndex);
+ }
}
}
}
-TString UniformHLSL::interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks)
+void UniformHLSL::samplerMetadataUniforms(TInfoSinkBase &out, const char *reg)
+{
+ // If mSamplerCount is 0 the shader doesn't use any textures for samplers.
+ if (mSamplerCount > 0)
+ {
+ out << " struct SamplerMetadata\n"
+ " {\n"
+ " int baseLevel;\n"
+ " int internalFormatBits;\n"
+ " int wrapModes;\n"
+ " int padding;\n"
+ " };\n"
+ " SamplerMetadata samplerMetadata["
+ << mSamplerCount << "] : packoffset(" << reg << ");\n";
+ }
+}
+
+TString UniformHLSL::uniformBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks)
{
TString interfaceBlocks;
for (ReferencedSymbols::const_iterator interfaceBlockIt = referencedInterfaceBlocks.begin();
interfaceBlockIt != referencedInterfaceBlocks.end(); interfaceBlockIt++)
{
- const TType &nodeType = interfaceBlockIt->second->getType();
+ const TType &nodeType = interfaceBlockIt->second->getType();
const TInterfaceBlock &interfaceBlock = *nodeType.getInterfaceBlock();
- unsigned int arraySize = static_cast<unsigned int>(interfaceBlock.arraySize());
- unsigned int activeRegister = mInterfaceBlockRegister;
+ // nodeType.isInterfaceBlock() == false means the node is a field of a uniform block which
+ // doesn't have instance name, so this block cannot be an array.
+ unsigned int interfaceBlockArraySize = 0u;
+ if (nodeType.isInterfaceBlock() && nodeType.isArray())
+ {
+ interfaceBlockArraySize = nodeType.getOutermostArraySize();
+ }
+ unsigned int activeRegister = mUniformBlockRegister;
- mInterfaceBlockRegisterMap[interfaceBlock.name().c_str()] = activeRegister;
- mInterfaceBlockRegister += std::max(1u, arraySize);
+ mUniformBlockRegisterMap[interfaceBlock.name().c_str()] = activeRegister;
+ mUniformBlockRegister += std::max(1u, interfaceBlockArraySize);
// FIXME: interface block field names
if (interfaceBlock.hasInstanceName())
{
- interfaceBlocks += interfaceBlockStructString(interfaceBlock);
+ interfaceBlocks += uniformBlockStructString(interfaceBlock);
}
- if (arraySize > 0)
+ if (interfaceBlockArraySize > 0)
{
- for (unsigned int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
+ for (unsigned int arrayIndex = 0; arrayIndex < interfaceBlockArraySize; arrayIndex++)
{
- interfaceBlocks += interfaceBlockString(interfaceBlock, activeRegister + arrayIndex, arrayIndex);
+ interfaceBlocks +=
+ uniformBlockString(interfaceBlock, activeRegister + arrayIndex, arrayIndex);
}
}
else
{
- interfaceBlocks += interfaceBlockString(interfaceBlock, activeRegister, GL_INVALID_INDEX);
+ interfaceBlocks += uniformBlockString(interfaceBlock, activeRegister, GL_INVALID_INDEX);
}
}
- return (interfaceBlocks.empty() ? "" : ("// Interface Blocks\n\n" + interfaceBlocks));
+ return (interfaceBlocks.empty() ? "" : ("// Uniform Blocks\n\n" + interfaceBlocks));
}
-TString UniformHLSL::interfaceBlockString(const TInterfaceBlock &interfaceBlock, unsigned int registerIndex, unsigned int arrayIndex)
+TString UniformHLSL::uniformBlockString(const TInterfaceBlock &interfaceBlock,
+ unsigned int registerIndex,
+ unsigned int arrayIndex)
{
- const TString &arrayIndexString = (arrayIndex != GL_INVALID_INDEX ? Decorate(str(arrayIndex)) : "");
+ const TString &arrayIndexString =
+ (arrayIndex != GL_INVALID_INDEX ? Decorate(str(arrayIndex)) : "");
const TString &blockName = interfaceBlock.name() + arrayIndexString;
TString hlsl;
- hlsl += "cbuffer " + blockName + " : register(b" + str(registerIndex) + ")\n"
+ hlsl += "cbuffer " + blockName + " : register(b" + str(registerIndex) +
+ ")\n"
"{\n";
if (interfaceBlock.hasInstanceName())
{
hlsl += " " + InterfaceBlockStructName(interfaceBlock) + " " +
- interfaceBlockInstanceString(interfaceBlock, arrayIndex) + ";\n";
+ uniformBlockInstanceString(interfaceBlock, arrayIndex) + ";\n";
}
else
{
const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
- hlsl += interfaceBlockMembersString(interfaceBlock, blockStorage);
+ hlsl += uniformBlockMembersString(interfaceBlock, blockStorage);
}
hlsl += "};\n\n";
@@ -310,13 +537,14 @@ TString UniformHLSL::interfaceBlockString(const TInterfaceBlock &interfaceBlock,
return hlsl;
}
-TString UniformHLSL::interfaceBlockInstanceString(const TInterfaceBlock& interfaceBlock, unsigned int arrayIndex)
+TString UniformHLSL::uniformBlockInstanceString(const TInterfaceBlock &interfaceBlock,
+ unsigned int arrayIndex)
{
if (!interfaceBlock.hasInstanceName())
{
return "";
}
- else if (interfaceBlock.isArray())
+ else if (arrayIndex != GL_INVALID_INDEX)
{
return DecoratePrivate(interfaceBlock.instanceName()) + "_" + str(arrayIndex);
}
@@ -326,7 +554,8 @@ TString UniformHLSL::interfaceBlockInstanceString(const TInterfaceBlock& interfa
}
}
-TString UniformHLSL::interfaceBlockMembersString(const TInterfaceBlock &interfaceBlock, TLayoutBlockStorage blockStorage)
+TString UniformHLSL::uniformBlockMembersString(const TInterfaceBlock &interfaceBlock,
+ TLayoutBlockStorage blockStorage)
{
TString hlsl;
@@ -334,7 +563,7 @@ TString UniformHLSL::interfaceBlockMembersString(const TInterfaceBlock &interfac
for (unsigned int typeIndex = 0; typeIndex < interfaceBlock.fields().size(); typeIndex++)
{
- const TField &field = *interfaceBlock.fields()[typeIndex];
+ const TField &field = *interfaceBlock.fields()[typeIndex];
const TType &fieldType = *field.type();
if (blockStorage == EbsStd140)
@@ -343,13 +572,15 @@ TString UniformHLSL::interfaceBlockMembersString(const TInterfaceBlock &interfac
hlsl += padHelper.prePaddingString(fieldType);
}
- hlsl += " " + InterfaceBlockFieldTypeString(field, blockStorage) +
- " " + Decorate(field.name()) + ArrayString(fieldType) + ";\n";
+ hlsl += " " + InterfaceBlockFieldTypeString(field, blockStorage) + " " +
+ Decorate(field.name()) + ArrayString(fieldType) + ";\n";
- // must pad out after matrices and arrays, where HLSL usually allows itself room to pack stuff
+ // must pad out after matrices and arrays, where HLSL usually allows itself room to pack
+ // stuff
if (blockStorage == EbsStd140)
{
- const bool useHLSLRowMajorPacking = (fieldType.getLayoutQualifier().matrixPacking == EmpColumnMajor);
+ const bool useHLSLRowMajorPacking =
+ (fieldType.getLayoutQualifier().matrixPacking == EmpColumnMajor);
hlsl += padHelper.postPaddingString(fieldType, useHLSLRowMajorPacking);
}
}
@@ -357,14 +588,13 @@ TString UniformHLSL::interfaceBlockMembersString(const TInterfaceBlock &interfac
return hlsl;
}
-TString UniformHLSL::interfaceBlockStructString(const TInterfaceBlock &interfaceBlock)
+TString UniformHLSL::uniformBlockStructString(const TInterfaceBlock &interfaceBlock)
{
const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
- return "struct " + InterfaceBlockStructName(interfaceBlock) + "\n"
+ return "struct " + InterfaceBlockStructName(interfaceBlock) +
+ "\n"
"{\n" +
- interfaceBlockMembersString(interfaceBlock, blockStorage) +
- "};\n\n";
+ uniformBlockMembersString(interfaceBlock, blockStorage) + "};\n\n";
}
-
}
diff --git a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h
index 0f51f349bb..8784e50533 100644
--- a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
// UniformHLSL.h:
-// Methods for GLSL to HLSL translation for uniforms and interface blocks.
+// Methods for GLSL to HLSL translation for uniforms and uniform blocks.
//
#ifndef COMPILER_TRANSLATOR_UNIFORMHLSL_H_
@@ -16,29 +16,35 @@
namespace sh
{
class StructureHLSL;
+class TSymbolTable;
class UniformHLSL : angle::NonCopyable
{
public:
- UniformHLSL(StructureHLSL *structureHLSL, ShShaderOutput outputType, const std::vector<Uniform> &uniforms);
+ UniformHLSL(sh::GLenum shaderType,
+ StructureHLSL *structureHLSL,
+ ShShaderOutput outputType,
+ const std::vector<Uniform> &uniforms);
void reserveUniformRegisters(unsigned int registerCount);
- void reserveInterfaceBlockRegisters(unsigned int registerCount);
- void outputHLSLSamplerUniformGroup(TInfoSinkBase &out,
- const HLSLTextureSamplerGroup textureGroup,
- const TVector<const TIntermSymbol *> &group,
- unsigned int *groupTextureRegisterIndex);
+ void reserveUniformBlockRegisters(unsigned int registerCount);
void uniformsHeader(TInfoSinkBase &out,
ShShaderOutput outputType,
- const ReferencedSymbols &referencedUniforms);
- TString interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks);
+ const ReferencedSymbols &referencedUniforms,
+ TSymbolTable *symbolTable);
+
+ // Must be called after uniformsHeader
+ void samplerMetadataUniforms(TInfoSinkBase &out, const char *reg);
+
+ TString uniformBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks);
// Used for direct index references
- static TString interfaceBlockInstanceString(const TInterfaceBlock& interfaceBlock, unsigned int arrayIndex);
+ static TString uniformBlockInstanceString(const TInterfaceBlock &interfaceBlock,
+ unsigned int arrayIndex);
- const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const
+ const std::map<std::string, unsigned int> &getUniformBlockRegisterMap() const
{
- return mInterfaceBlockRegisterMap;
+ return mUniformBlockRegisterMap;
}
const std::map<std::string, unsigned int> &getUniformRegisterMap() const
{
@@ -46,28 +52,59 @@ class UniformHLSL : angle::NonCopyable
}
private:
- TString interfaceBlockString(const TInterfaceBlock &interfaceBlock, unsigned int registerIndex, unsigned int arrayIndex);
- TString interfaceBlockMembersString(const TInterfaceBlock &interfaceBlock, TLayoutBlockStorage blockStorage);
- TString interfaceBlockStructString(const TInterfaceBlock &interfaceBlock);
+ TString uniformBlockString(const TInterfaceBlock &interfaceBlock,
+ unsigned int registerIndex,
+ unsigned int arrayIndex);
+ TString uniformBlockMembersString(const TInterfaceBlock &interfaceBlock,
+ TLayoutBlockStorage blockStorage);
+ TString uniformBlockStructString(const TInterfaceBlock &interfaceBlock);
const Uniform *findUniformByName(const TString &name) const;
+ void outputHLSL4_0_FL9_3Sampler(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex);
+ void outputHLSL4_1_FL11Texture(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex);
+ void outputHLSL4_1_FL11RWTexture(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex);
+ void outputUniform(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex);
+
// Returns the uniform's register index
- unsigned int declareUniformAndAssignRegister(const TType &type,
- const TString &name,
- unsigned int *registerCount);
- unsigned int declareUniformAndAssignRegister(const TType &type, const TString &name);
+ unsigned int assignUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount);
+ unsigned int assignSamplerInStructUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount);
+
+ void outputHLSLSamplerUniformGroup(
+ TInfoSinkBase &out,
+ const HLSLTextureGroup textureGroup,
+ const TVector<const TIntermSymbol *> &group,
+ const TMap<const TIntermSymbol *, TString> &samplerInStructSymbolsToAPINames,
+ unsigned int *groupTextureRegisterIndex);
unsigned int mUniformRegister;
- unsigned int mInterfaceBlockRegister;
- unsigned int mSamplerRegister;
+ unsigned int mUniformBlockRegister;
+ unsigned int mTextureRegister;
+ unsigned int mRWTextureRegister;
+ unsigned int mSamplerCount;
+ sh::GLenum mShaderType;
StructureHLSL *mStructureHLSL;
ShShaderOutput mOutputType;
const std::vector<Uniform> &mUniforms;
- std::map<std::string, unsigned int> mInterfaceBlockRegisterMap;
+ std::map<std::string, unsigned int> mUniformBlockRegisterMap;
std::map<std::string, unsigned int> mUniformRegisterMap;
};
-
}
-#endif // COMPILER_TRANSLATOR_UNIFORMHLSL_H_
+#endif // COMPILER_TRANSLATOR_UNIFORMHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.cpp b/src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.cpp
new file mode 100644
index 0000000000..40bd42afad
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.cpp
@@ -0,0 +1,105 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// UseInterfaceBlockFields.cpp: insert statements to reference all members in InterfaceBlock list at
+// the beginning of main. This is to work around a Mac driver that treats unused standard/shared
+// uniform blocks as inactive.
+
+#include "compiler/translator/UseInterfaceBlockFields.h"
+
+#include "compiler/translator/FindMain.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void AddNodeUseStatements(TIntermTyped *node, TIntermSequence *sequence)
+{
+ if (node->isArray())
+ {
+ for (unsigned int i = 0u; i < node->getOutermostArraySize(); ++i)
+ {
+ TIntermBinary *element =
+ new TIntermBinary(EOpIndexDirect, node->deepCopy(), CreateIndexNode(i));
+ AddNodeUseStatements(element, sequence);
+ }
+ }
+ else
+ {
+ sequence->insert(sequence->begin(), node);
+ }
+}
+
+void AddFieldUseStatements(const ShaderVariable &var,
+ TIntermSequence *sequence,
+ const TSymbolTable &symbolTable)
+{
+ TString name = TString(var.name.c_str());
+ ASSERT(name.find_last_of('[') == TString::npos);
+ TIntermSymbol *symbol = ReferenceGlobalVariable(name, symbolTable);
+ AddNodeUseStatements(symbol, sequence);
+}
+
+void InsertUseCode(const InterfaceBlock &block, TIntermTyped *blockNode, TIntermSequence *sequence)
+{
+ for (unsigned int i = 0; i < block.fields.size(); ++i)
+ {
+ TIntermBinary *element = new TIntermBinary(EOpIndexDirectInterfaceBlock,
+ blockNode->deepCopy(), CreateIndexNode(i));
+ sequence->insert(sequence->begin(), element);
+ }
+}
+
+void InsertUseCode(TIntermSequence *sequence,
+ const InterfaceBlockList &blocks,
+ const TSymbolTable &symbolTable)
+{
+ for (const auto &block : blocks)
+ {
+ if (block.instanceName.empty())
+ {
+ for (const auto &var : block.fields)
+ {
+ AddFieldUseStatements(var, sequence, symbolTable);
+ }
+ }
+ else if (block.arraySize > 0u)
+ {
+ TString name(block.instanceName.c_str());
+ TIntermSymbol *arraySymbol = ReferenceGlobalVariable(name, symbolTable);
+ for (unsigned int i = 0u; i < block.arraySize; ++i)
+ {
+ TIntermBinary *elementSymbol =
+ new TIntermBinary(EOpIndexDirect, arraySymbol->deepCopy(), CreateIndexNode(i));
+ InsertUseCode(block, elementSymbol, sequence);
+ }
+ }
+ else
+ {
+ TString name(block.instanceName.c_str());
+ TIntermSymbol *blockSymbol = ReferenceGlobalVariable(name, symbolTable);
+ InsertUseCode(block, blockSymbol, sequence);
+ }
+ }
+}
+
+} // namespace anonymous
+
+void UseInterfaceBlockFields(TIntermBlock *root,
+ const InterfaceBlockList &blocks,
+ const TSymbolTable &symbolTable)
+{
+ TIntermBlock *mainBody = FindMainBody(root);
+ InsertUseCode(mainBody->getSequence(), blocks, symbolTable);
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.h b/src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.h
new file mode 100644
index 0000000000..3e2a4815d4
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.h
@@ -0,0 +1,30 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// UseInterfaceBlockFields.h: insert statements to reference all members in InterfaceBlock list at
+// the beginning of main. This is to work around a Mac driver that treats unused standard/shared
+// uniform blocks as inactive.
+
+#ifndef COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
+#define COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
+
+#include <GLSLANG/ShaderLang.h>
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+using InterfaceBlockList = std::vector<sh::InterfaceBlock>;
+
+void UseInterfaceBlockFields(TIntermBlock *root,
+ const InterfaceBlockList &blocks,
+ const TSymbolTable &symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp
index 404ccee75d..8a77f0049a 100644
--- a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp
@@ -27,7 +27,7 @@ TString SamplerString(const TBasicType type)
}
}
-TString SamplerString(HLSLTextureSamplerGroup type)
+TString SamplerString(HLSLTextureGroup type)
{
if (type >= HLSL_COMPARISON_SAMPLER_GROUP_BEGIN && type <= HLSL_COMPARISON_SAMPLER_GROUP_END)
{
@@ -39,7 +39,8 @@ TString SamplerString(HLSLTextureSamplerGroup type)
}
}
-HLSLTextureSamplerGroup TextureGroup(const TBasicType type)
+HLSLTextureGroup TextureGroup(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+
{
switch (type)
{
@@ -53,6 +54,8 @@ HLSLTextureSamplerGroup TextureGroup(const TBasicType type)
return HLSL_TEXTURE_2D_ARRAY;
case EbtSampler3D:
return HLSL_TEXTURE_3D;
+ case EbtSampler2DMS:
+ return HLSL_TEXTURE_2D_MS;
case EbtISampler2D:
return HLSL_TEXTURE_2D_INT4;
case EbtISampler3D:
@@ -61,6 +64,8 @@ HLSLTextureSamplerGroup TextureGroup(const TBasicType type)
return HLSL_TEXTURE_2D_ARRAY_INT4;
case EbtISampler2DArray:
return HLSL_TEXTURE_2D_ARRAY_INT4;
+ case EbtISampler2DMS:
+ return HLSL_TEXTURE_2D_MS_INT4;
case EbtUSampler2D:
return HLSL_TEXTURE_2D_UINT4;
case EbtUSampler3D:
@@ -69,42 +74,196 @@ HLSLTextureSamplerGroup TextureGroup(const TBasicType type)
return HLSL_TEXTURE_2D_ARRAY_UINT4;
case EbtUSampler2DArray:
return HLSL_TEXTURE_2D_ARRAY_UINT4;
+ case EbtUSampler2DMS:
+ return HLSL_TEXTURE_2D_MS_UINT4;
case EbtSampler2DShadow:
return HLSL_TEXTURE_2D_COMPARISON;
case EbtSamplerCubeShadow:
return HLSL_TEXTURE_CUBE_COMPARISON;
case EbtSampler2DArrayShadow:
return HLSL_TEXTURE_2D_ARRAY_COMPARISON;
+ case EbtImage2D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return HLSL_TEXTURE_2D;
+ case EiifRGBA8:
+ return HLSL_TEXTURE_2D_UNORM;
+ case EiifRGBA8_SNORM:
+ return HLSL_TEXTURE_2D_SNORM;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImage2D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return HLSL_TEXTURE_2D_INT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImage2D:
+ {
+ switch (imageInternalFormat)
+ {
+
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return HLSL_TEXTURE_2D_UINT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtImage3D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return HLSL_TEXTURE_3D;
+ case EiifRGBA8:
+ return HLSL_TEXTURE_3D_UNORM;
+ case EiifRGBA8_SNORM:
+ return HLSL_TEXTURE_3D_SNORM;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImage3D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return HLSL_TEXTURE_3D_INT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImage3D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return HLSL_TEXTURE_3D_UINT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtImage2DArray:
+ case EbtImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return HLSL_TEXTURE_2D_ARRAY;
+ case EiifRGBA8:
+ return HLSL_TEXTURE_2D_ARRAY_UNORN;
+ case EiifRGBA8_SNORM:
+ return HLSL_TEXTURE_2D_ARRAY_SNORM;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImage2DArray:
+ case EbtIImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return HLSL_TEXTURE_2D_ARRAY_INT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImage2DArray:
+ case EbtUImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return HLSL_TEXTURE_2D_ARRAY_UINT4;
+ default:
+ UNREACHABLE();
+ }
+ }
default:
UNREACHABLE();
}
return HLSL_TEXTURE_UNKNOWN;
}
-TString TextureString(const HLSLTextureSamplerGroup type)
+TString TextureString(const HLSLTextureGroup textureGroup)
{
- switch (type)
+ switch (textureGroup)
{
case HLSL_TEXTURE_2D:
- return "Texture2D";
+ return "Texture2D<float4>";
case HLSL_TEXTURE_CUBE:
- return "TextureCube";
+ return "TextureCube<float4>";
case HLSL_TEXTURE_2D_ARRAY:
- return "Texture2DArray";
+ return "Texture2DArray<float4>";
case HLSL_TEXTURE_3D:
- return "Texture3D";
+ return "Texture3D<float4>";
+ case HLSL_TEXTURE_2D_UNORM:
+ return "Texture2D<unorm float4>";
+ case HLSL_TEXTURE_CUBE_UNORM:
+ return "TextureCube<unorm float4>";
+ case HLSL_TEXTURE_2D_ARRAY_UNORN:
+ return "Texture2DArray<unorm float4>";
+ case HLSL_TEXTURE_3D_UNORM:
+ return "Texture3D<unorm float4>";
+ case HLSL_TEXTURE_2D_SNORM:
+ return "Texture2D<snorm float4>";
+ case HLSL_TEXTURE_CUBE_SNORM:
+ return "TextureCube<snorm float4>";
+ case HLSL_TEXTURE_2D_ARRAY_SNORM:
+ return "Texture2DArray<snorm float4>";
+ case HLSL_TEXTURE_3D_SNORM:
+ return "Texture3D<snorm float4>";
+ case HLSL_TEXTURE_2D_MS:
+ return "Texture2DMS<float4>";
case HLSL_TEXTURE_2D_INT4:
return "Texture2D<int4>";
case HLSL_TEXTURE_3D_INT4:
return "Texture3D<int4>";
case HLSL_TEXTURE_2D_ARRAY_INT4:
return "Texture2DArray<int4>";
+ case HLSL_TEXTURE_2D_MS_INT4:
+ return "Texture2DMS<int4>";
case HLSL_TEXTURE_2D_UINT4:
return "Texture2D<uint4>";
case HLSL_TEXTURE_3D_UINT4:
return "Texture3D<uint4>";
case HLSL_TEXTURE_2D_ARRAY_UINT4:
return "Texture2DArray<uint4>";
+ case HLSL_TEXTURE_2D_MS_UINT4:
+ return "Texture2DMS<uint4>";
case HLSL_TEXTURE_2D_COMPARISON:
return "Texture2D";
case HLSL_TEXTURE_CUBE_COMPARISON:
@@ -115,15 +274,15 @@ TString TextureString(const HLSLTextureSamplerGroup type)
UNREACHABLE();
}
- return "<unknown texture type>";
+ return "<unknown read texture type>";
}
-TString TextureString(const TBasicType type)
+TString TextureString(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
{
- return TextureString(TextureGroup(type));
+ return TextureString(TextureGroup(type, imageInternalFormat));
}
-TString TextureGroupSuffix(const HLSLTextureSamplerGroup type)
+TString TextureGroupSuffix(const HLSLTextureGroup type)
{
switch (type)
{
@@ -135,18 +294,40 @@ TString TextureGroupSuffix(const HLSLTextureSamplerGroup type)
return "2DArray";
case HLSL_TEXTURE_3D:
return "3D";
+ case HLSL_TEXTURE_2D_UNORM:
+ return "2D_unorm_float4_";
+ case HLSL_TEXTURE_CUBE_UNORM:
+ return "Cube_unorm_float4_";
+ case HLSL_TEXTURE_2D_ARRAY_UNORN:
+ return "2DArray_unorm_float4_";
+ case HLSL_TEXTURE_3D_UNORM:
+ return "3D_unorm_float4_";
+ case HLSL_TEXTURE_2D_SNORM:
+ return "2D_snorm_float4_";
+ case HLSL_TEXTURE_CUBE_SNORM:
+ return "Cube_snorm_float4_";
+ case HLSL_TEXTURE_2D_ARRAY_SNORM:
+ return "2DArray_snorm_float4_";
+ case HLSL_TEXTURE_3D_SNORM:
+ return "3D_snorm_float4_";
+ case HLSL_TEXTURE_2D_MS:
+ return "2DMS";
case HLSL_TEXTURE_2D_INT4:
return "2D_int4_";
case HLSL_TEXTURE_3D_INT4:
return "3D_int4_";
case HLSL_TEXTURE_2D_ARRAY_INT4:
return "2DArray_int4_";
+ case HLSL_TEXTURE_2D_MS_INT4:
+ return "2DMS_int4_";
case HLSL_TEXTURE_2D_UINT4:
return "2D_uint4_";
case HLSL_TEXTURE_3D_UINT4:
return "3D_uint4_";
case HLSL_TEXTURE_2D_ARRAY_UINT4:
return "2DArray_uint4_";
+ case HLSL_TEXTURE_2D_MS_UINT4:
+ return "2DMS_uint4_";
case HLSL_TEXTURE_2D_COMPARISON:
return "2D_comparison";
case HLSL_TEXTURE_CUBE_COMPARISON:
@@ -160,12 +341,12 @@ TString TextureGroupSuffix(const HLSLTextureSamplerGroup type)
return "<unknown texture type>";
}
-TString TextureGroupSuffix(const TBasicType type)
+TString TextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
{
- return TextureGroupSuffix(TextureGroup(type));
+ return TextureGroupSuffix(TextureGroup(type, imageInternalFormat));
}
-TString TextureTypeSuffix(const TBasicType type)
+TString TextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
{
switch (type)
{
@@ -173,20 +354,340 @@ TString TextureTypeSuffix(const TBasicType type)
return "Cube_int4_";
case EbtUSamplerCube:
return "Cube_uint4_";
+ case EbtSamplerExternalOES:
+ return "_External";
+ case EbtImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return "Cube_float4_";
+ case EiifRGBA8:
+ return "Cube_unorm_float4_";
+ case EiifRGBA8_SNORM:
+ return "Cube_snorm_float4_";
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return "Cube_int4_";
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return "Cube_uint4_";
+ default:
+ UNREACHABLE();
+ }
+ }
default:
// All other types are identified by their group suffix
- return TextureGroupSuffix(type);
+ return TextureGroupSuffix(type, imageInternalFormat);
+ }
+}
+
+HLSLRWTextureGroup RWTextureGroup(const TBasicType type,
+ TLayoutImageInternalFormat imageInternalFormat)
+
+{
+ switch (type)
+ {
+ case EbtImage2D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return HLSL_RWTEXTURE_2D_FLOAT4;
+ case EiifRGBA8:
+ return HLSL_RWTEXTURE_2D_UNORM;
+ case EiifRGBA8_SNORM:
+ return HLSL_RWTEXTURE_2D_SNORM;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImage2D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return HLSL_RWTEXTURE_2D_INT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImage2D:
+ {
+ switch (imageInternalFormat)
+ {
+
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return HLSL_RWTEXTURE_2D_UINT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtImage3D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return HLSL_RWTEXTURE_3D_FLOAT4;
+ case EiifRGBA8:
+ return HLSL_RWTEXTURE_3D_UNORM;
+ case EiifRGBA8_SNORM:
+ return HLSL_RWTEXTURE_3D_SNORM;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImage3D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return HLSL_RWTEXTURE_3D_INT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImage3D:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return HLSL_RWTEXTURE_3D_UINT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtImage2DArray:
+ case EbtImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return HLSL_RWTEXTURE_2D_ARRAY_FLOAT4;
+ case EiifRGBA8:
+ return HLSL_RWTEXTURE_2D_ARRAY_UNORN;
+ case EiifRGBA8_SNORM:
+ return HLSL_RWTEXTURE_2D_ARRAY_SNORM;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImage2DArray:
+ case EbtIImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return HLSL_RWTEXTURE_2D_ARRAY_INT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImage2DArray:
+ case EbtUImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return HLSL_RWTEXTURE_2D_ARRAY_UINT4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ default:
+ UNREACHABLE();
}
+ return HLSL_RWTEXTURE_UNKNOWN;
}
-TString DecorateUniform(const TString &string, const TType &type)
+TString RWTextureString(const HLSLRWTextureGroup RWTextureGroup)
{
- if (type.getBasicType() == EbtSamplerExternalOES)
+ switch (RWTextureGroup)
{
- return "ex_" + string;
+ case HLSL_RWTEXTURE_2D_FLOAT4:
+ return "RWTexture2D<float4>";
+ case HLSL_RWTEXTURE_2D_ARRAY_FLOAT4:
+ return "RWTexture2DArray<float4>";
+ case HLSL_RWTEXTURE_3D_FLOAT4:
+ return "RWTexture3D<float4>";
+ case HLSL_RWTEXTURE_2D_UNORM:
+ return "RWTexture2D<unorm float4>";
+ case HLSL_RWTEXTURE_2D_ARRAY_UNORN:
+ return "RWTexture2DArray<unorm float4>";
+ case HLSL_RWTEXTURE_3D_UNORM:
+ return "RWTexture3D<unorm float4>";
+ case HLSL_RWTEXTURE_2D_SNORM:
+ return "RWTexture2D<snorm float4>";
+ case HLSL_RWTEXTURE_2D_ARRAY_SNORM:
+ return "RWTexture2DArray<snorm float4>";
+ case HLSL_RWTEXTURE_3D_SNORM:
+ return "RWTexture3D<snorm float4>";
+ case HLSL_RWTEXTURE_2D_UINT4:
+ return "RWTexture2D<uint4>";
+ case HLSL_RWTEXTURE_2D_ARRAY_UINT4:
+ return "RWTexture2DArray<uint4>";
+ case HLSL_RWTEXTURE_3D_UINT4:
+ return "RWTexture3D<uint4>";
+ case HLSL_RWTEXTURE_2D_INT4:
+ return "RWTexture2D<int4>";
+ case HLSL_RWTEXTURE_2D_ARRAY_INT4:
+ return "RWTexture2DArray<int4>";
+ case HLSL_RWTEXTURE_3D_INT4:
+ return "RWTexture3D<int4>";
+ default:
+ UNREACHABLE();
}
- return Decorate(string);
+ return "<unknown read and write texture type>";
+}
+
+TString RWTextureString(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+{
+ return RWTextureString(RWTextureGroup(type, imageInternalFormat));
+}
+
+TString RWTextureGroupSuffix(const HLSLRWTextureGroup type)
+{
+ switch (type)
+ {
+ case HLSL_RWTEXTURE_2D_FLOAT4:
+ return "RW2D_float4_";
+ case HLSL_RWTEXTURE_2D_ARRAY_FLOAT4:
+ return "RW2DArray_float4_";
+ case HLSL_RWTEXTURE_3D_FLOAT4:
+ return "RW3D_float4_";
+ case HLSL_RWTEXTURE_2D_UNORM:
+ return "RW2D_unorm_float4_";
+ case HLSL_RWTEXTURE_2D_ARRAY_UNORN:
+ return "RW2DArray_unorm_float4_";
+ case HLSL_RWTEXTURE_3D_UNORM:
+ return "RW3D_unorm_float4_";
+ case HLSL_RWTEXTURE_2D_SNORM:
+ return "RW2D_snorm_float4_";
+ case HLSL_RWTEXTURE_2D_ARRAY_SNORM:
+ return "RW2DArray_snorm_float4_";
+ case HLSL_RWTEXTURE_3D_SNORM:
+ return "RW3D_snorm_float4_";
+ case HLSL_RWTEXTURE_2D_UINT4:
+ return "RW2D_uint4_";
+ case HLSL_RWTEXTURE_2D_ARRAY_UINT4:
+ return "RW2DArray_uint4_";
+ case HLSL_RWTEXTURE_3D_UINT4:
+ return "RW3D_uint4_";
+ case HLSL_RWTEXTURE_2D_INT4:
+ return "RW2D_int4_";
+ case HLSL_RWTEXTURE_2D_ARRAY_INT4:
+ return "RW2DArray_int4_";
+ case HLSL_RWTEXTURE_3D_INT4:
+ return "RW3D_int4_";
+ default:
+ UNREACHABLE();
+ }
+
+ return "<unknown read and write resource>";
+}
+
+TString RWTextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+{
+ return RWTextureGroupSuffix(RWTextureGroup(type, imageInternalFormat));
+}
+
+TString RWTextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+{
+ switch (type)
+ {
+ case EbtImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ return "RWCube_float4_";
+ case EiifRGBA8:
+ return "RWCube_unorm_float4_";
+ case EiifRGBA8_SNORM:
+ return "RWCube_unorm_float4_";
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtIImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ return "RWCube_int4_";
+ default:
+ UNREACHABLE();
+ }
+ }
+ case EbtUImageCube:
+ {
+ switch (imageInternalFormat)
+ {
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ return "RWCube_uint4_";
+ default:
+ UNREACHABLE();
+ }
+ }
+ default:
+ // All other types are identified by their group suffix
+ return TextureGroupSuffix(type, imageInternalFormat);
+ }
}
TString DecorateField(const TString &string, const TStructure &structure)
@@ -214,10 +715,13 @@ TString Decorate(const TString &string)
return string;
}
-TString DecorateIfNeeded(const TName &name)
+TString DecorateVariableIfNeeded(const TName &name)
{
if (name.isInternal())
{
+ // The name should not have a prefix reserved for user-defined variables or functions.
+ ASSERT(name.getString().compare(0, 2, "f_") != 0);
+ ASSERT(name.getString().compare(0, 1, "_") != 0);
return name.getString();
}
else
@@ -230,25 +734,29 @@ TString DecorateFunctionIfNeeded(const TName &name)
{
if (name.isInternal())
{
- return TFunction::unmangleName(name.getString());
- }
- else
- {
- return Decorate(TFunction::unmangleName(name.getString()));
+ // The name should not have a prefix reserved for user-defined variables or functions.
+ ASSERT(name.getString().compare(0, 2, "f_") != 0);
+ ASSERT(name.getString().compare(0, 1, "_") != 0);
+ return name.getString();
}
+ ASSERT(name.getString().compare(0, 3, "gl_") != 0);
+ // Add an additional f prefix to functions so that they're always disambiguated from variables.
+ // This is necessary in the corner case where a variable declaration hides a function that it
+ // uses in its initializer.
+ return "f_" + name.getString();
}
TString TypeString(const TType &type)
{
- const TStructure* structure = type.getStruct();
+ const TStructure *structure = type.getStruct();
if (structure)
{
- const TString& typeName = structure->name();
+ const TString &typeName = structure->name();
if (typeName != "")
{
return StructNameString(*structure);
}
- else // Nameless structure, define in place
+ else // Nameless structure, define in place
{
return StructureHLSL::defineNameless(*structure);
}
@@ -263,55 +771,73 @@ TString TypeString(const TType &type)
{
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;
+ 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:
+ return "atomic_uint";
+ default:
+ break;
}
}
@@ -336,7 +862,8 @@ TString StructNameString(const TStructure &structure)
return "ss" + str(structure.uniqueId()) + "_" + structure.name();
}
-TString QualifiedStructNameString(const TStructure &structure, bool useHLSLRowMajorPacking,
+TString QualifiedStructNameString(const TStructure &structure,
+ bool useHLSLRowMajorPacking,
bool useStd140Packing)
{
if (structure.name() == "")
@@ -366,17 +893,28 @@ TString InterpolationString(TQualifier qualifier)
{
switch (qualifier)
{
- case EvqVaryingIn: return "";
- case EvqFragmentIn: return "";
- case EvqSmoothIn: return "linear";
- case EvqFlatIn: return "nointerpolation";
- case EvqCentroidIn: return "centroid";
- case EvqVaryingOut: return "";
- case EvqVertexOut: return "";
- case EvqSmoothOut: return "linear";
- case EvqFlatOut: return "nointerpolation";
- case EvqCentroidOut: return "centroid";
- default: UNREACHABLE();
+ case EvqVaryingIn:
+ return "";
+ case EvqFragmentIn:
+ return "";
+ case EvqSmoothIn:
+ return "linear";
+ case EvqFlatIn:
+ return "nointerpolation";
+ case EvqCentroidIn:
+ return "centroid";
+ case EvqVaryingOut:
+ return "";
+ case EvqVertexOut:
+ return "";
+ case EvqSmoothOut:
+ return "linear";
+ case EvqFlatOut:
+ return "nointerpolation";
+ case EvqCentroidOut:
+ return "centroid";
+ default:
+ UNREACHABLE();
}
return "";
@@ -386,53 +924,47 @@ TString QualifierString(TQualifier qualifier)
{
switch (qualifier)
{
- case EvqIn: return "in";
- case EvqOut: return "inout"; // 'out' results in an HLSL error if not all fields are written, for GLSL it's undefined
- case EvqInOut: return "inout";
- case EvqConstReadOnly: return "const";
- default: UNREACHABLE();
+ case EvqIn:
+ return "in";
+ case EvqOut:
+ return "inout"; // 'out' results in an HLSL error if not all fields are written, for
+ // GLSL it's undefined
+ case EvqInOut:
+ return "inout";
+ case EvqConstReadOnly:
+ return "const";
+ default:
+ UNREACHABLE();
}
return "";
}
-int HLSLTextureCoordsCount(const TBasicType samplerType)
+TString DisambiguateFunctionName(const TIntermSequence *parameters)
{
- switch (samplerType)
+ TString disambiguatingString;
+ for (auto parameter : *parameters)
{
- case EbtSampler2D:
- return 2;
- case EbtSampler3D:
- return 3;
- case EbtSamplerCube:
- return 3;
- case EbtSampler2DArray:
- return 3;
- case EbtISampler2D:
- return 2;
- case EbtISampler3D:
- return 3;
- case EbtISamplerCube:
- return 3;
- case EbtISampler2DArray:
- return 3;
- case EbtUSampler2D:
- return 2;
- case EbtUSampler3D:
- return 3;
- case EbtUSamplerCube:
- return 3;
- case EbtUSampler2DArray:
- return 3;
- case EbtSampler2DShadow:
- return 2;
- case EbtSamplerCubeShadow:
- return 3;
- case EbtSampler2DArrayShadow:
- return 3;
- default:
- UNREACHABLE();
+ const TType &paramType = parameter->getAsTyped()->getType();
+ // Parameter types are only added to function names if they are ambiguous according to the
+ // native HLSL compiler. Other parameter types are not added to function names to avoid
+ // making function names longer.
+ if (paramType.getObjectSize() == 4 && paramType.getBasicType() == EbtFloat)
+ {
+ // Disambiguation is needed for float2x2 and float4 parameters. These are the only
+ // built-in types that HLSL thinks are identical. float2x3 and float3x2 are different
+ // types, for example.
+ disambiguatingString += "_" + TypeString(paramType);
+ }
+ else if (paramType.getBasicType() == EbtStruct)
+ {
+ // Disambiguation is needed for struct parameters, since HLSL thinks that structs with
+ // the same fields but a different name are identical.
+ ASSERT(paramType.getStruct()->name() != "");
+ disambiguatingString += "_" + TypeString(paramType);
+ }
}
- return 0;
-}
+ return disambiguatingString;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h
index 42444e3a56..daeec8de41 100644
--- a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h
@@ -11,6 +11,7 @@
#define COMPILER_TRANSLATOR_UTILSHLSL_H_
#include <vector>
+#include "compiler/translator/IntermNode.h"
#include "compiler/translator/Types.h"
#include "angle_gl.h"
@@ -20,22 +21,33 @@ class TName;
namespace sh
{
-// Unique combinations of HLSL Texture type and HLSL Sampler type.
-enum HLSLTextureSamplerGroup
+// HLSL Texture type for GLSL sampler type and readonly image type.
+enum HLSLTextureGroup
{
- // Regular samplers
+ // read resources
HLSL_TEXTURE_2D,
HLSL_TEXTURE_MIN = HLSL_TEXTURE_2D,
HLSL_TEXTURE_CUBE,
HLSL_TEXTURE_2D_ARRAY,
HLSL_TEXTURE_3D,
+ HLSL_TEXTURE_2D_UNORM,
+ HLSL_TEXTURE_CUBE_UNORM,
+ HLSL_TEXTURE_2D_ARRAY_UNORN,
+ HLSL_TEXTURE_3D_UNORM,
+ HLSL_TEXTURE_2D_SNORM,
+ HLSL_TEXTURE_CUBE_SNORM,
+ HLSL_TEXTURE_2D_ARRAY_SNORM,
+ HLSL_TEXTURE_3D_SNORM,
+ HLSL_TEXTURE_2D_MS,
HLSL_TEXTURE_2D_INT4,
HLSL_TEXTURE_3D_INT4,
HLSL_TEXTURE_2D_ARRAY_INT4,
+ HLSL_TEXTURE_2D_MS_INT4,
HLSL_TEXTURE_2D_UINT4,
HLSL_TEXTURE_3D_UINT4,
HLSL_TEXTURE_2D_ARRAY_UINT4,
+ HLSL_TEXTURE_2D_MS_UINT4,
// Comparison samplers
@@ -50,29 +62,68 @@ enum HLSLTextureSamplerGroup
HLSL_TEXTURE_MAX = HLSL_TEXTURE_UNKNOWN
};
-HLSLTextureSamplerGroup TextureGroup(const TBasicType type);
-TString TextureString(const HLSLTextureSamplerGroup type);
-TString TextureString(const TBasicType type);
-TString TextureGroupSuffix(const HLSLTextureSamplerGroup type);
-TString TextureGroupSuffix(const TBasicType type);
-TString TextureTypeSuffix(const TBasicType type);
+// HLSL RWTexture type for GLSL read and write image type.
+enum HLSLRWTextureGroup
+{
+ // read/write resource
+ HLSL_RWTEXTURE_2D_FLOAT4,
+ HLSL_RWTEXTURE_MIN = HLSL_RWTEXTURE_2D_FLOAT4,
+ HLSL_RWTEXTURE_2D_ARRAY_FLOAT4,
+ HLSL_RWTEXTURE_3D_FLOAT4,
+ HLSL_RWTEXTURE_2D_UNORM,
+ HLSL_RWTEXTURE_2D_ARRAY_UNORN,
+ HLSL_RWTEXTURE_3D_UNORM,
+ HLSL_RWTEXTURE_2D_SNORM,
+ HLSL_RWTEXTURE_2D_ARRAY_SNORM,
+ HLSL_RWTEXTURE_3D_SNORM,
+ HLSL_RWTEXTURE_2D_UINT4,
+ HLSL_RWTEXTURE_2D_ARRAY_UINT4,
+ HLSL_RWTEXTURE_3D_UINT4,
+ HLSL_RWTEXTURE_2D_INT4,
+ HLSL_RWTEXTURE_2D_ARRAY_INT4,
+ HLSL_RWTEXTURE_3D_INT4,
+
+ HLSL_RWTEXTURE_UNKNOWN,
+ HLSL_RWTEXTURE_MAX = HLSL_RWTEXTURE_UNKNOWN
+};
+
+HLSLTextureGroup TextureGroup(const TBasicType type,
+ TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+TString TextureString(const HLSLTextureGroup textureGroup);
+TString TextureString(const TBasicType type,
+ TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+TString TextureGroupSuffix(const HLSLTextureGroup type);
+TString TextureGroupSuffix(const TBasicType type,
+ TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+TString TextureTypeSuffix(const TBasicType type,
+ TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+HLSLRWTextureGroup RWTextureGroup(const TBasicType type,
+ TLayoutImageInternalFormat imageInternalFormat);
+TString RWTextureString(const HLSLRWTextureGroup textureGroup);
+TString RWTextureString(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
+TString RWTextureGroupSuffix(const HLSLRWTextureGroup type);
+TString RWTextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
+TString RWTextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
+
TString SamplerString(const TBasicType type);
-TString SamplerString(HLSLTextureSamplerGroup type);
-// Prepends an underscore to avoid naming clashes
+TString SamplerString(HLSLTextureGroup type);
+
+// Adds a prefix to user-defined names to avoid naming clashes.
TString Decorate(const TString &string);
-TString DecorateIfNeeded(const TName &name);
-// Decorates and also unmangles the function name
+TString DecorateVariableIfNeeded(const TName &name);
TString DecorateFunctionIfNeeded(const TName &name);
-TString DecorateUniform(const TString &string, const TType &type);
TString DecorateField(const TString &string, const TStructure &structure);
TString DecoratePrivate(const TString &privateText);
TString TypeString(const TType &type);
TString StructNameString(const TStructure &structure);
-TString QualifiedStructNameString(const TStructure &structure, bool useHLSLRowMajorPacking,
+TString QualifiedStructNameString(const TStructure &structure,
+ bool useHLSLRowMajorPacking,
bool useStd140Packing);
TString InterpolationString(TQualifier qualifier);
TString QualifierString(TQualifier qualifier);
-int HLSLTextureCoordsCount(const TBasicType samplerType);
+// Parameters may need to be included in function names to disambiguate between overloaded
+// functions.
+TString DisambiguateFunctionName(const TIntermSequence *parameters);
}
-#endif // COMPILER_TRANSLATOR_UTILSHLSL_H_
+#endif // COMPILER_TRANSLATOR_UTILSHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp
index 2461b6a438..492972b60d 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp
@@ -6,8 +6,12 @@
#include "compiler/translator/ValidateGlobalInitializer.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/ParseContext.h"
+namespace sh
+{
+
namespace
{
@@ -32,7 +36,8 @@ class ValidateGlobalInitializerTraverser : public TIntermTraverser
void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node)
{
- const TSymbol *sym = mContext->symbolTable.find(node->getSymbol(), mContext->getShaderVersion());
+ const TSymbol *sym =
+ mContext->symbolTable.find(node->getSymbol(), mContext->getShaderVersion());
if (sym->isVariable())
{
// ESSL 1.00 section 4.3 (or ESSL 3.00 section 4.3):
@@ -40,33 +45,36 @@ void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node)
const TVariable *var = static_cast<const TVariable *>(sym);
switch (var->getType().getQualifier())
{
- case EvqConst:
- break;
- case EvqGlobal:
- case EvqTemporary:
- case EvqUniform:
- // We allow these cases to be compatible with legacy ESSL 1.00 content.
- // Implement stricter rules for ESSL 3.00 since there's no legacy content to deal with.
- if (mContext->getShaderVersion() >= 300)
- {
+ case EvqConst:
+ break;
+ case EvqGlobal:
+ case EvqTemporary:
+ case EvqUniform:
+ // We allow these cases to be compatible with legacy ESSL 1.00 content.
+ // Implement stricter rules for ESSL 3.00 since there's no legacy content to deal
+ // with.
+ if (mContext->getShaderVersion() >= 300)
+ {
+ mIsValid = false;
+ }
+ else
+ {
+ mIssueWarning = true;
+ }
+ break;
+ default:
mIsValid = false;
- }
- else
- {
- mIssueWarning = true;
- }
- break;
- default:
- mIsValid = false;
}
}
}
bool ValidateGlobalInitializerTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
- // Disallow calls to user-defined functions and texture lookup functions in global variable initializers.
- // This is done simply by disabling all function calls - built-in math functions don't use EOpFunctionCall.
- if (node->getOp() == EOpFunctionCall)
+ // Disallow calls to user-defined functions and texture lookup functions in global variable
+ // initializers.
+ // This is done simply by disabling all function calls - built-in math functions don't use
+ // the function call ops.
+ if (node->isFunctionCall())
{
mIsValid = false;
}
@@ -92,16 +100,15 @@ bool ValidateGlobalInitializerTraverser::visitUnary(Visit visit, TIntermUnary *n
}
ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(const TParseContext *context)
- : TIntermTraverser(true, false, false),
- mContext(context),
- mIsValid(true),
- mIssueWarning(false)
+ : TIntermTraverser(true, false, false), mContext(context), mIsValid(true), mIssueWarning(false)
{
}
-} // namespace
+} // namespace
-bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning)
+bool ValidateGlobalInitializer(TIntermTyped *initializer,
+ const TParseContext *context,
+ bool *warning)
{
ValidateGlobalInitializerTraverser validate(context);
initializer->traverse(&validate);
@@ -110,3 +117,4 @@ bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *c
return validate.isValid();
}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h
index c3d2a47eba..2e7570667a 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h
@@ -7,10 +7,17 @@
#ifndef COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
#define COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
+namespace sh
+{
+
class TIntermTyped;
class TParseContext;
// Returns true if the initializer is valid.
-bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning);
+bool ValidateGlobalInitializer(TIntermTyped *initializer,
+ const TParseContext *context,
+ bool *warning);
+
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
+#endif // COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp
index ba8cdd0aa8..941f79ae51 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp
@@ -5,14 +5,29 @@
//
#include "compiler/translator/ValidateLimitations.h"
-#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/InitializeParseContext.h"
-#include "compiler/translator/ParseContext.h"
+
#include "angle_gl.h"
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/ParseContext.h"
+
+namespace sh
+{
namespace
{
+int GetLoopSymbolId(TIntermLoop *loop)
+{
+ // Here we assume all the operations are valid, because the loop node is
+ // already validated before this call.
+ TIntermSequence *declSeq = loop->getInit()->getAsDeclarationNode()->getSequence();
+ TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
+ TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
+
+ return symbol->getId();
+}
+
// Traverses a node to check if it represents a constant index expression.
// Definition:
// constant-index-expressions are a superset of constant-expressions.
@@ -25,10 +40,8 @@ namespace
class ValidateConstIndexExpr : public TIntermTraverser
{
public:
- ValidateConstIndexExpr(TLoopStack& stack)
- : TIntermTraverser(true, false, false),
- mValid(true),
- mLoopStack(stack)
+ ValidateConstIndexExpr(const std::vector<int> &loopSymbols)
+ : TIntermTraverser(true, false, false), mValid(true), mLoopSymbolIds(loopSymbols)
{
}
@@ -41,93 +54,93 @@ class ValidateConstIndexExpr : public TIntermTraverser
// constant index expression.
if (mValid)
{
- mValid = (symbol->getQualifier() == EvqConst) ||
- (mLoopStack.findLoop(symbol));
+ bool isLoopSymbol = std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(),
+ symbol->getId()) != mLoopSymbolIds.end();
+ mValid = (symbol->getQualifier() == EvqConst) || isLoopSymbol;
}
}
private:
bool mValid;
- TLoopStack& mLoopStack;
+ const std::vector<int> mLoopSymbolIds;
};
-} // namespace anonymous
+// Traverses intermediate tree to ensure that the shader does not exceed the
+// minimum functionality mandated in GLSL 1.0 spec, Appendix A.
+class ValidateLimitationsTraverser : public TLValueTrackingTraverser
+{
+ public:
+ ValidateLimitationsTraverser(sh::GLenum shaderType,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ TDiagnostics *diagnostics);
-ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink)
- : TIntermTraverser(true, false, false),
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitBinary(Visit, TIntermBinary *) override;
+ bool visitLoop(Visit, TIntermLoop *) override;
+
+ private:
+ void error(TSourceLoc loc, const char *reason, const char *token);
+
+ bool withinLoopBody() const;
+ bool isLoopIndex(TIntermSymbol *symbol);
+ bool validateLoopType(TIntermLoop *node);
+
+ bool validateForLoopHeader(TIntermLoop *node);
+ // If valid, return the index symbol id; Otherwise, return -1.
+ int validateForLoopInit(TIntermLoop *node);
+ bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
+ bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
+
+ // Returns true if indexing does not exceed the minimum functionality
+ // mandated in GLSL 1.0 spec, Appendix A, Section 5.
+ bool isConstExpr(TIntermNode *node);
+ bool isConstIndexExpr(TIntermNode *node);
+ bool validateIndexing(TIntermBinary *node);
+
+ sh::GLenum mShaderType;
+ TDiagnostics *mDiagnostics;
+ std::vector<int> mLoopSymbolIds;
+};
+
+ValidateLimitationsTraverser::ValidateLimitationsTraverser(sh::GLenum shaderType,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ TDiagnostics *diagnostics)
+ : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
mShaderType(shaderType),
- mSink(sink),
- mNumErrors(0),
- mValidateIndexing(true),
- mValidateInnerLoops(true)
+ mDiagnostics(diagnostics)
{
+ ASSERT(diagnostics);
}
-// static
-bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop)
+void ValidateLimitationsTraverser::visitSymbol(TIntermSymbol *node)
{
- // The shader type doesn't matter in this case.
- ValidateLimitations validate(GL_FRAGMENT_SHADER, nullptr);
- validate.mValidateIndexing = false;
- validate.mValidateInnerLoops = false;
- if (!validate.validateLoopType(loop))
- return false;
- if (!validate.validateForLoopHeader(loop))
- return false;
- TIntermNode *body = loop->getBody();
- if (body != nullptr)
+ if (isLoopIndex(node) && isLValueRequiredHere())
{
- validate.mLoopStack.push(loop);
- body->traverse(&validate);
- validate.mLoopStack.pop();
+ error(node->getLine(),
+ "Loop index cannot be statically assigned to within the body of the loop",
+ node->getSymbol().c_str());
}
- return (validate.mNumErrors == 0);
}
-bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node)
+bool ValidateLimitationsTraverser::visitBinary(Visit, TIntermBinary *node)
{
- // Check if loop index is modified in the loop body.
- validateOperation(node, node->getLeft());
-
// Check indexing.
switch (node->getOp())
{
- case EOpIndexDirect:
- case EOpIndexIndirect:
- if (mValidateIndexing)
- validateIndexing(node);
- break;
- default:
- break;
- }
- return true;
-}
-
-bool ValidateLimitations::visitUnary(Visit, TIntermUnary *node)
-{
- // Check if loop index is modified in the loop body.
- validateOperation(node, node->getOperand());
-
- return true;
-}
-
-bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node)
-{
- switch (node->getOp()) {
- case EOpFunctionCall:
- validateFunctionCall(node);
- break;
- default:
- break;
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ validateIndexing(node);
+ break;
+ default:
+ break;
}
return true;
}
-bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node)
+bool ValidateLimitationsTraverser::visitLoop(Visit, TIntermLoop *node)
{
- if (!mValidateInnerLoops)
- return true;
-
if (!validateLoopType(node))
return false;
@@ -135,53 +148,45 @@ bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node)
return false;
TIntermNode *body = node->getBody();
- if (body != NULL)
+ if (body != nullptr)
{
- mLoopStack.push(node);
+ mLoopSymbolIds.push_back(GetLoopSymbolId(node));
body->traverse(this);
- mLoopStack.pop();
+ mLoopSymbolIds.pop_back();
}
// The loop is fully processed - no need to visit children.
return false;
}
-void ValidateLimitations::error(TSourceLoc loc,
- const char *reason, const char *token)
+void ValidateLimitationsTraverser::error(TSourceLoc loc, const char *reason, const char *token)
{
- if (mSink)
- {
- mSink->prefix(EPrefixError);
- mSink->location(loc);
- (*mSink) << "'" << token << "' : " << reason << "\n";
- }
- ++mNumErrors;
+ mDiagnostics->error(loc, reason, token);
}
-bool ValidateLimitations::withinLoopBody() const
+bool ValidateLimitationsTraverser::withinLoopBody() const
{
- return !mLoopStack.empty();
+ return !mLoopSymbolIds.empty();
}
-bool ValidateLimitations::isLoopIndex(TIntermSymbol *symbol)
+bool ValidateLimitationsTraverser::isLoopIndex(TIntermSymbol *symbol)
{
- return mLoopStack.findLoop(symbol) != NULL;
+ return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->getId()) !=
+ mLoopSymbolIds.end();
}
-bool ValidateLimitations::validateLoopType(TIntermLoop *node)
+bool ValidateLimitationsTraverser::validateLoopType(TIntermLoop *node)
{
TLoopType type = node->getType();
if (type == ELoopFor)
return true;
// Reject while and do-while loops.
- error(node->getLine(),
- "This type of loop is not allowed",
- type == ELoopWhile ? "while" : "do");
+ error(node->getLine(), "This type of loop is not allowed", type == ELoopWhile ? "while" : "do");
return false;
}
-bool ValidateLimitations::validateForLoopHeader(TIntermLoop *node)
+bool ValidateLimitationsTraverser::validateForLoopHeader(TIntermLoop *node)
{
ASSERT(node->getType() == ELoopFor);
@@ -200,10 +205,10 @@ bool ValidateLimitations::validateForLoopHeader(TIntermLoop *node)
return true;
}
-int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
+int ValidateLimitationsTraverser::validateForLoopInit(TIntermLoop *node)
{
TIntermNode *init = node->getInit();
- if (init == NULL)
+ if (init == nullptr)
{
error(node->getLine(), "Missing init declaration", "for");
return -1;
@@ -213,8 +218,8 @@ int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
// init-declaration has the form:
// type-specifier identifier = constant-expression
//
- TIntermAggregate *decl = init->getAsAggregate();
- if ((decl == NULL) || (decl->getOp() != EOpDeclaration))
+ TIntermDeclaration *decl = init->getAsDeclarationNode();
+ if (decl == nullptr)
{
error(init->getLine(), "Invalid init declaration", "for");
return -1;
@@ -227,29 +232,28 @@ int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
return -1;
}
TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
- if ((declInit == NULL) || (declInit->getOp() != EOpInitialize))
+ if ((declInit == nullptr) || (declInit->getOp() != EOpInitialize))
{
error(decl->getLine(), "Invalid init declaration", "for");
return -1;
}
TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
- if (symbol == NULL)
+ if (symbol == nullptr)
{
error(declInit->getLine(), "Invalid init declaration", "for");
return -1;
}
// The loop index has type int or float.
TBasicType type = symbol->getBasicType();
- if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat)) {
- error(symbol->getLine(),
- "Invalid type for loop index", getBasicString(type));
+ if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat))
+ {
+ error(symbol->getLine(), "Invalid type for loop index", getBasicString(type));
return -1;
}
// The loop index is initialized with constant expression.
if (!isConstExpr(declInit->getRight()))
{
- error(declInit->getLine(),
- "Loop index cannot be initialized with non-constant expression",
+ error(declInit->getLine(), "Loop index cannot be initialized with non-constant expression",
symbol->getSymbol().c_str());
return -1;
}
@@ -257,11 +261,10 @@ int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
return symbol->getId();
}
-bool ValidateLimitations::validateForLoopCond(TIntermLoop *node,
- int indexSymbolId)
+bool ValidateLimitationsTraverser::validateForLoopCond(TIntermLoop *node, int indexSymbolId)
{
TIntermNode *cond = node->getCondition();
- if (cond == NULL)
+ if (cond == nullptr)
{
error(node->getLine(), "Missing condition", "for");
return false;
@@ -271,45 +274,42 @@ bool ValidateLimitations::validateForLoopCond(TIntermLoop *node,
// loop_index relational_operator constant_expression
//
TIntermBinary *binOp = cond->getAsBinaryNode();
- if (binOp == NULL)
+ if (binOp == nullptr)
{
error(node->getLine(), "Invalid condition", "for");
return false;
}
// Loop index should be to the left of relational operator.
TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
- if (symbol == NULL)
+ if (symbol == nullptr)
{
error(binOp->getLine(), "Invalid condition", "for");
return false;
}
if (symbol->getId() != indexSymbolId)
{
- error(symbol->getLine(),
- "Expected loop index", symbol->getSymbol().c_str());
+ error(symbol->getLine(), "Expected loop index", symbol->getSymbol().c_str());
return false;
}
// Relational operator is one of: > >= < <= == or !=.
switch (binOp->getOp())
{
- case EOpEqual:
- case EOpNotEqual:
- case EOpLessThan:
- case EOpGreaterThan:
- case EOpLessThanEqual:
- case EOpGreaterThanEqual:
- break;
- default:
- error(binOp->getLine(),
- "Invalid relational operator",
- GetOperatorString(binOp->getOp()));
- break;
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ break;
+ default:
+ error(binOp->getLine(), "Invalid relational operator",
+ GetOperatorString(binOp->getOp()));
+ break;
}
// Loop index must be compared with a constant.
if (!isConstExpr(binOp->getRight()))
{
- error(binOp->getLine(),
- "Loop index cannot be compared with non-constant expression",
+ error(binOp->getLine(), "Loop index cannot be compared with non-constant expression",
symbol->getSymbol().c_str());
return false;
}
@@ -317,11 +317,10 @@ bool ValidateLimitations::validateForLoopCond(TIntermLoop *node,
return true;
}
-bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node,
- int indexSymbolId)
+bool ValidateLimitationsTraverser::validateForLoopExpr(TIntermLoop *node, int indexSymbolId)
{
TIntermNode *expr = node->getExpression();
- if (expr == NULL)
+ if (expr == nullptr)
{
error(node->getLine(), "Missing expression", "for");
return false;
@@ -336,60 +335,58 @@ bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node,
// --loop_index
// The last two forms are not specified in the spec, but I am assuming
// its an oversight.
- TIntermUnary *unOp = expr->getAsUnaryNode();
- TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
+ TIntermUnary *unOp = expr->getAsUnaryNode();
+ TIntermBinary *binOp = unOp ? nullptr : expr->getAsBinaryNode();
- TOperator op = EOpNull;
- TIntermSymbol *symbol = NULL;
- if (unOp != NULL)
+ TOperator op = EOpNull;
+ TIntermSymbol *symbol = nullptr;
+ if (unOp != nullptr)
{
- op = unOp->getOp();
+ op = unOp->getOp();
symbol = unOp->getOperand()->getAsSymbolNode();
}
- else if (binOp != NULL)
+ else if (binOp != nullptr)
{
- op = binOp->getOp();
+ op = binOp->getOp();
symbol = binOp->getLeft()->getAsSymbolNode();
}
// The operand must be loop index.
- if (symbol == NULL)
+ if (symbol == nullptr)
{
error(expr->getLine(), "Invalid expression", "for");
return false;
}
if (symbol->getId() != indexSymbolId)
{
- error(symbol->getLine(),
- "Expected loop index", symbol->getSymbol().c_str());
+ error(symbol->getLine(), "Expected loop index", symbol->getSymbol().c_str());
return false;
}
// The operator is one of: ++ -- += -=.
switch (op)
{
- case EOpPostIncrement:
- case EOpPostDecrement:
- case EOpPreIncrement:
- case EOpPreDecrement:
- ASSERT((unOp != NULL) && (binOp == NULL));
- break;
- case EOpAddAssign:
- case EOpSubAssign:
- ASSERT((unOp == NULL) && (binOp != NULL));
- break;
- default:
- error(expr->getLine(), "Invalid operator", GetOperatorString(op));
- return false;
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ ASSERT((unOp != nullptr) && (binOp == nullptr));
+ break;
+ case EOpAddAssign:
+ case EOpSubAssign:
+ ASSERT((unOp == nullptr) && (binOp != nullptr));
+ break;
+ default:
+ error(expr->getLine(), "Invalid operator", GetOperatorString(op));
+ return false;
}
// Loop index must be incremented/decremented with a constant.
- if (binOp != NULL)
+ if (binOp != nullptr)
{
if (!isConstExpr(binOp->getRight()))
{
- error(binOp->getLine(),
- "Loop index cannot be modified by non-constant expression",
+ error(binOp->getLine(), "Loop index cannot be modified by non-constant expression",
symbol->getSymbol().c_str());
return false;
}
@@ -398,95 +395,31 @@ bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node,
return true;
}
-bool ValidateLimitations::validateFunctionCall(TIntermAggregate *node)
-{
- ASSERT(node->getOp() == EOpFunctionCall);
-
- // If not within loop body, there is nothing to check.
- if (!withinLoopBody())
- return true;
-
- // List of param indices for which loop indices are used as argument.
- typedef std::vector<size_t> ParamIndex;
- ParamIndex pIndex;
- TIntermSequence *params = node->getSequence();
- for (TIntermSequence::size_type i = 0; i < params->size(); ++i)
- {
- TIntermSymbol *symbol = (*params)[i]->getAsSymbolNode();
- if (symbol && isLoopIndex(symbol))
- pIndex.push_back(i);
- }
- // If none of the loop indices are used as arguments,
- // there is nothing to check.
- if (pIndex.empty())
- return true;
-
- bool valid = true;
- TSymbolTable& symbolTable = GetGlobalParseContext()->symbolTable;
- TSymbol* symbol = symbolTable.find(node->getName(), GetGlobalParseContext()->getShaderVersion());
- ASSERT(symbol && symbol->isFunction());
- TFunction *function = static_cast<TFunction *>(symbol);
- for (ParamIndex::const_iterator i = pIndex.begin();
- i != pIndex.end(); ++i)
- {
- const TConstParameter &param = function->getParam(*i);
- TQualifier qual = param.type->getQualifier();
- if ((qual == EvqOut) || (qual == EvqInOut))
- {
- error((*params)[*i]->getLine(),
- "Loop index cannot be used as argument to a function out or inout parameter",
- (*params)[*i]->getAsSymbolNode()->getSymbol().c_str());
- valid = false;
- }
- }
-
- return valid;
-}
-
-bool ValidateLimitations::validateOperation(TIntermOperator *node,
- TIntermNode* operand)
-{
- // Check if loop index is modified in the loop body.
- if (!withinLoopBody() || !node->isAssignment())
- return true;
-
- TIntermSymbol *symbol = operand->getAsSymbolNode();
- if (symbol && isLoopIndex(symbol))
- {
- error(node->getLine(),
- "Loop index cannot be statically assigned to within the body of the loop",
- symbol->getSymbol().c_str());
- }
- return true;
-}
-
-bool ValidateLimitations::isConstExpr(TIntermNode *node)
+bool ValidateLimitationsTraverser::isConstExpr(TIntermNode *node)
{
ASSERT(node != nullptr);
return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
}
-bool ValidateLimitations::isConstIndexExpr(TIntermNode *node)
+bool ValidateLimitationsTraverser::isConstIndexExpr(TIntermNode *node)
{
- ASSERT(node != NULL);
+ ASSERT(node != nullptr);
- ValidateConstIndexExpr validate(mLoopStack);
+ ValidateConstIndexExpr validate(mLoopSymbolIds);
node->traverse(&validate);
return validate.isValid();
}
-bool ValidateLimitations::validateIndexing(TIntermBinary *node)
+bool ValidateLimitationsTraverser::validateIndexing(TIntermBinary *node)
{
- ASSERT((node->getOp() == EOpIndexDirect) ||
- (node->getOp() == EOpIndexIndirect));
+ ASSERT((node->getOp() == EOpIndexDirect) || (node->getOp() == EOpIndexIndirect));
- bool valid = true;
+ bool valid = true;
TIntermTyped *index = node->getRight();
// The index expession must be a constant-index-expression unless
// the operand is a uniform in a vertex shader.
TIntermTyped *operand = node->getLeft();
- bool skip = (mShaderType == GL_VERTEX_SHADER) &&
- (operand->getQualifier() == EvqUniform);
+ bool skip = (mShaderType == GL_VERTEX_SHADER) && (operand->getQualifier() == EvqUniform);
if (!skip && !isConstIndexExpr(index))
{
error(index->getLine(), "Index expression must be constant", "[]");
@@ -495,3 +428,17 @@ bool ValidateLimitations::validateIndexing(TIntermBinary *node)
return valid;
}
+} // namespace anonymous
+
+bool ValidateLimitations(TIntermNode *root,
+ GLenum shaderType,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ TDiagnostics *diagnostics)
+{
+ ValidateLimitationsTraverser validate(shaderType, symbolTable, shaderVersion, diagnostics);
+ root->traverse(&validate);
+ return diagnostics->numErrors() == 0;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h
index 666e38ff5c..9149b8c216 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h
@@ -8,56 +8,20 @@
#define COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/LoopInfo.h"
-class TInfoSinkBase;
-
-// Traverses intermediate tree to ensure that the shader does not exceed the
-// minimum functionality mandated in GLSL 1.0 spec, Appendix A.
-class ValidateLimitations : public TIntermTraverser
+namespace sh
{
- public:
- ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink);
-
- int numErrors() const { return mNumErrors; }
-
- bool visitBinary(Visit, TIntermBinary *) override;
- bool visitUnary(Visit, TIntermUnary *) override;
- bool visitAggregate(Visit, TIntermAggregate *) override;
- bool visitLoop(Visit, TIntermLoop *) override;
-
- static bool IsLimitedForLoop(TIntermLoop *node);
-
- private:
- void error(TSourceLoc loc, const char *reason, const char *token);
-
- bool withinLoopBody() const;
- bool isLoopIndex(TIntermSymbol *symbol);
- bool validateLoopType(TIntermLoop *node);
-
- bool validateForLoopHeader(TIntermLoop *node);
- // If valid, return the index symbol id; Otherwise, return -1.
- int validateForLoopInit(TIntermLoop *node);
- bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
- bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
- // Returns true if none of the loop indices is used as the argument to
- // the given function out or inout parameter.
- bool validateFunctionCall(TIntermAggregate *node);
- bool validateOperation(TIntermOperator *node, TIntermNode *operand);
+class TDiagnostics;
- // Returns true if indexing does not exceed the minimum functionality
- // mandated in GLSL 1.0 spec, Appendix A, Section 5.
- bool isConstExpr(TIntermNode *node);
- bool isConstIndexExpr(TIntermNode *node);
- bool validateIndexing(TIntermBinary *node);
+// Returns true if the given shader does not exceed the minimum functionality mandated in GLSL ES
+// 1.00 spec Appendix A.
+bool ValidateLimitations(TIntermNode *root,
+ GLenum shaderType,
+ TSymbolTable *symbolTable,
+ int shaderVersion,
+ TDiagnostics *diagnostics);
- sh::GLenum mShaderType;
- TInfoSinkBase *mSink;
- int mNumErrors;
- TLoopStack mLoopStack;
- bool mValidateIndexing;
- bool mValidateInnerLoops;
-};
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
+#endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.cpp
new file mode 100644
index 0000000000..9dccbf413f
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.cpp
@@ -0,0 +1,29 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ValidateMaxParameters checks if function definitions have more than a set number of parameters.
+
+#include "compiler/translator/ValidateMaxParameters.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+bool ValidateMaxParameters(TIntermBlock *root, unsigned int maxParameters)
+{
+ for (TIntermNode *node : *root->getSequence())
+ {
+ TIntermFunctionDefinition *definition = node->getAsFunctionDefinition();
+ if (definition != nullptr &&
+ definition->getFunctionPrototype()->getSequence()->size() > maxParameters)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.h b/src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.h
new file mode 100644
index 0000000000..dec7597da4
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ValidateMaxParameters checks if function definitions have more than a set number of parameters.
+
+#ifndef COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
+#define COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+
+// Return true if valid.
+bool ValidateMaxParameters(TIntermBlock *root, unsigned int maxParameters);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp
index cd37aeacd1..26f0e81ba7 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp
@@ -3,64 +3,101 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// ValidateOutputs validates fragment shader outputs. It checks for conflicting locations,
+// out-of-range locations, that locations are specified when using multiple outputs, and YUV output
+// validity.
#include "compiler/translator/ValidateOutputs.h"
+
+#include <set>
+
#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/InitializeParseContext.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/ParseContext.h"
+namespace sh
+{
+
namespace
{
-void error(int *errorCount, TInfoSinkBase &sink, const TIntermSymbol &symbol, const char *reason)
+void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
{
- sink.prefix(EPrefixError);
- sink.location(symbol.getLine());
- sink << "'" << symbol.getSymbol() << "' : " << reason << "\n";
- (*errorCount)++;
+ diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str());
}
-} // namespace
+class ValidateOutputsTraverser : public TIntermTraverser
+{
+ public:
+ ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, int maxDrawBuffers);
+
+ void validate(TDiagnostics *diagnostics) const;
+
+ void visitSymbol(TIntermSymbol *) override;
-ValidateOutputs::ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers)
+ private:
+ int mMaxDrawBuffers;
+ bool mAllowUnspecifiedOutputLocationResolution;
+ bool mUsesFragDepth;
+
+ typedef std::vector<TIntermSymbol *> OutputVector;
+ OutputVector mOutputs;
+ OutputVector mUnspecifiedLocationOutputs;
+ OutputVector mYuvOutputs;
+ std::set<std::string> mVisitedSymbols;
+};
+
+ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
+ int maxDrawBuffers)
: TIntermTraverser(true, false, false),
mMaxDrawBuffers(maxDrawBuffers),
mAllowUnspecifiedOutputLocationResolution(
- IsExtensionEnabled(extBehavior, "GL_EXT_blend_func_extended"))
+ IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)),
+ mUsesFragDepth(false)
{
}
-void ValidateOutputs::visitSymbol(TIntermSymbol *symbol)
+void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol)
{
- TString name = symbol->getSymbol();
+ TString name = symbol->getSymbol();
TQualifier qualifier = symbol->getQualifier();
- if (mVisitedSymbols.count(name) == 1)
+ if (mVisitedSymbols.count(name.c_str()) == 1)
return;
- mVisitedSymbols.insert(name);
+ mVisitedSymbols.insert(name.c_str());
if (qualifier == EvqFragmentOut)
{
- if (symbol->getType().getLayoutQualifier().location == -1)
+ if (symbol->getType().getLayoutQualifier().location != -1)
{
- mUnspecifiedLocationOutputs.push_back(symbol);
+ mOutputs.push_back(symbol);
+ }
+ else if (symbol->getType().getLayoutQualifier().yuv == true)
+ {
+ mYuvOutputs.push_back(symbol);
}
else
{
- mOutputs.push_back(symbol);
+ mUnspecifiedLocationOutputs.push_back(symbol);
}
}
+ else if (qualifier == EvqFragDepth || qualifier == EvqFragDepthEXT)
+ {
+ mUsesFragDepth = true;
+ }
}
-int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
+void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
{
+ ASSERT(diagnostics);
OutputVector validOutputs(mMaxDrawBuffers);
- int errorCount = 0;
for (const auto &symbol : mOutputs)
{
const TType &type = symbol->getType();
- const size_t elementCount = static_cast<size_t>(type.isArray() ? type.getArraySize() : 1);
+ ASSERT(!type.isArrayOfArrays()); // Disallowed in GLSL ES 3.10 section 4.3.6.
+ const size_t elementCount =
+ static_cast<size_t>(type.isArray() ? type.getOutermostArraySize() : 1u);
const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
ASSERT(type.getLayoutQualifier().location != -1);
@@ -75,7 +112,7 @@ int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
std::stringstream strstr;
strstr << "conflicting output locations with previously defined output '"
<< validOutputs[offsetLocation]->getSymbol() << "'";
- error(&errorCount, sink, *symbol, strstr.str().c_str());
+ error(*symbol, strstr.str().c_str(), diagnostics);
}
else
{
@@ -87,9 +124,10 @@ int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
{
if (elementCount > 0)
{
- error(&errorCount, sink, *symbol,
+ error(*symbol,
elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
- : "output location must be < MAX_DRAW_BUFFERS");
+ : "output location must be < MAX_DRAW_BUFFERS",
+ diagnostics);
}
}
}
@@ -100,9 +138,37 @@ int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
{
for (const auto &symbol : mUnspecifiedLocationOutputs)
{
- error(&errorCount, sink, *symbol,
- "must explicitly specify all locations when using multiple fragment outputs");
+ error(*symbol,
+ "must explicitly specify all locations when using multiple fragment outputs",
+ diagnostics);
}
}
- return errorCount;
+
+ if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() ||
+ !mUnspecifiedLocationOutputs.empty()))
+ {
+ for (const auto &symbol : mYuvOutputs)
+ {
+ error(*symbol,
+ "not allowed to specify yuv qualifier when using depth or multiple color "
+ "fragment outputs",
+ diagnostics);
+ }
+ }
+}
+
+} // anonymous namespace
+
+bool ValidateOutputs(TIntermBlock *root,
+ const TExtensionBehavior &extBehavior,
+ int maxDrawBuffers,
+ TDiagnostics *diagnostics)
+{
+ ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers);
+ root->traverse(&validateOutputs);
+ int numErrorsBefore = diagnostics->numErrors();
+ validateOutputs.validate(diagnostics);
+ return (diagnostics->numErrors() == numErrorsBefore);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h
index 06f63994cd..e41ccd990c 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h
@@ -3,34 +3,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// ValidateOutputs validates fragment shader outputs. It checks for conflicting locations,
+// out-of-range locations, that locations are specified when using multiple outputs, and YUV output
+// validity.
+//
#ifndef COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
#define COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
#include "compiler/translator/ExtensionBehavior.h"
-#include "compiler/translator/IntermNode.h"
-
-#include <set>
-class TInfoSinkBase;
-
-class ValidateOutputs : public TIntermTraverser
+namespace sh
{
- public:
- ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers);
-
- int validateAndCountErrors(TInfoSinkBase &sink) const;
- void visitSymbol(TIntermSymbol *) override;
+class TIntermBlock;
+class TDiagnostics;
- private:
- int mMaxDrawBuffers;
- bool mAllowUnspecifiedOutputLocationResolution;
+// Returns true if the shader has no conflicting or otherwise erroneous fragment outputs.
+bool ValidateOutputs(TIntermBlock *root,
+ const TExtensionBehavior &extBehavior,
+ int maxDrawBuffers,
+ TDiagnostics *diagnostics);
- typedef std::vector<TIntermSymbol *> OutputVector;
- OutputVector mOutputs;
- OutputVector mUnspecifiedLocationOutputs;
- std::set<TString> mVisitedSymbols;
-};
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
+#endif // COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.cpp
index 9a4ed33632..9f7a264e58 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.cpp
@@ -6,21 +6,76 @@
#include "compiler/translator/ValidateSwitch.h"
-#include "compiler/translator/ParseContext.h"
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/IntermTraverse.h"
-bool ValidateSwitch::validate(TBasicType switchType, TParseContext *context,
- TIntermAggregate *statementList, const TSourceLoc &loc)
+namespace sh
{
- ValidateSwitch validate(switchType, context);
+
+namespace
+{
+
+class ValidateSwitch : public TIntermTraverser
+{
+ public:
+ static bool validate(TBasicType switchType,
+ int shaderVersion,
+ TDiagnostics *diagnostics,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc);
+
+ void visitSymbol(TIntermSymbol *) override;
+ void visitConstantUnion(TIntermConstantUnion *) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *) override;
+ bool visitBlock(Visit, TIntermBlock *) override;
+ bool visitBinary(Visit, TIntermBinary *) override;
+ bool visitUnary(Visit, TIntermUnary *) override;
+ bool visitTernary(Visit, TIntermTernary *) override;
+ bool visitSwizzle(Visit, TIntermSwizzle *) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *) override;
+ bool visitSwitch(Visit, TIntermSwitch *) override;
+ bool visitCase(Visit, TIntermCase *node) override;
+ bool visitAggregate(Visit, TIntermAggregate *) override;
+ bool visitLoop(Visit visit, TIntermLoop *) override;
+ bool visitBranch(Visit, TIntermBranch *) override;
+
+ private:
+ ValidateSwitch(TBasicType switchType, int shaderVersion, TDiagnostics *context);
+
+ bool validateInternal(const TSourceLoc &loc);
+
+ TBasicType mSwitchType;
+ int mShaderVersion;
+ TDiagnostics *mDiagnostics;
+ bool mCaseTypeMismatch;
+ bool mFirstCaseFound;
+ bool mStatementBeforeCase;
+ bool mLastStatementWasCase;
+ int mControlFlowDepth;
+ bool mCaseInsideControlFlow;
+ int mDefaultCount;
+ std::set<int> mCasesSigned;
+ std::set<unsigned int> mCasesUnsigned;
+ bool mDuplicateCases;
+};
+
+bool ValidateSwitch::validate(TBasicType switchType,
+ int shaderVersion,
+ TDiagnostics *diagnostics,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc)
+{
+ ValidateSwitch validate(switchType, shaderVersion, diagnostics);
ASSERT(statementList);
statementList->traverse(&validate);
return validate.validateInternal(loc);
}
-ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
+ValidateSwitch::ValidateSwitch(TBasicType switchType, int shaderVersion, TDiagnostics *diagnostics)
: TIntermTraverser(true, false, true),
mSwitchType(switchType),
- mContext(context),
+ mShaderVersion(shaderVersion),
+ mDiagnostics(diagnostics),
mCaseTypeMismatch(false),
mFirstCaseFound(false),
mStatementBeforeCase(false),
@@ -29,13 +84,14 @@ ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
mCaseInsideControlFlow(false),
mDefaultCount(0),
mDuplicateCases(false)
-{}
+{
+}
void ValidateSwitch::visitSymbol(TIntermSymbol *)
{
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
}
void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
@@ -44,14 +100,33 @@ void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
// Could be just a statement like "0;"
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
+}
+
+bool ValidateSwitch::visitDeclaration(Visit, TIntermDeclaration *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitBlock(Visit, TIntermBlock *)
+{
+ if (getParentNode() != nullptr)
+ {
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ }
+ return true;
}
bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
{
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
return true;
}
@@ -59,11 +134,27 @@ bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
{
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
return true;
}
-bool ValidateSwitch::visitSelection(Visit visit, TIntermSelection *)
+bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
{
if (visit == PreVisit)
++mControlFlowDepth;
@@ -71,7 +162,7 @@ bool ValidateSwitch::visitSelection(Visit visit, TIntermSelection *)
--mControlFlowDepth;
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
return true;
}
@@ -79,7 +170,7 @@ bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
{
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
// Don't go into nested switch statements
return false;
}
@@ -89,17 +180,17 @@ bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
const char *nodeStr = node->hasCondition() ? "case" : "default";
if (mControlFlowDepth > 0)
{
- mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
+ mDiagnostics->error(node->getLine(), "label statement nested inside control flow", nodeStr);
mCaseInsideControlFlow = true;
}
- mFirstCaseFound = true;
+ mFirstCaseFound = true;
mLastStatementWasCase = true;
if (!node->hasCondition())
{
++mDefaultCount;
if (mDefaultCount > 1)
{
- mContext->error(node->getLine(), "duplicate default label", nodeStr);
+ mDiagnostics->error(node->getLine(), "duplicate default label", nodeStr);
}
}
else
@@ -113,8 +204,9 @@ bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
TBasicType conditionType = condition->getBasicType();
if (conditionType != mSwitchType)
{
- mContext->error(condition->getLine(),
- "case label type does not match switch init-expression type", nodeStr);
+ mDiagnostics->error(condition->getLine(),
+ "case label type does not match switch init-expression type",
+ nodeStr);
mCaseTypeMismatch = true;
}
@@ -123,7 +215,7 @@ bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
int iConst = condition->getIConst(0);
if (mCasesSigned.find(iConst) != mCasesSigned.end())
{
- mContext->error(condition->getLine(), "duplicate case label", nodeStr);
+ mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
mDuplicateCases = true;
}
else
@@ -136,7 +228,7 @@ bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
unsigned int uConst = condition->getUConst(0);
if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
{
- mContext->error(condition->getLine(), "duplicate case label", nodeStr);
+ mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
mDuplicateCases = true;
}
else
@@ -158,7 +250,7 @@ bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
// This is not the statementList node, but some other node.
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
}
return true;
}
@@ -171,7 +263,7 @@ bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
--mControlFlowDepth;
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
return true;
}
@@ -179,7 +271,7 @@ bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
{
if (!mFirstCaseFound)
mStatementBeforeCase = true;
- mLastStatementWasCase = false;
+ mLastStatementWasCase = false;
return true;
}
@@ -187,14 +279,41 @@ bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
{
if (mStatementBeforeCase)
{
- mContext->error(loc,
- "statement before the first label", "switch");
+ mDiagnostics->error(loc, "statement before the first label", "switch");
}
+ bool lastStatementWasCaseError = false;
if (mLastStatementWasCase)
{
- mContext->error(loc,
- "no statement between the last label and the end of the switch statement", "switch");
+ if (mShaderVersion == 300)
+ {
+ lastStatementWasCaseError = true;
+ // This error has been proposed to be made optional in GLSL ES 3.00, but dEQP tests
+ // still require it.
+ mDiagnostics->error(
+ loc, "no statement between the last label and the end of the switch statement",
+ "switch");
+ }
+ else
+ {
+ // The error has been removed from GLSL ES 3.10.
+ mDiagnostics->warning(
+ loc, "no statement between the last label and the end of the switch statement",
+ "switch");
+ }
}
- return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
- !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
+ return !mStatementBeforeCase && !lastStatementWasCaseError && !mCaseInsideControlFlow &&
+ !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
+}
+
+} // anonymous namespace
+
+bool ValidateSwitchStatementList(TBasicType switchType,
+ int shaderVersion,
+ TDiagnostics *diagnostics,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc)
+{
+ return ValidateSwitch::validate(switchType, shaderVersion, diagnostics, statementList, loc);
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h b/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h
index ddbefc5619..2d2dd70f78 100644
--- a/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h
@@ -7,46 +7,22 @@
#ifndef COMPILER_TRANSLATOR_VALIDATESWITCH_H_
#define COMPILER_TRANSLATOR_VALIDATESWITCH_H_
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Common.h"
-class TParseContext;
-
-class ValidateSwitch : public TIntermTraverser
+namespace sh
{
- public:
- // Check for errors and output messages any remaining errors on the context.
- // Returns true if there are no errors.
- static bool validate(TBasicType switchType, TParseContext *context,
- TIntermAggregate *statementList, const TSourceLoc &loc);
-
- void visitSymbol(TIntermSymbol *) override;
- void visitConstantUnion(TIntermConstantUnion *) override;
- bool visitBinary(Visit, TIntermBinary *) override;
- bool visitUnary(Visit, TIntermUnary *) override;
- bool visitSelection(Visit visit, TIntermSelection *) override;
- bool visitSwitch(Visit, TIntermSwitch *) override;
- bool visitCase(Visit, TIntermCase *node) override;
- bool visitAggregate(Visit, TIntermAggregate *) override;
- bool visitLoop(Visit visit, TIntermLoop *) override;
- bool visitBranch(Visit, TIntermBranch *) override;
-
- private:
- ValidateSwitch(TBasicType switchType, TParseContext *context);
+class TDiagnostics;
+class TIntermBlock;
- bool validateInternal(const TSourceLoc &loc);
+// Check for errors and output error messages on the context.
+// Returns true if there are no errors.
+bool ValidateSwitchStatementList(TBasicType switchType,
+ int shaderVersion,
+ TDiagnostics *diagnostics,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc);
- TBasicType mSwitchType;
- TParseContext *mContext;
- bool mCaseTypeMismatch;
- bool mFirstCaseFound;
- bool mStatementBeforeCase;
- bool mLastStatementWasCase;
- int mControlFlowDepth;
- bool mCaseInsideControlFlow;
- int mDefaultCount;
- std::set<int> mCasesSigned;
- std::set<unsigned int> mCasesUnsigned;
- bool mDuplicateCases;
-};
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_VALIDATESWITCH_H_
+#endif // COMPILER_TRANSLATOR_VALIDATESWITCH_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.cpp
new file mode 100644
index 0000000000..9c36fcea78
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.cpp
@@ -0,0 +1,174 @@
+//
+// Copyright (c) 2002-2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The ValidateVaryingLocations function checks if there exists location conflicts on shader
+// varyings.
+//
+
+#include "ValidateVaryingLocations.h"
+
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
+{
+ diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str());
+}
+
+int GetLocationCount(const TIntermSymbol *varying, bool ignoreVaryingArraySize)
+{
+ const auto &varyingType = varying->getType();
+ if (varyingType.getStruct() != nullptr)
+ {
+ ASSERT(!varyingType.isArray());
+ int totalLocation = 0;
+ for (const auto *field : varyingType.getStruct()->fields())
+ {
+ const auto *fieldType = field->type();
+ ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray());
+
+ totalLocation += fieldType->getSecondarySize();
+ }
+ return totalLocation;
+ }
+ // [GL_OES_shader_io_blocks SPEC Chapter 4.4.1]
+ // Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
+ // evaluation inputs all have an additional level of arrayness relative to other shader inputs
+ // and outputs. This outer array level is removed from the type before considering how many
+ // locations the type consumes.
+ else if (ignoreVaryingArraySize)
+ {
+ // Array-of-arrays cannot be inputs or outputs of a geometry shader.
+ // (GL_OES_geometry_shader SPEC issues(5))
+ ASSERT(!varyingType.isArrayOfArrays());
+ return varyingType.getSecondarySize();
+ }
+ else
+ {
+ return varyingType.getSecondarySize() * static_cast<int>(varyingType.getArraySizeProduct());
+ }
+}
+
+using VaryingVector = std::vector<const TIntermSymbol *>;
+
+void ValidateShaderInterface(TDiagnostics *diagnostics,
+ VaryingVector &varyingVector,
+ bool ignoreVaryingArraySize)
+{
+ // Location conflicts can only happen when there are two or more varyings in varyingVector.
+ if (varyingVector.size() <= 1)
+ {
+ return;
+ }
+
+ std::map<int, const TIntermSymbol *> locationMap;
+ for (const TIntermSymbol *varying : varyingVector)
+ {
+ const int location = varying->getType().getLayoutQualifier().location;
+ ASSERT(location >= 0);
+
+ const int elementCount = GetLocationCount(varying, ignoreVaryingArraySize);
+ for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
+ {
+ const int offsetLocation = location + elementIndex;
+ if (locationMap.find(offsetLocation) != locationMap.end())
+ {
+ std::stringstream strstr;
+ strstr << "'" << varying->getSymbol()
+ << "' conflicting location with previously defined '"
+ << locationMap[offsetLocation]->getSymbol() << "'";
+ error(*varying, strstr.str().c_str(), diagnostics);
+ }
+ else
+ {
+ locationMap[offsetLocation] = varying;
+ }
+ }
+ }
+}
+
+class ValidateVaryingLocationsTraverser : public TIntermTraverser
+{
+ public:
+ ValidateVaryingLocationsTraverser(GLenum shaderType);
+ void validate(TDiagnostics *diagnostics);
+
+ private:
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+
+ VaryingVector mInputVaryingsWithLocation;
+ VaryingVector mOutputVaryingsWithLocation;
+ GLenum mShaderType;
+};
+
+ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType)
+ : TIntermTraverser(true, false, false), mShaderType(shaderType)
+{
+}
+
+bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ const TIntermSequence &sequence = *(node->getSequence());
+ ASSERT(!sequence.empty());
+
+ const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
+ if (symbol == nullptr)
+ {
+ return false;
+ }
+
+ // Collect varyings that have explicit 'location' qualifiers.
+ const TQualifier qualifier = symbol->getQualifier();
+ if (symbol->getType().getLayoutQualifier().location != -1)
+ {
+ if (IsVaryingIn(qualifier))
+ {
+ mInputVaryingsWithLocation.push_back(symbol);
+ }
+ else if (IsVaryingOut(qualifier))
+ {
+ mOutputVaryingsWithLocation.push_back(symbol);
+ }
+ }
+
+ return false;
+}
+
+bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit,
+ TIntermFunctionDefinition *node)
+{
+ // We stop traversing function definitions because varyings cannot be defined in a function.
+ return false;
+}
+
+void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
+{
+ ASSERT(diagnostics);
+
+ ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation,
+ mShaderType == GL_GEOMETRY_SHADER_OES);
+ ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation, false);
+}
+
+} // anonymous namespace
+
+bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType)
+{
+ ValidateVaryingLocationsTraverser varyingValidator(shaderType);
+ root->traverse(&varyingValidator);
+ int numErrorsBefore = diagnostics->numErrors();
+ varyingValidator.validate(diagnostics);
+ return (diagnostics->numErrors() == numErrorsBefore);
+}
+
+} // namespace sh \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.h b/src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.h
new file mode 100644
index 0000000000..1e53977c68
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) 2002-2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The ValidateVaryingLocations function checks if there exists location conflicts on shader
+// varyings.
+//
+
+#ifndef COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_
+#define COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_
+
+#include "GLSLANG/ShaderVars.h"
+
+namespace sh
+{
+
+class TIntermBlock;
+class TDiagnostics;
+
+bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType);
+
+} // namespace sh
+
+#endif \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp b/src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp
deleted file mode 100644
index 3b6aa6a68e..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-//
-// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "angle_gl.h"
-#include "compiler/translator/SymbolTable.h"
-#include "compiler/translator/VariableInfo.h"
-#include "compiler/translator/util.h"
-#include "common/utilities.h"
-
-namespace sh
-{
-
-namespace
-{
-
-BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
-{
- switch (blockStorage)
- {
- case EbsPacked: return BLOCKLAYOUT_PACKED;
- case EbsShared: return BLOCKLAYOUT_SHARED;
- case EbsStd140: return BLOCKLAYOUT_STANDARD;
- default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
- }
-}
-
-void ExpandUserDefinedVariable(const ShaderVariable &variable,
- const std::string &name,
- const std::string &mappedName,
- bool markStaticUse,
- std::vector<ShaderVariable> *expanded);
-
-void ExpandVariable(const ShaderVariable &variable,
- const std::string &name,
- const std::string &mappedName,
- bool markStaticUse,
- std::vector<ShaderVariable> *expanded)
-{
- if (variable.isStruct())
- {
- if (variable.isArray())
- {
- for (unsigned int elementIndex = 0; elementIndex < variable.elementCount();
- elementIndex++)
- {
- std::string lname = name + ::ArrayString(elementIndex);
- std::string lmappedName = mappedName + ::ArrayString(elementIndex);
- ExpandUserDefinedVariable(variable, lname, lmappedName, markStaticUse, expanded);
- }
- }
- else
- {
- ExpandUserDefinedVariable(variable, name, mappedName, markStaticUse, expanded);
- }
- }
- else
- {
- ShaderVariable expandedVar = variable;
-
- expandedVar.name = name;
- expandedVar.mappedName = mappedName;
-
- // Mark all expanded fields as used if the parent is used
- if (markStaticUse)
- {
- expandedVar.staticUse = true;
- }
-
- if (expandedVar.isArray())
- {
- expandedVar.name += "[0]";
- expandedVar.mappedName += "[0]";
- }
-
- expanded->push_back(expandedVar);
- }
-}
-
-void ExpandUserDefinedVariable(const ShaderVariable &variable,
- const std::string &name,
- const std::string &mappedName,
- bool markStaticUse,
- std::vector<ShaderVariable> *expanded)
-{
- ASSERT(variable.isStruct());
-
- const std::vector<ShaderVariable> &fields = variable.fields;
-
- for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
- {
- const ShaderVariable &field = fields[fieldIndex];
- ExpandVariable(field,
- name + "." + field.name,
- mappedName + "." + field.mappedName,
- markStaticUse,
- expanded);
- }
-}
-
-template <class VarT>
-VarT *FindVariable(const TString &name,
- std::vector<VarT> *infoList)
-{
- // TODO(zmo): optimize this function.
- for (size_t ii = 0; ii < infoList->size(); ++ii)
- {
- if ((*infoList)[ii].name.c_str() == name)
- return &((*infoList)[ii]);
- }
-
- return NULL;
-}
-
-}
-
-CollectVariables::CollectVariables(std::vector<sh::Attribute> *attribs,
- std::vector<sh::OutputVariable> *outputVariables,
- std::vector<sh::Uniform> *uniforms,
- std::vector<sh::Varying> *varyings,
- std::vector<sh::InterfaceBlock> *interfaceBlocks,
- ShHashFunction64 hashFunction,
- const TSymbolTable &symbolTable)
- : TIntermTraverser(true, false, false),
- mAttribs(attribs),
- mOutputVariables(outputVariables),
- mUniforms(uniforms),
- mVaryings(varyings),
- mInterfaceBlocks(interfaceBlocks),
- mDepthRangeAdded(false),
- mPointCoordAdded(false),
- mFrontFacingAdded(false),
- mFragCoordAdded(false),
- mInstanceIDAdded(false),
- mPositionAdded(false),
- mPointSizeAdded(false),
- mLastFragDataAdded(false),
- mFragColorAdded(false),
- mFragDataAdded(false),
- mFragDepthEXTAdded(false),
- mFragDepthAdded(false),
- mSecondaryFragColorEXTAdded(false),
- mSecondaryFragDataEXTAdded(false),
- mHashFunction(hashFunction),
- mSymbolTable(symbolTable)
-{
-}
-
-// We want to check whether a uniform/varying is statically used
-// because we only count the used ones in packing computing.
-// Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
-// toward varying counting if they are statically used in a fragment
-// shader.
-void CollectVariables::visitSymbol(TIntermSymbol *symbol)
-{
- ASSERT(symbol != NULL);
- ShaderVariable *var = NULL;
- const TString &symbolName = symbol->getSymbol();
-
- if (IsVarying(symbol->getQualifier()))
- {
- var = FindVariable(symbolName, mVaryings);
- }
- else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
- {
- UNREACHABLE();
- }
- else if (symbolName == "gl_DepthRange")
- {
- ASSERT(symbol->getQualifier() == EvqUniform);
-
- if (!mDepthRangeAdded)
- {
- Uniform info;
- const char kName[] = "gl_DepthRange";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_STRUCT_ANGLEX;
- info.arraySize = 0;
- info.precision = GL_NONE;
- info.staticUse = true;
-
- ShaderVariable nearInfo;
- const char kNearName[] = "near";
- nearInfo.name = kNearName;
- nearInfo.mappedName = kNearName;
- nearInfo.type = GL_FLOAT;
- nearInfo.arraySize = 0;
- nearInfo.precision = GL_HIGH_FLOAT;
- nearInfo.staticUse = true;
-
- ShaderVariable farInfo;
- const char kFarName[] = "far";
- farInfo.name = kFarName;
- farInfo.mappedName = kFarName;
- farInfo.type = GL_FLOAT;
- farInfo.arraySize = 0;
- farInfo.precision = GL_HIGH_FLOAT;
- farInfo.staticUse = true;
-
- ShaderVariable diffInfo;
- const char kDiffName[] = "diff";
- diffInfo.name = kDiffName;
- diffInfo.mappedName = kDiffName;
- diffInfo.type = GL_FLOAT;
- diffInfo.arraySize = 0;
- diffInfo.precision = GL_HIGH_FLOAT;
- diffInfo.staticUse = true;
-
- info.fields.push_back(nearInfo);
- info.fields.push_back(farInfo);
- info.fields.push_back(diffInfo);
-
- mUniforms->push_back(info);
- mDepthRangeAdded = true;
- }
- }
- else
- {
- switch (symbol->getQualifier())
- {
- case EvqAttribute:
- case EvqVertexIn:
- var = FindVariable(symbolName, mAttribs);
- break;
- case EvqFragmentOut:
- var = FindVariable(symbolName, mOutputVariables);
- break;
- case EvqUniform:
- {
- const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
- if (interfaceBlock)
- {
- InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
- ASSERT(namedBlock);
- var = FindVariable(symbolName, &namedBlock->fields);
-
- // Set static use on the parent interface block here
- namedBlock->staticUse = true;
- }
- else
- {
- var = FindVariable(symbolName, mUniforms);
- }
-
- // It's an internal error to reference an undefined user uniform
- ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
- }
- break;
- case EvqFragCoord:
- if (!mFragCoordAdded)
- {
- Varying info;
- const char kName[] = "gl_FragCoord";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC4;
- info.arraySize = 0;
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
- mVaryings->push_back(info);
- mFragCoordAdded = true;
- }
- return;
- case EvqFrontFacing:
- if (!mFrontFacingAdded)
- {
- Varying info;
- const char kName[] = "gl_FrontFacing";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_BOOL;
- info.arraySize = 0;
- info.precision = GL_NONE;
- info.staticUse = true;
- info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
- mVaryings->push_back(info);
- mFrontFacingAdded = true;
- }
- return;
- case EvqPointCoord:
- if (!mPointCoordAdded)
- {
- Varying info;
- const char kName[] = "gl_PointCoord";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC2;
- info.arraySize = 0;
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
- mVaryings->push_back(info);
- mPointCoordAdded = true;
- }
- return;
- case EvqInstanceID:
- if (!mInstanceIDAdded)
- {
- Attribute info;
- const char kName[] = "gl_InstanceID";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_INT;
- info.arraySize = 0;
- info.precision = GL_HIGH_INT; // Defined by spec.
- info.staticUse = true;
- info.location = -1;
- mAttribs->push_back(info);
- mInstanceIDAdded = true;
- }
- return;
- case EvqPosition:
- if (!mPositionAdded)
- {
- Varying info;
- const char kName[] = "gl_Position";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC4;
- info.arraySize = 0;
- info.precision = GL_HIGH_FLOAT; // Defined by spec.
- info.staticUse = true;
- info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
- mVaryings->push_back(info);
- mPositionAdded = true;
- }
- return;
- case EvqPointSize:
- if (!mPointSizeAdded)
- {
- Varying info;
- const char kName[] = "gl_PointSize";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT;
- info.arraySize = 0;
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
- mVaryings->push_back(info);
- mPointSizeAdded = true;
- }
- return;
- case EvqLastFragData:
- if (!mLastFragDataAdded)
- {
- Varying info;
- const char kName[] = "gl_LastFragData";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC4;
- info.arraySize = static_cast<const TVariable*>(mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))->getConstPointer()->getIConst();
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
- mVaryings->push_back(info);
- mLastFragDataAdded = true;
- }
- return;
- case EvqFragColor:
- if (!mFragColorAdded)
- {
- OutputVariable info;
- const char kName[] = "gl_FragColor";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC4;
- info.arraySize = 0;
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- mOutputVariables->push_back(info);
- mFragColorAdded = true;
- }
- return;
- case EvqFragData:
- if (!mFragDataAdded)
- {
- OutputVariable info;
- const char kName[] = "gl_FragData";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC4;
- info.arraySize = static_cast<const TVariable *>(
- mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))
- ->getConstPointer()
- ->getIConst();
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- mOutputVariables->push_back(info);
- mFragDataAdded = true;
- }
- return;
- case EvqFragDepthEXT:
- if (!mFragDepthEXTAdded)
- {
- OutputVariable info;
- const char kName[] = "gl_FragDepthEXT";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT;
- info.arraySize = 0;
- info.precision =
- GLVariablePrecision(static_cast<const TVariable *>(
- mSymbolTable.findBuiltIn("gl_FragDepthEXT", 100))
- ->getType());
- info.staticUse = true;
- mOutputVariables->push_back(info);
- mFragDepthEXTAdded = true;
- }
- return;
- case EvqFragDepth:
- if (!mFragDepthAdded)
- {
- OutputVariable info;
- const char kName[] = "gl_FragDepth";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT;
- info.arraySize = 0;
- info.precision = GL_HIGH_FLOAT;
- info.staticUse = true;
- mOutputVariables->push_back(info);
- mFragDepthAdded = true;
- }
- return;
- case EvqSecondaryFragColorEXT:
- if (!mSecondaryFragColorEXTAdded)
- {
- OutputVariable info;
- const char kName[] = "gl_SecondaryFragColorEXT";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC4;
- info.arraySize = 0;
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- mOutputVariables->push_back(info);
- mSecondaryFragColorEXTAdded = true;
- }
- return;
- case EvqSecondaryFragDataEXT:
- if (!mSecondaryFragDataEXTAdded)
- {
- OutputVariable info;
- const char kName[] = "gl_SecondaryFragDataEXT";
- info.name = kName;
- info.mappedName = kName;
- info.type = GL_FLOAT_VEC4;
-
- const TVariable *maxDualSourceDrawBuffersVar = static_cast<const TVariable *>(
- mSymbolTable.findBuiltIn("gl_MaxDualSourceDrawBuffersEXT", 100));
- info.arraySize = maxDualSourceDrawBuffersVar->getConstPointer()->getIConst();
- info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
- info.staticUse = true;
- mOutputVariables->push_back(info);
- mSecondaryFragDataEXTAdded = true;
- }
- return;
- default:
- break;
- }
- }
- if (var)
- {
- var->staticUse = true;
- }
-}
-
-class NameHashingTraverser : public GetVariableTraverser
-{
- public:
- NameHashingTraverser(ShHashFunction64 hashFunction,
- const TSymbolTable &symbolTable)
- : GetVariableTraverser(symbolTable),
- mHashFunction(hashFunction)
- {}
-
- private:
- void visitVariable(ShaderVariable *variable) override
- {
- TString stringName = TString(variable->name.c_str());
- variable->mappedName = TIntermTraverser::hash(stringName, mHashFunction).c_str();
- }
-
- ShHashFunction64 mHashFunction;
-};
-
-// Attributes, which cannot have struct fields, are a special case
-template <>
-void CollectVariables::visitVariable(const TIntermSymbol *variable,
- std::vector<Attribute> *infoList) const
-{
- ASSERT(variable);
- const TType &type = variable->getType();
- ASSERT(!type.getStruct());
-
- Attribute attribute;
-
- attribute.type = GLVariableType(type);
- attribute.precision = GLVariablePrecision(type);
- attribute.name = variable->getSymbol().c_str();
- attribute.arraySize = static_cast<unsigned int>(type.getArraySize());
- attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
- attribute.location = variable->getType().getLayoutQualifier().location;
-
- infoList->push_back(attribute);
-}
-
-template <>
-void CollectVariables::visitVariable(const TIntermSymbol *variable,
- std::vector<OutputVariable> *infoList) const
-{
- ASSERT(variable);
- const TType &type = variable->getType();
- ASSERT(!type.getStruct());
-
- OutputVariable attribute;
-
- attribute.type = GLVariableType(type);
- attribute.precision = GLVariablePrecision(type);
- attribute.name = variable->getSymbol().c_str();
- attribute.arraySize = static_cast<unsigned int>(type.getArraySize());
- attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
- attribute.location = variable->getType().getLayoutQualifier().location;
-
- infoList->push_back(attribute);
-}
-
-template <>
-void CollectVariables::visitVariable(const TIntermSymbol *variable,
- std::vector<InterfaceBlock> *infoList) const
-{
- InterfaceBlock interfaceBlock;
- const TInterfaceBlock *blockType = variable->getType().getInterfaceBlock();
- ASSERT(blockType);
-
- interfaceBlock.name = blockType->name().c_str();
- interfaceBlock.mappedName =
- TIntermTraverser::hash(blockType->name().c_str(), mHashFunction).c_str();
- interfaceBlock.instanceName = (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
- interfaceBlock.arraySize = variable->getArraySize();
- interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
- interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage());
-
- // Gather field information
- for (const TField *field : blockType->fields())
- {
- const TType &fieldType = *field->type();
-
- NameHashingTraverser traverser(mHashFunction, mSymbolTable);
- traverser.traverse(fieldType, field->name(), &interfaceBlock.fields);
-
- interfaceBlock.fields.back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
- }
-
- infoList->push_back(interfaceBlock);
-}
-
-template <typename VarT>
-void CollectVariables::visitVariable(const TIntermSymbol *variable,
- std::vector<VarT> *infoList) const
-{
- NameHashingTraverser traverser(mHashFunction, mSymbolTable);
- traverser.traverse(variable->getType(), variable->getSymbol(), infoList);
-}
-
-template <typename VarT>
-void CollectVariables::visitInfoList(const TIntermSequence &sequence,
- std::vector<VarT> *infoList) const
-{
- for (size_t seqIndex = 0; seqIndex < sequence.size(); seqIndex++)
- {
- const TIntermSymbol *variable = sequence[seqIndex]->getAsSymbolNode();
- // The only case in which the sequence will not contain a
- // TIntermSymbol node is initialization. It will contain a
- // TInterBinary node in that case. Since attributes, uniforms,
- // and varyings cannot be initialized in a shader, we must have
- // only TIntermSymbol nodes in the sequence.
- ASSERT(variable != NULL);
- visitVariable(variable, infoList);
- }
-}
-
-bool CollectVariables::visitAggregate(Visit, TIntermAggregate *node)
-{
- bool visitChildren = true;
-
- switch (node->getOp())
- {
- case EOpDeclaration:
- {
- const TIntermSequence &sequence = *(node->getSequence());
- ASSERT(!sequence.empty());
-
- const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
- TQualifier qualifier = typedNode.getQualifier();
-
- if (typedNode.getBasicType() == EbtInterfaceBlock)
- {
- visitInfoList(sequence, mInterfaceBlocks);
- visitChildren = false;
- }
- else if (qualifier == EvqAttribute || qualifier == EvqVertexIn ||
- qualifier == EvqFragmentOut || qualifier == EvqUniform ||
- IsVarying(qualifier))
- {
- switch (qualifier)
- {
- case EvqAttribute:
- case EvqVertexIn:
- visitInfoList(sequence, mAttribs);
- break;
- case EvqFragmentOut:
- visitInfoList(sequence, mOutputVariables);
- break;
- case EvqUniform:
- visitInfoList(sequence, mUniforms);
- break;
- default:
- visitInfoList(sequence, mVaryings);
- break;
- }
-
- visitChildren = false;
- }
- break;
- }
- default: break;
- }
-
- return visitChildren;
-}
-
-bool CollectVariables::visitBinary(Visit, TIntermBinary *binaryNode)
-{
- if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
- {
- // NOTE: we do not determine static use for individual blocks of an array
- TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
- ASSERT(blockNode);
-
- TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
- ASSERT(constantUnion);
-
- const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
- InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
- ASSERT(namedBlock);
- namedBlock->staticUse = true;
-
- unsigned int fieldIndex = constantUnion->getUConst(0);
- ASSERT(fieldIndex < namedBlock->fields.size());
- namedBlock->fields[fieldIndex].staticUse = true;
- return false;
- }
-
- return true;
-}
-
-void ExpandUniforms(const std::vector<Uniform> &compact,
- std::vector<ShaderVariable> *expanded)
-{
- for (size_t variableIndex = 0; variableIndex < compact.size(); variableIndex++)
- {
- const ShaderVariable &variable = compact[variableIndex];
- ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse, expanded);
- }
-}
-
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/VariableInfo.h b/src/3rdparty/angle/src/compiler/translator/VariableInfo.h
deleted file mode 100644
index 9498e9b3a0..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/VariableInfo.h
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_VARIABLEINFO_H_
-#define COMPILER_TRANSLATOR_VARIABLEINFO_H_
-
-#include <GLSLANG/ShaderLang.h>
-
-#include "compiler/translator/IntermNode.h"
-
-class TSymbolTable;
-
-namespace sh
-{
-
-// Traverses intermediate tree to collect all attributes, uniforms, varyings.
-class CollectVariables : public TIntermTraverser
-{
- public:
- CollectVariables(std::vector<Attribute> *attribs,
- std::vector<OutputVariable> *outputVariables,
- std::vector<Uniform> *uniforms,
- std::vector<Varying> *varyings,
- std::vector<InterfaceBlock> *interfaceBlocks,
- ShHashFunction64 hashFunction,
- const TSymbolTable &symbolTable);
-
- void visitSymbol(TIntermSymbol *symbol) override;
- bool visitAggregate(Visit, TIntermAggregate *node) override;
- bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
-
- private:
- template <typename VarT>
- void visitVariable(const TIntermSymbol *variable, std::vector<VarT> *infoList) const;
-
- template <typename VarT>
- void visitInfoList(const TIntermSequence &sequence, std::vector<VarT> *infoList) const;
-
- std::vector<Attribute> *mAttribs;
- std::vector<OutputVariable> *mOutputVariables;
- std::vector<Uniform> *mUniforms;
- std::vector<Varying> *mVaryings;
- std::vector<InterfaceBlock> *mInterfaceBlocks;
-
- std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
-
- bool mDepthRangeAdded;
- bool mPointCoordAdded;
- bool mFrontFacingAdded;
- bool mFragCoordAdded;
-
- bool mInstanceIDAdded;
- bool mPositionAdded;
- bool mPointSizeAdded;
- bool mLastFragDataAdded;
- bool mFragColorAdded;
- bool mFragDataAdded;
- bool mFragDepthEXTAdded;
- bool mFragDepthAdded;
- bool mSecondaryFragColorEXTAdded;
- bool mSecondaryFragDataEXTAdded;
-
- ShHashFunction64 mHashFunction;
-
- const TSymbolTable &mSymbolTable;
-};
-
-// Expand struct uniforms to flattened lists of split variables
-void ExpandUniforms(const std::vector<Uniform> &compact,
- std::vector<ShaderVariable> *expanded);
-
-}
-
-#endif // COMPILER_TRANSLATOR_VARIABLEINFO_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/VariablePacker.cpp b/src/3rdparty/angle/src/compiler/translator/VariablePacker.cpp
index e69052162a..6dd396ff02 100644
--- a/src/3rdparty/angle/src/compiler/translator/VariablePacker.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/VariablePacker.cpp
@@ -3,6 +3,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// Check whether variables fit within packing limits according to the packing rules from the GLSL ES
+// 1.00.17 spec, Appendix A, section 7.
#include <algorithm>
@@ -11,259 +13,401 @@
#include "compiler/translator/VariablePacker.h"
#include "common/utilities.h"
-int VariablePacker::GetNumComponentsPerRow(sh::GLenum type)
+namespace sh
{
- switch (type)
+
+namespace
+{
+
+// Expand the variable so that struct variables are split into their individual fields.
+// Will not set the mappedName or staticUse fields on the expanded variables.
+void ExpandVariable(const ShaderVariable &variable,
+ const std::string &name,
+ std::vector<ShaderVariable> *expanded);
+
+void ExpandStructVariable(const ShaderVariable &variable,
+ const std::string &name,
+ std::vector<ShaderVariable> *expanded)
+{
+ ASSERT(variable.isStruct());
+
+ const std::vector<ShaderVariable> &fields = variable.fields;
+
+ for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
{
- case GL_FLOAT_MAT4:
- case GL_FLOAT_MAT2:
- case GL_FLOAT_MAT2x4:
- case GL_FLOAT_MAT3x4:
- case GL_FLOAT_MAT4x2:
- case GL_FLOAT_MAT4x3:
- case GL_FLOAT_VEC4:
- case GL_INT_VEC4:
- case GL_BOOL_VEC4:
- case GL_UNSIGNED_INT_VEC4:
- return 4;
- case GL_FLOAT_MAT3:
- case GL_FLOAT_MAT2x3:
- case GL_FLOAT_MAT3x2:
- case GL_FLOAT_VEC3:
- case GL_INT_VEC3:
- case GL_BOOL_VEC3:
- case GL_UNSIGNED_INT_VEC3:
- return 3;
- case GL_FLOAT_VEC2:
- case GL_INT_VEC2:
- case GL_BOOL_VEC2:
- case GL_UNSIGNED_INT_VEC2:
- return 2;
- default:
- ASSERT(gl::VariableComponentCount(type) == 1);
- return 1;
+ const ShaderVariable &field = fields[fieldIndex];
+ ExpandVariable(field, name + "." + field.name, expanded);
}
}
-int VariablePacker::GetNumRows(sh::GLenum type)
+void ExpandStructArrayVariable(const ShaderVariable &variable,
+ unsigned int arrayNestingIndex,
+ const std::string &name,
+ std::vector<ShaderVariable> *expanded)
{
- switch (type)
+ // Nested arrays are processed starting from outermost (arrayNestingIndex 0u) and ending at the
+ // innermost.
+ const unsigned int currentArraySize = variable.getNestedArraySize(arrayNestingIndex);
+ for (unsigned int arrayElement = 0u; arrayElement < currentArraySize; ++arrayElement)
{
- case GL_FLOAT_MAT4:
- case GL_FLOAT_MAT2x4:
- case GL_FLOAT_MAT3x4:
- case GL_FLOAT_MAT4x3:
- case GL_FLOAT_MAT4x2:
- return 4;
- case GL_FLOAT_MAT3:
- case GL_FLOAT_MAT2x3:
- case GL_FLOAT_MAT3x2:
- return 3;
- case GL_FLOAT_MAT2:
- return 2;
- default:
- ASSERT(gl::VariableRowCount(type) == 1);
- return 1;
+ const std::string elementName = name + ArrayString(arrayElement);
+ if (arrayNestingIndex + 1u < variable.arraySizes.size())
+ {
+ ExpandStructArrayVariable(variable, arrayNestingIndex + 1u, elementName, expanded);
+ }
+ else
+ {
+ ExpandStructVariable(variable, elementName, expanded);
+ }
}
}
+void ExpandVariable(const ShaderVariable &variable,
+ const std::string &name,
+ std::vector<ShaderVariable> *expanded)
+{
+ if (variable.isStruct())
+ {
+ if (variable.isArray())
+ {
+ ExpandStructArrayVariable(variable, 0u, name, expanded);
+ }
+ else
+ {
+ ExpandStructVariable(variable, name, expanded);
+ }
+ }
+ else
+ {
+ ShaderVariable expandedVar = variable;
+ expandedVar.name = name;
+
+ expanded->push_back(expandedVar);
+ }
+}
+
+int GetVariablePackingRows(const ShaderVariable &variable)
+{
+ return GetTypePackingRows(variable.type) * variable.getArraySizeProduct();
+}
+
+class VariablePacker
+{
+ public:
+ bool checkExpandedVariablesWithinPackingLimits(unsigned int maxVectors,
+ std::vector<sh::ShaderVariable> *variables);
+
+ private:
+ static const int kNumColumns = 4;
+ static const unsigned kColumnMask = (1 << kNumColumns) - 1;
+
+ unsigned makeColumnFlags(int column, int numComponentsPerRow);
+ void fillColumns(int topRow, int numRows, int column, int numComponentsPerRow);
+ bool searchColumn(int column, int numRows, int *destRow, int *destSize);
+
+ int topNonFullRow_;
+ int bottomNonFullRow_;
+ int maxRows_;
+ std::vector<unsigned> rows_;
+};
+
struct TVariableInfoComparer
{
bool operator()(const sh::ShaderVariable &lhs, const sh::ShaderVariable &rhs) const
{
int lhsSortOrder = gl::VariableSortOrder(lhs.type);
int rhsSortOrder = gl::VariableSortOrder(rhs.type);
- if (lhsSortOrder != rhsSortOrder) {
+ if (lhsSortOrder != rhsSortOrder)
+ {
return lhsSortOrder < rhsSortOrder;
}
// Sort by largest first.
- return lhs.arraySize > rhs.arraySize;
+ return lhs.getArraySizeProduct() > rhs.getArraySizeProduct();
}
};
unsigned VariablePacker::makeColumnFlags(int column, int numComponentsPerRow)
{
- return ((kColumnMask << (kNumColumns - numComponentsPerRow)) &
- kColumnMask) >> column;
+ return ((kColumnMask << (kNumColumns - numComponentsPerRow)) & kColumnMask) >> column;
}
void VariablePacker::fillColumns(int topRow, int numRows, int column, int numComponentsPerRow)
{
unsigned columnFlags = makeColumnFlags(column, numComponentsPerRow);
- for (int r = 0; r < numRows; ++r) {
+ for (int r = 0; r < numRows; ++r)
+ {
int row = topRow + r;
ASSERT((rows_[row] & columnFlags) == 0);
rows_[row] |= columnFlags;
}
}
-bool VariablePacker::searchColumn(int column, int numRows, int* destRow, int* destSize)
+bool VariablePacker::searchColumn(int column, int numRows, int *destRow, int *destSize)
{
ASSERT(destRow);
- for (; topNonFullRow_ < maxRows_ && rows_[topNonFullRow_] == kColumnMask;
- ++topNonFullRow_) {
+ for (; topNonFullRow_ < maxRows_ && rows_[topNonFullRow_] == kColumnMask; ++topNonFullRow_)
+ {
}
- for (; bottomNonFullRow_ >= 0 && rows_[bottomNonFullRow_] == kColumnMask;
- --bottomNonFullRow_) {
+ for (; bottomNonFullRow_ >= 0 && rows_[bottomNonFullRow_] == kColumnMask; --bottomNonFullRow_)
+ {
}
- if (bottomNonFullRow_ - topNonFullRow_ + 1 < numRows) {
+ if (bottomNonFullRow_ - topNonFullRow_ + 1 < numRows)
+ {
return false;
}
unsigned columnFlags = makeColumnFlags(column, 1);
- int topGoodRow = 0;
- int smallestGoodTop = -1;
+ int topGoodRow = 0;
+ int smallestGoodTop = -1;
int smallestGoodSize = maxRows_ + 1;
- int bottomRow = bottomNonFullRow_ + 1;
- bool found = false;
- for (int row = topNonFullRow_; row <= bottomRow; ++row) {
+ int bottomRow = bottomNonFullRow_ + 1;
+ bool found = false;
+ for (int row = topNonFullRow_; row <= bottomRow; ++row)
+ {
bool rowEmpty = row < bottomRow ? ((rows_[row] & columnFlags) == 0) : false;
- if (rowEmpty) {
- if (!found) {
+ if (rowEmpty)
+ {
+ if (!found)
+ {
topGoodRow = row;
- found = true;
+ found = true;
}
- } else {
- if (found) {
+ }
+ else
+ {
+ if (found)
+ {
int size = row - topGoodRow;
- if (size >= numRows && size < smallestGoodSize) {
+ if (size >= numRows && size < smallestGoodSize)
+ {
smallestGoodSize = size;
- smallestGoodTop = topGoodRow;
+ smallestGoodTop = topGoodRow;
}
}
found = false;
}
}
- if (smallestGoodTop < 0) {
+ if (smallestGoodTop < 0)
+ {
return false;
}
*destRow = smallestGoodTop;
- if (destSize) {
+ if (destSize)
+ {
*destSize = smallestGoodSize;
}
return true;
}
-template <typename VarT>
-bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int maxVectors,
- const std::vector<VarT> &in_variables)
+bool VariablePacker::checkExpandedVariablesWithinPackingLimits(
+ unsigned int maxVectors,
+ std::vector<sh::ShaderVariable> *variables)
{
ASSERT(maxVectors > 0);
- maxRows_ = maxVectors;
- topNonFullRow_ = 0;
+ maxRows_ = maxVectors;
+ topNonFullRow_ = 0;
bottomNonFullRow_ = maxRows_ - 1;
- std::vector<VarT> variables(in_variables);
// Check whether each variable fits in the available vectors.
- for (size_t i = 0; i < variables.size(); i++) {
- const sh::ShaderVariable &variable = variables[i];
- if (variable.elementCount() > maxVectors / GetNumRows(variable.type)) {
+ for (const sh::ShaderVariable &variable : *variables)
+ {
+ // Structs should have been expanded before reaching here.
+ ASSERT(!variable.isStruct());
+ if (variable.getArraySizeProduct() > maxVectors / GetTypePackingRows(variable.type))
+ {
return false;
}
}
// As per GLSL 1.017 Appendix A, Section 7 variables are packed in specific
// order by type, then by size of array, largest first.
- std::sort(variables.begin(), variables.end(), TVariableInfoComparer());
+ std::sort(variables->begin(), variables->end(), TVariableInfoComparer());
rows_.clear();
rows_.resize(maxVectors, 0);
// Packs the 4 column variables.
size_t ii = 0;
- for (; ii < variables.size(); ++ii) {
- const sh::ShaderVariable &variable = variables[ii];
- if (GetNumComponentsPerRow(variable.type) != 4) {
+ for (; ii < variables->size(); ++ii)
+ {
+ const sh::ShaderVariable &variable = (*variables)[ii];
+ if (GetTypePackingComponentsPerRow(variable.type) != 4)
+ {
break;
}
- topNonFullRow_ += GetNumRows(variable.type) * variable.elementCount();
+ topNonFullRow_ += GetVariablePackingRows(variable);
}
- if (topNonFullRow_ > maxRows_) {
+ if (topNonFullRow_ > maxRows_)
+ {
return false;
}
// Packs the 3 column variables.
int num3ColumnRows = 0;
- for (; ii < variables.size(); ++ii) {
- const sh::ShaderVariable &variable = variables[ii];
- if (GetNumComponentsPerRow(variable.type) != 3) {
+ for (; ii < variables->size(); ++ii)
+ {
+ const sh::ShaderVariable &variable = (*variables)[ii];
+ if (GetTypePackingComponentsPerRow(variable.type) != 3)
+ {
break;
}
- num3ColumnRows += GetNumRows(variable.type) * variable.elementCount();
+ num3ColumnRows += GetVariablePackingRows(variable);
}
- if (topNonFullRow_ + num3ColumnRows > maxRows_) {
+ if (topNonFullRow_ + num3ColumnRows > maxRows_)
+ {
return false;
}
fillColumns(topNonFullRow_, num3ColumnRows, 0, 3);
// Packs the 2 column variables.
- int top2ColumnRow = topNonFullRow_ + num3ColumnRows;
- int twoColumnRowsAvailable = maxRows_ - top2ColumnRow;
+ int top2ColumnRow = topNonFullRow_ + num3ColumnRows;
+ int twoColumnRowsAvailable = maxRows_ - top2ColumnRow;
int rowsAvailableInColumns01 = twoColumnRowsAvailable;
int rowsAvailableInColumns23 = twoColumnRowsAvailable;
- for (; ii < variables.size(); ++ii) {
- const sh::ShaderVariable &variable = variables[ii];
- if (GetNumComponentsPerRow(variable.type) != 2) {
+ for (; ii < variables->size(); ++ii)
+ {
+ const sh::ShaderVariable &variable = (*variables)[ii];
+ if (GetTypePackingComponentsPerRow(variable.type) != 2)
+ {
break;
}
- int numRows = GetNumRows(variable.type) * variable.elementCount();
- if (numRows <= rowsAvailableInColumns01) {
+ int numRows = GetVariablePackingRows(variable);
+ if (numRows <= rowsAvailableInColumns01)
+ {
rowsAvailableInColumns01 -= numRows;
- } else if (numRows <= rowsAvailableInColumns23) {
+ }
+ else if (numRows <= rowsAvailableInColumns23)
+ {
rowsAvailableInColumns23 -= numRows;
- } else {
+ }
+ else
+ {
return false;
}
}
- int numRowsUsedInColumns01 =
- twoColumnRowsAvailable - rowsAvailableInColumns01;
- int numRowsUsedInColumns23 =
- twoColumnRowsAvailable - rowsAvailableInColumns23;
+ int numRowsUsedInColumns01 = twoColumnRowsAvailable - rowsAvailableInColumns01;
+ int numRowsUsedInColumns23 = twoColumnRowsAvailable - rowsAvailableInColumns23;
fillColumns(top2ColumnRow, numRowsUsedInColumns01, 0, 2);
- fillColumns(maxRows_ - numRowsUsedInColumns23, numRowsUsedInColumns23,
- 2, 2);
+ fillColumns(maxRows_ - numRowsUsedInColumns23, numRowsUsedInColumns23, 2, 2);
// Packs the 1 column variables.
- for (; ii < variables.size(); ++ii) {
- const sh::ShaderVariable &variable = variables[ii];
- ASSERT(1 == GetNumComponentsPerRow(variable.type));
- int numRows = GetNumRows(variable.type) * variable.elementCount();
+ for (; ii < variables->size(); ++ii)
+ {
+ const sh::ShaderVariable &variable = (*variables)[ii];
+ ASSERT(1 == GetTypePackingComponentsPerRow(variable.type));
+ int numRows = GetVariablePackingRows(variable);
int smallestColumn = -1;
- int smallestSize = maxRows_ + 1;
- int topRow = -1;
- for (int column = 0; column < kNumColumns; ++column) {
- int row = 0;
+ int smallestSize = maxRows_ + 1;
+ int topRow = -1;
+ for (int column = 0; column < kNumColumns; ++column)
+ {
+ int row = 0;
int size = 0;
- if (searchColumn(column, numRows, &row, &size)) {
- if (size < smallestSize) {
- smallestSize = size;
+ if (searchColumn(column, numRows, &row, &size))
+ {
+ if (size < smallestSize)
+ {
+ smallestSize = size;
smallestColumn = column;
- topRow = row;
+ topRow = row;
}
}
}
- if (smallestColumn < 0) {
+ if (smallestColumn < 0)
+ {
return false;
}
fillColumns(topRow, numRows, smallestColumn, 1);
}
- ASSERT(variables.size() == ii);
+ ASSERT(variables->size() == ii);
return true;
}
-// Instantiate all possible variable packings
-template bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int, const std::vector<sh::ShaderVariable> &);
-template bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int, const std::vector<sh::Attribute> &);
-template bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int, const std::vector<sh::Uniform> &);
-template bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int, const std::vector<sh::Varying> &);
+} // anonymous namespace
+
+int GetTypePackingComponentsPerRow(sh::GLenum type)
+{
+ switch (type)
+ {
+ case GL_FLOAT_MAT4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ case GL_BOOL_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ return 4;
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ case GL_BOOL_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ return 3;
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_BOOL_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ return 2;
+ default:
+ ASSERT(gl::VariableComponentCount(type) == 1);
+ return 1;
+ }
+}
+
+int GetTypePackingRows(sh::GLenum type)
+{
+ switch (type)
+ {
+ case GL_FLOAT_MAT4:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_MAT4x2:
+ return 4;
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT3x2:
+ return 3;
+ case GL_FLOAT_MAT2:
+ return 2;
+ default:
+ ASSERT(gl::VariableRowCount(type) == 1);
+ return 1;
+ }
+}
+
+template <typename T>
+bool CheckVariablesInPackingLimits(unsigned int maxVectors, const std::vector<T> &variables)
+{
+ VariablePacker packer;
+ std::vector<sh::ShaderVariable> expandedVariables;
+ for (const ShaderVariable &variable : variables)
+ {
+ ExpandVariable(variable, variable.name, &expandedVariables);
+ }
+ return packer.checkExpandedVariablesWithinPackingLimits(maxVectors, &expandedVariables);
+}
+
+template bool CheckVariablesInPackingLimits<ShaderVariable>(
+ unsigned int maxVectors,
+ const std::vector<ShaderVariable> &variables);
+template bool CheckVariablesInPackingLimits<Uniform>(unsigned int maxVectors,
+ const std::vector<Uniform> &variables);
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/VariablePacker.h b/src/3rdparty/angle/src/compiler/translator/VariablePacker.h
index 9c80eea618..36b2104cd0 100644
--- a/src/3rdparty/angle/src/compiler/translator/VariablePacker.h
+++ b/src/3rdparty/angle/src/compiler/translator/VariablePacker.h
@@ -3,39 +3,30 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
+// Check whether variables fit within packing limits according to the packing rules from the GLSL ES
+// 1.00.17 spec, Appendix A, section 7.
#ifndef COMPILER_TRANSLATOR_VARIABLEPACKER_H_
#define COMPILER_TRANSLATOR_VARIABLEPACKER_H_
#include <vector>
-#include "compiler/translator/VariableInfo.h"
-class VariablePacker {
- public:
- // Returns true if the passed in variables pack in maxVectors following
- // the packing rules from the GLSL 1.017 spec, Appendix A, section 7.
- template <typename VarT>
- bool CheckVariablesWithinPackingLimits(unsigned int maxVectors,
- const std::vector<VarT> &in_variables);
+#include <GLSLANG/ShaderLang.h>
- // Gets how many components in a row a data type takes.
- static int GetNumComponentsPerRow(sh::GLenum type);
+namespace sh
+{
- // Gets how many rows a data type takes.
- static int GetNumRows(sh::GLenum type);
+// Gets how many components in a row a data type takes.
+int GetTypePackingComponentsPerRow(sh::GLenum type);
- private:
- static const int kNumColumns = 4;
- static const unsigned kColumnMask = (1 << kNumColumns) - 1;
+// Gets how many rows a data type takes.
+int GetTypePackingRows(sh::GLenum type);
- unsigned makeColumnFlags(int column, int numComponentsPerRow);
- void fillColumns(int topRow, int numRows, int column, int numComponentsPerRow);
- bool searchColumn(int column, int numRows, int* destRow, int* destSize);
+// Returns true if the passed in variables pack in maxVectors.
+// T should be ShaderVariable or one of the subclasses of ShaderVariable.
+template <typename T>
+bool CheckVariablesInPackingLimits(unsigned int maxVectors, const std::vector<T> &variables);
- int topNonFullRow_;
- int bottomNonFullRow_;
- int maxRows_;
- std::vector<unsigned> rows_;
-};
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_VARIABLEPACKER_H_
+#endif // COMPILER_TRANSLATOR_VARIABLEPACKER_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.cpp b/src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.cpp
new file mode 100644
index 0000000000..1e79a60991
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.cpp
@@ -0,0 +1,284 @@
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// VectorizeVectorScalarArithmetic.cpp: Turn some arithmetic operations that operate on a float
+// vector-scalar pair into vector-vector operations. This is done recursively. Some scalar binary
+// operations inside vector constructors are also turned into vector operations.
+//
+// This is targeted to work around a bug in NVIDIA OpenGL drivers that was reproducible on NVIDIA
+// driver version 387.92. It works around the most common occurrences of the bug.
+
+#include "compiler/translator/VectorizeVectorScalarArithmetic.h"
+
+#include <set>
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class VectorizeVectorScalarArithmeticTraverser : public TIntermTraverser
+{
+ public:
+ VectorizeVectorScalarArithmeticTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable), mReplaced(false)
+ {
+ }
+
+ bool didReplaceScalarsWithVectors() { return mReplaced; }
+ void nextIteration()
+ {
+ mReplaced = false;
+ mModifiedBlocks.clear();
+ }
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ private:
+ // These helpers should only be called from visitAggregate when visiting a constructor.
+ // argBinary is the only argument of the constructor.
+ void replaceMathInsideConstructor(TIntermAggregate *node, TIntermBinary *argBinary);
+ void replaceAssignInsideConstructor(const TIntermAggregate *node,
+ const TIntermBinary *argBinary);
+
+ static TIntermTyped *Vectorize(TIntermTyped *node,
+ TType vectorType,
+ TIntermTraverser::OriginalNode *originalNodeFate);
+
+ bool mReplaced;
+ std::set<const TIntermBlock *> mModifiedBlocks;
+};
+
+TIntermTyped *VectorizeVectorScalarArithmeticTraverser::Vectorize(
+ TIntermTyped *node,
+ TType vectorType,
+ TIntermTraverser::OriginalNode *originalNodeFate)
+{
+ ASSERT(node->isScalar());
+ vectorType.setQualifier(EvqTemporary);
+ TIntermSequence vectorConstructorArgs;
+ vectorConstructorArgs.push_back(node);
+ TIntermAggregate *vectorized =
+ TIntermAggregate::CreateConstructor(vectorType, &vectorConstructorArgs);
+ TIntermTyped *vectorizedFolded = vectorized->fold(nullptr);
+ if (originalNodeFate != nullptr)
+ {
+ if (vectorizedFolded != vectorized)
+ {
+ *originalNodeFate = OriginalNode::IS_DROPPED;
+ }
+ else
+ {
+ *originalNodeFate = OriginalNode::BECOMES_CHILD;
+ }
+ }
+ return vectorizedFolded;
+}
+
+bool VectorizeVectorScalarArithmeticTraverser::visitBinary(Visit /*visit*/, TIntermBinary *node)
+{
+ TIntermTyped *left = node->getLeft();
+ TIntermTyped *right = node->getRight();
+ ASSERT(left);
+ ASSERT(right);
+ switch (node->getOp())
+ {
+ case EOpAdd:
+ case EOpAddAssign:
+ // Only these specific ops are necessary to turn into vector ops.
+ break;
+ default:
+ return true;
+ }
+ if (node->getBasicType() != EbtFloat)
+ {
+ // Only float ops have reproduced the bug.
+ return true;
+ }
+ if (left->isScalar() && right->isVector())
+ {
+ ASSERT(!node->isAssignment());
+ ASSERT(!right->isArray());
+ OriginalNode originalNodeFate;
+ TIntermTyped *leftVectorized = Vectorize(left, right->getType(), &originalNodeFate);
+ queueReplacementWithParent(node, left, leftVectorized, originalNodeFate);
+ mReplaced = true;
+ // Don't replace more nodes in the same subtree on this traversal. However, nodes elsewhere
+ // in the tree may still be replaced.
+ return false;
+ }
+ else if (left->isVector() && right->isScalar())
+ {
+ OriginalNode originalNodeFate;
+ TIntermTyped *rightVectorized = Vectorize(right, left->getType(), &originalNodeFate);
+ queueReplacementWithParent(node, right, rightVectorized, originalNodeFate);
+ mReplaced = true;
+ // Don't replace more nodes in the same subtree on this traversal. However, nodes elsewhere
+ // in the tree may still be replaced.
+ return false;
+ }
+ return true;
+}
+
+void VectorizeVectorScalarArithmeticTraverser::replaceMathInsideConstructor(
+ TIntermAggregate *node,
+ TIntermBinary *argBinary)
+{
+ // Turn:
+ // a * b
+ // into:
+ // gvec(a) * gvec(b)
+
+ TIntermTyped *left = argBinary->getLeft();
+ TIntermTyped *right = argBinary->getRight();
+ ASSERT(left->isScalar() && right->isScalar());
+
+ TType leftVectorizedType = left->getType();
+ leftVectorizedType.setPrimarySize(static_cast<unsigned char>(node->getType().getNominalSize()));
+ TIntermTyped *leftVectorized = Vectorize(left, leftVectorizedType, nullptr);
+ TType rightVectorizedType = right->getType();
+ rightVectorizedType.setPrimarySize(
+ static_cast<unsigned char>(node->getType().getNominalSize()));
+ TIntermTyped *rightVectorized = Vectorize(right, rightVectorizedType, nullptr);
+
+ TIntermBinary *newArg = new TIntermBinary(argBinary->getOp(), leftVectorized, rightVectorized);
+ queueReplacementWithParent(node, argBinary, newArg, OriginalNode::IS_DROPPED);
+}
+
+void VectorizeVectorScalarArithmeticTraverser::replaceAssignInsideConstructor(
+ const TIntermAggregate *node,
+ const TIntermBinary *argBinary)
+{
+ // Turn:
+ // gvec(a *= b);
+ // into:
+ // // This is inserted into the parent block:
+ // gvec s0 = gvec(a);
+ //
+ // // This goes where the gvec constructor used to be:
+ // ((s0 *= b, a = s0.x), s0);
+
+ TIntermTyped *left = argBinary->getLeft();
+ TIntermTyped *right = argBinary->getRight();
+ ASSERT(left->isScalar() && right->isScalar());
+ ASSERT(!left->hasSideEffects());
+
+ TType vecType = node->getType();
+ vecType.setQualifier(EvqTemporary);
+
+ nextTemporaryId();
+ // gvec s0 = gvec(a);
+ // s0 is called "tempAssignmentTarget" below.
+ TIntermTyped *tempAssignmentTargetInitializer = Vectorize(left->deepCopy(), vecType, nullptr);
+ TIntermDeclaration *tempAssignmentTargetDeclaration =
+ createTempInitDeclaration(tempAssignmentTargetInitializer);
+
+ // s0 *= b
+ TOperator compoundAssignmentOp = argBinary->getOp();
+ if (compoundAssignmentOp == EOpMulAssign)
+ {
+ compoundAssignmentOp = EOpVectorTimesScalarAssign;
+ }
+ TIntermBinary *replacementCompoundAssignment =
+ new TIntermBinary(compoundAssignmentOp, createTempSymbol(vecType), right->deepCopy());
+
+ // s0.x
+ TVector<int> swizzleXOffset;
+ swizzleXOffset.push_back(0);
+ TIntermSwizzle *tempAssignmentTargetX =
+ new TIntermSwizzle(createTempSymbol(vecType), swizzleXOffset);
+ // a = s0.x
+ TIntermBinary *replacementAssignBackToTarget =
+ new TIntermBinary(EOpAssign, left->deepCopy(), tempAssignmentTargetX);
+
+ // s0 *= b, a = s0.x
+ TIntermBinary *replacementSequenceLeft =
+ new TIntermBinary(EOpComma, replacementCompoundAssignment, replacementAssignBackToTarget);
+ // (s0 *= b, a = s0.x), s0
+ TIntermBinary *replacementSequence =
+ new TIntermBinary(EOpComma, replacementSequenceLeft, createTempSymbol(vecType));
+
+ insertStatementInParentBlock(tempAssignmentTargetDeclaration);
+ queueReplacement(replacementSequence, OriginalNode::IS_DROPPED);
+}
+
+bool VectorizeVectorScalarArithmeticTraverser::visitAggregate(Visit /*visit*/,
+ TIntermAggregate *node)
+{
+ // Transform scalar binary expressions inside vector constructors.
+ if (!node->isConstructor() || !node->isVector() || node->getSequence()->size() != 1)
+ {
+ return true;
+ }
+ TIntermTyped *argument = node->getSequence()->back()->getAsTyped();
+ ASSERT(argument);
+ if (!argument->isScalar() || argument->getBasicType() != EbtFloat)
+ {
+ return true;
+ }
+ TIntermBinary *argBinary = argument->getAsBinaryNode();
+ if (!argBinary)
+ {
+ return true;
+ }
+
+ // Only specific ops are necessary to change.
+ switch (argBinary->getOp())
+ {
+ case EOpMul:
+ case EOpDiv:
+ {
+ replaceMathInsideConstructor(node, argBinary);
+ mReplaced = true;
+ // Don't replace more nodes in the same subtree on this traversal. However, nodes
+ // elsewhere in the tree may still be replaced.
+ return false;
+ }
+ case EOpMulAssign:
+ case EOpDivAssign:
+ {
+ // The case where the left side has side effects is too complicated to deal with, so we
+ // leave that be.
+ if (!argBinary->getLeft()->hasSideEffects())
+ {
+ const TIntermBlock *parentBlock = getParentBlock();
+ // We can't do more than one insertion to the same block on the same traversal.
+ if (mModifiedBlocks.find(parentBlock) == mModifiedBlocks.end())
+ {
+ replaceAssignInsideConstructor(node, argBinary);
+ mModifiedBlocks.insert(parentBlock);
+ mReplaced = true;
+ // Don't replace more nodes in the same subtree on this traversal.
+ // However, nodes elsewhere in the tree may still be replaced.
+ return false;
+ }
+ }
+ break;
+ }
+ default:
+ return true;
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+void VectorizeVectorScalarArithmetic(TIntermBlock *root, TSymbolTable *symbolTable)
+{
+ VectorizeVectorScalarArithmeticTraverser traverser(symbolTable);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ traverser.updateTree();
+ } while (traverser.didReplaceScalarsWithVectors());
+}
+
+} // namespace sh \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.h b/src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.h
new file mode 100644
index 0000000000..69f092e039
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// VectorizeVectorScalarArithmetic.h: Turn some arithmetic operations that operate on a float
+// vector-scalar pair into vector-vector operations. This is done recursively. Some scalar binary
+// operations inside vector constructors are also turned into vector operations.
+//
+// This is targeted to work around a bug in NVIDIA OpenGL drivers that was reproducible on NVIDIA
+// driver version 387.92. It works around the most common occurrences of the bug.
+
+#ifndef COMPILER_TRANSLATOR_VECTORIZEVECTORSCALARARITHMETIC_H_
+#define COMPILER_TRANSLATOR_VECTORIZEVECTORSCALARARITHMETIC_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+void VectorizeVectorScalarArithmetic(TIntermBlock *root, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VECTORIZEVECTORSCALARARITHMETIC_H_ \ No newline at end of file
diff --git a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp
index c8718daa10..81688765b8 100644
--- a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp
@@ -6,22 +6,40 @@
#include "compiler/translator/VersionGLSL.h"
+#include "angle_gl.h"
+
+namespace sh
+{
+
int ShaderOutputTypeToGLSLVersion(ShShaderOutput output)
{
switch (output)
{
- case SH_GLSL_130_OUTPUT: return GLSL_VERSION_130;
- case SH_GLSL_140_OUTPUT: return GLSL_VERSION_140;
- case SH_GLSL_150_CORE_OUTPUT: return GLSL_VERSION_150;
- case SH_GLSL_330_CORE_OUTPUT: return GLSL_VERSION_330;
- case SH_GLSL_400_CORE_OUTPUT: return GLSL_VERSION_400;
- case SH_GLSL_410_CORE_OUTPUT: return GLSL_VERSION_410;
- case SH_GLSL_420_CORE_OUTPUT: return GLSL_VERSION_420;
- case SH_GLSL_430_CORE_OUTPUT: return GLSL_VERSION_430;
- case SH_GLSL_440_CORE_OUTPUT: return GLSL_VERSION_440;
- case SH_GLSL_450_CORE_OUTPUT: return GLSL_VERSION_450;
- case SH_GLSL_COMPATIBILITY_OUTPUT: return GLSL_VERSION_110;
- default: UNREACHABLE(); return 0;
+ case SH_GLSL_130_OUTPUT:
+ return GLSL_VERSION_130;
+ case SH_GLSL_140_OUTPUT:
+ return GLSL_VERSION_140;
+ case SH_GLSL_150_CORE_OUTPUT:
+ return GLSL_VERSION_150;
+ case SH_GLSL_330_CORE_OUTPUT:
+ return GLSL_VERSION_330;
+ case SH_GLSL_400_CORE_OUTPUT:
+ return GLSL_VERSION_400;
+ case SH_GLSL_410_CORE_OUTPUT:
+ return GLSL_VERSION_410;
+ case SH_GLSL_420_CORE_OUTPUT:
+ return GLSL_VERSION_420;
+ case SH_GLSL_430_CORE_OUTPUT:
+ return GLSL_VERSION_430;
+ case SH_GLSL_440_CORE_OUTPUT:
+ return GLSL_VERSION_440;
+ case SH_GLSL_450_CORE_OUTPUT:
+ return GLSL_VERSION_450;
+ case SH_GLSL_COMPATIBILITY_OUTPUT:
+ return GLSL_VERSION_110;
+ default:
+ UNREACHABLE();
+ return 0;
}
}
@@ -42,9 +60,7 @@ int ShaderOutputTypeToGLSLVersion(ShShaderOutput output)
// GLSL 1.2 relaxed the restriction on arrays, section 5.8: "Variables that
// are built-in types, entire structures or arrays... are all l-values."
//
-TVersionGLSL::TVersionGLSL(sh::GLenum type,
- const TPragma &pragma,
- ShShaderOutput output)
+TVersionGLSL::TVersionGLSL(sh::GLenum type, const TPragma &pragma, ShShaderOutput output)
: TIntermTraverser(true, false, false)
{
mVersion = ShaderOutputTypeToGLSLVersion(output);
@@ -52,6 +68,10 @@ TVersionGLSL::TVersionGLSL(sh::GLenum type,
{
ensureVersionIsAtLeast(GLSL_VERSION_120);
}
+ if (type == GL_COMPUTE_SHADER)
+ {
+ ensureVersionIsAtLeast(GLSL_VERSION_430);
+ }
}
void TVersionGLSL::visitSymbol(TIntermSymbol *node)
@@ -62,75 +82,57 @@ void TVersionGLSL::visitSymbol(TIntermSymbol *node)
}
}
-bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node)
+bool TVersionGLSL::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ const TIntermSequence &sequence = *(node->getSequence());
+ if (sequence.front()->getAsTyped()->getType().isInvariant())
+ {
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ }
+ return true;
+}
+
+bool TVersionGLSL::visitInvariantDeclaration(Visit, TIntermInvariantDeclaration *node)
{
- bool visitChildren = true;
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ return true;
+}
- switch (node->getOp())
+bool TVersionGLSL::visitFunctionPrototype(Visit, TIntermFunctionPrototype *node)
+{
+ const TIntermSequence &params = *(node->getSequence());
+ for (TIntermSequence::const_iterator iter = params.begin(); iter != params.end(); ++iter)
{
- case EOpSequence:
- // We need to visit sequence children to get to global or inner scope.
- visitChildren = true;
- break;
- case EOpDeclaration:
+ const TIntermTyped *param = (*iter)->getAsTyped();
+ if (param->isArray())
{
- const TIntermSequence &sequence = *(node->getSequence());
- if (sequence.front()->getAsTyped()->getType().isInvariant())
+ TQualifier qualifier = param->getQualifier();
+ if ((qualifier == EvqOut) || (qualifier == EvqInOut))
{
ensureVersionIsAtLeast(GLSL_VERSION_120);
+ break;
}
- break;
}
- case EOpInvariantDeclaration:
- ensureVersionIsAtLeast(GLSL_VERSION_120);
- break;
- case EOpParameters:
- {
- const TIntermSequence &params = *(node->getSequence());
- for (TIntermSequence::const_iterator iter = params.begin();
- iter != params.end(); ++iter)
- {
- const TIntermTyped *param = (*iter)->getAsTyped();
- if (param->isArray())
- {
- TQualifier qualifier = param->getQualifier();
- if ((qualifier == EvqOut) || (qualifier == EvqInOut))
- {
- ensureVersionIsAtLeast(GLSL_VERSION_120);
- break;
- }
- }
- }
- // Fully processed. No need to visit children.
- visitChildren = false;
- break;
- }
- case EOpConstructMat2:
- case EOpConstructMat2x3:
- case EOpConstructMat2x4:
- case EOpConstructMat3x2:
- case EOpConstructMat3:
- case EOpConstructMat3x4:
- case EOpConstructMat4x2:
- case EOpConstructMat4x3:
- case EOpConstructMat4:
+ }
+ // Fully processed. No need to visit children.
+ return false;
+}
+
+bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node)
+{
+ if (node->getOp() == EOpConstruct && node->getType().isMatrix())
+ {
+ const TIntermSequence &sequence = *(node->getSequence());
+ if (sequence.size() == 1)
{
- const TIntermSequence &sequence = *(node->getSequence());
- if (sequence.size() == 1)
+ TIntermTyped *typed = sequence.front()->getAsTyped();
+ if (typed && typed->isMatrix())
{
- TIntermTyped *typed = sequence.front()->getAsTyped();
- if (typed && typed->isMatrix())
- {
- ensureVersionIsAtLeast(GLSL_VERSION_120);
- }
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
}
- break;
}
- default:
- break;
}
-
- return visitChildren;
+ return true;
}
void TVersionGLSL::ensureVersionIsAtLeast(int version)
@@ -138,3 +140,4 @@ void TVersionGLSL::ensureVersionIsAtLeast(int version)
mVersion = std::max(version, mVersion);
}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h
index c41069d42d..8b82eb9615 100644
--- a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h
@@ -7,10 +7,13 @@
#ifndef COMPILER_TRANSLATOR_VERSIONGLSL_H_
#define COMPILER_TRANSLATOR_VERSIONGLSL_H_
-#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/Pragma.h"
+namespace sh
+{
+
static const int GLSL_VERSION_110 = 110;
static const int GLSL_VERSION_120 = 120;
static const int GLSL_VERSION_130 = 130;
@@ -56,8 +59,11 @@ class TVersionGLSL : public TIntermTraverser
// Else 110 is returned.
int getVersion() const { return mVersion; }
- void visitSymbol(TIntermSymbol *) override;
- bool visitAggregate(Visit, TIntermAggregate *) override;
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitAggregate(Visit, TIntermAggregate *node) override;
+ bool visitInvariantDeclaration(Visit, TIntermInvariantDeclaration *node) override;
+ bool visitFunctionPrototype(Visit, TIntermFunctionPrototype *node) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
private:
void ensureVersionIsAtLeast(int version);
@@ -65,4 +71,6 @@ class TVersionGLSL : public TIntermTraverser
int mVersion;
};
+} // namespace sh
+
#endif // COMPILER_TRANSLATOR_VERSIONGLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.cpp b/src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.cpp
new file mode 100644
index 0000000000..85a11c998d
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.cpp
@@ -0,0 +1,132 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// WrapSwitchStatementsInBlocks.cpp: Wrap switch statements in blocks and declare all switch-scoped
+// variables there to make the AST compatible with HLSL output.
+//
+// switch (init)
+// {
+// case 0:
+// float f;
+// default:
+// f = 1.0;
+// }
+//
+// becomes
+//
+// {
+// float f;
+// switch (init)
+// {
+// case 0:
+// default:
+// f = 1.0;
+// }
+// }
+
+#include "compiler/translator/WrapSwitchStatementsInBlocks.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class WrapSwitchStatementsInBlocksTraverser : public TIntermTraverser
+{
+ public:
+ WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false), mDidWrap(false)
+ {
+ }
+
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override;
+
+ bool didWrap() const { return mDidWrap; }
+
+ private:
+ bool mDidWrap;
+};
+
+bool WrapSwitchStatementsInBlocksTraverser::visitSwitch(Visit, TIntermSwitch *node)
+{
+ std::vector<TIntermDeclaration *> declarations;
+ TIntermSequence *statementList = node->getStatementList()->getSequence();
+ for (TIntermNode *statement : *statementList)
+ {
+ TIntermDeclaration *asDeclaration = statement->getAsDeclarationNode();
+ if (asDeclaration)
+ {
+ declarations.push_back(asDeclaration);
+ }
+ }
+ if (declarations.empty())
+ {
+ // We don't need to wrap the switch if it doesn't contain declarations as its direct
+ // descendants.
+ return true;
+ }
+
+ TIntermBlock *wrapperBlock = new TIntermBlock();
+ for (TIntermDeclaration *declaration : declarations)
+ {
+ // SeparateDeclarations should have already been run.
+ ASSERT(declaration->getSequence()->size() == 1);
+
+ TIntermDeclaration *declarationInBlock = new TIntermDeclaration();
+ TIntermSymbol *declaratorAsSymbol = declaration->getSequence()->at(0)->getAsSymbolNode();
+ if (declaratorAsSymbol)
+ {
+ // This is a simple declaration like: "float f;"
+ // Remove the declaration from inside the switch and put it in the wrapping block.
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(
+ node->getStatementList(), declaration, emptyReplacement));
+
+ declarationInBlock->appendDeclarator(declaratorAsSymbol->deepCopy());
+ }
+ else
+ {
+ // This is an init declaration like: "float f = 0.0;"
+ // Change the init declaration inside the switch into an assignment and put a plain
+ // declaration in the wrapping block.
+ TIntermBinary *declaratorAsBinary =
+ declaration->getSequence()->at(0)->getAsBinaryNode();
+ ASSERT(declaratorAsBinary);
+
+ TIntermBinary *initAssignment = new TIntermBinary(
+ EOpAssign, declaratorAsBinary->getLeft(), declaratorAsBinary->getRight());
+
+ queueReplacementWithParent(node->getStatementList(), declaration, initAssignment,
+ OriginalNode::IS_DROPPED);
+
+ declarationInBlock->appendDeclarator(declaratorAsBinary->getLeft()->deepCopy());
+ }
+ wrapperBlock->appendStatement(declarationInBlock);
+ }
+
+ wrapperBlock->appendStatement(node);
+ queueReplacement(wrapperBlock, OriginalNode::BECOMES_CHILD);
+ mDidWrap = true;
+
+ // Should be fine to process multiple switch statements, even nesting ones in the same
+ // traversal.
+ return true;
+}
+
+} // anonymous namespace
+
+// Wrap switch statements in the AST into blocks when needed.
+bool WrapSwitchStatementsInBlocks(TIntermBlock *root)
+{
+ WrapSwitchStatementsInBlocksTraverser traverser;
+ root->traverse(&traverser);
+ traverser.updateTree();
+ return traverser.didWrap();
+}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.h b/src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.h
new file mode 100644
index 0000000000..bc0179926d
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// WrapSwitchStatementsInBlocks.h: Wrap switch statements in blocks and declare all switch-scoped
+// variables there to make the AST compatible with HLSL output.
+
+#ifndef COMPILER_TRANSLATOR_WRAPSWITCHSTATEMENTSINBLOCKS_H_
+#define COMPILER_TRANSLATOR_WRAPSWITCHSTATEMENTSINBLOCKS_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+
+// Wrap switch statements in the AST into blocks when needed. Returns true if the AST was changed.
+bool WrapSwitchStatementsInBlocks(TIntermBlock *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_WRAPSWITCHSTATEMENTSINBLOCKS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp b/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp
index ba6322848e..fd8c450c20 100644
--- a/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp
@@ -15,24 +15,105 @@
namespace sh
{
-BlockLayoutEncoder::BlockLayoutEncoder()
- : mCurrentOffset(0)
+namespace
{
+bool IsRowMajorLayout(const InterfaceBlockField &var)
+{
+ return var.isRowMajorLayout;
+}
+
+bool IsRowMajorLayout(const ShaderVariable &var)
+{
+ return false;
+}
+
+template <typename VarT>
+void GetUniformBlockStructMemberInfo(const std::vector<VarT> &fields,
+ const std::string &fieldName,
+ sh::BlockLayoutEncoder *encoder,
+ bool inRowMajorLayout,
+ BlockLayoutMap *blockInfoOut)
+{
+ encoder->enterAggregateType();
+ GetUniformBlockInfo(fields, fieldName, encoder, inRowMajorLayout, blockInfoOut);
+ encoder->exitAggregateType();
+}
+
+template <typename VarT>
+void GetUniformBlockStructArrayMemberInfo(const VarT &field,
+ unsigned int arrayNestingIndex,
+ const std::string &arrayName,
+ sh::BlockLayoutEncoder *encoder,
+ bool inRowMajorLayout,
+ BlockLayoutMap *blockInfoOut)
+{
+ // Nested arrays are processed starting from outermost (arrayNestingIndex 0u) and ending at the
+ // innermost.
+ const unsigned int currentArraySize = field.getNestedArraySize(arrayNestingIndex);
+ for (unsigned int arrayElement = 0u; arrayElement < currentArraySize; ++arrayElement)
+ {
+ const std::string elementName = arrayName + ArrayString(arrayElement);
+ if (arrayNestingIndex + 1u < field.arraySizes.size())
+ {
+ GetUniformBlockStructArrayMemberInfo(field, arrayNestingIndex + 1u, elementName,
+ encoder, inRowMajorLayout, blockInfoOut);
+ }
+ else
+ {
+ GetUniformBlockStructMemberInfo(field.fields, elementName, encoder, inRowMajorLayout,
+ blockInfoOut);
+ }
+ }
}
-BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type, unsigned int arraySize, bool isRowMajorMatrix)
+template <typename VarT>
+void GetUniformBlockArrayOfArraysMemberInfo(const VarT &field,
+ unsigned int arrayNestingIndex,
+ const std::string &arrayName,
+ sh::BlockLayoutEncoder *encoder,
+ bool inRowMajorLayout,
+ BlockLayoutMap *blockInfoOut)
+{
+ const unsigned int currentArraySize = field.getNestedArraySize(arrayNestingIndex);
+ for (unsigned int arrayElement = 0u; arrayElement < currentArraySize; ++arrayElement)
+ {
+ const std::string elementName = arrayName + ArrayString(arrayElement);
+ if (arrayNestingIndex + 2u < field.arraySizes.size())
+ {
+ GetUniformBlockArrayOfArraysMemberInfo(field, arrayNestingIndex + 1u, elementName,
+ encoder, inRowMajorLayout, blockInfoOut);
+ }
+ else
+ {
+ std::vector<unsigned int> innermostArraySize(
+ 1u, field.getNestedArraySize(arrayNestingIndex + 1u));
+ (*blockInfoOut)[elementName] =
+ encoder->encodeType(field.type, innermostArraySize, inRowMajorLayout);
+ }
+ }
+}
+
+} // anonymous namespace
+
+BlockLayoutEncoder::BlockLayoutEncoder() : mCurrentOffset(0)
+{
+}
+
+BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix)
{
int arrayStride;
int matrixStride;
- getBlockLayoutInfo(type, arraySize, isRowMajorMatrix, &arrayStride, &matrixStride);
+ getBlockLayoutInfo(type, arraySizes, isRowMajorMatrix, &arrayStride, &matrixStride);
const BlockMemberInfo memberInfo(static_cast<int>(mCurrentOffset * BytesPerComponent),
static_cast<int>(arrayStride * BytesPerComponent),
static_cast<int>(matrixStride * BytesPerComponent),
isRowMajorMatrix);
- advanceOffset(type, arraySize, isRowMajorMatrix, arrayStride, matrixStride);
+ advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, matrixStride);
return memberInfo;
}
@@ -68,48 +149,56 @@ void Std140BlockEncoder::exitAggregateType()
nextRegister();
}
-void Std140BlockEncoder::getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut)
+void Std140BlockEncoder::getBlockLayoutInfo(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int *arrayStrideOut,
+ int *matrixStrideOut)
{
// We assume we are only dealing with 4 byte components (no doubles or half-words currently)
ASSERT(gl::VariableComponentSize(gl::VariableComponentType(type)) == BytesPerComponent);
size_t baseAlignment = 0;
- int matrixStride = 0;
- int arrayStride = 0;
+ int matrixStride = 0;
+ int arrayStride = 0;
if (gl::IsMatrixType(type))
{
baseAlignment = ComponentsPerRegister;
- matrixStride = ComponentsPerRegister;
+ matrixStride = ComponentsPerRegister;
- if (arraySize > 0)
+ if (!arraySizes.empty())
{
const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
- arrayStride = ComponentsPerRegister * numRegisters;
+ arrayStride = ComponentsPerRegister * numRegisters;
}
}
- else if (arraySize > 0)
+ else if (!arraySizes.empty())
{
baseAlignment = ComponentsPerRegister;
- arrayStride = ComponentsPerRegister;
+ arrayStride = ComponentsPerRegister;
}
else
{
const int numComponents = gl::VariableComponentCount(type);
- baseAlignment = (numComponents == 3 ? 4u : static_cast<size_t>(numComponents));
+ baseAlignment = (numComponents == 3 ? 4u : static_cast<size_t>(numComponents));
}
mCurrentOffset = rx::roundUp(mCurrentOffset, baseAlignment);
*matrixStrideOut = matrixStride;
- *arrayStrideOut = arrayStride;
+ *arrayStrideOut = arrayStride;
}
-void Std140BlockEncoder::advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride)
+void Std140BlockEncoder::advanceOffset(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int arrayStride,
+ int matrixStride)
{
- if (arraySize > 0)
+ if (!arraySizes.empty())
{
- mCurrentOffset += arrayStride * arraySize;
+ mCurrentOffset += arrayStride * gl::ArraySizeProduct(arraySizes);
}
else if (gl::IsMatrixType(type))
{
@@ -123,4 +212,70 @@ void Std140BlockEncoder::advanceOffset(GLenum type, unsigned int arraySize, bool
}
}
+template <typename VarT>
+void GetUniformBlockInfo(const std::vector<VarT> &fields,
+ const std::string &prefix,
+ sh::BlockLayoutEncoder *encoder,
+ bool inRowMajorLayout,
+ BlockLayoutMap *blockInfoOut)
+{
+ for (const VarT &field : fields)
+ {
+ // Skip samplers. On Vulkan we use this for the default uniform block, so samplers may be
+ // included.
+ if (gl::IsSamplerType(field.type))
+ {
+ continue;
+ }
+
+ const std::string &fieldName = (prefix.empty() ? field.name : prefix + "." + field.name);
+
+ if (field.isStruct())
+ {
+ bool rowMajorLayout = (inRowMajorLayout || IsRowMajorLayout(field));
+
+ if (field.isArray())
+ {
+ GetUniformBlockStructArrayMemberInfo(field, 0u, fieldName, encoder, rowMajorLayout,
+ blockInfoOut);
+ }
+ else
+ {
+ GetUniformBlockStructMemberInfo(field.fields, fieldName, encoder, rowMajorLayout,
+ blockInfoOut);
+ }
+ }
+ else if (field.isArrayOfArrays())
+ {
+ bool isRowMajorMatrix = (gl::IsMatrixType(field.type) && inRowMajorLayout);
+ GetUniformBlockArrayOfArraysMemberInfo(field, 0u, fieldName, encoder, isRowMajorMatrix,
+ blockInfoOut);
+ }
+ else
+ {
+ bool isRowMajorMatrix = (gl::IsMatrixType(field.type) && inRowMajorLayout);
+ (*blockInfoOut)[fieldName] =
+ encoder->encodeType(field.type, field.arraySizes, isRowMajorMatrix);
+ }
+ }
}
+
+template void GetUniformBlockInfo(const std::vector<InterfaceBlockField> &,
+ const std::string &,
+ sh::BlockLayoutEncoder *,
+ bool,
+ BlockLayoutMap *);
+
+template void GetUniformBlockInfo(const std::vector<Uniform> &,
+ const std::string &,
+ sh::BlockLayoutEncoder *,
+ bool,
+ BlockLayoutMap *);
+
+template void GetUniformBlockInfo(const std::vector<ShaderVariable> &,
+ const std::string &,
+ sh::BlockLayoutEncoder *,
+ bool,
+ BlockLayoutMap *);
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/blocklayout.h b/src/3rdparty/angle/src/compiler/translator/blocklayout.h
index dd5fe07376..2b7acf4e60 100644
--- a/src/3rdparty/angle/src/compiler/translator/blocklayout.h
+++ b/src/3rdparty/angle/src/compiler/translator/blocklayout.h
@@ -11,6 +11,7 @@
#define COMMON_BLOCKLAYOUT_H_
#include <cstddef>
+#include <map>
#include <vector>
#include "angle_gl.h"
@@ -24,42 +25,64 @@ struct Uniform;
struct Varying;
struct InterfaceBlock;
-struct COMPILER_EXPORT BlockMemberInfo
+struct BlockMemberInfo
{
- BlockMemberInfo() : offset(-1), arrayStride(-1), matrixStride(-1), isRowMajorMatrix(false) {}
+ BlockMemberInfo()
+ : offset(-1),
+ arrayStride(-1),
+ matrixStride(-1),
+ isRowMajorMatrix(false),
+ topLevelArrayStride(-1)
+ {
+ }
BlockMemberInfo(int offset, int arrayStride, int matrixStride, bool isRowMajorMatrix)
: offset(offset),
arrayStride(arrayStride),
matrixStride(matrixStride),
- isRowMajorMatrix(isRowMajorMatrix)
- {}
+ isRowMajorMatrix(isRowMajorMatrix),
+ topLevelArrayStride(-1)
+ {
+ }
- static BlockMemberInfo getDefaultBlockInfo()
+ BlockMemberInfo(int offset,
+ int arrayStride,
+ int matrixStride,
+ bool isRowMajorMatrix,
+ int topLevelArrayStride)
+ : offset(offset),
+ arrayStride(arrayStride),
+ matrixStride(matrixStride),
+ isRowMajorMatrix(isRowMajorMatrix),
+ topLevelArrayStride(topLevelArrayStride)
{
- return BlockMemberInfo(-1, -1, -1, false);
}
+ static BlockMemberInfo getDefaultBlockInfo() { return BlockMemberInfo(-1, -1, -1, false, -1); }
+
int offset;
int arrayStride;
int matrixStride;
bool isRowMajorMatrix;
+ int topLevelArrayStride; // Only used for shader storage block members.
};
-class COMPILER_EXPORT BlockLayoutEncoder
+class BlockLayoutEncoder
{
public:
BlockLayoutEncoder();
virtual ~BlockLayoutEncoder() {}
- BlockMemberInfo encodeType(GLenum type, unsigned int arraySize, bool isRowMajorMatrix);
+ BlockMemberInfo encodeType(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix);
size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; }
virtual void enterAggregateType() = 0;
- virtual void exitAggregateType() = 0;
+ virtual void exitAggregateType() = 0;
- static const size_t BytesPerComponent = 4u;
+ static const size_t BytesPerComponent = 4u;
static const unsigned int ComponentsPerRegister = 4u;
static size_t getBlockRegister(const BlockMemberInfo &info);
@@ -70,14 +93,22 @@ class COMPILER_EXPORT BlockLayoutEncoder
void nextRegister();
- virtual void getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut) = 0;
- virtual void advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride) = 0;
+ virtual void getBlockLayoutInfo(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int *arrayStrideOut,
+ int *matrixStrideOut) = 0;
+ virtual void advanceOffset(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int arrayStride,
+ int matrixStride) = 0;
};
// Block layout according to the std140 block layout
// See "Standard Uniform Block Layout" in Section 2.11.6 of the OpenGL ES 3.0 specification
-class COMPILER_EXPORT Std140BlockEncoder : public BlockLayoutEncoder
+class Std140BlockEncoder : public BlockLayoutEncoder
{
public:
Std140BlockEncoder();
@@ -87,17 +118,27 @@ class COMPILER_EXPORT Std140BlockEncoder : public BlockLayoutEncoder
protected:
void getBlockLayoutInfo(GLenum type,
- unsigned int arraySize,
+ const std::vector<unsigned int> &arraySizes,
bool isRowMajorMatrix,
int *arrayStrideOut,
int *matrixStrideOut) override;
void advanceOffset(GLenum type,
- unsigned int arraySize,
+ const std::vector<unsigned int> &arraySizes,
bool isRowMajorMatrix,
int arrayStride,
int matrixStride) override;
};
-}
+using BlockLayoutMap = std::map<std::string, BlockMemberInfo>;
+
+// Only valid to call with ShaderVariable, InterfaceBlockField and Uniform.
+template <typename VarT>
+void GetUniformBlockInfo(const std::vector<VarT> &fields,
+ const std::string &prefix,
+ sh::BlockLayoutEncoder *encoder,
+ bool inRowMajorLayout,
+ BlockLayoutMap *blockLayoutMap);
+
+} // namespace sh
-#endif // COMMON_BLOCKLAYOUT_H_
+#endif // COMMON_BLOCKLAYOUT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp
index 43119248eb..867821f1ea 100644
--- a/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp
@@ -15,9 +15,8 @@
namespace sh
{
-HLSLBlockEncoder::HLSLBlockEncoder(HLSLBlockEncoderStrategy strategy)
- : mEncoderStrategy(strategy),
- mTransposeMatrices(false)
+HLSLBlockEncoder::HLSLBlockEncoder(HLSLBlockEncoderStrategy strategy, bool transposeMatrices)
+ : mEncoderStrategy(strategy), mTransposeMatrices(transposeMatrices)
{
}
@@ -30,7 +29,11 @@ void HLSLBlockEncoder::exitAggregateType()
{
}
-void HLSLBlockEncoder::getBlockLayoutInfo(GLenum typeIn, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut)
+void HLSLBlockEncoder::getBlockLayoutInfo(GLenum typeIn,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int *arrayStrideOut,
+ int *matrixStrideOut)
{
GLenum type = (mTransposeMatrices ? gl::TransposeMatrixType(typeIn) : typeIn);
@@ -38,14 +41,12 @@ void HLSLBlockEncoder::getBlockLayoutInfo(GLenum typeIn, unsigned int arraySize,
ASSERT(gl::VariableComponentSize(gl::VariableComponentType(type)) == BytesPerComponent);
int matrixStride = 0;
- int arrayStride = 0;
+ int arrayStride = 0;
// if variables are not to be packed, or we're about to
// pack a matrix or array, skip to the start of the next
// register
- if (!isPacked() ||
- gl::IsMatrixType(type) ||
- arraySize > 0)
+ if (!isPacked() || gl::IsMatrixType(type) || !arraySizes.empty())
{
nextRegister();
}
@@ -54,13 +55,13 @@ void HLSLBlockEncoder::getBlockLayoutInfo(GLenum typeIn, unsigned int arraySize,
{
matrixStride = ComponentsPerRegister;
- if (arraySize > 0)
+ if (!arraySizes.empty())
{
const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
- arrayStride = ComponentsPerRegister * numRegisters;
+ arrayStride = ComponentsPerRegister * numRegisters;
}
}
- else if (arraySize > 0)
+ else if (!arraySizes.empty())
{
arrayStride = ComponentsPerRegister;
}
@@ -74,22 +75,26 @@ void HLSLBlockEncoder::getBlockLayoutInfo(GLenum typeIn, unsigned int arraySize,
}
*matrixStrideOut = matrixStride;
- *arrayStrideOut = arrayStride;
+ *arrayStrideOut = arrayStride;
}
-void HLSLBlockEncoder::advanceOffset(GLenum typeIn, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride)
+void HLSLBlockEncoder::advanceOffset(GLenum typeIn,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int arrayStride,
+ int matrixStride)
{
GLenum type = (mTransposeMatrices ? gl::TransposeMatrixType(typeIn) : typeIn);
- if (arraySize > 0)
+ if (!arraySizes.empty())
{
- mCurrentOffset += arrayStride * (arraySize - 1);
+ mCurrentOffset += arrayStride * (gl::ArraySizeProduct(arraySizes) - 1);
}
if (gl::IsMatrixType(type))
{
ASSERT(matrixStride == ComponentsPerRegister);
- const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
+ const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
const int numComponents = gl::MatrixComponentCount(type, isRowMajorMatrix);
mCurrentOffset += ComponentsPerRegister * (numRegisters - 1);
mCurrentOffset += numComponents;
@@ -109,7 +114,8 @@ void HLSLBlockEncoder::skipRegisters(unsigned int numRegisters)
mCurrentOffset += (numRegisters * ComponentsPerRegister);
}
-HLSLBlockEncoder::HLSLBlockEncoderStrategy HLSLBlockEncoder::GetStrategyFor(ShShaderOutput outputType)
+HLSLBlockEncoder::HLSLBlockEncoderStrategy HLSLBlockEncoder::GetStrategyFor(
+ ShShaderOutput outputType)
{
switch (outputType)
{
@@ -129,7 +135,7 @@ void HLSLVariableRegisterCount(const ShaderVarType &variable, HLSLBlockEncoder *
{
if (variable.isStruct())
{
- for (size_t arrayElement = 0; arrayElement < variable.elementCount(); arrayElement++)
+ for (size_t arrayElement = 0; arrayElement < variable.getArraySizeProduct(); arrayElement++)
{
encoder->enterAggregateType();
@@ -144,28 +150,17 @@ void HLSLVariableRegisterCount(const ShaderVarType &variable, HLSLBlockEncoder *
else
{
// We operate only on varyings and uniforms, which do not have matrix layout qualifiers
- encoder->encodeType(variable.type, variable.arraySize, false);
+ encoder->encodeType(variable.type, variable.arraySizes, false);
}
}
-unsigned int HLSLVariableRegisterCount(const Varying &variable, bool transposeMatrices)
-{
- HLSLBlockEncoder encoder(HLSLBlockEncoder::ENCODE_PACKED);
- encoder.setTransposeMatrices(transposeMatrices);
- HLSLVariableRegisterCount(variable, &encoder);
-
- const size_t registerBytes = (encoder.BytesPerComponent * encoder.ComponentsPerRegister);
- return static_cast<unsigned int>(rx::roundUp<size_t>(encoder.getBlockSize(), registerBytes) / registerBytes);
-}
-
unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType)
{
- HLSLBlockEncoder encoder(HLSLBlockEncoder::GetStrategyFor(outputType));
- encoder.setTransposeMatrices(true);
+ HLSLBlockEncoder encoder(HLSLBlockEncoder::GetStrategyFor(outputType), true);
HLSLVariableRegisterCount(variable, &encoder);
const size_t registerBytes = (encoder.BytesPerComponent * encoder.ComponentsPerRegister);
- return static_cast<unsigned int>(rx::roundUp<size_t>(encoder.getBlockSize(), registerBytes) / registerBytes);
-}
-
+ return static_cast<unsigned int>(rx::roundUp<size_t>(encoder.getBlockSize(), registerBytes) /
+ registerBytes);
}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.h b/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.h
index c61cb1ae57..8f4a51a906 100644
--- a/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.h
+++ b/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.h
@@ -24,7 +24,7 @@ namespace sh
// The strategy should be ENCODE_LOOSE for D3D9 constant blocks, and ENCODE_PACKED
// for everything else (D3D10+ constant blocks and all attributes/varyings).
-class COMPILER_EXPORT HLSLBlockEncoder : public BlockLayoutEncoder
+class HLSLBlockEncoder : public BlockLayoutEncoder
{
public:
enum HLSLBlockEncoderStrategy
@@ -33,30 +33,36 @@ class COMPILER_EXPORT HLSLBlockEncoder : public BlockLayoutEncoder
ENCODE_LOOSE
};
- HLSLBlockEncoder(HLSLBlockEncoderStrategy strategy);
+ HLSLBlockEncoder(HLSLBlockEncoderStrategy strategy, bool transposeMatrices);
- virtual void enterAggregateType();
- virtual void exitAggregateType();
+ void enterAggregateType() override;
+ void exitAggregateType() override;
void skipRegisters(unsigned int numRegisters);
bool isPacked() const { return mEncoderStrategy == ENCODE_PACKED; }
- void setTransposeMatrices(bool enabled) { mTransposeMatrices = enabled; }
static HLSLBlockEncoderStrategy GetStrategyFor(ShShaderOutput outputType);
protected:
- virtual void getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut);
- virtual void advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride);
+ void getBlockLayoutInfo(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int *arrayStrideOut,
+ int *matrixStrideOut) override;
+ void advanceOffset(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int arrayStride,
+ int matrixStride) override;
HLSLBlockEncoderStrategy mEncoderStrategy;
bool mTransposeMatrices;
};
-// This method returns the number of used registers for a ShaderVariable. It is dependent on the HLSLBlockEncoder
-// class to count the number of used registers in a struct (which are individually packed according to the same rules).
-COMPILER_EXPORT unsigned int HLSLVariableRegisterCount(const Varying &variable, bool transposeMatrices);
-COMPILER_EXPORT unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType);
-
+// This method returns the number of used registers for a ShaderVariable. It is dependent on the
+// HLSLBlockEncoder class to count the number of used registers in a struct (which are individually
+// packed according to the same rules).
+unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType);
}
-#endif // COMMON_BLOCKLAYOUTHLSL_H_
+#endif // COMMON_BLOCKLAYOUTHLSL_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/compilerdebug.cpp b/src/3rdparty/angle/src/compiler/translator/compilerdebug.cpp
deleted file mode 100644
index 10cbe43b8d..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/compilerdebug.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-// debug.cpp: Debugging utilities.
-
-#include "compiler/translator/compilerdebug.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "compiler/translator/InitializeParseContext.h"
-#include "compiler/translator/ParseContext.h"
-
-#ifdef TRACE_ENABLED
-static const int kTraceBufferLen = 1024;
-
-extern "C" {
-void Trace(const char *format, ...) {
- if (!format) return;
-
- TParseContext* parseContext = GetGlobalParseContext();
- if (parseContext) {
- char buf[kTraceBufferLen];
- va_list args;
- va_start(args, format);
- vsnprintf(buf, kTraceBufferLen, format, args);
- va_end(args);
-
- parseContext->trace(buf);
- }
-}
-} // extern "C"
-#endif // TRACE_ENABLED
-
diff --git a/src/3rdparty/angle/src/compiler/translator/compilerdebug.h b/src/3rdparty/angle/src/compiler/translator/compilerdebug.h
deleted file mode 100644
index 84a12ad2f8..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/compilerdebug.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-// debug.h: Debugging utilities.
-
-#ifndef COMPILER_TRANSLATOR_COMPILERDEBUG_H_
-#define COMPILER_TRANSLATOR_COMPILERDEBUG_H_
-
-#include <assert.h>
-
-#ifdef _DEBUG
-#define TRACE_ENABLED // define to enable debug message tracing
-#endif // _DEBUG
-
-// Outputs text to the debug log
-#ifdef TRACE_ENABLED
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-void Trace(const char* format, ...);
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-#else // TRACE_ENABLED
-
-#define Trace(...) ((void)0)
-
-#endif // TRACE_ENABLED
-
-// A macro asserting a condition and outputting failures to the debug log
-#define ASSERT(expression) do { \
- if(!(expression)) \
- Trace("Assert failed: %s(%d): "#expression"\n", __FUNCTION__, __LINE__); \
- assert(expression); \
-} while(0)
-
-#define UNIMPLEMENTED() do { \
- Trace("Unimplemented invoked: %s(%d)\n", __FUNCTION__, __LINE__); \
- assert(false); \
-} while(0)
-
-#define UNREACHABLE() do { \
- Trace("Unreachable reached: %s(%d)\n", __FUNCTION__, __LINE__); \
- assert(false); \
-} while(0)
-
-#endif // COMPILER_TRANSLATOR_COMPILERDEBUG_H_
-
diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp
deleted file mode 100644
index 4dee0dbd2e..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/depgraph/DependencyGraph.h"
-#include "compiler/translator/depgraph/DependencyGraphBuilder.h"
-
-TDependencyGraph::TDependencyGraph(TIntermNode* intermNode)
-{
- TDependencyGraphBuilder::build(intermNode, this);
-}
-
-TDependencyGraph::~TDependencyGraph()
-{
- for (TGraphNodeVector::const_iterator iter = mAllNodes.begin(); iter != mAllNodes.end(); ++iter)
- {
- TGraphNode* node = *iter;
- delete node;
- }
-}
-
-TGraphArgument* TDependencyGraph::createArgument(TIntermAggregate* intermFunctionCall,
- int argumentNumber)
-{
- TGraphArgument* argument = new TGraphArgument(intermFunctionCall, argumentNumber);
- mAllNodes.push_back(argument);
- return argument;
-}
-
-TGraphFunctionCall* TDependencyGraph::createFunctionCall(TIntermAggregate* intermFunctionCall)
-{
- TGraphFunctionCall* functionCall = new TGraphFunctionCall(intermFunctionCall);
- mAllNodes.push_back(functionCall);
- if (functionCall->getIntermFunctionCall()->isUserDefined())
- mUserDefinedFunctionCalls.push_back(functionCall);
- return functionCall;
-}
-
-TGraphSymbol* TDependencyGraph::getOrCreateSymbol(TIntermSymbol* intermSymbol)
-{
- TSymbolIdMap::const_iterator iter = mSymbolIdMap.find(intermSymbol->getId());
-
- TGraphSymbol* symbol = NULL;
-
- if (iter != mSymbolIdMap.end()) {
- TSymbolIdPair pair = *iter;
- symbol = pair.second;
- } else {
- symbol = new TGraphSymbol(intermSymbol);
- mAllNodes.push_back(symbol);
-
- TSymbolIdPair pair(intermSymbol->getId(), symbol);
- mSymbolIdMap.insert(pair);
-
- // We save all sampler symbols in a collection, so we can start graph traversals from them quickly.
- if (IsSampler(intermSymbol->getBasicType()))
- mSamplerSymbols.push_back(symbol);
- }
-
- return symbol;
-}
-
-TGraphSelection* TDependencyGraph::createSelection(TIntermSelection* intermSelection)
-{
- TGraphSelection* selection = new TGraphSelection(intermSelection);
- mAllNodes.push_back(selection);
- return selection;
-}
-
-TGraphLoop* TDependencyGraph::createLoop(TIntermLoop* intermLoop)
-{
- TGraphLoop* loop = new TGraphLoop(intermLoop);
- mAllNodes.push_back(loop);
- return loop;
-}
-
-TGraphLogicalOp* TDependencyGraph::createLogicalOp(TIntermBinary* intermLogicalOp)
-{
- TGraphLogicalOp* logicalOp = new TGraphLogicalOp(intermLogicalOp);
- mAllNodes.push_back(logicalOp);
- return logicalOp;
-}
-
-const char* TGraphLogicalOp::getOpString() const
-{
- const char* opString = NULL;
- switch (getIntermLogicalOp()->getOp()) {
- case EOpLogicalAnd: opString = "and"; break;
- case EOpLogicalOr: opString = "or"; break;
- default: opString = "unknown"; break;
- }
- return opString;
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h
deleted file mode 100644
index 2f7f7b9ab8..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h
+++ /dev/null
@@ -1,199 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPH_H_
-#define COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPH_H_
-
-#include "compiler/translator/IntermNode.h"
-
-#include <set>
-#include <stack>
-
-class TGraphNode;
-class TGraphParentNode;
-class TGraphArgument;
-class TGraphFunctionCall;
-class TGraphSymbol;
-class TGraphSelection;
-class TGraphLoop;
-class TGraphLogicalOp;
-class TDependencyGraphTraverser;
-class TDependencyGraphOutput;
-
-typedef std::set<TGraphNode*> TGraphNodeSet;
-typedef std::vector<TGraphNode*> TGraphNodeVector;
-typedef std::vector<TGraphSymbol*> TGraphSymbolVector;
-typedef std::vector<TGraphFunctionCall*> TFunctionCallVector;
-
-//
-// Base class for all dependency graph nodes.
-//
-class TGraphNode {
-public:
- TGraphNode(TIntermNode* node) : intermNode(node) {}
- virtual ~TGraphNode() {}
- virtual void traverse(TDependencyGraphTraverser* graphTraverser);
-protected:
- TIntermNode* intermNode;
-};
-
-//
-// Base class for dependency graph nodes that may have children.
-//
-class TGraphParentNode : public TGraphNode {
-public:
- TGraphParentNode(TIntermNode* node) : TGraphNode(node) {}
- ~TGraphParentNode() override {}
- void addDependentNode(TGraphNode* node) { if (node != this) mDependentNodes.insert(node); }
- void traverse(TDependencyGraphTraverser *graphTraverser) override;
-
-private:
- TGraphNodeSet mDependentNodes;
-};
-
-//
-// Handle function call arguments.
-//
-class TGraphArgument : public TGraphParentNode {
-public:
- TGraphArgument(TIntermAggregate* intermFunctionCall, int argumentNumber)
- : TGraphParentNode(intermFunctionCall)
- , mArgumentNumber(argumentNumber) {}
- ~TGraphArgument() override {}
- const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); }
- int getArgumentNumber() const { return mArgumentNumber; }
- void traverse(TDependencyGraphTraverser *graphTraverser) override;
-
-private:
- int mArgumentNumber;
-};
-
-//
-// Handle function calls.
-//
-class TGraphFunctionCall : public TGraphParentNode {
-public:
- TGraphFunctionCall(TIntermAggregate* intermFunctionCall)
- : TGraphParentNode(intermFunctionCall) {}
- ~TGraphFunctionCall() override {}
- const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); }
- void traverse(TDependencyGraphTraverser *graphTraverser) override;
-};
-
-//
-// Handle symbols.
-//
-class TGraphSymbol : public TGraphParentNode {
-public:
- TGraphSymbol(TIntermSymbol* intermSymbol) : TGraphParentNode(intermSymbol) {}
- ~TGraphSymbol() override {}
- const TIntermSymbol* getIntermSymbol() const { return intermNode->getAsSymbolNode(); }
- void traverse(TDependencyGraphTraverser *graphTraverser) override;
-};
-
-//
-// Handle if statements and ternary operators.
-//
-class TGraphSelection : public TGraphNode {
-public:
- TGraphSelection(TIntermSelection* intermSelection) : TGraphNode(intermSelection) {}
- ~TGraphSelection() override {}
- const TIntermSelection* getIntermSelection() const { return intermNode->getAsSelectionNode(); }
- void traverse(TDependencyGraphTraverser *graphTraverser) override;
-};
-
-//
-// Handle for, do-while, and while loops.
-//
-class TGraphLoop : public TGraphNode {
-public:
- TGraphLoop(TIntermLoop* intermLoop) : TGraphNode(intermLoop) {}
- ~TGraphLoop() override {}
- const TIntermLoop* getIntermLoop() const { return intermNode->getAsLoopNode(); }
- void traverse(TDependencyGraphTraverser *graphTraverser) override;
-};
-
-//
-// Handle logical and, or.
-//
-class TGraphLogicalOp : public TGraphNode {
-public:
- TGraphLogicalOp(TIntermBinary* intermLogicalOp) : TGraphNode(intermLogicalOp) {}
- ~TGraphLogicalOp() override {}
- const TIntermBinary* getIntermLogicalOp() const { return intermNode->getAsBinaryNode(); }
- const char* getOpString() const;
- void traverse(TDependencyGraphTraverser *graphTraverser) override;
-};
-
-//
-// A dependency graph of symbols, function calls, conditions etc.
-//
-// This class provides an interface to the entry points of the dependency graph.
-//
-// Dependency graph nodes should be created by using one of the provided "create..." methods.
-// This class (and nobody else) manages the memory of the created nodes.
-// Nodes may not be removed after being added, so all created nodes will exist while the
-// TDependencyGraph instance exists.
-//
-class TDependencyGraph {
-public:
- TDependencyGraph(TIntermNode* intermNode);
- ~TDependencyGraph();
- const TGraphNodeVector &allNodes() const { return mAllNodes; }
- const TGraphSymbolVector &samplerSymbols() const { return mSamplerSymbols; }
- const TFunctionCallVector &userDefinedFunctionCalls() const
- {
- return mUserDefinedFunctionCalls;
- }
-
- TGraphArgument* createArgument(TIntermAggregate* intermFunctionCall, int argumentNumber);
- TGraphFunctionCall* createFunctionCall(TIntermAggregate* intermFunctionCall);
- TGraphSymbol* getOrCreateSymbol(TIntermSymbol* intermSymbol);
- TGraphSelection* createSelection(TIntermSelection* intermSelection);
- TGraphLoop* createLoop(TIntermLoop* intermLoop);
- TGraphLogicalOp* createLogicalOp(TIntermBinary* intermLogicalOp);
-private:
- typedef TMap<int, TGraphSymbol*> TSymbolIdMap;
- typedef std::pair<int, TGraphSymbol*> TSymbolIdPair;
-
- TGraphNodeVector mAllNodes;
- TGraphSymbolVector mSamplerSymbols;
- TFunctionCallVector mUserDefinedFunctionCalls;
- TSymbolIdMap mSymbolIdMap;
-};
-
-//
-// For traversing the dependency graph. Users should derive from this,
-// put their traversal specific data in it, and then pass it to a
-// traverse method.
-//
-// When using this, just fill in the methods for nodes you want visited.
-//
-class TDependencyGraphTraverser : angle::NonCopyable {
-public:
- TDependencyGraphTraverser() : mDepth(0) {}
- virtual ~TDependencyGraphTraverser() {}
-
- virtual void visitSymbol(TGraphSymbol* symbol) {};
- virtual void visitArgument(TGraphArgument* selection) {};
- virtual void visitFunctionCall(TGraphFunctionCall* functionCall) {};
- virtual void visitSelection(TGraphSelection* selection) {};
- virtual void visitLoop(TGraphLoop* loop) {};
- virtual void visitLogicalOp(TGraphLogicalOp* logicalOp) {};
-
- int getDepth() const { return mDepth; }
- void incrementDepth() { ++mDepth; }
- void decrementDepth() { --mDepth; }
-
- void clearVisited() { mVisited.clear(); }
- void markVisited(TGraphNode* node) { mVisited.insert(node); }
- bool isVisited(TGraphNode* node) const { return mVisited.find(node) != mVisited.end(); }
-private:
- int mDepth;
- TGraphNodeSet mVisited;
-};
-
-#endif // COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPH_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp
deleted file mode 100644
index 1aeb822d51..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/depgraph/DependencyGraphBuilder.h"
-
-void TDependencyGraphBuilder::build(TIntermNode *node, TDependencyGraph *graph)
-{
- TDependencyGraphBuilder builder(graph);
- builder.build(node);
-}
-
-bool TDependencyGraphBuilder::visitAggregate(
- Visit visit, TIntermAggregate *intermAggregate)
-{
- switch (intermAggregate->getOp())
- {
- case EOpFunction:
- visitFunctionDefinition(intermAggregate);
- break;
- case EOpFunctionCall:
- visitFunctionCall(intermAggregate);
- break;
- default:
- visitAggregateChildren(intermAggregate);
- break;
- }
- return false;
-}
-
-void TDependencyGraphBuilder::visitFunctionDefinition(
- TIntermAggregate *intermAggregate)
-{
- // Currently, we do not support user defined functions.
- if (intermAggregate->getName() != "main(")
- return;
-
- visitAggregateChildren(intermAggregate);
-}
-
-// Takes an expression like "f(x)" and creates a dependency graph like
-// "x -> argument 0 -> function call".
-void TDependencyGraphBuilder::visitFunctionCall(
- TIntermAggregate *intermFunctionCall)
-{
- TGraphFunctionCall *functionCall =
- mGraph->createFunctionCall(intermFunctionCall);
-
- // Run through the function call arguments.
- int argumentNumber = 0;
- TIntermSequence *intermArguments = intermFunctionCall->getSequence();
- for (TIntermSequence::const_iterator iter = intermArguments->begin();
- iter != intermArguments->end();
- ++iter, ++argumentNumber)
- {
- TNodeSetMaintainer nodeSetMaintainer(this);
-
- TIntermNode *intermArgument = *iter;
- intermArgument->traverse(this);
-
- if (TParentNodeSet *argumentNodes = mNodeSets.getTopSet())
- {
- TGraphArgument *argument = mGraph->createArgument(
- intermFunctionCall, argumentNumber);
- connectMultipleNodesToSingleNode(argumentNodes, argument);
- argument->addDependentNode(functionCall);
- }
- }
-
- // Push the leftmost symbol of this function call into the current set of
- // dependent symbols to represent the result of this function call.
- // Thus, an expression like "y = f(x)" will yield a dependency graph like
- // "x -> argument 0 -> function call -> y".
- // This line essentially passes the function call node back up to an earlier
- // visitAssignment call, which will create the connection "function call -> y".
- mNodeSets.insertIntoTopSet(functionCall);
-}
-
-void TDependencyGraphBuilder::visitAggregateChildren(
- TIntermAggregate *intermAggregate)
-{
- TIntermSequence *sequence = intermAggregate->getSequence();
- for (TIntermSequence::const_iterator iter = sequence->begin();
- iter != sequence->end(); ++iter)
- {
- TIntermNode *intermChild = *iter;
- intermChild->traverse(this);
- }
-}
-
-void TDependencyGraphBuilder::visitSymbol(TIntermSymbol *intermSymbol)
-{
- // Push this symbol into the set of dependent symbols for the current
- // assignment or condition that we are traversing.
- TGraphSymbol *symbol = mGraph->getOrCreateSymbol(intermSymbol);
- mNodeSets.insertIntoTopSet(symbol);
-
- // If this symbol is the current leftmost symbol under an assignment, replace
- // the previous leftmost symbol with this symbol.
- if (!mLeftmostSymbols.empty() && mLeftmostSymbols.top() != &mRightSubtree)
- {
- mLeftmostSymbols.pop();
- mLeftmostSymbols.push(symbol);
- }
-}
-
-bool TDependencyGraphBuilder::visitBinary(Visit visit, TIntermBinary *intermBinary)
-{
- TOperator op = intermBinary->getOp();
- if (op == EOpInitialize || intermBinary->isAssignment())
- visitAssignment(intermBinary);
- else if (op == EOpLogicalAnd || op == EOpLogicalOr)
- visitLogicalOp(intermBinary);
- else
- visitBinaryChildren(intermBinary);
-
- return false;
-}
-
-void TDependencyGraphBuilder::visitAssignment(TIntermBinary *intermAssignment)
-{
- TIntermTyped *intermLeft = intermAssignment->getLeft();
- if (!intermLeft)
- return;
-
- TGraphSymbol *leftmostSymbol = NULL;
-
- {
- TNodeSetMaintainer nodeSetMaintainer(this);
-
- {
- TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mLeftSubtree);
- intermLeft->traverse(this);
- leftmostSymbol = mLeftmostSymbols.top();
-
- // After traversing the left subtree of this assignment, we should
- // have found a real leftmost symbol, and the leftmost symbol should
- // not be a placeholder.
- ASSERT(leftmostSymbol != &mLeftSubtree);
- ASSERT(leftmostSymbol != &mRightSubtree);
- }
-
- if (TIntermTyped *intermRight = intermAssignment->getRight())
- {
- TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree);
- intermRight->traverse(this);
- }
-
- if (TParentNodeSet *assignmentNodes = mNodeSets.getTopSet())
- connectMultipleNodesToSingleNode(assignmentNodes, leftmostSymbol);
- }
-
- // Push the leftmost symbol of this assignment into the current set of dependent
- // symbols to represent the result of this assignment.
- // An expression like "a = (b = c)" will yield a dependency graph like
- // "c -> b -> a".
- // This line essentially passes the leftmost symbol of the nested assignment
- // ("b" in this example) back up to the earlier visitAssignment call for the
- // outer assignment, which will create the connection "b -> a".
- mNodeSets.insertIntoTopSet(leftmostSymbol);
-}
-
-void TDependencyGraphBuilder::visitLogicalOp(TIntermBinary *intermLogicalOp)
-{
- if (TIntermTyped *intermLeft = intermLogicalOp->getLeft())
- {
- TNodeSetPropagatingMaintainer nodeSetMaintainer(this);
-
- intermLeft->traverse(this);
- if (TParentNodeSet *leftNodes = mNodeSets.getTopSet())
- {
- TGraphLogicalOp *logicalOp = mGraph->createLogicalOp(intermLogicalOp);
- connectMultipleNodesToSingleNode(leftNodes, logicalOp);
- }
- }
-
- if (TIntermTyped *intermRight = intermLogicalOp->getRight())
- {
- TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree);
- intermRight->traverse(this);
- }
-}
-
-void TDependencyGraphBuilder::visitBinaryChildren(TIntermBinary *intermBinary)
-{
- if (TIntermTyped *intermLeft = intermBinary->getLeft())
- intermLeft->traverse(this);
-
- if (TIntermTyped *intermRight = intermBinary->getRight())
- {
- TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree);
- intermRight->traverse(this);
- }
-}
-
-bool TDependencyGraphBuilder::visitSelection(
- Visit visit, TIntermSelection *intermSelection)
-{
- if (TIntermNode *intermCondition = intermSelection->getCondition())
- {
- TNodeSetMaintainer nodeSetMaintainer(this);
-
- intermCondition->traverse(this);
- if (TParentNodeSet *conditionNodes = mNodeSets.getTopSet())
- {
- TGraphSelection *selection = mGraph->createSelection(intermSelection);
- connectMultipleNodesToSingleNode(conditionNodes, selection);
- }
- }
-
- if (TIntermNode *intermTrueBlock = intermSelection->getTrueBlock())
- intermTrueBlock->traverse(this);
-
- if (TIntermNode *intermFalseBlock = intermSelection->getFalseBlock())
- intermFalseBlock->traverse(this);
-
- return false;
-}
-
-bool TDependencyGraphBuilder::visitLoop(Visit visit, TIntermLoop *intermLoop)
-{
- if (TIntermTyped *intermCondition = intermLoop->getCondition())
- {
- TNodeSetMaintainer nodeSetMaintainer(this);
-
- intermCondition->traverse(this);
- if (TParentNodeSet *conditionNodes = mNodeSets.getTopSet())
- {
- TGraphLoop *loop = mGraph->createLoop(intermLoop);
- connectMultipleNodesToSingleNode(conditionNodes, loop);
- }
- }
-
- if (TIntermNode* intermBody = intermLoop->getBody())
- intermBody->traverse(this);
-
- if (TIntermTyped *intermExpression = intermLoop->getExpression())
- intermExpression->traverse(this);
-
- return false;
-}
-
-
-void TDependencyGraphBuilder::connectMultipleNodesToSingleNode(
- TParentNodeSet *nodes, TGraphNode *node) const
-{
- for (TParentNodeSet::const_iterator iter = nodes->begin();
- iter != nodes->end(); ++iter)
- {
- TGraphParentNode *currentNode = *iter;
- currentNode->addDependentNode(node);
- }
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h
deleted file mode 100644
index c7b54f66b7..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h
+++ /dev/null
@@ -1,199 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHBUILDER_H_
-#define COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHBUILDER_H_
-
-#include "compiler/translator/depgraph/DependencyGraph.h"
-
-//
-// Creates a dependency graph of symbols, function calls, conditions etc. by
-// traversing a intermediate tree.
-//
-class TDependencyGraphBuilder : public TIntermTraverser
-{
- public:
- static void build(TIntermNode *node, TDependencyGraph *graph);
-
- void visitSymbol(TIntermSymbol *) override;
- bool visitBinary(Visit visit, TIntermBinary *) override;
- bool visitSelection(Visit visit, TIntermSelection *) override;
- bool visitAggregate(Visit visit, TIntermAggregate *) override;
- bool visitLoop(Visit visit, TIntermLoop *) override;
-
- private:
- typedef std::stack<TGraphSymbol *> TSymbolStack;
- typedef std::set<TGraphParentNode *> TParentNodeSet;
-
- //
- // For collecting the dependent nodes of assignments, conditions, etc.
- // while traversing the intermediate tree.
- //
- // This data structure is stack of sets. Each set contains dependency graph
- // parent nodes.
- //
- class TNodeSetStack
- {
- public:
- TNodeSetStack() {};
- ~TNodeSetStack() { clear(); }
-
- // This should only be called after a pushSet.
- // Returns NULL if the top set is empty.
- TParentNodeSet *getTopSet() const
- {
- ASSERT(!mNodeSets.empty());
- TParentNodeSet *topSet = mNodeSets.top();
- return !topSet->empty() ? topSet : NULL;
- }
-
- void pushSet() { mNodeSets.push(new TParentNodeSet()); }
- void popSet()
- {
- ASSERT(!mNodeSets.empty());
- delete mNodeSets.top();
- mNodeSets.pop();
- }
-
- // Pops the top set and adds its contents to the new top set.
- // This should only be called after a pushSet.
- // If there is no set below the top set, the top set is just deleted.
- void popSetIntoNext()
- {
- ASSERT(!mNodeSets.empty());
- TParentNodeSet *oldTopSet = mNodeSets.top();
- mNodeSets.pop();
-
- if (!mNodeSets.empty())
- {
- TParentNodeSet *newTopSet = mNodeSets.top();
- newTopSet->insert(oldTopSet->begin(), oldTopSet->end());
- }
-
- delete oldTopSet;
- }
-
- // Does nothing if there is no top set.
- // This can be called when there is no top set if we are visiting
- // symbols that are not under an assignment or condition.
- // We don't need to track those symbols.
- void insertIntoTopSet(TGraphParentNode *node)
- {
- if (mNodeSets.empty())
- return;
-
- mNodeSets.top()->insert(node);
- }
-
- void clear()
- {
- while (!mNodeSets.empty())
- popSet();
- }
-
- private:
- typedef std::stack<TParentNodeSet *> TParentNodeSetStack;
-
- TParentNodeSetStack mNodeSets;
- };
-
- //
- // An instance of this class pushes a new node set when instantiated.
- // When the instance goes out of scope, it and pops the node set.
- //
- class TNodeSetMaintainer : angle::NonCopyable
- {
- public:
- TNodeSetMaintainer(TDependencyGraphBuilder *factory)
- : mSets(factory->mNodeSets)
- {
- mSets.pushSet();
- }
- ~TNodeSetMaintainer() { mSets.popSet(); }
- protected:
- TNodeSetStack &mSets;
- };
-
- //
- // An instance of this class pushes a new node set when instantiated.
- // When the instance goes out of scope, it and pops the top node set and adds
- // its contents to the new top node set.
- //
- class TNodeSetPropagatingMaintainer : angle::NonCopyable
- {
- public:
- TNodeSetPropagatingMaintainer(TDependencyGraphBuilder *factory)
- : mSets(factory->mNodeSets)
- {
- mSets.pushSet();
- }
- ~TNodeSetPropagatingMaintainer() { mSets.popSetIntoNext(); }
- protected:
- TNodeSetStack &mSets;
- };
-
- //
- // An instance of this class keeps track of the leftmost symbol while we're
- // exploring an assignment.
- // It will push the placeholder symbol kLeftSubtree when instantiated under a
- // left subtree, and kRightSubtree under a right subtree.
- // When it goes out of scope, it will pop the leftmost symbol at the top of the
- // scope.
- // During traversal, the TDependencyGraphBuilder will replace kLeftSubtree with
- // a real symbol.
- // kRightSubtree will never be replaced by a real symbol because we are tracking
- // the leftmost symbol.
- //
- class TLeftmostSymbolMaintainer : angle::NonCopyable
- {
- public:
- TLeftmostSymbolMaintainer(
- TDependencyGraphBuilder *factory, TGraphSymbol &subtree)
- : mLeftmostSymbols(factory->mLeftmostSymbols)
- {
- mNeedsPlaceholderSymbol =
- mLeftmostSymbols.empty() || mLeftmostSymbols.top() != &subtree;
- if (mNeedsPlaceholderSymbol)
- mLeftmostSymbols.push(&subtree);
- }
-
- ~TLeftmostSymbolMaintainer()
- {
- if (mNeedsPlaceholderSymbol)
- mLeftmostSymbols.pop();
- }
-
- protected:
- TSymbolStack& mLeftmostSymbols;
- bool mNeedsPlaceholderSymbol;
- };
-
- TDependencyGraphBuilder(TDependencyGraph *graph)
- : TIntermTraverser(true, false, false),
- mLeftSubtree(NULL),
- mRightSubtree(NULL),
- mGraph(graph) {}
- void build(TIntermNode *intermNode) { intermNode->traverse(this); }
-
- void connectMultipleNodesToSingleNode(
- TParentNodeSet *nodes, TGraphNode *node) const;
-
- void visitAssignment(TIntermBinary *);
- void visitLogicalOp(TIntermBinary *);
- void visitBinaryChildren(TIntermBinary *);
- void visitFunctionDefinition(TIntermAggregate *);
- void visitFunctionCall(TIntermAggregate *intermFunctionCall);
- void visitAggregateChildren(TIntermAggregate *);
-
- TGraphSymbol mLeftSubtree;
- TGraphSymbol mRightSubtree;
-
- TDependencyGraph *mGraph;
- TNodeSetStack mNodeSets;
- TSymbolStack mLeftmostSymbols;
-};
-
-#endif // COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHBUILDER_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp
deleted file mode 100644
index 32a2f30141..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/depgraph/DependencyGraphOutput.h"
-
-void TDependencyGraphOutput::outputIndentation()
-{
- for (int i = 0; i < getDepth(); ++i)
- mSink << " ";
-}
-
-void TDependencyGraphOutput::visitArgument(TGraphArgument* parameter)
-{
- outputIndentation();
- mSink << "argument " << parameter->getArgumentNumber() << " of call to "
- << parameter->getIntermFunctionCall()->getName() << "\n";
-}
-
-void TDependencyGraphOutput::visitFunctionCall(TGraphFunctionCall* functionCall)
-{
- outputIndentation();
- mSink << "function call " << functionCall->getIntermFunctionCall()->getName() << "\n";
-}
-
-void TDependencyGraphOutput::visitSymbol(TGraphSymbol* symbol)
-{
- outputIndentation();
- mSink << symbol->getIntermSymbol()->getSymbol() << " (symbol id: "
- << symbol->getIntermSymbol()->getId() << ")\n";
-}
-
-void TDependencyGraphOutput::visitSelection(TGraphSelection* selection)
-{
- outputIndentation();
- mSink << "selection\n";
-}
-
-void TDependencyGraphOutput::visitLoop(TGraphLoop* loop)
-{
- outputIndentation();
- mSink << "loop condition\n";
-}
-
-void TDependencyGraphOutput::visitLogicalOp(TGraphLogicalOp* logicalOp)
-{
- outputIndentation();
- mSink << "logical " << logicalOp->getOpString() << "\n";
-}
-
-void TDependencyGraphOutput::outputAllSpanningTrees(TDependencyGraph& graph)
-{
- mSink << "\n";
-
- for (auto symbol : graph.allNodes())
- {
- mSink << "--- Dependency graph spanning tree ---\n";
- clearVisited();
- symbol->traverse(this);
- mSink << "\n";
- }
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h
deleted file mode 100644
index b201e0a671..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHOUTPUT_H_
-#define COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHOUTPUT_H_
-
-#include "compiler/translator/depgraph/DependencyGraph.h"
-#include "compiler/translator/InfoSink.h"
-
-class TDependencyGraphOutput : public TDependencyGraphTraverser
-{
- public:
- TDependencyGraphOutput(TInfoSinkBase& sink) : mSink(sink) {}
- void visitSymbol(TGraphSymbol* symbol) override;
- void visitArgument(TGraphArgument* parameter) override;
- void visitFunctionCall(TGraphFunctionCall* functionCall) override;
- void visitSelection(TGraphSelection* selection) override;
- void visitLoop(TGraphLoop* loop) override;
- void visitLogicalOp(TGraphLogicalOp* logicalOp) override;
-
- void outputAllSpanningTrees(TDependencyGraph& graph);
- private:
- void outputIndentation();
-
- TInfoSinkBase& mSink;
-};
-
-#endif // COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHOUTPUT_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp
deleted file mode 100644
index 197fde97e2..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/depgraph/DependencyGraph.h"
-
-// These methods do a breadth-first traversal through the graph and mark visited nodes.
-
-void TGraphNode::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- graphTraverser->markVisited(this);
-}
-
-void TGraphParentNode::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- TGraphNode::traverse(graphTraverser);
-
- graphTraverser->incrementDepth();
-
- // Visit the parent node's children.
- for (TGraphNodeSet::const_iterator iter = mDependentNodes.begin();
- iter != mDependentNodes.end();
- ++iter)
- {
- TGraphNode* node = *iter;
- if (!graphTraverser->isVisited(node))
- node->traverse(graphTraverser);
- }
-
- graphTraverser->decrementDepth();
-}
-
-void TGraphArgument::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- graphTraverser->visitArgument(this);
- TGraphParentNode::traverse(graphTraverser);
-}
-
-void TGraphFunctionCall::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- graphTraverser->visitFunctionCall(this);
- TGraphParentNode::traverse(graphTraverser);
-}
-
-void TGraphSymbol::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- graphTraverser->visitSymbol(this);
- TGraphParentNode::traverse(graphTraverser);
-}
-
-void TGraphSelection::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- graphTraverser->visitSelection(this);
- TGraphNode::traverse(graphTraverser);
-}
-
-void TGraphLoop::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- graphTraverser->visitLoop(this);
- TGraphNode::traverse(graphTraverser);
-}
-
-void TGraphLogicalOp::traverse(TDependencyGraphTraverser* graphTraverser)
-{
- graphTraverser->visitLogicalOp(this);
- TGraphNode::traverse(graphTraverser);
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/emulated_builtin_function_data_hlsl.json b/src/3rdparty/angle/src/compiler/translator/emulated_builtin_function_data_hlsl.json
new file mode 100644
index 0000000000..32e500fe67
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/emulated_builtin_function_data_hlsl.json
@@ -0,0 +1,1382 @@
+[
+ {
+ "op":"mod",
+ "return_type":"float",
+ "args":[
+ "float x",
+ "float y"
+ ],
+ "body":[
+ "return x - y * floor(x / y);"
+ ]
+ },
+ {
+ "op":"mod",
+ "return_type":"float2",
+ "args":[
+ "float2 x",
+ "float2 y"
+ ],
+ "body":[
+ "return x - y * floor(x / y);"
+ ]
+ },
+ {
+ "op":"mod",
+ "return_type":"float2",
+ "args":[
+ "float2 x",
+ "float y"
+ ],
+ "body":[
+ "return x - y * floor(x / y);"
+ ]
+ },
+ {
+ "op":"mod",
+ "return_type":"float3",
+ "args":[
+ "float3 x",
+ "float3 y"
+ ],
+ "body":[
+ "return x - y * floor(x / y);"
+ ]
+ },
+ {
+ "op":"mod",
+ "return_type":"float3",
+ "args":[
+ "float3 x",
+ "float y"
+ ],
+ "body":[
+ "return x - y * floor(x / y);"
+ ]
+ },
+ {
+ "op":"mod",
+ "return_type":"float4",
+ "args":[
+ "float4 x",
+ "float4 y"
+ ],
+ "body":[
+ "return x - y * floor(x / y);"
+ ]
+ },
+ {
+ "op":"mod",
+ "return_type":"float4",
+ "args":[
+ "float4 x",
+ "float y"
+ ],
+ "body":[
+ "return x - y * floor(x / y);"
+ ]
+ },
+ {
+ "op":"frexp",
+ "return_type":"float",
+ "args":[
+ "float x",
+ "out int exp"
+ ],
+ "body":[
+ "float fexp;",
+ "float mantissa = frexp(abs(x), fexp) * sign(x);",
+ "exp = int(fexp);",
+ "return mantissa;"
+ ]
+ },
+ {
+ "op":"frexp",
+ "return_type":"float2",
+ "args":[
+ "float2 x",
+ "out int2 exp"
+ ],
+ "body":[
+ "float2 fexp;",
+ "float2 mantissa = frexp(abs(x), fexp) * sign(x);",
+ "exp = int2(fexp);",
+ "return mantissa;"
+ ]
+ },
+ {
+ "op":"frexp",
+ "return_type":"float3",
+ "args":[
+ "float3 x",
+ "out int3 exp"
+ ],
+ "body":[
+ "float3 fexp;",
+ "float3 mantissa = frexp(abs(x), fexp) * sign(x);",
+ "exp = int3(fexp);",
+ "return mantissa;"
+ ]
+ },
+ {
+ "op":"frexp",
+ "return_type":"float4",
+ "args":[
+ "float4 x",
+ "out int4 exp"
+ ],
+ "body":[
+ "float4 fexp;",
+ "float4 mantissa = frexp(abs(x), fexp) * sign(x);",
+ "exp = int4(fexp);",
+ "return mantissa;"
+ ]
+ },
+ {
+ "op":"ldexp",
+ "return_type":"float",
+ "args":[
+ "float x",
+ "int exp"
+ ],
+ "body":[
+ "return ldexp(x, float(exp));"
+ ]
+ },
+ {
+ "op":"ldexp",
+ "return_type":"float2",
+ "args":[
+ "float2 x",
+ "int2 exp"
+ ],
+ "body":[
+ "return ldexp(x, float2(exp));"
+ ]
+ },
+ {
+ "op":"ldexp",
+ "return_type":"float3",
+ "args":[
+ "float3 x",
+ "int3 exp"
+ ],
+ "body":[
+ "return ldexp(x, float3(exp));"
+ ]
+ },
+ {
+ "op":"ldexp",
+ "return_type":"float4",
+ "args":[
+ "float4 x",
+ "int4 exp"
+ ],
+ "body":[
+ "return ldexp(x, float4(exp));"
+ ]
+ },
+ {
+ "op":"faceforward",
+ "return_type":"float",
+ "args":[
+ "float N",
+ "float I",
+ "float Nref"
+ ],
+ "body":[
+ "if(dot(Nref, I) >= 0)",
+ "{",
+ " return -N;",
+ "}",
+ "else",
+ "{",
+ " return N;",
+ "}"
+ ]
+ },
+ {
+ "op":"faceforward",
+ "return_type":"float2",
+ "args":[
+ "float2 N",
+ "float2 I",
+ "float2 Nref"
+ ],
+ "body":[
+ "if(dot(Nref, I) >= 0)",
+ "{",
+ " return -N;",
+ "}",
+ "else",
+ "{",
+ " return N;",
+ "}"
+ ]
+ },
+ {
+ "op":"faceforward",
+ "return_type":"float3",
+ "args":[
+ "float3 N",
+ "float3 I",
+ "float3 Nref"
+ ],
+ "body":[
+ "if(dot(Nref, I) >= 0)",
+ "{",
+ " return -N;",
+ "}",
+ "else",
+ "{",
+ " return N;",
+ "}"
+ ]
+ },
+ {
+ "op":"faceforward",
+ "return_type":"float4",
+ "args":[
+ "float4 N",
+ "float4 I",
+ "float4 Nref"
+ ],
+ "body":[
+ "if(dot(Nref, I) >= 0)",
+ "{",
+ " return -N;",
+ "}",
+ "else",
+ "{",
+ " return N;",
+ "}"
+ ]
+ },
+ {
+ "op":"atan",
+ "return_type":"float",
+ "args":[
+ "float y",
+ "float x"
+ ],
+ "body":[
+ "if(x == 0 && y == 0) x = 1;",
+ "return atan2(y, x);"
+ ]
+ },
+ {
+ "op":"atan",
+ "return_type":"float2",
+ "args":[
+ "float2 y",
+ "float2 x"
+ ],
+ "body":[
+ "if(x[0] == 0 && y[0] == 0) x[0] = 1;",
+ "if(x[1] == 0 && y[1] == 0) x[1] = 1;",
+ "return float2(atan2(y[0], x[0]), atan2(y[1], x[1]));"
+ ]
+ },
+ {
+ "op":"atan",
+ "return_type":"float3",
+ "args":[
+ "float3 y",
+ "float3 x"
+ ],
+ "body":[
+ "if(x[0] == 0 && y[0] == 0) x[0] = 1;",
+ "if(x[1] == 0 && y[1] == 0) x[1] = 1;",
+ "if(x[2] == 0 && y[2] == 0) x[2] = 1;",
+ "return float3(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]));"
+ ]
+ },
+ {
+ "op":"atan",
+ "return_type":"float4",
+ "args":[
+ "float4 y",
+ "float4 x"
+ ],
+ "body":[
+ "if(x[0] == 0 && y[0] == 0) x[0] = 1;",
+ "if(x[1] == 0 && y[1] == 0) x[1] = 1;",
+ "if(x[2] == 0 && y[2] == 0) x[2] = 1;",
+ "if(x[3] == 0 && y[3] == 0) x[3] = 1;",
+ "return float4(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], ",
+ "x[2]), atan2(y[3], x[3]));"
+ ]
+ },
+ {
+ "op":"asinh",
+ "return_type":"float",
+ "args":[
+ "in float x"
+ ],
+ "body":[
+ "return log(x + sqrt(pow(x, 2.0) + 1.0));"
+ ]
+ },
+ {
+ "op":"asinh",
+ "return_type":"float2",
+ "args":[
+ "in float2 x"
+ ],
+ "body":[
+ "return log(x + sqrt(pow(x, 2.0) + 1.0));"
+ ]
+ },
+ {
+ "op":"asinh",
+ "return_type":"float3",
+ "args":[
+ "in float3 x"
+ ],
+ "body":[
+ "return log(x + sqrt(pow(x, 2.0) + 1.0));"
+ ]
+ },
+ {
+ "op":"asinh",
+ "return_type":"float4",
+ "args":[
+ "in float4 x"
+ ],
+ "body":[
+ "return log(x + sqrt(pow(x, 2.0) + 1.0));"
+ ]
+ },
+ {
+ "op":"acosh",
+ "return_type":"float",
+ "args":[
+ "in float x"
+ ],
+ "body":[
+ "return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));"
+ ]
+ },
+ {
+ "op":"acosh",
+ "return_type":"float2",
+ "args":[
+ "in float2 x"
+ ],
+ "body":[
+ "return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));"
+ ]
+ },
+ {
+ "op":"acosh",
+ "return_type":"float3",
+ "args":[
+ "in float3 x"
+ ],
+ "body":[
+ "return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));"
+ ]
+ },
+ {
+ "op":"acosh",
+ "return_type":"float4",
+ "args":[
+ "in float4 x"
+ ],
+ "body":[
+ "return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));"
+ ]
+ },
+ {
+ "op":"atanh",
+ "return_type":"float",
+ "args":[
+ "in float x"
+ ],
+ "body":[
+ "return 0.5 * log((1.0 + x) / (1.0 - x));"
+ ]
+ },
+ {
+ "op":"atanh",
+ "return_type":"float2",
+ "args":[
+ "in float2 x"
+ ],
+ "body":[
+ "return 0.5 * log((1.0 + x) / (1.0 - x));"
+ ]
+ },
+ {
+ "op":"atanh",
+ "return_type":"float3",
+ "args":[
+ "in float3 x"
+ ],
+ "body":[
+ "return 0.5 * log((1.0 + x) / (1.0 - x));"
+ ]
+ },
+ {
+ "op":"atanh",
+ "return_type":"float4",
+ "args":[
+ "in float4 x"
+ ],
+ "body":[
+ "return 0.5 * log((1.0 + x) / (1.0 - x));"
+ ]
+ },
+ {
+ "op":"roundEven",
+ "return_type":"float",
+ "args":[
+ "in float x"
+ ],
+ "body":[
+ "return (frac(x) == 0.5 && trunc(x) % 2.0 == 0.0) ? trunc(x) : round(x);"
+ ]
+ },
+ {
+ "op":"roundEven",
+ "return_type":"float2",
+ "args":[
+ "in float2 x"
+ ],
+ "body":[
+ "float2 v;",
+ "v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);",
+ "v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);",
+ "return v;"
+ ]
+ },
+ {
+ "op":"roundEven",
+ "return_type":"float3",
+ "args":[
+ "in float3 x"
+ ],
+ "body":[
+ "float3 v;",
+ "v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);",
+ "v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);",
+ "v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);",
+ "return v;"
+ ]
+ },
+ {
+ "op":"roundEven",
+ "return_type":"float4",
+ "args":[
+ "in float4 x"
+ ],
+ "body":[
+ "float4 v;",
+ "v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);",
+ "v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);",
+ "v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);",
+ "v[3] = (frac(x[3]) == 0.5 && trunc(x[3]) % 2.0 == 0.0) ? trunc(x[3]) : round(x[3]);",
+ "return v;"
+ ]
+ },
+ {
+ "op":"packSnorm2x16",
+ "return_type":"uint",
+ "args":[
+ "in float2 v"
+ ],
+ "helper":[
+ "int webgl_toSnorm16(in float x) {",
+ " return int(round(clamp(x, -1.0, 1.0) * 32767.0));",
+ "}"
+ ],
+ "body":[
+ "int x = webgl_toSnorm16(v.x);",
+ "int y = webgl_toSnorm16(v.y);",
+ "return (asuint(y) << 16) | (asuint(x) & 0xffffu);"
+ ]
+ },
+ {
+ "op":"packUnorm2x16",
+ "return_type":"uint",
+ "args":[
+ "in float2 v"
+ ],
+ "helper":[
+ "uint webgl_toUnorm16(in float x) {",
+ " return uint(round(clamp(x, 0.0, 1.0) * 65535.0));",
+ "}"
+ ],
+ "body":[
+ "uint x = webgl_toUnorm16(v.x);",
+ "uint y = webgl_toUnorm16(v.y);",
+ "return (y << 16) | x;"
+ ]
+ },
+ {
+ "op":"packHalf2x16",
+ "return_type":"uint",
+ "args":[
+ "in float2 v"
+ ],
+ "body":[
+ "uint x = f32tof16(v.x);",
+ "uint y = f32tof16(v.y);",
+ "return (y << 16) | x;"
+ ]
+ },
+ {
+ "op":"unpackSnorm2x16",
+ "return_type":"float2",
+ "args":[
+ "in uint u"
+ ],
+ "helper":[
+ "float webgl_fromSnorm16(in uint x) {",
+ " int xi = asint(x & 0x7fffu) - asint(x & 0x8000u);",
+ " return clamp(float(xi) / 32767.0, -1.0, 1.0);",
+ "}"
+ ],
+ "body":[
+ "uint y = (u >> 16);",
+ "uint x = u;",
+ "return float2(webgl_fromSnorm16(x), webgl_fromSnorm16(y));"
+ ]
+ },
+ {
+ "op":"unpackUnorm2x16",
+ "return_type":"float2",
+ "args":[
+ "in uint u"
+ ],
+ "helper":[
+ "float webgl_fromUnorm16(in uint x) {",
+ " return float(x) / 65535.0;",
+ "}"
+ ],
+ "body":[
+ "uint y = (u >> 16);",
+ "uint x = u & 0xffffu;",
+ "return float2(webgl_fromUnorm16(x), webgl_fromUnorm16(y));"
+ ]
+ },
+ {
+ "op":"unpackHalf2x16",
+ "return_type":"float2",
+ "args":[
+ "in uint u"
+ ],
+ "body":[
+ "uint y = (u >> 16);",
+ "uint x = u & 0xffffu;",
+ "return float2(f16tof32(x), f16tof32(y));"
+ ]
+ },
+ {
+ "op":"packSnorm4x8",
+ "return_type":"uint",
+ "args":[
+ "in float4 v"
+ ],
+ "helper":[
+ "int webgl_toSnorm8(in float x) {",
+ " return int(round(clamp(x, -1.0, 1.0) * 127.0));",
+ "}"
+ ],
+ "body":[
+ "int x = webgl_toSnorm8(v.x);",
+ "int y = webgl_toSnorm8(v.y);",
+ "int z = webgl_toSnorm8(v.z);",
+ "int w = webgl_toSnorm8(v.w);",
+ "return ((asuint(w) & 0xffu) << 24) | ((asuint(z) & 0xffu) << 16) ",
+ "| ((asuint(y) & 0xffu) << 8) | (asuint(x) & 0xffu);"
+ ]
+ },
+ {
+ "op":"packUnorm4x8",
+ "return_type":"uint",
+ "args":[
+ "in float4 v"
+ ],
+ "helper":[
+ "uint webgl_toUnorm8(in float x) {",
+ " return uint(round(clamp(x, 0.0, 1.0) * 255.0));",
+ "}"
+ ],
+ "body":[
+ "uint x = webgl_toUnorm8(v.x);",
+ "uint y = webgl_toUnorm8(v.y);",
+ "uint z = webgl_toUnorm8(v.z);",
+ "uint w = webgl_toUnorm8(v.w);",
+ "return (w << 24) | (z << 16) | (y << 8) | x;"
+ ]
+ },
+ {
+ "op":"unpackSnorm4x8",
+ "return_type":"float4",
+ "args":[
+ "in uint u"
+ ],
+ "helper":[
+ "float webgl_fromSnorm8(in uint x) {",
+ " int xi = asint(x & 0x7fu) - asint(x & 0x80u);",
+ " return clamp(float(xi) / 127.0, -1.0, 1.0);",
+ "}"
+ ],
+ "body":[
+ "uint w = (u >> 24);",
+ "uint z = (u >> 16);",
+ "uint y = (u >> 8);",
+ "uint x = u;",
+ "return float4(webgl_fromSnorm8(x), webgl_fromSnorm8(y), ",
+ "webgl_fromSnorm8(z), webgl_fromSnorm8(w));"
+ ]
+ },
+ {
+ "op":"unpackUnorm4x8",
+ "return_type":"float4",
+ "args":[
+ "in uint u"
+ ],
+ "helper":[
+ "float webgl_fromUnorm8(in uint x) {",
+ " return float(x) / 255.0;",
+ "}"
+ ],
+ "body":[
+ "uint w = (u >> 24) & 0xffu;",
+ "uint z = (u >> 16) & 0xffu;",
+ "uint y = (u >> 8) & 0xffu;",
+ "uint x = u & 0xffu;",
+ "return float4(webgl_fromUnorm8(x), webgl_fromUnorm8(y), ",
+ "webgl_fromUnorm8(z), webgl_fromUnorm8(w));"
+ ]
+ },
+ {
+ "comment":[
+ "The matrix resulting from outer product needs to be transposed",
+ "(matrices are stored as transposed to simplify element access in HLSL).",
+ "So the function should return transpose(c * r) where c is a column vector",
+ "and r is a row vector. This can be simplified by using the following",
+ "formula:",
+ "transpose(c * r) = transpose(r) * transpose(c)",
+ "transpose(r) and transpose(c) are in a sense free, since to get the",
+ "transpose of r, we simply can build a column matrix out of the original",
+ "vector instead of a row matrix."
+ ],
+ "op":"outerProduct",
+ "return_type":"float2x2",
+ "args":[
+ "in float2 c",
+ "in float2 r"
+ ],
+ "body":[
+ "return mul(float2x1(r), float1x2(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float3x3",
+ "args":[
+ "in float3 c",
+ "in float3 r"
+ ],
+ "body":[
+ "return mul(float3x1(r), float1x3(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float4x4",
+ "args":[
+ "in float4 c",
+ "in float4 r"
+ ],
+ "body":[
+ "return mul(float4x1(r), float1x4(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float2x3",
+ "args":[
+ "in float3 c",
+ "in float2 r"
+ ],
+ "body":[
+ "return mul(float2x1(r), float1x3(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float3x2",
+ "args":[
+ "in float2 c",
+ "in float3 r"
+ ],
+ "body":[
+ "return mul(float3x1(r), float1x2(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float2x4",
+ "args":[
+ "in float4 c",
+ "in float2 r"
+ ],
+ "body":[
+ "return mul(float2x1(r), float1x4(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float4x2",
+ "args":[
+ "in float2 c",
+ "in float4 r"
+ ],
+ "body":[
+ "return mul(float4x1(r), float1x2(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float3x4",
+ "args":[
+ "in float4 c",
+ "in float3 r"
+ ],
+ "body":[
+ "return mul(float3x1(r), float1x4(c));"
+ ]
+ },
+ {
+ "op":"outerProduct",
+ "return_type":"float4x3",
+ "args":[
+ "in float3 c",
+ "in float4 r"
+ ],
+ "body":[
+ "return mul(float4x1(r), float1x3(c));"
+ ]
+ },
+ {
+ "comment":[
+ "Remember here that the parameter matrix is actually the transpose",
+ "of the matrix that we're trying to invert, and the resulting matrix",
+ "should also be the transpose of the inverse.",
+ "When accessing the parameter matrix with m[a][b] it can be thought of so",
+ "that a is the column and b is the row of the matrix that we're inverting.",
+ "We calculate the inverse as the adjugate matrix divided by the",
+ "determinant of the matrix being inverted. However, as the result needs",
+ "to be transposed, we actually use of the transpose of the adjugate matrix",
+ "which happens to be the cofactor matrix. That's stored in 'cof'.",
+ "We don't need to care about divide-by-zero since results are undefined",
+ "for singular or poorly-conditioned matrices."
+ ],
+ "op":"inverse",
+ "return_type":"float2x2",
+ "args":[
+ "in float2x2 m"
+ ],
+ "body":[
+ "float2x2 cof = { m[1][1], -m[0][1], -m[1][0], m[0][0] };",
+ "return cof / determinant(transpose(m));"
+ ]
+ },
+ {
+ "comment":[
+ "cofAB is the cofactor for column A and row B."
+ ],
+ "op":"inverse",
+ "return_type":"float3x3",
+ "args":[
+ "in float3x3 m"
+ ],
+ "body":[
+ "float cof00 = m[1][1] * m[2][2] - m[2][1] * m[1][2];",
+ "float cof01 = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]);",
+ "float cof02 = m[1][0] * m[2][1] - m[2][0] * m[1][1];",
+ "float cof10 = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]);",
+ "float cof11 = m[0][0] * m[2][2] - m[2][0] * m[0][2];",
+ "float cof12 = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]);",
+ "float cof20 = m[0][1] * m[1][2] - m[1][1] * m[0][2];",
+ "float cof21 = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]);",
+ "float cof22 = m[0][0] * m[1][1] - m[1][0] * m[0][1];",
+ "float3x3 cof = { cof00, cof10, cof20, cof01, cof11, cof21, cof02, cof12, cof22 };",
+ "return cof / determinant(transpose(m));"
+ ]
+ },
+ {
+ "op":"inverse",
+ "return_type":"float4x4",
+ "args":[
+ "in float4x4 m"
+ ],
+ "body":[
+ "float cof00 = m[1][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[1][3] + m[3][1] * ",
+ "m[1][2] * m[2][3]",
+ " - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] - m[3][1] * m[2][2] * ",
+ "m[1][3];",
+ "float cof01 = -(m[1][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[1][3] + m[3][0] * ",
+ "m[1][2] * m[2][3]",
+ " - m[1][0] * m[3][2] * m[2][3] - m[2][0] * m[1][2] * m[3][3] - m[3][0] * m[2][2] * ",
+ "m[1][3]);",
+ "float cof02 = m[1][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[1][3] + m[3][0] * ",
+ "m[1][1] * m[2][3]",
+ " - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] - m[3][0] * m[2][1] * ",
+ "m[1][3];",
+ "float cof03 = -(m[1][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[1][2] + m[3][0] * ",
+ "m[1][1] * m[2][2]",
+ " - m[1][0] * m[3][1] * m[2][2] - m[2][0] * m[1][1] * m[3][2] - m[3][0] * m[2][1] * ",
+ "m[1][2]);",
+ "float cof10 = -(m[0][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[0][3] + m[3][1] * ",
+ "m[0][2] * m[2][3]",
+ " - m[0][1] * m[3][2] * m[2][3] - m[2][1] * m[0][2] * m[3][3] - m[3][1] * m[2][2] * ",
+ "m[0][3]);",
+ "float cof11 = m[0][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[0][3] + m[3][0] * ",
+ "m[0][2] * m[2][3]",
+ " - m[0][0] * m[3][2] * m[2][3] - m[2][0] * m[0][2] * m[3][3] - m[3][0] * m[2][2] * ",
+ "m[0][3];",
+ "float cof12 = -(m[0][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[0][3] + m[3][0] * ",
+ "m[0][1] * m[2][3]",
+ " - m[0][0] * m[3][1] * m[2][3] - m[2][0] * m[0][1] * m[3][3] - m[3][0] * m[2][1] * ",
+ "m[0][3]);",
+ "float cof13 = m[0][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[0][2] + m[3][0] * ",
+ "m[0][1] * m[2][2]",
+ " - m[0][0] * m[3][1] * m[2][2] - m[2][0] * m[0][1] * m[3][2] - m[3][0] * m[2][1] * ",
+ "m[0][2];",
+ "float cof20 = m[0][1] * m[1][2] * m[3][3] + m[1][1] * m[3][2] * m[0][3] + m[3][1] * ",
+ "m[0][2] * m[1][3]",
+ " - m[0][1] * m[3][2] * m[1][3] - m[1][1] * m[0][2] * m[3][3] - m[3][1] * m[1][2] * ",
+ "m[0][3];",
+ "float cof21 = -(m[0][0] * m[1][2] * m[3][3] + m[1][0] * m[3][2] * m[0][3] + m[3][0] * ",
+ "m[0][2] * m[1][3]",
+ " - m[0][0] * m[3][2] * m[1][3] - m[1][0] * m[0][2] * m[3][3] - m[3][0] * m[1][2] * ",
+ "m[0][3]);",
+ "float cof22 = m[0][0] * m[1][1] * m[3][3] + m[1][0] * m[3][1] * m[0][3] + m[3][0] * ",
+ "m[0][1] * m[1][3]",
+ " - m[0][0] * m[3][1] * m[1][3] - m[1][0] * m[0][1] * m[3][3] - m[3][0] * m[1][1] * ",
+ "m[0][3];",
+ "float cof23 = -(m[0][0] * m[1][1] * m[3][2] + m[1][0] * m[3][1] * m[0][2] + m[3][0] * ",
+ "m[0][1] * m[1][2]",
+ " - m[0][0] * m[3][1] * m[1][2] - m[1][0] * m[0][1] * m[3][2] - m[3][0] * m[1][1] * ",
+ "m[0][2]);",
+ "float cof30 = -(m[0][1] * m[1][2] * m[2][3] + m[1][1] * m[2][2] * m[0][3] + m[2][1] * ",
+ "m[0][2] * m[1][3]",
+ " - m[0][1] * m[2][2] * m[1][3] - m[1][1] * m[0][2] * m[2][3] - m[2][1] * m[1][2] * ",
+ "m[0][3]);",
+ "float cof31 = m[0][0] * m[1][2] * m[2][3] + m[1][0] * m[2][2] * m[0][3] + m[2][0] * ",
+ "m[0][2] * m[1][3]",
+ " - m[0][0] * m[2][2] * m[1][3] - m[1][0] * m[0][2] * m[2][3] - m[2][0] * m[1][2] * ",
+ "m[0][3];",
+ "float cof32 = -(m[0][0] * m[1][1] * m[2][3] + m[1][0] * m[2][1] * m[0][3] + m[2][0] * ",
+ "m[0][1] * m[1][3]",
+ " - m[0][0] * m[2][1] * m[1][3] - m[1][0] * m[0][1] * m[2][3] - m[2][0] * m[1][1] * ",
+ "m[0][3]);",
+ "float cof33 = m[0][0] * m[1][1] * m[2][2] + m[1][0] * m[2][1] * m[0][2] + m[2][0] * ",
+ "m[0][1] * m[1][2]",
+ " - m[0][0] * m[2][1] * m[1][2] - m[1][0] * m[0][1] * m[2][2] - m[2][0] * m[1][1] * ",
+ "m[0][2];",
+ "float4x4 cof = { cof00, cof10, cof20, cof30, cof01, cof11, cof21, cof31,",
+ " cof02, cof12, cof22, cof32, cof03, cof13, cof23, cof33 };",
+ "return cof / determinant(transpose(m));"
+ ]
+ },
+ {
+ "comment":[
+ "Emulate ESSL3 variant of mix that takes last argument as boolean vector.",
+ "genType mix(genType x, genType y, genBType a): Selects which vector each returned component",
+ "comes from. For a component of 'a' that is false, the corresponding component of 'x' is",
+ "returned. For a component of 'a' that is true, the corresponding component of 'y' is returned."
+ ],
+ "op":"mix",
+ "return_type":"float",
+ "args":[
+ "float x",
+ "float y",
+ "bool a"
+ ],
+ "body":[
+ "return a ? y : x;"
+ ]
+ },
+ {
+ "op":"mix",
+ "return_type":"float2",
+ "args":[
+ "float2 x",
+ "float2 y",
+ "bool2 a"
+ ],
+ "body":[
+ "return a ? y : x;"
+ ]
+ },
+ {
+ "op":"mix",
+ "return_type":"float3",
+ "args":[
+ "float3 x",
+ "float3 y",
+ "bool3 a"
+ ],
+ "body":[
+ "return a ? y : x;"
+ ]
+ },
+ {
+ "op":"mix",
+ "return_type":"float4",
+ "args":[
+ "float4 x",
+ "float4 y",
+ "bool4 a"
+ ],
+ "body":[
+ "return a ? y : x;"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"uint",
+ "args":[
+ "uint value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return 0u;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "return (value & mask) >> offset;"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"uint2",
+ "args":[
+ "uint2 value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return uint2(0u, 0u);",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "return (value & mask) >> offset;"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"uint3",
+ "args":[
+ "uint3 value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return uint3(0u, 0u, 0u);",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "return (value & mask) >> offset;"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"uint4",
+ "args":[
+ "uint4 value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return uint4(0u, 0u, 0u, 0u);",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "return (value & mask) >> offset;"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"int",
+ "args":[
+ "int value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return 0;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint resultUnsigned = (asuint(value) & mask) >> offset;",
+ "if (bits != 32 && (resultUnsigned & maskMsb) != 0)",
+ "{",
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;",
+ " resultUnsigned |= higherBitsMask;",
+ "}",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"int2",
+ "args":[
+ "int2 value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return int2(0, 0);",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint2 resultUnsigned = (asuint(value) & mask) >> offset;",
+ "if (bits != 32)",
+ "{",
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;",
+ " resultUnsigned |= ((resultUnsigned & maskMsb) >> (bits - 1)) * higherBitsMask;",
+ "}",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"int3",
+ "args":[
+ "int3 value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return int3(0, 0, 0);",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint3 resultUnsigned = (asuint(value) & mask) >> offset;",
+ "if (bits != 32)",
+ "{",
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;",
+ " resultUnsigned |= ((resultUnsigned & maskMsb) >> (bits - 1)) * higherBitsMask;",
+ "}",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"bitfieldExtract",
+ "return_type":"int4",
+ "args":[
+ "int4 value",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return int4(0, 0, 0, 0);",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint mask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint4 resultUnsigned = (asuint(value) & mask) >> offset;",
+ "if (bits != 32)",
+ "{",
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;",
+ " resultUnsigned |= ((resultUnsigned & maskMsb) >> (bits - 1)) * higherBitsMask;",
+ "}",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"uint",
+ "args":[
+ "uint base",
+ "uint insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "return (base & baseMask) | ((insert << offset) & insertMask);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"uint2",
+ "args":[
+ "uint2 base",
+ "uint2 insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "return (base & baseMask) | ((insert << offset) & insertMask);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"uint3",
+ "args":[
+ "uint3 base",
+ "uint3 insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "return (base & baseMask) | ((insert << offset) & insertMask);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"uint4",
+ "args":[
+ "uint4 base",
+ "uint4 insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "return (base & baseMask) | ((insert << offset) & insertMask);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"int",
+ "args":[
+ "int base",
+ "int insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "uint resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & ",
+ " insertMask);",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"int2",
+ "args":[
+ "int2 base",
+ "int2 insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "uint2 resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & ",
+ " insertMask);",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"int3",
+ "args":[
+ "int3 base",
+ "int3 insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "uint3 resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & ",
+ " insertMask);",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"bitfieldInsert",
+ "return_type":"int4",
+ "args":[
+ "int4 base",
+ "int4 insert",
+ "int offset",
+ "int bits"
+ ],
+ "body":[
+ "if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)",
+ "{",
+ " return base;",
+ "}",
+ "uint maskMsb = (1u << (bits - 1));",
+ "uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;",
+ "uint baseMask = ~insertMask;",
+ "uint4 resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & ",
+ "insertMask);",
+ "return asint(resultUnsigned);"
+ ]
+ },
+ {
+ "op":"uaddCarry",
+ "return_type":"uint",
+ "args":[
+ "uint x",
+ "uint y",
+ "out uint carry"
+ ],
+ "body":[
+ "carry = uint(x > (0xffffffffu - y));",
+ "return x + y;"
+ ]
+ },
+ {
+ "op":"uaddCarry",
+ "return_type":"uint2",
+ "args":[
+ "uint2 x",
+ "uint2 y",
+ "out uint2 carry"
+ ],
+ "body":[
+ "carry = uint2(x > (0xffffffffu - y));",
+ "return x + y;"
+ ]
+ },
+ {
+ "op":"uaddCarry",
+ "return_type":"uint3",
+ "args":[
+ "uint3 x",
+ "uint3 y",
+ "out uint3 carry"
+ ],
+ "body":[
+ "carry = uint3(x > (0xffffffffu - y));",
+ "return x + y;"
+ ]
+ },
+ {
+ "op":"uaddCarry",
+ "return_type":"uint4",
+ "args":[
+ "uint4 x",
+ "uint4 y",
+ "out uint4 carry"
+ ],
+ "body":[
+ "carry = uint4(x > (0xffffffffu - y));",
+ "return x + y;"
+ ]
+ },
+ {
+ "op":"usubBorrow",
+ "return_type":"uint",
+ "args":[
+ "uint x",
+ "uint y",
+ "out uint borrow"
+ ],
+ "body":[
+ "borrow = uint(x < y);",
+ "return x - y;"
+ ]
+ },
+ {
+ "op":"usubBorrow",
+ "return_type":"uint2",
+ "args":[
+ "uint2 x",
+ "uint2 y",
+ "out uint2 borrow"
+ ],
+ "body":[
+ "borrow = uint2(x < y);",
+ "return x - y;"
+ ]
+ },
+ {
+ "op":"usubBorrow",
+ "return_type":"uint3",
+ "args":[
+ "uint3 x",
+ "uint3 y",
+ "out uint3 borrow"
+ ],
+ "body":[
+ "borrow = uint3(x < y);",
+ "return x - y;"
+ ]
+ },
+ {
+ "op":"usubBorrow",
+ "return_type":"uint4",
+ "args":[
+ "uint4 x",
+ "uint4 y",
+ "out uint4 borrow"
+ ],
+ "body":[
+ "borrow = uint4(x < y);",
+ "return x - y;"
+ ]
+ }
+]
diff --git a/src/3rdparty/angle/src/compiler/translator/emulated_builtin_functions_hlsl_autogen.cpp b/src/3rdparty/angle/src/compiler/translator/emulated_builtin_functions_hlsl_autogen.cpp
new file mode 100644
index 0000000000..288da5e0f5
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/emulated_builtin_functions_hlsl_autogen.cpp
@@ -0,0 +1,859 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by gen_emulated_builtin_function_tables.py using data from
+// emulated_builtin_function_data_hlsl.json.
+//
+// Copyright 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// emulated_builtin_functions_hlsl:
+// HLSL code for emulating GLSL builtin functions not present in HLSL.
+
+#include "compiler/translator/BuiltInFunctionEmulator.h"
+
+namespace sh
+{
+
+namespace
+{
+
+struct FunctionPair
+{
+ constexpr FunctionPair(const MiniFunctionId &idIn, const char *bodyIn) : id(idIn), body(bodyIn)
+ {
+ }
+
+ MiniFunctionId id;
+ const char *body;
+};
+
+constexpr FunctionPair g_hlslFunctions[] = {
+ {{EOpMod, ParamType::Float1, ParamType::Float1},
+ "float mod_emu(float x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"},
+ {{EOpMod, ParamType::Float2, ParamType::Float2},
+ "float2 mod_emu(float2 x, float2 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"},
+ {{EOpMod, ParamType::Float2, ParamType::Float1},
+ "float2 mod_emu(float2 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"},
+ {{EOpMod, ParamType::Float3, ParamType::Float3},
+ "float3 mod_emu(float3 x, float3 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"},
+ {{EOpMod, ParamType::Float3, ParamType::Float1},
+ "float3 mod_emu(float3 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"},
+ {{EOpMod, ParamType::Float4, ParamType::Float4},
+ "float4 mod_emu(float4 x, float4 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"},
+ {{EOpMod, ParamType::Float4, ParamType::Float1},
+ "float4 mod_emu(float4 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"},
+ {{EOpFrexp, ParamType::Float1, ParamType::Int1},
+ "float frexp_emu(float x, out int exp)\n"
+ "{\n"
+ " float fexp;\n"
+ " float mantissa = frexp(abs(x), fexp) * sign(x);\n"
+ " exp = int(fexp);\n"
+ " return mantissa;\n"
+ "}\n"},
+ {{EOpFrexp, ParamType::Float2, ParamType::Int2},
+ "float2 frexp_emu(float2 x, out int2 exp)\n"
+ "{\n"
+ " float2 fexp;\n"
+ " float2 mantissa = frexp(abs(x), fexp) * sign(x);\n"
+ " exp = int2(fexp);\n"
+ " return mantissa;\n"
+ "}\n"},
+ {{EOpFrexp, ParamType::Float3, ParamType::Int3},
+ "float3 frexp_emu(float3 x, out int3 exp)\n"
+ "{\n"
+ " float3 fexp;\n"
+ " float3 mantissa = frexp(abs(x), fexp) * sign(x);\n"
+ " exp = int3(fexp);\n"
+ " return mantissa;\n"
+ "}\n"},
+ {{EOpFrexp, ParamType::Float4, ParamType::Int4},
+ "float4 frexp_emu(float4 x, out int4 exp)\n"
+ "{\n"
+ " float4 fexp;\n"
+ " float4 mantissa = frexp(abs(x), fexp) * sign(x);\n"
+ " exp = int4(fexp);\n"
+ " return mantissa;\n"
+ "}\n"},
+ {{EOpLdexp, ParamType::Float1, ParamType::Int1},
+ "float ldexp_emu(float x, int exp)\n"
+ "{\n"
+ " return ldexp(x, float(exp));\n"
+ "}\n"},
+ {{EOpLdexp, ParamType::Float2, ParamType::Int2},
+ "float2 ldexp_emu(float2 x, int2 exp)\n"
+ "{\n"
+ " return ldexp(x, float2(exp));\n"
+ "}\n"},
+ {{EOpLdexp, ParamType::Float3, ParamType::Int3},
+ "float3 ldexp_emu(float3 x, int3 exp)\n"
+ "{\n"
+ " return ldexp(x, float3(exp));\n"
+ "}\n"},
+ {{EOpLdexp, ParamType::Float4, ParamType::Int4},
+ "float4 ldexp_emu(float4 x, int4 exp)\n"
+ "{\n"
+ " return ldexp(x, float4(exp));\n"
+ "}\n"},
+ {{EOpFaceforward, ParamType::Float1, ParamType::Float1, ParamType::Float1},
+ "float faceforward_emu(float N, float I, float Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"},
+ {{EOpFaceforward, ParamType::Float2, ParamType::Float2, ParamType::Float2},
+ "float2 faceforward_emu(float2 N, float2 I, float2 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"},
+ {{EOpFaceforward, ParamType::Float3, ParamType::Float3, ParamType::Float3},
+ "float3 faceforward_emu(float3 N, float3 I, float3 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"},
+ {{EOpFaceforward, ParamType::Float4, ParamType::Float4, ParamType::Float4},
+ "float4 faceforward_emu(float4 N, float4 I, float4 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"},
+ {{EOpAtan, ParamType::Float1, ParamType::Float1},
+ "float atan_emu(float y, float x)\n"
+ "{\n"
+ " if(x == 0 && y == 0) x = 1;\n"
+ " return atan2(y, x);\n"
+ "}\n"},
+ {{EOpAtan, ParamType::Float2, ParamType::Float2},
+ "float2 atan_emu(float2 y, float2 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " return float2(atan2(y[0], x[0]), atan2(y[1], x[1]));\n"
+ "}\n"},
+ {{EOpAtan, ParamType::Float3, ParamType::Float3},
+ "float3 atan_emu(float3 y, float3 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
+ " return float3(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]));\n"
+ "}\n"},
+ {{EOpAtan, ParamType::Float4, ParamType::Float4},
+ "float4 atan_emu(float4 y, float4 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
+ " if(x[3] == 0 && y[3] == 0) x[3] = 1;\n"
+ " return float4(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], \n"
+ " x[2]), atan2(y[3], x[3]));\n"
+ "}\n"},
+ {{EOpAsinh, ParamType::Float1},
+ "float asinh_emu(in float x)\n"
+ "{\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n"},
+ {{EOpAsinh, ParamType::Float2},
+ "float2 asinh_emu(in float2 x)\n"
+ "{\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n"},
+ {{EOpAsinh, ParamType::Float3},
+ "float3 asinh_emu(in float3 x)\n"
+ "{\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n"},
+ {{EOpAsinh, ParamType::Float4},
+ "float4 asinh_emu(in float4 x)\n"
+ "{\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n"},
+ {{EOpAcosh, ParamType::Float1},
+ "float acosh_emu(in float x)\n"
+ "{\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n"},
+ {{EOpAcosh, ParamType::Float2},
+ "float2 acosh_emu(in float2 x)\n"
+ "{\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n"},
+ {{EOpAcosh, ParamType::Float3},
+ "float3 acosh_emu(in float3 x)\n"
+ "{\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n"},
+ {{EOpAcosh, ParamType::Float4},
+ "float4 acosh_emu(in float4 x)\n"
+ "{\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n"},
+ {{EOpAtanh, ParamType::Float1},
+ "float atanh_emu(in float x)\n"
+ "{\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n"},
+ {{EOpAtanh, ParamType::Float2},
+ "float2 atanh_emu(in float2 x)\n"
+ "{\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n"},
+ {{EOpAtanh, ParamType::Float3},
+ "float3 atanh_emu(in float3 x)\n"
+ "{\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n"},
+ {{EOpAtanh, ParamType::Float4},
+ "float4 atanh_emu(in float4 x)\n"
+ "{\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n"},
+ {{EOpRoundEven, ParamType::Float1},
+ "float roundEven_emu(in float x)\n"
+ "{\n"
+ " return (frac(x) == 0.5 && trunc(x) % 2.0 == 0.0) ? trunc(x) : round(x);\n"
+ "}\n"},
+ {{EOpRoundEven, ParamType::Float2},
+ "float2 roundEven_emu(in float2 x)\n"
+ "{\n"
+ " float2 v;\n"
+ " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
+ " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
+ " return v;\n"
+ "}\n"},
+ {{EOpRoundEven, ParamType::Float3},
+ "float3 roundEven_emu(in float3 x)\n"
+ "{\n"
+ " float3 v;\n"
+ " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
+ " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
+ " v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
+ " return v;\n"
+ "}\n"},
+ {{EOpRoundEven, ParamType::Float4},
+ "float4 roundEven_emu(in float4 x)\n"
+ "{\n"
+ " float4 v;\n"
+ " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
+ " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
+ " v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
+ " v[3] = (frac(x[3]) == 0.5 && trunc(x[3]) % 2.0 == 0.0) ? trunc(x[3]) : round(x[3]);\n"
+ " return v;\n"
+ "}\n"},
+ {{EOpPackSnorm2x16, ParamType::Float2},
+ "int webgl_toSnorm16(in float x) {\n"
+ " return int(round(clamp(x, -1.0, 1.0) * 32767.0));\n"
+ "}\n"
+ "uint packSnorm2x16_emu(in float2 v)\n"
+ "{\n"
+ " int x = webgl_toSnorm16(v.x);\n"
+ " int y = webgl_toSnorm16(v.y);\n"
+ " return (asuint(y) << 16) | (asuint(x) & 0xffffu);\n"
+ "}\n"},
+ {{EOpPackUnorm2x16, ParamType::Float2},
+ "uint webgl_toUnorm16(in float x) {\n"
+ " return uint(round(clamp(x, 0.0, 1.0) * 65535.0));\n"
+ "}\n"
+ "uint packUnorm2x16_emu(in float2 v)\n"
+ "{\n"
+ " uint x = webgl_toUnorm16(v.x);\n"
+ " uint y = webgl_toUnorm16(v.y);\n"
+ " return (y << 16) | x;\n"
+ "}\n"},
+ {{EOpPackHalf2x16, ParamType::Float2},
+ "uint packHalf2x16_emu(in float2 v)\n"
+ "{\n"
+ " uint x = f32tof16(v.x);\n"
+ " uint y = f32tof16(v.y);\n"
+ " return (y << 16) | x;\n"
+ "}\n"},
+ {{EOpUnpackSnorm2x16, ParamType::Uint1},
+ "float webgl_fromSnorm16(in uint x) {\n"
+ " int xi = asint(x & 0x7fffu) - asint(x & 0x8000u);\n"
+ " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n"
+ "}\n"
+ "float2 unpackSnorm2x16_emu(in uint u)\n"
+ "{\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u;\n"
+ " return float2(webgl_fromSnorm16(x), webgl_fromSnorm16(y));\n"
+ "}\n"},
+ {{EOpUnpackUnorm2x16, ParamType::Uint1},
+ "float webgl_fromUnorm16(in uint x) {\n"
+ " return float(x) / 65535.0;\n"
+ "}\n"
+ "float2 unpackUnorm2x16_emu(in uint u)\n"
+ "{\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u & 0xffffu;\n"
+ " return float2(webgl_fromUnorm16(x), webgl_fromUnorm16(y));\n"
+ "}\n"},
+ {{EOpUnpackHalf2x16, ParamType::Uint1},
+ "float2 unpackHalf2x16_emu(in uint u)\n"
+ "{\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u & 0xffffu;\n"
+ " return float2(f16tof32(x), f16tof32(y));\n"
+ "}\n"},
+ {{EOpPackSnorm4x8, ParamType::Float4},
+ "int webgl_toSnorm8(in float x) {\n"
+ " return int(round(clamp(x, -1.0, 1.0) * 127.0));\n"
+ "}\n"
+ "uint packSnorm4x8_emu(in float4 v)\n"
+ "{\n"
+ " int x = webgl_toSnorm8(v.x);\n"
+ " int y = webgl_toSnorm8(v.y);\n"
+ " int z = webgl_toSnorm8(v.z);\n"
+ " int w = webgl_toSnorm8(v.w);\n"
+ " return ((asuint(w) & 0xffu) << 24) | ((asuint(z) & 0xffu) << 16) \n"
+ " | ((asuint(y) & 0xffu) << 8) | (asuint(x) & 0xffu);\n"
+ "}\n"},
+ {{EOpPackUnorm4x8, ParamType::Float4},
+ "uint webgl_toUnorm8(in float x) {\n"
+ " return uint(round(clamp(x, 0.0, 1.0) * 255.0));\n"
+ "}\n"
+ "uint packUnorm4x8_emu(in float4 v)\n"
+ "{\n"
+ " uint x = webgl_toUnorm8(v.x);\n"
+ " uint y = webgl_toUnorm8(v.y);\n"
+ " uint z = webgl_toUnorm8(v.z);\n"
+ " uint w = webgl_toUnorm8(v.w);\n"
+ " return (w << 24) | (z << 16) | (y << 8) | x;\n"
+ "}\n"},
+ {{EOpUnpackSnorm4x8, ParamType::Uint1},
+ "float webgl_fromSnorm8(in uint x) {\n"
+ " int xi = asint(x & 0x7fu) - asint(x & 0x80u);\n"
+ " return clamp(float(xi) / 127.0, -1.0, 1.0);\n"
+ "}\n"
+ "float4 unpackSnorm4x8_emu(in uint u)\n"
+ "{\n"
+ " uint w = (u >> 24);\n"
+ " uint z = (u >> 16);\n"
+ " uint y = (u >> 8);\n"
+ " uint x = u;\n"
+ " return float4(webgl_fromSnorm8(x), webgl_fromSnorm8(y), \n"
+ " webgl_fromSnorm8(z), webgl_fromSnorm8(w));\n"
+ "}\n"},
+ {{EOpUnpackUnorm4x8, ParamType::Uint1},
+ "float webgl_fromUnorm8(in uint x) {\n"
+ " return float(x) / 255.0;\n"
+ "}\n"
+ "float4 unpackUnorm4x8_emu(in uint u)\n"
+ "{\n"
+ " uint w = (u >> 24) & 0xffu;\n"
+ " uint z = (u >> 16) & 0xffu;\n"
+ " uint y = (u >> 8) & 0xffu;\n"
+ " uint x = u & 0xffu;\n"
+ " return float4(webgl_fromUnorm8(x), webgl_fromUnorm8(y), \n"
+ " webgl_fromUnorm8(z), webgl_fromUnorm8(w));\n"
+ "}\n"},
+ // The matrix resulting from outer product needs to be transposed
+ // (matrices are stored as transposed to simplify element access in HLSL).
+ // So the function should return transpose(c * r) where c is a column vector
+ // and r is a row vector. This can be simplified by using the following
+ // formula:
+ // transpose(c * r) = transpose(r) * transpose(c)
+ // transpose(r) and transpose(c) are in a sense free, since to get the
+ // transpose of r, we simply can build a column matrix out of the original
+ // vector instead of a row matrix.
+ {{EOpOuterProduct, ParamType::Float2, ParamType::Float2},
+ "float2x2 outerProduct_emu(in float2 c, in float2 r)\n"
+ "{\n"
+ " return mul(float2x1(r), float1x2(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float3, ParamType::Float3},
+ "float3x3 outerProduct_emu(in float3 c, in float3 r)\n"
+ "{\n"
+ " return mul(float3x1(r), float1x3(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float4, ParamType::Float4},
+ "float4x4 outerProduct_emu(in float4 c, in float4 r)\n"
+ "{\n"
+ " return mul(float4x1(r), float1x4(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float3, ParamType::Float2},
+ "float2x3 outerProduct_emu(in float3 c, in float2 r)\n"
+ "{\n"
+ " return mul(float2x1(r), float1x3(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float2, ParamType::Float3},
+ "float3x2 outerProduct_emu(in float2 c, in float3 r)\n"
+ "{\n"
+ " return mul(float3x1(r), float1x2(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float4, ParamType::Float2},
+ "float2x4 outerProduct_emu(in float4 c, in float2 r)\n"
+ "{\n"
+ " return mul(float2x1(r), float1x4(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float2, ParamType::Float4},
+ "float4x2 outerProduct_emu(in float2 c, in float4 r)\n"
+ "{\n"
+ " return mul(float4x1(r), float1x2(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float4, ParamType::Float3},
+ "float3x4 outerProduct_emu(in float4 c, in float3 r)\n"
+ "{\n"
+ " return mul(float3x1(r), float1x4(c));\n"
+ "}\n"},
+ {{EOpOuterProduct, ParamType::Float3, ParamType::Float4},
+ "float4x3 outerProduct_emu(in float3 c, in float4 r)\n"
+ "{\n"
+ " return mul(float4x1(r), float1x3(c));\n"
+ "}\n"},
+ // Remember here that the parameter matrix is actually the transpose
+ // of the matrix that we're trying to invert, and the resulting matrix
+ // should also be the transpose of the inverse.
+ // When accessing the parameter matrix with m[a][b] it can be thought of so
+ // that a is the column and b is the row of the matrix that we're inverting.
+ // We calculate the inverse as the adjugate matrix divided by the
+ // determinant of the matrix being inverted. However, as the result needs
+ // to be transposed, we actually use of the transpose of the adjugate matrix
+ // which happens to be the cofactor matrix. That's stored in 'cof'.
+ // We don't need to care about divide-by-zero since results are undefined
+ // for singular or poorly-conditioned matrices.
+ {{EOpInverse, ParamType::Mat2},
+ "float2x2 inverse_emu(in float2x2 m)\n"
+ "{\n"
+ " float2x2 cof = { m[1][1], -m[0][1], -m[1][0], m[0][0] };\n"
+ " return cof / determinant(transpose(m));\n"
+ "}\n"},
+ // cofAB is the cofactor for column A and row B.
+ {{EOpInverse, ParamType::Mat3},
+ "float3x3 inverse_emu(in float3x3 m)\n"
+ "{\n"
+ " float cof00 = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n"
+ " float cof01 = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]);\n"
+ " float cof02 = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n"
+ " float cof10 = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]);\n"
+ " float cof11 = m[0][0] * m[2][2] - m[2][0] * m[0][2];\n"
+ " float cof12 = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]);\n"
+ " float cof20 = m[0][1] * m[1][2] - m[1][1] * m[0][2];\n"
+ " float cof21 = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]);\n"
+ " float cof22 = m[0][0] * m[1][1] - m[1][0] * m[0][1];\n"
+ " float3x3 cof = { cof00, cof10, cof20, cof01, cof11, cof21, cof02, cof12, cof22 };\n"
+ " return cof / determinant(transpose(m));\n"
+ "}\n"},
+ {{EOpInverse, ParamType::Mat4},
+ "float4x4 inverse_emu(in float4x4 m)\n"
+ "{\n"
+ " float cof00 = m[1][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[1][3] + m[3][1] * \n"
+ " m[1][2] * m[2][3]\n"
+ " - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] - m[3][1] * m[2][2] * \n"
+ " m[1][3];\n"
+ " float cof01 = -(m[1][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[1][3] + m[3][0] * \n"
+ " m[1][2] * m[2][3]\n"
+ " - m[1][0] * m[3][2] * m[2][3] - m[2][0] * m[1][2] * m[3][3] - m[3][0] * m[2][2] * \n"
+ " m[1][3]);\n"
+ " float cof02 = m[1][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[1][3] + m[3][0] * \n"
+ " m[1][1] * m[2][3]\n"
+ " - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] - m[3][0] * m[2][1] * \n"
+ " m[1][3];\n"
+ " float cof03 = -(m[1][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[1][2] + m[3][0] * \n"
+ " m[1][1] * m[2][2]\n"
+ " - m[1][0] * m[3][1] * m[2][2] - m[2][0] * m[1][1] * m[3][2] - m[3][0] * m[2][1] * \n"
+ " m[1][2]);\n"
+ " float cof10 = -(m[0][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[0][3] + m[3][1] * \n"
+ " m[0][2] * m[2][3]\n"
+ " - m[0][1] * m[3][2] * m[2][3] - m[2][1] * m[0][2] * m[3][3] - m[3][1] * m[2][2] * \n"
+ " m[0][3]);\n"
+ " float cof11 = m[0][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[0][3] + m[3][0] * \n"
+ " m[0][2] * m[2][3]\n"
+ " - m[0][0] * m[3][2] * m[2][3] - m[2][0] * m[0][2] * m[3][3] - m[3][0] * m[2][2] * \n"
+ " m[0][3];\n"
+ " float cof12 = -(m[0][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[0][3] + m[3][0] * \n"
+ " m[0][1] * m[2][3]\n"
+ " - m[0][0] * m[3][1] * m[2][3] - m[2][0] * m[0][1] * m[3][3] - m[3][0] * m[2][1] * \n"
+ " m[0][3]);\n"
+ " float cof13 = m[0][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[0][2] + m[3][0] * \n"
+ " m[0][1] * m[2][2]\n"
+ " - m[0][0] * m[3][1] * m[2][2] - m[2][0] * m[0][1] * m[3][2] - m[3][0] * m[2][1] * \n"
+ " m[0][2];\n"
+ " float cof20 = m[0][1] * m[1][2] * m[3][3] + m[1][1] * m[3][2] * m[0][3] + m[3][1] * \n"
+ " m[0][2] * m[1][3]\n"
+ " - m[0][1] * m[3][2] * m[1][3] - m[1][1] * m[0][2] * m[3][3] - m[3][1] * m[1][2] * \n"
+ " m[0][3];\n"
+ " float cof21 = -(m[0][0] * m[1][2] * m[3][3] + m[1][0] * m[3][2] * m[0][3] + m[3][0] * \n"
+ " m[0][2] * m[1][3]\n"
+ " - m[0][0] * m[3][2] * m[1][3] - m[1][0] * m[0][2] * m[3][3] - m[3][0] * m[1][2] * \n"
+ " m[0][3]);\n"
+ " float cof22 = m[0][0] * m[1][1] * m[3][3] + m[1][0] * m[3][1] * m[0][3] + m[3][0] * \n"
+ " m[0][1] * m[1][3]\n"
+ " - m[0][0] * m[3][1] * m[1][3] - m[1][0] * m[0][1] * m[3][3] - m[3][0] * m[1][1] * \n"
+ " m[0][3];\n"
+ " float cof23 = -(m[0][0] * m[1][1] * m[3][2] + m[1][0] * m[3][1] * m[0][2] + m[3][0] * \n"
+ " m[0][1] * m[1][2]\n"
+ " - m[0][0] * m[3][1] * m[1][2] - m[1][0] * m[0][1] * m[3][2] - m[3][0] * m[1][1] * \n"
+ " m[0][2]);\n"
+ " float cof30 = -(m[0][1] * m[1][2] * m[2][3] + m[1][1] * m[2][2] * m[0][3] + m[2][1] * \n"
+ " m[0][2] * m[1][3]\n"
+ " - m[0][1] * m[2][2] * m[1][3] - m[1][1] * m[0][2] * m[2][3] - m[2][1] * m[1][2] * \n"
+ " m[0][3]);\n"
+ " float cof31 = m[0][0] * m[1][2] * m[2][3] + m[1][0] * m[2][2] * m[0][3] + m[2][0] * \n"
+ " m[0][2] * m[1][3]\n"
+ " - m[0][0] * m[2][2] * m[1][3] - m[1][0] * m[0][2] * m[2][3] - m[2][0] * m[1][2] * \n"
+ " m[0][3];\n"
+ " float cof32 = -(m[0][0] * m[1][1] * m[2][3] + m[1][0] * m[2][1] * m[0][3] + m[2][0] * \n"
+ " m[0][1] * m[1][3]\n"
+ " - m[0][0] * m[2][1] * m[1][3] - m[1][0] * m[0][1] * m[2][3] - m[2][0] * m[1][1] * \n"
+ " m[0][3]);\n"
+ " float cof33 = m[0][0] * m[1][1] * m[2][2] + m[1][0] * m[2][1] * m[0][2] + m[2][0] * \n"
+ " m[0][1] * m[1][2]\n"
+ " - m[0][0] * m[2][1] * m[1][2] - m[1][0] * m[0][1] * m[2][2] - m[2][0] * m[1][1] * \n"
+ " m[0][2];\n"
+ " float4x4 cof = { cof00, cof10, cof20, cof30, cof01, cof11, cof21, cof31,\n"
+ " cof02, cof12, cof22, cof32, cof03, cof13, cof23, cof33 };\n"
+ " return cof / determinant(transpose(m));\n"
+ "}\n"},
+ // Emulate ESSL3 variant of mix that takes last argument as boolean vector.
+ // genType mix(genType x, genType y, genBType a): Selects which vector each returned component
+ // comes from. For a component of 'a' that is false, the corresponding component of 'x' is
+ // returned. For a component of 'a' that is true, the corresponding component of 'y' is
+ // returned.
+ {{EOpMix, ParamType::Float1, ParamType::Float1, ParamType::Bool1},
+ "float mix_emu(float x, float y, bool a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n"},
+ {{EOpMix, ParamType::Float2, ParamType::Float2, ParamType::Bool2},
+ "float2 mix_emu(float2 x, float2 y, bool2 a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n"},
+ {{EOpMix, ParamType::Float3, ParamType::Float3, ParamType::Bool3},
+ "float3 mix_emu(float3 x, float3 y, bool3 a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n"},
+ {{EOpMix, ParamType::Float4, ParamType::Float4, ParamType::Bool4},
+ "float4 mix_emu(float4 x, float4 y, bool4 a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Uint1, ParamType::Int1, ParamType::Int1},
+ "uint bitfieldExtract_emu(uint value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return 0u;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " return (value & mask) >> offset;\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Uint2, ParamType::Int1, ParamType::Int1},
+ "uint2 bitfieldExtract_emu(uint2 value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return uint2(0u, 0u);\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " return (value & mask) >> offset;\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Uint3, ParamType::Int1, ParamType::Int1},
+ "uint3 bitfieldExtract_emu(uint3 value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return uint3(0u, 0u, 0u);\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " return (value & mask) >> offset;\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Uint4, ParamType::Int1, ParamType::Int1},
+ "uint4 bitfieldExtract_emu(uint4 value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return uint4(0u, 0u, 0u, 0u);\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " return (value & mask) >> offset;\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Int1, ParamType::Int1, ParamType::Int1},
+ "int bitfieldExtract_emu(int value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return 0;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint resultUnsigned = (asuint(value) & mask) >> offset;\n"
+ " if (bits != 32 && (resultUnsigned & maskMsb) != 0)\n"
+ " {\n"
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;\n"
+ " resultUnsigned |= higherBitsMask;\n"
+ " }\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Int2, ParamType::Int1, ParamType::Int1},
+ "int2 bitfieldExtract_emu(int2 value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return int2(0, 0);\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint2 resultUnsigned = (asuint(value) & mask) >> offset;\n"
+ " if (bits != 32)\n"
+ " {\n"
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;\n"
+ " resultUnsigned |= ((resultUnsigned & maskMsb) >> (bits - 1)) * higherBitsMask;\n"
+ " }\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Int3, ParamType::Int1, ParamType::Int1},
+ "int3 bitfieldExtract_emu(int3 value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return int3(0, 0, 0);\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint3 resultUnsigned = (asuint(value) & mask) >> offset;\n"
+ " if (bits != 32)\n"
+ " {\n"
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;\n"
+ " resultUnsigned |= ((resultUnsigned & maskMsb) >> (bits - 1)) * higherBitsMask;\n"
+ " }\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpBitfieldExtract, ParamType::Int4, ParamType::Int1, ParamType::Int1},
+ "int4 bitfieldExtract_emu(int4 value, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return int4(0, 0, 0, 0);\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint mask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint4 resultUnsigned = (asuint(value) & mask) >> offset;\n"
+ " if (bits != 32)\n"
+ " {\n"
+ " uint higherBitsMask = ((1u << (32 - bits)) - 1u) << bits;\n"
+ " resultUnsigned |= ((resultUnsigned & maskMsb) >> (bits - 1)) * higherBitsMask;\n"
+ " }\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Uint1, ParamType::Uint1, ParamType::Int1, ParamType::Int1},
+ "uint bitfieldInsert_emu(uint base, uint insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " return (base & baseMask) | ((insert << offset) & insertMask);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Uint2, ParamType::Uint2, ParamType::Int1, ParamType::Int1},
+ "uint2 bitfieldInsert_emu(uint2 base, uint2 insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " return (base & baseMask) | ((insert << offset) & insertMask);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Uint3, ParamType::Uint3, ParamType::Int1, ParamType::Int1},
+ "uint3 bitfieldInsert_emu(uint3 base, uint3 insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " return (base & baseMask) | ((insert << offset) & insertMask);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Uint4, ParamType::Uint4, ParamType::Int1, ParamType::Int1},
+ "uint4 bitfieldInsert_emu(uint4 base, uint4 insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " return (base & baseMask) | ((insert << offset) & insertMask);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Int1, ParamType::Int1, ParamType::Int1, ParamType::Int1},
+ "int bitfieldInsert_emu(int base, int insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " uint resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & \n"
+ " insertMask);\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Int2, ParamType::Int2, ParamType::Int1, ParamType::Int1},
+ "int2 bitfieldInsert_emu(int2 base, int2 insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " uint2 resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & \n"
+ " insertMask);\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Int3, ParamType::Int3, ParamType::Int1, ParamType::Int1},
+ "int3 bitfieldInsert_emu(int3 base, int3 insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " uint3 resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & \n"
+ " insertMask);\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpBitfieldInsert, ParamType::Int4, ParamType::Int4, ParamType::Int1, ParamType::Int1},
+ "int4 bitfieldInsert_emu(int4 base, int4 insert, int offset, int bits)\n"
+ "{\n"
+ " if (offset < 0 || bits <= 0 || offset >= 32 || bits > 32 || offset + bits > 32)\n"
+ " {\n"
+ " return base;\n"
+ " }\n"
+ " uint maskMsb = (1u << (bits - 1));\n"
+ " uint insertMask = ((maskMsb - 1u) | maskMsb) << offset;\n"
+ " uint baseMask = ~insertMask;\n"
+ " uint4 resultUnsigned = (asuint(base) & baseMask) | ((asuint(insert) << offset) & \n"
+ " insertMask);\n"
+ " return asint(resultUnsigned);\n"
+ "}\n"},
+ {{EOpUaddCarry, ParamType::Uint1, ParamType::Uint1, ParamType::Uint1},
+ "uint uaddCarry_emu(uint x, uint y, out uint carry)\n"
+ "{\n"
+ " carry = uint(x > (0xffffffffu - y));\n"
+ " return x + y;\n"
+ "}\n"},
+ {{EOpUaddCarry, ParamType::Uint2, ParamType::Uint2, ParamType::Uint2},
+ "uint2 uaddCarry_emu(uint2 x, uint2 y, out uint2 carry)\n"
+ "{\n"
+ " carry = uint2(x > (0xffffffffu - y));\n"
+ " return x + y;\n"
+ "}\n"},
+ {{EOpUaddCarry, ParamType::Uint3, ParamType::Uint3, ParamType::Uint3},
+ "uint3 uaddCarry_emu(uint3 x, uint3 y, out uint3 carry)\n"
+ "{\n"
+ " carry = uint3(x > (0xffffffffu - y));\n"
+ " return x + y;\n"
+ "}\n"},
+ {{EOpUaddCarry, ParamType::Uint4, ParamType::Uint4, ParamType::Uint4},
+ "uint4 uaddCarry_emu(uint4 x, uint4 y, out uint4 carry)\n"
+ "{\n"
+ " carry = uint4(x > (0xffffffffu - y));\n"
+ " return x + y;\n"
+ "}\n"},
+ {{EOpUsubBorrow, ParamType::Uint1, ParamType::Uint1, ParamType::Uint1},
+ "uint usubBorrow_emu(uint x, uint y, out uint borrow)\n"
+ "{\n"
+ " borrow = uint(x < y);\n"
+ " return x - y;\n"
+ "}\n"},
+ {{EOpUsubBorrow, ParamType::Uint2, ParamType::Uint2, ParamType::Uint2},
+ "uint2 usubBorrow_emu(uint2 x, uint2 y, out uint2 borrow)\n"
+ "{\n"
+ " borrow = uint2(x < y);\n"
+ " return x - y;\n"
+ "}\n"},
+ {{EOpUsubBorrow, ParamType::Uint3, ParamType::Uint3, ParamType::Uint3},
+ "uint3 usubBorrow_emu(uint3 x, uint3 y, out uint3 borrow)\n"
+ "{\n"
+ " borrow = uint3(x < y);\n"
+ " return x - y;\n"
+ "}\n"},
+ {{EOpUsubBorrow, ParamType::Uint4, ParamType::Uint4, ParamType::Uint4},
+ "uint4 usubBorrow_emu(uint4 x, uint4 y, out uint4 borrow)\n"
+ "{\n"
+ " borrow = uint4(x < y);\n"
+ " return x - y;\n"
+ "}\n"},
+};
+} // anonymous namespace
+
+const char *FindHLSLFunction(const FunctionId &functionID)
+{
+ for (size_t index = 0; index < ArraySize(g_hlslFunctions); ++index)
+ {
+ const auto &function = g_hlslFunctions[index];
+ if (function.id == functionID)
+ {
+ return function.body;
+ }
+ }
+
+ return nullptr;
+}
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/glslang.h b/src/3rdparty/angle/src/compiler/translator/glslang.h
index 0555e96d45..e54c31ba3e 100644
--- a/src/3rdparty/angle/src/compiler/translator/glslang.h
+++ b/src/3rdparty/angle/src/compiler/translator/glslang.h
@@ -7,14 +7,18 @@
#ifndef COMPILER_TRANSLATOR_GLSLANG_H_
#define COMPILER_TRANSLATOR_GLSLANG_H_
+namespace sh
+{
class TParseContext;
-extern int glslang_initialize(TParseContext* context);
-extern int glslang_finalize(TParseContext* context);
+}
+
+extern int glslang_initialize(sh::TParseContext *context);
+extern int glslang_finalize(sh::TParseContext *context);
extern int glslang_scan(size_t count,
- const char* const string[],
+ const char *const string[],
const int length[],
- TParseContext* context);
-extern int glslang_parse(TParseContext* context);
+ sh::TParseContext *context);
+extern int glslang_parse(sh::TParseContext *context);
-#endif // COMPILER_TRANSLATOR_GLSLANG_H_
+#endif // COMPILER_TRANSLATOR_GLSLANG_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/glslang.l b/src/3rdparty/angle/src/compiler/translator/glslang.l
index d09358dd8a..858ffd96bb 100644
--- a/src/3rdparty/angle/src/compiler/translator/glslang.l
+++ b/src/3rdparty/angle/src/compiler/translator/glslang.l
@@ -22,6 +22,8 @@ WHICH GENERATES THE GLSL ES LEXER (glslang_lex.cpp).
// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+/* clang-format off */
+
// Ignore errors in auto-generated code.
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-function"
@@ -44,6 +46,9 @@ WHICH GENERATES THE GLSL ES LEXER (glslang_lex.cpp).
#include "compiler/preprocessor/Token.h"
#include "compiler/translator/util.h"
#include "compiler/translator/length_limits.h"
+
+using namespace sh;
+
#include "glslang_tab.h"
/* windows only pragma */
@@ -71,10 +76,16 @@ static int reserved_word(yyscan_t yyscanner);
static int ES2_reserved_ES3_keyword(TParseContext *context, int token);
static int ES2_keyword_ES3_reserved(TParseContext *context, int token);
static int ES2_ident_ES3_keyword(TParseContext *context, int token);
+static int ES2_ident_ES3_keyword_multiview_keyword(TParseContext *context, int token);
+static int ES2_ident_ES3_reserved_ES3_1_keyword(TParseContext *context, int token);
+static int ES2_and_ES3_reserved_ES3_1_keyword(TParseContext *context, int token);
+static int ES2_and_ES3_ident_ES3_1_keyword(TParseContext *context, int token);
+static int ES3_extension_keyword_else_ident(TParseContext *context, TExtension extension, int token);
static int uint_constant(TParseContext *context);
static int int_constant(TParseContext *context);
static int float_constant(yyscan_t yyscanner);
static int floatsuffix_check(TParseContext* context);
+static int yuvcscstandardext_constant(TParseContext *context);
%}
%option noyywrap nounput never-interactive
@@ -103,6 +114,7 @@ O [0-7]
"attribute" { return ES2_keyword_ES3_reserved(context, ATTRIBUTE); }
"const" { return CONST_QUAL; }
"uniform" { return UNIFORM; }
+"buffer" { return ES2_and_ES3_ident_ES3_1_keyword(context, BUFFER); }
"varying" { return ES2_keyword_ES3_reserved(context, VARYING); }
"break" { return BREAK; }
@@ -124,6 +136,7 @@ O [0-7]
"in" { return IN_QUAL; }
"out" { return OUT_QUAL; }
"inout" { return INOUT_QUAL; }
+"shared" { return ES2_and_ES3_ident_ES3_1_keyword(context, SHARED); }
"float" { return FLOAT_TYPE; }
"int" { return INT_TYPE; }
@@ -171,29 +184,52 @@ O [0-7]
"sampler3DRect" { return ES2_reserved_ES3_keyword(context, SAMPLER3DRECT); }
"sampler2DRect" { return SAMPLER2DRECT; }
"sampler2DArray" { return ES2_ident_ES3_keyword(context, SAMPLER2DARRAY); }
+"sampler2DMS" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, SAMPLER2DMS); }
"isampler2D" { return ES2_ident_ES3_keyword(context, ISAMPLER2D); }
"isampler3D" { return ES2_ident_ES3_keyword(context, ISAMPLER3D); }
"isamplerCube" { return ES2_ident_ES3_keyword(context, ISAMPLERCUBE); }
"isampler2DArray" { return ES2_ident_ES3_keyword(context, ISAMPLER2DARRAY); }
+"isampler2DMS" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, ISAMPLER2DMS); }
"usampler2D" { return ES2_ident_ES3_keyword(context, USAMPLER2D); }
"usampler3D" { return ES2_ident_ES3_keyword(context, USAMPLER3D); }
"usamplerCube" { return ES2_ident_ES3_keyword(context, USAMPLERCUBE); }
"usampler2DArray" { return ES2_ident_ES3_keyword(context, USAMPLER2DARRAY); }
+"usampler2DMS" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, USAMPLER2DMS); }
"sampler2DShadow" { return ES2_reserved_ES3_keyword(context, SAMPLER2DSHADOW); }
"samplerCubeShadow" { return ES2_ident_ES3_keyword(context, SAMPLERCUBESHADOW); }
"sampler2DArrayShadow" { return ES2_ident_ES3_keyword(context, SAMPLER2DARRAYSHADOW); }
+"__samplerExternal2DY2YEXT" { return ES3_extension_keyword_else_ident(context, TExtension::EXT_YUV_target, SAMPLEREXTERNAL2DY2YEXT); }
"struct" { return STRUCT; }
-"layout" { return ES2_ident_ES3_keyword(context, LAYOUT); }
+"layout" { return ES2_ident_ES3_keyword_multiview_keyword(context, LAYOUT); }
+
+"yuvCscStandardEXT" { return ES3_extension_keyword_else_ident(context, TExtension::EXT_YUV_target, YUVCSCSTANDARDEXT); }
+"itu_601" { return yuvcscstandardext_constant(context); }
+"itu_601_full_range" { return yuvcscstandardext_constant(context); }
+"itu_709" { return yuvcscstandardext_constant(context); }
+
+"image2D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE2D); }
+"iimage2D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE2D); }
+"uimage2D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE2D); }
+"image2DArray" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE2DARRAY); }
+"iimage2DArray" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE2DARRAY); }
+"uimage2DArray" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE2DARRAY); }
+"image3D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE3D); }
+"uimage3D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE3D); }
+"iimage3D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE3D); }
+"iimageCube" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGECUBE); }
+"uimageCube" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGECUBE); }
+"imageCube" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGECUBE); }
+"readonly" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, READONLY); }
+"writeonly" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, WRITEONLY); }
+"coherent" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, COHERENT); }
+"restrict" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, RESTRICT); }
+"volatile" { return ES2_and_ES3_reserved_ES3_1_keyword(context, VOLATILE); }
+"atomic_uint" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, ATOMICUINT); }
/* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */
-"coherent" |
-"restrict" |
-"readonly" |
-"writeonly" |
"resource" |
-"atomic_uint" |
"noperspective" |
"patch" |
"sample" |
@@ -204,23 +240,11 @@ O [0-7]
"filter" |
"image1D" |
-"image2D" |
-"image3D" |
-"imageCube" |
"iimage1D" |
-"iimage2D" |
-"iimage3D" |
-"iimageCube" |
"uimage1D" |
-"uimage2D" |
-"uimage3D" |
-"uimageCube" |
"image1DArray" |
-"image2DArray" |
"iimage1DArray" |
-"iimage2DArray" |
"uimage1DArray" |
-"uimage2DArray" |
"image1DShadow" |
"image2DShadow" |
"image1DArrayShadow" |
@@ -240,9 +264,6 @@ O [0-7]
"samplerBuffer" |
"isamplerBuffer" |
"usamplerBuffer" |
-"sampler2DMS" |
-"isampler2DMS" |
-"usampler2DMS" |
"sampler2DMSArray" |
"isampler2DMSArray" |
"usampler2DMSArray" {
@@ -278,7 +299,6 @@ O [0-7]
"inline" |
"noinline" |
-"volatile" |
"public" |
"static" |
"extern" |
@@ -390,6 +410,10 @@ O [0-7]
return FIELD_SELECTION;
}
<FIELDS>[ \t\v\f\r] {}
+<FIELDS>. {
+ yyextra->error(*yylloc, "Illegal character at fieldname start", yytext);
+ return 0;
+}
[ \t\v\n\f\r] { }
<*><<EOF>> { yyterminate(); }
@@ -431,8 +455,7 @@ int check_type(yyscan_t yyscanner) {
int reserved_word(yyscan_t yyscanner) {
struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
- yyextra->error(*yylloc, "Illegal use of reserved word", yytext, "");
- yyextra->recover();
+ yyextra->error(*yylloc, "Illegal use of reserved word", yytext);
return 0;
}
@@ -460,6 +483,24 @@ int ES2_keyword_ES3_reserved(TParseContext *context, int token)
return token;
}
+int ES2_ident_ES3_reserved_ES3_1_keyword(TParseContext *context, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+ else if (context->getShaderVersion() == 300)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
int ES2_ident_ES3_keyword(TParseContext *context, int token)
{
struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
@@ -475,19 +516,76 @@ int ES2_ident_ES3_keyword(TParseContext *context, int token)
return token;
}
+int ES2_ident_ES3_keyword_multiview_keyword(TParseContext *context, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ // not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
+ // except when multiview extension is enabled
+ if (context->getShaderVersion() < 300 && !context->isExtensionEnabled(TExtension::OVR_multiview))
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_and_ES3_reserved_ES3_1_keyword(TParseContext *context, int token)
+{
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 310)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_and_ES3_ident_ES3_1_keyword(TParseContext *context, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ // not a reserved word in GLSL ES 1.00 and GLSL ES 3.00, so could be used as an identifier/type name
+ if (context->getShaderVersion() < 310)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+
+ return token;
+}
+
+int ES3_extension_keyword_else_ident(TParseContext *context, TExtension extension, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ // a reserved word in GLSL ES 3.00 with enabled extension, otherwise could be used as an identifier/type name
+ if (context->getShaderVersion() >= 300 && context->isExtensionEnabled(extension))
+ {
+ return token;
+ }
+
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+}
+
int uint_constant(TParseContext *context)
{
struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
if (context->getShaderVersion() < 300)
{
- context->error(*yylloc, "Unsigned integers are unsupported prior to GLSL ES 3.00", yytext, "");
- context->recover();
+ context->error(*yylloc, "Unsigned integers are unsupported prior to GLSL ES 3.00", yytext);
return 0;
}
if (!atoi_clamp(yytext, &(yylval->lex.u)))
- yyextra->error(*yylloc, "Integer overflow", yytext, "");
+ yyextra->error(*yylloc, "Integer overflow", yytext);
return UINTCONSTANT;
}
@@ -499,21 +597,19 @@ int floatsuffix_check(TParseContext* context)
if (context->getShaderVersion() < 300)
{
context->error(*yylloc, "Floating-point suffix unsupported prior to GLSL ES 3.00", yytext);
- context->recover();
return 0;
}
std::string text = yytext;
text.resize(text.size() - 1);
if (!strtof_clamp(text, &(yylval->lex.f)))
- yyextra->warning(*yylloc, "Float overflow", yytext, "");
+ yyextra->warning(*yylloc, "Float overflow", yytext);
return(FLOATCONSTANT);
}
void yyerror(YYLTYPE* lloc, TParseContext* context, void *scanner, const char* reason) {
context->error(*lloc, reason, yyget_text(scanner));
- context->recover();
}
int int_constant(TParseContext *context) {
@@ -523,9 +619,9 @@ int int_constant(TParseContext *context) {
if (!atoi_clamp(yytext, &u))
{
if (context->getShaderVersion() >= 300)
- yyextra->error(*yylloc, "Integer overflow", yytext, "");
+ yyextra->error(*yylloc, "Integer overflow", yytext);
else
- yyextra->warning(*yylloc, "Integer overflow", yytext, "");
+ yyextra->warning(*yylloc, "Integer overflow", yytext);
}
yylval->lex.i = static_cast<int>(u);
return INTCONSTANT;
@@ -535,10 +631,26 @@ int float_constant(yyscan_t yyscanner) {
struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
if (!strtof_clamp(yytext, &(yylval->lex.f)))
- yyextra->warning(*yylloc, "Float overflow", yytext, "");
+ yyextra->warning(*yylloc, "Float overflow", yytext);
return FLOATCONSTANT;
}
+int yuvcscstandardext_constant(TParseContext *context)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ // a reserved word in GLSL ES 3.00 with enabled extension, otherwise could be used as an identifier/type name
+ if (context->getShaderVersion() >= 300 && context->isExtensionEnabled(TExtension::EXT_YUV_target))
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return YUVCSCSTANDARDEXTCONSTANT;
+ }
+
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+}
+
int glslang_initialize(TParseContext* context) {
yyscan_t scanner = NULL;
if (yylex_init_extra(context, &scanner))
@@ -574,13 +686,12 @@ int glslang_scan(size_t count, const char* const string[], const int length[],
const TExtensionBehavior& extBehavior = context->extensionBehavior();
for (TExtensionBehavior::const_iterator iter = extBehavior.begin();
iter != extBehavior.end(); ++iter) {
- preprocessor->predefineMacro(iter->first.c_str(), 1);
+ preprocessor->predefineMacro(GetExtensionNameString(iter->first), 1);
}
if (context->getFragmentPrecisionHigh())
preprocessor->predefineMacro("GL_FRAGMENT_PRECISION_HIGH", 1);
- preprocessor->setMaxTokenSize(GetGlobalMaxTokenSize(context->getShaderSpec()));
+ preprocessor->setMaxTokenSize(sh::GetGlobalMaxTokenSize(context->getShaderSpec()));
return 0;
}
-
diff --git a/src/3rdparty/angle/src/compiler/translator/glslang.y b/src/3rdparty/angle/src/compiler/translator/glslang.y
index aba2706311..3e506caac8 100644
--- a/src/3rdparty/angle/src/compiler/translator/glslang.y
+++ b/src/3rdparty/angle/src/compiler/translator/glslang.y
@@ -22,6 +22,8 @@ WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h).
// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+// clang-format off
+
// Ignore errors in auto-generated code.
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-function"
@@ -44,6 +46,8 @@ WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h).
#define YYENABLE_NLS 0
+using namespace sh;
+
%}
%expect 1 /* One shift reduce conflict because of if | else */
%parse-param {TParseContext* context}
@@ -70,22 +74,30 @@ WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h).
struct {
TOperator op;
union {
- TIntermNode* intermNode;
+ TIntermNode *intermNode;
TIntermNodePair nodePair;
- TIntermTyped* intermTypedNode;
- TIntermAggregate* intermAggregate;
- TIntermSwitch* intermSwitch;
- TIntermCase* intermCase;
+ TIntermFunctionCallOrMethod callOrMethodPair;
+ TIntermTyped *intermTypedNode;
+ TIntermAggregate *intermAggregate;
+ TIntermBlock *intermBlock;
+ TIntermDeclaration *intermDeclaration;
+ TIntermFunctionPrototype *intermFunctionPrototype;
+ TIntermSwitch *intermSwitch;
+ TIntermCase *intermCase;
};
union {
+ TVector<unsigned int> *arraySizes;
+ TTypeSpecifierNonArray typeSpecifierNonArray;
TPublicType type;
TPrecision precision;
TLayoutQualifier layoutQualifier;
TQualifier qualifier;
- TFunction* function;
+ TFunction *function;
TParameter param;
- TField* field;
- TFieldList* fieldList;
+ TField *field;
+ TFieldList *fieldList;
+ TQualifierWrapperBase *qualifierWrapper;
+ TTypeQualifierBuilder *typeQualifierBuilder;
};
} interm;
}
@@ -112,29 +124,37 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
#define VERTEX_ONLY(S, L) { \
if (context->getShaderType() != GL_VERTEX_SHADER) { \
- context->error(L, " supported in vertex shaders only ", S); \
- context->recover(); \
+ context->error(L, " supported in vertex shaders only", S); \
} \
}
-#define FRAG_ONLY(S, L) { \
- if (context->getShaderType() != GL_FRAGMENT_SHADER) { \
- context->error(L, " supported in fragment shaders only ", S); \
- context->recover(); \
+#define COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_COMPUTE_SHADER) { \
+ context->error(L, " supported in compute shaders only", S); \
} \
}
#define ES2_ONLY(S, L) { \
if (context->getShaderVersion() != 100) { \
- context->error(L, " supported in GLSL ES 1.00 only ", S); \
- context->recover(); \
+ context->error(L, " supported in GLSL ES 1.00 only", S); \
+ } \
+}
+
+#define ES3_OR_NEWER(TOKEN, LINE, REASON) { \
+ if (context->getShaderVersion() < 300) { \
+ context->error(LINE, REASON " supported in GLSL ES 3.00 and above only", TOKEN); \
} \
}
-#define ES3_ONLY(TOKEN, LINE, REASON) { \
- if (context->getShaderVersion() != 300) { \
- context->error(LINE, REASON " supported in GLSL ES 3.00 only ", TOKEN); \
- context->recover(); \
+#define ES3_OR_NEWER_OR_MULTIVIEW(TOKEN, LINE, REASON) { \
+ if (context->getShaderVersion() < 300 && !context->isExtensionEnabled(TExtension::OVR_multiview)) { \
+ context->error(LINE, REASON " supported in GLSL ES 3.00 and above only", TOKEN); \
+ } \
+}
+
+#define ES3_1_ONLY(TOKEN, LINE, REASON) { \
+ if (context->getShaderVersion() != 310) { \
+ context->error(LINE, REASON " supported in GLSL ES 3.10 only", TOKEN); \
} \
}
%}
@@ -143,15 +163,22 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
%token <lex> ATTRIBUTE CONST_QUAL BOOL_TYPE FLOAT_TYPE INT_TYPE UINT_TYPE
%token <lex> BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT
%token <lex> BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4 UVEC2 UVEC3 UVEC4
-%token <lex> MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM VARYING
+%token <lex> MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM BUFFER VARYING
%token <lex> MATRIX2x3 MATRIX3x2 MATRIX2x4 MATRIX4x2 MATRIX3x4 MATRIX4x3
%token <lex> CENTROID FLAT SMOOTH
+%token <lex> READONLY WRITEONLY COHERENT RESTRICT VOLATILE SHARED
%token <lex> STRUCT VOID_TYPE WHILE
%token <lex> SAMPLER2D SAMPLERCUBE SAMPLER_EXTERNAL_OES SAMPLER2DRECT SAMPLER2DARRAY
%token <lex> ISAMPLER2D ISAMPLER3D ISAMPLERCUBE ISAMPLER2DARRAY
%token <lex> USAMPLER2D USAMPLER3D USAMPLERCUBE USAMPLER2DARRAY
+%token <lex> SAMPLER2DMS ISAMPLER2DMS USAMPLER2DMS
%token <lex> SAMPLER3D SAMPLER3DRECT SAMPLER2DSHADOW SAMPLERCUBESHADOW SAMPLER2DARRAYSHADOW
+%token <lex> SAMPLEREXTERNAL2DY2YEXT
+%token <lex> IMAGE2D IIMAGE2D UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY
+%token <lex> IMAGECUBE IIMAGECUBE UIMAGECUBE
+%token <lex> ATOMICUINT
%token <lex> LAYOUT
+%token <lex> YUVCSCSTANDARDEXT YUVCSCSTANDARDEXTCONSTANT
%token <lex> IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT
%token <lex> FIELD_SELECTION
@@ -166,7 +193,7 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
%token <lex> LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION
%type <lex> identifier
-%type <interm> assignment_operator unary_operator
+%type <interm.op> assignment_operator unary_operator
%type <interm.intermTypedNode> variable_identifier primary_expression postfix_expression
%type <interm.intermTypedNode> expression integer_expression assignment_expression
%type <interm.intermTypedNode> unary_expression multiplicative_expression additive_expression
@@ -174,11 +201,12 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
%type <interm.intermTypedNode> conditional_expression constant_expression
%type <interm.intermTypedNode> logical_or_expression logical_xor_expression logical_and_expression
%type <interm.intermTypedNode> shift_expression and_expression exclusive_or_expression inclusive_or_expression
-%type <interm.intermTypedNode> function_call initializer condition conditionopt
+%type <interm.intermTypedNode> function_call initializer
-%type <interm.intermNode> translation_unit function_definition
-%type <interm.intermNode> statement simple_statement
-%type <interm.intermAggregate> statement_list compound_statement compound_statement_no_new_scope
+%type <interm.intermNode> condition conditionopt
+%type <interm.intermBlock> translation_unit
+%type <interm.intermNode> function_definition statement simple_statement
+%type <interm.intermBlock> statement_list compound_statement_with_scope compound_statement_no_new_scope
%type <interm.intermNode> declaration_statement selection_statement expression_statement
%type <interm.intermNode> declaration external_declaration
%type <interm.intermNode> for_init_statement
@@ -188,14 +216,22 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
%type <interm.intermNode> iteration_statement jump_statement statement_no_new_scope statement_with_scope
%type <interm> single_declaration init_declarator_list
-%type <interm> parameter_declaration parameter_declarator parameter_type_specifier
-%type <interm.qualifier> parameter_qualifier parameter_type_qualifier
-%type <interm.layoutQualifier> layout_qualifier layout_qualifier_id_list layout_qualifier_id
+%type <interm.param> parameter_declaration parameter_declarator parameter_type_specifier
+%type <interm.layoutQualifier> layout_qualifier_id_list layout_qualifier_id
+
+// Note: array_specifier guaranteed to be non-null.
+%type <interm.arraySizes> array_specifier
+
+%type <interm.type> fully_specified_type type_specifier
%type <interm.precision> precision_qualifier
-%type <interm.type> type_qualifier fully_specified_type type_specifier storage_qualifier interpolation_qualifier
-%type <interm.type> type_specifier_no_prec type_specifier_nonarray
-%type <interm.type> struct_specifier
+%type <interm.layoutQualifier> layout_qualifier
+%type <interm.qualifier> interpolation_qualifier
+%type <interm.qualifierWrapper> storage_qualifier single_type_qualifier invariant_qualifier
+%type <interm.typeQualifierBuilder> type_qualifier
+
+%type <interm.typeSpecifierNonArray> type_specifier_nonarray struct_specifier
+%type <interm.type> type_specifier_no_prec
%type <interm.field> struct_declarator
%type <interm.fieldList> struct_declarator_list struct_declaration struct_declaration_list
%type <interm.function> function_header function_declarator function_identifier
@@ -229,22 +265,31 @@ primary_expression
| INTCONSTANT {
TConstantUnion *unionArray = new TConstantUnion[1];
unionArray->setIConst($1.i);
- $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @1);
+ $$ = context->addScalarLiteral(unionArray, @1);
}
| UINTCONSTANT {
TConstantUnion *unionArray = new TConstantUnion[1];
unionArray->setUConst($1.u);
- $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtUInt, EbpUndefined, EvqConst), @1);
+ $$ = context->addScalarLiteral(unionArray, @1);
}
| FLOATCONSTANT {
TConstantUnion *unionArray = new TConstantUnion[1];
unionArray->setFConst($1.f);
- $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1);
+ $$ = context->addScalarLiteral(unionArray, @1);
}
| BOOLCONSTANT {
TConstantUnion *unionArray = new TConstantUnion[1];
unionArray->setBConst($1.b);
- $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @1);
+ $$ = context->addScalarLiteral(unionArray, @1);
+ }
+ | YUVCSCSTANDARDEXTCONSTANT {
+ if (!context->checkCanUseExtension(@1, TExtension::EXT_YUV_target))
+ {
+ context->error(@1, "unsupported value", $1.string->c_str());
+ }
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setYuvCscStandardEXTConst(getYuvCscStandardEXT($1.string->c_str()));
+ $$ = context->addScalarLiteral(unionArray, @1);
}
| LEFT_PAREN expression RIGHT_PAREN {
$$ = $2;
@@ -274,32 +319,26 @@ postfix_expression
integer_expression
: expression {
- if (context->integerErrorCheck($1, "[]"))
- context->recover();
+ context->checkIsScalarInteger($1, "[]");
$$ = $1;
}
;
function_call
: function_call_or_method {
- bool fatalError = false;
- $$ = context->addFunctionCallOrMethod($1.function, $1.nodePair.node1, $1.nodePair.node2, @1, &fatalError);
- if (fatalError)
- {
- YYERROR;
- }
+ $$ = context->addFunctionCallOrMethod($1.function, $1.callOrMethodPair.arguments, $1.callOrMethodPair.thisNode, @1);
}
;
function_call_or_method
: function_call_generic {
$$ = $1;
- $$.nodePair.node2 = nullptr;
+ $$.callOrMethodPair.thisNode = nullptr;
}
| postfix_expression DOT function_call_generic {
- ES3_ONLY("", @3, "methods");
+ ES3_OR_NEWER("", @3, "methods");
$$ = $3;
- $$.nodePair.node2 = $1;
+ $$.callOrMethodPair.thisNode = $1;
}
;
@@ -315,26 +354,23 @@ function_call_generic
function_call_header_no_parameters
: function_call_header VOID_TYPE {
$$.function = $1;
- $$.nodePair.node1 = nullptr;
+ $$.callOrMethodPair.arguments = context->createEmptyArgumentsList();
}
| function_call_header {
$$.function = $1;
- $$.nodePair.node1 = nullptr;
+ $$.callOrMethodPair.arguments = context->createEmptyArgumentsList();
}
;
function_call_header_with_parameters
: function_call_header assignment_expression {
- const TType *type = new TType($2->getType());
- $1->addParameter(TConstParameter(type));
+ $$.callOrMethodPair.arguments = context->createEmptyArgumentsList();
$$.function = $1;
- $$.nodePair.node1 = context->intermediate.makeAggregate($2, @2);
+ $$.callOrMethodPair.arguments->push_back($2);
}
| function_call_header_with_parameters COMMA assignment_expression {
- const TType *type = new TType($3->getType());
- $1.function->addParameter(TConstParameter(type));
$$.function = $1.function;
- $$.nodePair.node1 = context->intermediate.growAggregate($1.intermNode, $3, @2);
+ $$.callOrMethodPair.arguments->push_back($3);
}
;
@@ -348,24 +384,13 @@ function_call_header
function_identifier
: type_specifier_no_prec {
- if ($1.array) {
- ES3_ONLY("[]", @1, "array constructor");
- }
$$ = context->addConstructorFunc($1);
}
| IDENTIFIER {
- if (context->reservedErrorCheck(@1, *$1.string))
- context->recover();
- const TType *type = TCache::getType(EbtVoid, EbpUndefined);
- TFunction *function = new TFunction($1.string, type);
- $$ = function;
+ $$ = context->addNonConstructorFunc($1.string, @1);
}
| FIELD_SELECTION {
- if (context->reservedErrorCheck(@1, *$1.string))
- context->recover();
- const TType *type = TCache::getType(EbtVoid, EbpUndefined);
- TFunction *function = new TFunction($1.string, type);
- $$ = function;
+ $$ = context->addNonConstructorFunc($1.string, @1);
}
;
@@ -380,21 +405,18 @@ unary_expression
$$ = context->addUnaryMathLValue(EOpPreDecrement, $2, @1);
}
| unary_operator unary_expression {
- if ($1.op != EOpNull) {
- $$ = context->addUnaryMath($1.op, $2, @1);
- } else
- $$ = $2;
+ $$ = context->addUnaryMath($1, $2, @1);
}
;
// Grammar Note: No traditional style type casts.
unary_operator
- : PLUS { $$.op = EOpPositive; }
- | DASH { $$.op = EOpNegative; }
- | BANG { $$.op = EOpLogicalNot; }
+ : PLUS { $$ = EOpPositive; }
+ | DASH { $$ = EOpNegative; }
+ | BANG { $$ = EOpLogicalNot; }
| TILDE {
- ES3_ONLY("~", @$, "bit-wise operator");
- $$.op = EOpBitwiseNot;
+ ES3_OR_NEWER("~", @$, "bit-wise operator");
+ $$ = EOpBitwiseNot;
}
;
// Grammar Note: No '*' or '&' unary ops. Pointers are not supported.
@@ -408,7 +430,7 @@ multiplicative_expression
$$ = context->addBinaryMath(EOpDiv, $1, $3, @2);
}
| multiplicative_expression PERCENT unary_expression {
- ES3_ONLY("%", @2, "integer modulus operator");
+ ES3_OR_NEWER("%", @2, "integer modulus operator");
$$ = context->addBinaryMath(EOpIMod, $1, $3, @2);
}
;
@@ -426,11 +448,11 @@ additive_expression
shift_expression
: additive_expression { $$ = $1; }
| shift_expression LEFT_OP additive_expression {
- ES3_ONLY("<<", @2, "bit-wise operator");
+ ES3_OR_NEWER("<<", @2, "bit-wise operator");
$$ = context->addBinaryMath(EOpBitShiftLeft, $1, $3, @2);
}
| shift_expression RIGHT_OP additive_expression {
- ES3_ONLY(">>", @2, "bit-wise operator");
+ ES3_OR_NEWER(">>", @2, "bit-wise operator");
$$ = context->addBinaryMath(EOpBitShiftRight, $1, $3, @2);
}
;
@@ -464,7 +486,7 @@ equality_expression
and_expression
: equality_expression { $$ = $1; }
| and_expression AMPERSAND equality_expression {
- ES3_ONLY("&", @2, "bit-wise operator");
+ ES3_OR_NEWER("&", @2, "bit-wise operator");
$$ = context->addBinaryMath(EOpBitwiseAnd, $1, $3, @2);
}
;
@@ -472,7 +494,7 @@ and_expression
exclusive_or_expression
: and_expression { $$ = $1; }
| exclusive_or_expression CARET and_expression {
- ES3_ONLY("^", @2, "bit-wise operator");
+ ES3_OR_NEWER("^", @2, "bit-wise operator");
$$ = context->addBinaryMath(EOpBitwiseXor, $1, $3, @2);
}
;
@@ -480,7 +502,7 @@ exclusive_or_expression
inclusive_or_expression
: exclusive_or_expression { $$ = $1; }
| inclusive_or_expression VERTICAL_BAR exclusive_or_expression {
- ES3_ONLY("|", @2, "bit-wise operator");
+ ES3_OR_NEWER("|", @2, "bit-wise operator");
$$ = context->addBinaryMath(EOpBitwiseOr, $1, $3, @2);
}
;
@@ -516,41 +538,39 @@ conditional_expression
assignment_expression
: conditional_expression { $$ = $1; }
| unary_expression assignment_operator assignment_expression {
- if (context->lValueErrorCheck(@2, "assign", $1))
- context->recover();
- $$ = context->addAssign($2.op, $1, $3, @2);
+ $$ = context->addAssign($2, $1, $3, @2);
}
;
assignment_operator
- : EQUAL { $$.op = EOpAssign; }
- | MUL_ASSIGN { $$.op = EOpMulAssign; }
- | DIV_ASSIGN { $$.op = EOpDivAssign; }
+ : EQUAL { $$ = EOpAssign; }
+ | MUL_ASSIGN { $$ = EOpMulAssign; }
+ | DIV_ASSIGN { $$ = EOpDivAssign; }
| MOD_ASSIGN {
- ES3_ONLY("%=", @$, "integer modulus operator");
- $$.op = EOpIModAssign;
+ ES3_OR_NEWER("%=", @$, "integer modulus operator");
+ $$ = EOpIModAssign;
}
- | ADD_ASSIGN { $$.op = EOpAddAssign; }
- | SUB_ASSIGN { $$.op = EOpSubAssign; }
+ | ADD_ASSIGN { $$ = EOpAddAssign; }
+ | SUB_ASSIGN { $$ = EOpSubAssign; }
| LEFT_ASSIGN {
- ES3_ONLY("<<=", @$, "bit-wise operator");
- $$.op = EOpBitShiftLeftAssign;
+ ES3_OR_NEWER("<<=", @$, "bit-wise operator");
+ $$ = EOpBitShiftLeftAssign;
}
| RIGHT_ASSIGN {
- ES3_ONLY(">>=", @$, "bit-wise operator");
- $$.op = EOpBitShiftRightAssign;
+ ES3_OR_NEWER(">>=", @$, "bit-wise operator");
+ $$ = EOpBitShiftRightAssign;
}
| AND_ASSIGN {
- ES3_ONLY("&=", @$, "bit-wise operator");
- $$.op = EOpBitwiseAndAssign;
+ ES3_OR_NEWER("&=", @$, "bit-wise operator");
+ $$ = EOpBitwiseAndAssign;
}
| XOR_ASSIGN {
- ES3_ONLY("^=", @$, "bit-wise operator");
- $$.op = EOpBitwiseXorAssign;
+ ES3_OR_NEWER("^=", @$, "bit-wise operator");
+ $$ = EOpBitwiseXorAssign;
}
| OR_ASSIGN {
- ES3_ONLY("|=", @$, "bit-wise operator");
- $$.op = EOpBitwiseOrAssign;
+ ES3_OR_NEWER("|=", @$, "bit-wise operator");
+ $$ = EOpBitwiseOrAssign;
}
;
@@ -565,16 +585,14 @@ expression
constant_expression
: conditional_expression {
- if (context->constErrorCheck($1))
- context->recover();
+ context->checkIsConst($1);
$$ = $1;
}
;
enter_struct
: IDENTIFIER LEFT_BRACE {
- if (context->enterStructDeclaration(@1, *$1.string))
- context->recover();
+ context->enterStructDeclaration(@1, *$1.string);
$$ = $1;
}
;
@@ -584,43 +602,38 @@ declaration
$$ = context->addFunctionPrototypeDeclaration(*($1.function), @1);
}
| init_declarator_list SEMICOLON {
- TIntermAggregate *aggNode = $1.intermAggregate;
- if (aggNode && aggNode->getOp() == EOpNull)
- aggNode->setOp(EOpDeclaration);
- $$ = aggNode;
+ $$ = $1.intermDeclaration;
}
| PRECISION precision_qualifier type_specifier_no_prec SEMICOLON {
- if (($2 == EbpHigh) && (context->getShaderType() == GL_FRAGMENT_SHADER) && !context->getFragmentPrecisionHigh()) {
- context->error(@1, "precision is not supported in fragment shader", "highp");
- context->recover();
- }
- if (!context->symbolTable.setDefaultPrecision( $3, $2 )) {
- context->error(@1, "illegal type argument for default precision qualifier", getBasicString($3.type));
- context->recover();
- }
- $$ = 0;
+ context->parseDefaultPrecisionQualifier($2, $3, @1);
+ $$ = nullptr;
}
| type_qualifier enter_struct struct_declaration_list RIGHT_BRACE SEMICOLON {
- ES3_ONLY(getQualifierString($1.qualifier), @1, "interface blocks");
- $$ = context->addInterfaceBlock($1, @2, *$2.string, $3, NULL, @$, NULL, @$);
+ ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
+ $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, NULL, @$, NULL, @$);
}
| type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON {
- ES3_ONLY(getQualifierString($1.qualifier), @1, "interface blocks");
- $$ = context->addInterfaceBlock($1, @2, *$2.string, $3, $5.string, @5, NULL, @$);
+ ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
+ $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, NULL, @$);
}
| type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET SEMICOLON {
- ES3_ONLY(getQualifierString($1.qualifier), @1, "interface blocks");
- $$ = context->addInterfaceBlock($1, @2, *$2.string, $3, $5.string, @5, $7, @6);
+ ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
+ $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, $7, @6);
}
| type_qualifier SEMICOLON {
- context->parseGlobalLayoutQualifier($1);
- $$ = 0;
+ context->parseGlobalLayoutQualifier(*$1);
+ $$ = nullptr;
+ }
+ | type_qualifier IDENTIFIER SEMICOLON // e.g. to qualify an existing variable as invariant
+ {
+ $$ = context->parseInvariantDeclaration(*$1, @2, $2.string, $2.symbol);
}
;
function_prototype
: function_declarator RIGHT_PAREN {
$$.function = context->parseFunctionDeclarator(@2, $1);
+ context->exitFunctionDeclaration();
}
;
@@ -638,145 +651,69 @@ function_header_with_parameters
: function_header parameter_declaration {
// Add the parameter
$$ = $1;
- if ($2.param.type->getBasicType() != EbtVoid)
- $1->addParameter($2.param.turnToConst());
- else
- delete $2.param.type;
+ if ($2.type->getBasicType() != EbtVoid)
+ {
+ $1->addParameter($2.turnToConst());
+ }
}
| function_header_with_parameters COMMA parameter_declaration {
- //
+ $$ = $1;
// Only first parameter of one-parameter functions can be void
// The check for named parameters not being void is done in parameter_declarator
- //
- if ($3.param.type->getBasicType() == EbtVoid) {
- //
+ if ($3.type->getBasicType() == EbtVoid)
+ {
// This parameter > first is void
- //
- context->error(@2, "cannot be an argument type except for '(void)'", "void");
- context->recover();
- delete $3.param.type;
- } else {
- // Add the parameter
- $$ = $1;
- $1->addParameter($3.param.turnToConst());
+ context->error(@2, "cannot be a parameter type except for '(void)'", "void");
+ }
+ else
+ {
+ $1->addParameter($3.turnToConst());
}
}
;
function_header
: fully_specified_type IDENTIFIER LEFT_PAREN {
- if ($1.qualifier != EvqGlobal && $1.qualifier != EvqTemporary)
- {
- context->error(@2, "no qualifiers allowed for function return", getQualifierString($1.qualifier));
- context->recover();
- }
- if (!$1.layoutQualifier.isEmpty())
- {
- context->error(@2, "no qualifiers allowed for function return", "layout");
- context->recover();
- }
- // make sure a sampler is not involved as well...
- if (context->samplerErrorCheck(@2, $1, "samplers can't be function return values"))
- context->recover();
-
- // Add the function as a prototype after parsing it (we do not support recursion)
- TFunction *function;
- const TType *type = new TType($1);
- function = new TFunction($2.string, type);
- $$ = function;
-
+ $$ = context->parseFunctionHeader($1, $2.string, @2);
+
context->symbolTable.push();
+ context->enterFunctionDeclaration();
}
;
parameter_declarator
// Type + name
: type_specifier identifier {
- if ($1.type == EbtVoid) {
- context->error(@2, "illegal use of type 'void'", $2.string->c_str());
- context->recover();
- }
- if (context->reservedErrorCheck(@2, *$2.string))
- context->recover();
- TParameter param = {$2.string, new TType($1)};
- $$.param = param;
+ $$ = context->parseParameterDeclarator($1, $2.string, @2);
}
- | type_specifier identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
- // Check that we can make an array out of this type
- if (context->arrayTypeErrorCheck(@3, $1))
- context->recover();
-
- if (context->reservedErrorCheck(@2, *$2.string))
- context->recover();
-
- int size;
- if (context->arraySizeErrorCheck(@3, $4, size))
- context->recover();
- $1.setArraySize(size);
-
- TType* type = new TType($1);
- TParameter param = { $2.string, type };
- $$.param = param;
+ | type_specifier identifier array_specifier {
+ $$ = context->parseParameterArrayDeclarator($2.string, @2, *($3), @3, &$1);
}
;
parameter_declaration
- //
- // The only parameter qualifier a parameter can have are
- // IN_QUAL, OUT_QUAL, INOUT_QUAL, or CONST.
- //
-
- //
- // Type + name
- //
- : parameter_type_qualifier parameter_qualifier parameter_declarator {
- $$ = $3;
- if (context->paramErrorCheck(@3, $1, $2, $$.param.type))
- context->recover();
- }
- | parameter_qualifier parameter_declarator {
- $$ = $2;
- if (context->parameterSamplerErrorCheck(@2, $1, *$2.param.type))
- context->recover();
- if (context->paramErrorCheck(@2, EvqTemporary, $1, $$.param.type))
- context->recover();
- }
- //
- // Only type
- //
- | parameter_type_qualifier parameter_qualifier parameter_type_specifier {
- $$ = $3;
- if (context->paramErrorCheck(@3, $1, $2, $$.param.type))
- context->recover();
- }
- | parameter_qualifier parameter_type_specifier {
+ : type_qualifier parameter_declarator {
$$ = $2;
- if (context->parameterSamplerErrorCheck(@2, $1, *$2.param.type))
- context->recover();
- if (context->paramErrorCheck(@2, EvqTemporary, $1, $$.param.type))
- context->recover();
+ context->checkIsParameterQualifierValid(@2, *$1, $2.type);
}
- ;
-
-parameter_qualifier
- : /* empty */ {
- $$ = EvqIn;
- }
- | IN_QUAL {
- $$ = EvqIn;
+ | parameter_declarator {
+ $$ = $1;
+ $$.type->setQualifier(EvqIn);
}
- | OUT_QUAL {
- $$ = EvqOut;
+ | type_qualifier parameter_type_specifier {
+ $$ = $2;
+ context->checkIsParameterQualifierValid(@2, *$1, $2.type);
}
- | INOUT_QUAL {
- $$ = EvqInOut;
+ | parameter_type_specifier {
+ $$ = $1;
+ $$.type->setQualifier(EvqIn);
}
;
parameter_type_specifier
: type_specifier {
TParameter param = { 0, new TType($1) };
- $$.param = param;
+ $$ = param;
}
;
@@ -786,213 +723,164 @@ init_declarator_list
}
| init_declarator_list COMMA identifier {
$$ = $1;
- $$.intermAggregate = context->parseDeclarator($$.type, $1.intermAggregate, @3, *$3.string);
- }
- | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
- $$ = $1;
- $$.intermAggregate = context->parseArrayDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5);
+ context->parseDeclarator($$.type, @3, *$3.string, $$.intermDeclaration);
}
- | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
- ES3_ONLY("[]", @3, "implicitly sized array");
+ | init_declarator_list COMMA identifier array_specifier {
$$ = $1;
- $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, nullptr, @6, $7);
+ context->parseArrayDeclarator($$.type, @3, *$3.string, @4, *($4), $$.intermDeclaration);
}
- | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer {
- ES3_ONLY("=", @7, "first-class arrays (array initializer)");
+ | init_declarator_list COMMA identifier array_specifier EQUAL initializer {
+ ES3_OR_NEWER("=", @5, "first-class arrays (array initializer)");
$$ = $1;
- $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5, @7, $8);
+ context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, *($4), @5, $6, $$.intermDeclaration);
}
| init_declarator_list COMMA identifier EQUAL initializer {
$$ = $1;
- $$.intermAggregate = context->parseInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5);
+ context->parseInitDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration);
}
;
single_declaration
: fully_specified_type {
$$.type = $1;
- $$.intermAggregate = context->parseSingleDeclaration($$.type, @1, "");
+ $$.intermDeclaration = context->parseSingleDeclaration($$.type, @1, "");
}
| fully_specified_type identifier {
$$.type = $1;
- $$.intermAggregate = context->parseSingleDeclaration($$.type, @2, *$2.string);
- }
- | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
- $$.type = $1;
- $$.intermAggregate = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4);
+ $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string);
}
- | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
- ES3_ONLY("[]", @3, "implicitly sized array");
+ | fully_specified_type identifier array_specifier {
$$.type = $1;
- $$.intermAggregate = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, nullptr, @5, $6);
+ $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, *($3));
}
- | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer {
- ES3_ONLY("=", @6, "first-class arrays (array initializer)");
+ | fully_specified_type identifier array_specifier EQUAL initializer {
+ ES3_OR_NEWER("[]", @3, "first-class arrays (array initializer)");
$$.type = $1;
- $$.intermAggregate = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7);
+ $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, *($3), @4, $5);
}
| fully_specified_type identifier EQUAL initializer {
$$.type = $1;
- $$.intermAggregate = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4);
- }
- | INVARIANT IDENTIFIER {
- // $$.type is not used in invariant declarations.
- $$.intermAggregate = context->parseInvariantDeclaration(@1, @2, $2.string, $2.symbol);
+ $$.intermDeclaration = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4);
}
;
fully_specified_type
: type_specifier {
+ context->addFullySpecifiedType(&$1);
$$ = $1;
-
- if ($1.array) {
- ES3_ONLY("[]", @1, "first-class-array");
- if (context->getShaderVersion() != 300) {
- $1.clearArrayness();
- }
- }
}
- | type_qualifier type_specifier {
- $$ = context->addFullySpecifiedType($1.qualifier, $1.invariant, $1.layoutQualifier, $2);
+ | type_qualifier type_specifier {
+ $$ = context->addFullySpecifiedType(*$1, $2);
}
;
interpolation_qualifier
: SMOOTH {
- $$.qualifier = EvqSmooth;
+ $$ = EvqSmooth;
}
| FLAT {
- $$.qualifier = EvqFlat;
- }
- ;
-
-parameter_type_qualifier
- : CONST_QUAL {
- $$ = EvqConst;
+ $$ = EvqFlat;
}
;
type_qualifier
- : ATTRIBUTE {
- VERTEX_ONLY("attribute", @1);
- ES2_ONLY("attribute", @1);
- if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "attribute"))
- context->recover();
- $$.setBasic(EbtVoid, EvqAttribute, @1);
- }
- | VARYING {
- ES2_ONLY("varying", @1);
- if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "varying"))
- context->recover();
- if (context->getShaderType() == GL_VERTEX_SHADER)
- $$.setBasic(EbtVoid, EvqVaryingOut, @1);
- else
- $$.setBasic(EbtVoid, EvqVaryingIn, @1);
+ : single_type_qualifier {
+ $$ = context->createTypeQualifierBuilder(@1);
+ $$->appendQualifier($1);
}
- | INVARIANT VARYING {
- ES2_ONLY("varying", @1);
- if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "invariant varying"))
- context->recover();
- if (context->getShaderType() == GL_VERTEX_SHADER)
- $$.setBasic(EbtVoid, EvqVaryingOut, @1);
- else
- $$.setBasic(EbtVoid, EvqVaryingIn, @1);
- $$.invariant = true;
- }
- | storage_qualifier {
- if ($1.qualifier != EvqConst && !context->symbolTable.atGlobalLevel())
- {
- context->error(@1, "Local variables can only use the const storage qualifier.", getQualifierString($1.qualifier));
- context->recover();
- }
- $$.setBasic(EbtVoid, $1.qualifier, @1);
+ | type_qualifier single_type_qualifier {
+ $$ = $1;
+ $$->appendQualifier($2);
}
- | interpolation_qualifier storage_qualifier {
- $$ = context->joinInterpolationQualifiers(@1, $1.qualifier, @2, $2.qualifier);
+ ;
+
+invariant_qualifier
+ : INVARIANT {
+ // empty
}
- | interpolation_qualifier {
- context->error(@1, "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier", getInterpolationString($1.qualifier));
- context->recover();
-
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtVoid, qual, @1);
+ ;
+
+single_type_qualifier
+ : storage_qualifier {
+ context->checkLocalVariableConstStorageQualifier(*$1);
+ $$ = $1;
}
| layout_qualifier {
- $$.qualifier = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.layoutQualifier = $1;
+ context->checkIsAtGlobalLevel(@1, "layout");
+ $$ = new TLayoutQualifierWrapper($1, @1);
}
- | layout_qualifier storage_qualifier {
- $$.setBasic(EbtVoid, $2.qualifier, @2);
- $$.layoutQualifier = $1;
+ | precision_qualifier {
+ $$ = new TPrecisionQualifierWrapper($1, @1);
}
- | INVARIANT storage_qualifier {
- context->es3InvariantErrorCheck($2.qualifier, @1);
- $$.setBasic(EbtVoid, $2.qualifier, @2);
- $$.invariant = true;
+ | interpolation_qualifier {
+ $$ = new TInterpolationQualifierWrapper($1, @1);
}
- | INVARIANT interpolation_qualifier storage_qualifier {
- context->es3InvariantErrorCheck($3.qualifier, @1);
- $$ = context->joinInterpolationQualifiers(@2, $2.qualifier, @3, $3.qualifier);
- $$.invariant = true;
+ | invariant_qualifier {
+ context->checkIsAtGlobalLevel(@1, "invariant");
+ $$ = new TInvariantQualifierWrapper(@1);
}
;
+
storage_qualifier
- : CONST_QUAL {
- $$.qualifier = EvqConst;
+ :
+ ATTRIBUTE {
+ VERTEX_ONLY("attribute", @1);
+ ES2_ONLY("attribute", @1);
+ $$ = context->parseGlobalStorageQualifier(EvqAttribute, @1);
+ }
+ | VARYING {
+ ES2_ONLY("varying", @1);
+ $$ = context->parseVaryingQualifier(@1);
+ }
+ | CONST_QUAL {
+ $$ = new TStorageQualifierWrapper(EvqConst, @1);
}
| IN_QUAL {
- ES3_ONLY("in", @1, "storage qualifier");
- $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentIn : EvqVertexIn;
+ $$ = context->parseInQualifier(@1);
}
| OUT_QUAL {
- ES3_ONLY("out", @1, "storage qualifier");
- $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut;
+ $$ = context->parseOutQualifier(@1);
}
- | CENTROID IN_QUAL {
- ES3_ONLY("centroid in", @1, "storage qualifier");
- if (context->getShaderType() == GL_VERTEX_SHADER)
- {
- context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid in' in the vertex shader");
- context->recover();
- }
- $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqCentroidIn : EvqVertexIn;
+ | INOUT_QUAL {
+ $$ = context->parseInOutQualifier(@1);
}
- | CENTROID OUT_QUAL {
- ES3_ONLY("centroid out", @1, "storage qualifier");
- if (context->getShaderType() == GL_FRAGMENT_SHADER)
- {
- context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid out' in the fragment shader");
- context->recover();
- }
- $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqCentroidOut;
+ | CENTROID {
+ ES3_OR_NEWER("centroid", @1, "storage qualifier");
+ $$ = new TStorageQualifierWrapper(EvqCentroid, @1);
}
| UNIFORM {
- if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "uniform"))
- context->recover();
- $$.qualifier = EvqUniform;
+ $$ = context->parseGlobalStorageQualifier(EvqUniform, @1);
+ }
+ | BUFFER {
+ ES3_1_ONLY("buffer", @1, "storage qualifier");
+ $$ = context->parseGlobalStorageQualifier(EvqBuffer, @1);
+ }
+ | READONLY {
+ $$ = new TMemoryQualifierWrapper(EvqReadOnly, @1);
+ }
+ | WRITEONLY {
+ $$ = new TMemoryQualifierWrapper(EvqWriteOnly, @1);
+ }
+ | COHERENT {
+ $$ = new TMemoryQualifierWrapper(EvqCoherent, @1);
+ }
+ | RESTRICT {
+ $$ = new TMemoryQualifierWrapper(EvqRestrict, @1);
+ }
+ | VOLATILE {
+ $$ = new TMemoryQualifierWrapper(EvqVolatile, @1);
+ }
+ | SHARED {
+ COMPUTE_ONLY("shared", @1);
+ $$ = context->parseGlobalStorageQualifier(EvqShared, @1);
}
;
type_specifier
: type_specifier_no_prec {
$$ = $1;
-
- if ($$.precision == EbpUndefined) {
- $$.precision = context->symbolTable.getDefaultPrecision($1.type);
- if (context->precisionErrorCheck(@1, $$.precision, $1.type)) {
- context->recover();
- }
- }
- }
- | precision_qualifier type_specifier_no_prec {
- $$ = $2;
- $$.precision = $1;
-
- if (!SupportsPrecision($2.type)) {
- context->error(@1, "illegal type for precision qualifier", getBasicString($2.type));
- context->recover();
- }
+ $$.precision = context->symbolTable.getDefaultPrecision($1.getBasicType());
}
;
@@ -1010,7 +898,7 @@ precision_qualifier
layout_qualifier
: LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN {
- ES3_ONLY("layout", @1, "qualifier");
+ ES3_OR_NEWER_OR_MULTIVIEW("layout", @1, "qualifier");
$$ = $3;
}
;
@@ -1020,7 +908,7 @@ layout_qualifier_id_list
$$ = $1;
}
| layout_qualifier_id_list COMMA layout_qualifier_id {
- $$ = context->joinLayoutQualifiers($1, $3);
+ $$ = context->joinLayoutQualifiers($1, $3, @3);
}
;
@@ -1029,279 +917,303 @@ layout_qualifier_id
$$ = context->parseLayoutQualifier(*$1.string, @1);
}
| IDENTIFIER EQUAL INTCONSTANT {
- $$ = context->parseLayoutQualifier(*$1.string, @1, *$3.string, $3.i, @3);
+ $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
}
| IDENTIFIER EQUAL UINTCONSTANT {
- $$ = context->parseLayoutQualifier(*$1.string, @1, *$3.string, $3.i, @3);
+ $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
+ }
+ | SHARED {
+ $$ = context->parseLayoutQualifier("shared", @1);
}
;
type_specifier_no_prec
: type_specifier_nonarray {
- $$ = $1;
+ $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ }
+ | type_specifier_nonarray array_specifier {
+ $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ $$.setArraySizes($2);
+ }
+ ;
+
+array_specifier
+ : LEFT_BRACKET RIGHT_BRACKET {
+ ES3_OR_NEWER("[]", @1, "implicitly sized array");
+ $$ = new TVector<unsigned int>();
+ $$->push_back(0u);
}
- | type_specifier_nonarray LEFT_BRACKET RIGHT_BRACKET {
- ES3_ONLY("[]", @2, "implicitly sized array");
+ | LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ $$ = new TVector<unsigned int>();
+ unsigned int size = context->checkIsValidArraySize(@1, $2);
+ // Make the type an array even if size check failed.
+ // This ensures useless error messages regarding a variable's non-arrayness won't follow.
+ $$->push_back(size);
+ }
+ | array_specifier LEFT_BRACKET RIGHT_BRACKET {
+ ES3_1_ONLY("[]", @2, "arrays of arrays");
$$ = $1;
- $$.setArraySize(0);
+ $$->insert($$->begin(), 0u);
}
- | type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ | array_specifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ ES3_1_ONLY("[]", @2, "arrays of arrays");
$$ = $1;
-
- if (context->arrayTypeErrorCheck(@2, $1))
- context->recover();
- else {
- int size;
- if (context->arraySizeErrorCheck(@2, $3, size))
- context->recover();
- $$.setArraySize(size);
- }
+ unsigned int size = context->checkIsValidArraySize(@2, $3);
+ // Make the type an array even if size check failed.
+ // This ensures useless error messages regarding a variable's non-arrayness won't follow.
+ $$->insert($$->begin(), size);
}
;
type_specifier_nonarray
: VOID_TYPE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtVoid, qual, @1);
+ $$.initialize(EbtVoid, @1);
}
| FLOAT_TYPE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
}
| INT_TYPE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtInt, qual, @1);
+ $$.initialize(EbtInt, @1);
}
| UINT_TYPE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUInt, qual, @1);
+ $$.initialize(EbtUInt, @1);
}
| BOOL_TYPE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtBool, qual, @1);
+ $$.initialize(EbtBool, @1);
}
| VEC2 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setAggregate(2);
}
| VEC3 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setAggregate(3);
}
| VEC4 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setAggregate(4);
}
| BVEC2 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtBool, qual, @1);
+ $$.initialize(EbtBool, @1);
$$.setAggregate(2);
}
| BVEC3 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtBool, qual, @1);
+ $$.initialize(EbtBool, @1);
$$.setAggregate(3);
}
| BVEC4 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtBool, qual, @1);
+ $$.initialize(EbtBool, @1);
$$.setAggregate(4);
}
| IVEC2 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtInt, qual, @1);
+ $$.initialize(EbtInt, @1);
$$.setAggregate(2);
}
| IVEC3 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtInt, qual, @1);
+ $$.initialize(EbtInt, @1);
$$.setAggregate(3);
}
| IVEC4 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtInt, qual, @1);
+ $$.initialize(EbtInt, @1);
$$.setAggregate(4);
}
| UVEC2 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUInt, qual, @1);
+ $$.initialize(EbtUInt, @1);
$$.setAggregate(2);
}
| UVEC3 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUInt, qual, @1);
+ $$.initialize(EbtUInt, @1);
$$.setAggregate(3);
}
| UVEC4 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUInt, qual, @1);
+ $$.initialize(EbtUInt, @1);
$$.setAggregate(4);
}
| MATRIX2 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(2, 2);
}
| MATRIX3 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(3, 3);
}
| MATRIX4 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(4, 4);
}
| MATRIX2x3 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(2, 3);
}
| MATRIX3x2 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(3, 2);
}
| MATRIX2x4 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(2, 4);
}
| MATRIX4x2 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(4, 2);
}
| MATRIX3x4 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(3, 4);
}
| MATRIX4x3 {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtFloat, qual, @1);
+ $$.initialize(EbtFloat, @1);
$$.setMatrix(4, 3);
}
+ | YUVCSCSTANDARDEXT {
+ if (!context->checkCanUseExtension(@1, TExtension::EXT_YUV_target))
+ {
+ context->error(@1, "unsupported type", "yuvCscStandardEXT");
+ }
+ $$.initialize(EbtYuvCscStandardEXT, @1);
+ }
| SAMPLER2D {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSampler2D, qual, @1);
+ $$.initialize(EbtSampler2D, @1);
}
| SAMPLER3D {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSampler3D, qual, @1);
+ $$.initialize(EbtSampler3D, @1);
}
| SAMPLERCUBE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSamplerCube, qual, @1);
+ $$.initialize(EbtSamplerCube, @1);
}
| SAMPLER2DARRAY {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSampler2DArray, qual, @1);
+ $$.initialize(EbtSampler2DArray, @1);
+ }
+ | SAMPLER2DMS {
+ $$.initialize(EbtSampler2DMS, @1);
}
| ISAMPLER2D {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtISampler2D, qual, @1);
+ $$.initialize(EbtISampler2D, @1);
}
| ISAMPLER3D {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtISampler3D, qual, @1);
+ $$.initialize(EbtISampler3D, @1);
}
| ISAMPLERCUBE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtISamplerCube, qual, @1);
+ $$.initialize(EbtISamplerCube, @1);
}
| ISAMPLER2DARRAY {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtISampler2DArray, qual, @1);
+ $$.initialize(EbtISampler2DArray, @1);
+ }
+ | ISAMPLER2DMS {
+ $$.initialize(EbtISampler2DMS, @1);
}
| USAMPLER2D {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUSampler2D, qual, @1);
+ $$.initialize(EbtUSampler2D, @1);
}
| USAMPLER3D {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUSampler3D, qual, @1);
+ $$.initialize(EbtUSampler3D, @1);
}
| USAMPLERCUBE {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUSamplerCube, qual, @1);
+ $$.initialize(EbtUSamplerCube, @1);
}
| USAMPLER2DARRAY {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtUSampler2DArray, qual, @1);
+ $$.initialize(EbtUSampler2DArray, @1);
+ }
+ | USAMPLER2DMS {
+ $$.initialize(EbtUSampler2DMS, @1);
}
| SAMPLER2DSHADOW {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSampler2DShadow, qual, @1);
+ $$.initialize(EbtSampler2DShadow, @1);
}
| SAMPLERCUBESHADOW {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSamplerCubeShadow, qual, @1);
+ $$.initialize(EbtSamplerCubeShadow, @1);
}
| SAMPLER2DARRAYSHADOW {
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSampler2DArrayShadow, qual, @1);
+ $$.initialize(EbtSampler2DArrayShadow, @1);
}
| SAMPLER_EXTERNAL_OES {
- if (!context->supportsExtension("GL_OES_EGL_image_external")) {
+ constexpr std::array<TExtension, 3u> extensions{ { TExtension::NV_EGL_stream_consumer_external,
+ TExtension::OES_EGL_image_external_essl3,
+ TExtension::OES_EGL_image_external } };
+ if (!context->checkCanUseOneOfExtensions(@1, extensions))
+ {
context->error(@1, "unsupported type", "samplerExternalOES");
- context->recover();
}
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSamplerExternalOES, qual, @1);
+ $$.initialize(EbtSamplerExternalOES, @1);
+ }
+ | SAMPLEREXTERNAL2DY2YEXT {
+ if (!context->checkCanUseExtension(@1, TExtension::EXT_YUV_target))
+ {
+ context->error(@1, "unsupported type", "__samplerExternal2DY2YEXT");
+ }
+ $$.initialize(EbtSamplerExternal2DY2YEXT, @1);
}
| SAMPLER2DRECT {
- if (!context->supportsExtension("GL_ARB_texture_rectangle")) {
+ if (!context->checkCanUseExtension(@1, TExtension::ARB_texture_rectangle))
+ {
context->error(@1, "unsupported type", "sampler2DRect");
- context->recover();
}
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtSampler2DRect, qual, @1);
+ $$.initialize(EbtSampler2DRect, @1);
}
| struct_specifier {
$$ = $1;
- $$.qualifier = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
+ }
+ | IMAGE2D {
+ $$.initialize(EbtImage2D, @1);
+ }
+ | IIMAGE2D {
+ $$.initialize(EbtIImage2D, @1);
+ }
+ | UIMAGE2D {
+ $$.initialize(EbtUImage2D, @1);
+ }
+ | IMAGE3D {
+ $$.initialize(EbtImage3D, @1);
+ }
+ | IIMAGE3D {
+ $$.initialize(EbtIImage3D, @1);
+ }
+ | UIMAGE3D {
+ $$.initialize(EbtUImage3D, @1);
+ }
+ | IMAGE2DARRAY {
+ $$.initialize(EbtImage2DArray, @1);
+ }
+ | IIMAGE2DARRAY {
+ $$.initialize(EbtIImage2DArray, @1);
+ }
+ | UIMAGE2DARRAY {
+ $$.initialize(EbtUImage2DArray, @1);
+ }
+ | IMAGECUBE {
+ $$.initialize(EbtImageCube, @1);
+ }
+ | IIMAGECUBE {
+ $$.initialize(EbtIImageCube, @1);
+ }
+ | UIMAGECUBE {
+ $$.initialize(EbtUImageCube, @1);
+ }
+ | ATOMICUINT {
+ $$.initialize(EbtAtomicCounter, @1);
}
| TYPE_NAME {
- //
- // This is for user defined type names. The lexical phase looked up the
- // type.
- //
+ // This is for user defined type names. The lexical phase looked up the type.
TType& structure = static_cast<TVariable*>($1.symbol)->getType();
- TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
- $$.setBasic(EbtStruct, qual, @1);
- $$.userDef = &structure;
+ $$.initializeStruct(structure.getStruct(), false, @1);
}
;
struct_specifier
- : STRUCT identifier LEFT_BRACE { if (context->enterStructDeclaration(@2, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE {
+ : STRUCT identifier LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE {
$$ = context->addStructure(@1, @2, $2.string, $5);
}
- | STRUCT LEFT_BRACE { if (context->enterStructDeclaration(@2, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE {
+ | STRUCT LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE {
$$ = context->addStructure(@1, @$, NewPoolTString(""), $4);
}
;
struct_declaration_list
: struct_declaration {
- $$ = $1;
+ $$ = context->addStructFieldList($1, @1);
}
| struct_declaration_list struct_declaration {
- $$ = $1;
- for (size_t i = 0; i < $2->size(); ++i) {
- TField* field = (*$2)[i];
- for (size_t j = 0; j < $$->size(); ++j) {
- if ((*$$)[j]->name() == field->name()) {
- context->error(@2, "duplicate field name in structure:", "struct", field->name().c_str());
- context->recover();
- }
- }
- $$->push_back(field);
- }
+ $$ = context->combineStructFieldLists($1, $2, @2);
}
;
@@ -1311,9 +1223,7 @@ struct_declaration
}
| type_qualifier type_specifier struct_declarator_list SEMICOLON {
// ES3 Only, but errors should be handled elsewhere
- $2.qualifier = $1.qualifier;
- $2.layoutQualifier = $1.layoutQualifier;
- $$ = context->addStructDeclaratorList($2, $3);
+ $$ = context->addStructDeclaratorListWithQualifiers(*$1, &$2, $3);
}
;
@@ -1329,23 +1239,10 @@ struct_declarator_list
struct_declarator
: identifier {
- if (context->reservedErrorCheck(@1, *$1.string))
- context->recover();
-
- TType* type = new TType(EbtVoid, EbpUndefined);
- $$ = new TField(type, $1.string, @1);
+ $$ = context->parseStructDeclarator($1.string, @1);
}
- | identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
- if (context->reservedErrorCheck(@1, *$1.string))
- context->recover();
-
- TType* type = new TType(EbtVoid, EbpUndefined);
- int size;
- if (context->arraySizeErrorCheck(@3, $3, size))
- context->recover();
- type->setArraySize(size);
-
- $$ = new TField(type, $1.string, @1);
+ | identifier array_specifier {
+ $$ = context->parseStructArrayDeclarator($1.string, @1, *($2), @2);
}
;
@@ -1358,8 +1255,8 @@ declaration_statement
;
statement
- : compound_statement { $$ = $1; }
- | simple_statement { $$ = $1; }
+ : compound_statement_with_scope { $$ = $1; }
+ | simple_statement { $$ = $1; }
;
// Grammar Note: Labeled statements for SWITCH only; 'goto' is not supported.
@@ -1374,13 +1271,13 @@ simple_statement
| jump_statement { $$ = $1; }
;
-compound_statement
- : LEFT_BRACE RIGHT_BRACE { $$ = 0; }
+compound_statement_with_scope
+ : LEFT_BRACE RIGHT_BRACE {
+ $$ = new TIntermBlock();
+ $$->setLine(@$);
+ }
| LEFT_BRACE { context->symbolTable.push(); } statement_list { context->symbolTable.pop(); } RIGHT_BRACE {
- if ($3 != 0) {
- $3->setOp(EOpSequence);
- $3->setLine(@$);
- }
+ $3->setLine(@$);
$$ = $3;
}
;
@@ -1396,38 +1293,36 @@ statement_with_scope
;
compound_statement_no_new_scope
- // Statement that doesn't create a new scope, for selection_statement, iteration_statement
+ // Statement that doesn't create a new scope for iteration_statement, function definition (scope is created for parameters)
: LEFT_BRACE RIGHT_BRACE {
- $$ = 0;
+ $$ = new TIntermBlock();
+ $$->setLine(@$);
}
| LEFT_BRACE statement_list RIGHT_BRACE {
- if ($2) {
- $2->setOp(EOpSequence);
- $2->setLine(@$);
- }
+ $2->setLine(@$);
$$ = $2;
}
;
statement_list
: statement {
- $$ = context->intermediate.makeAggregate($1, @$);
+ $$ = new TIntermBlock();
+ $$->appendStatement($1);
}
| statement_list statement {
- $$ = context->intermediate.growAggregate($1, $2, @$);
+ $$ = $1;
+ $$->appendStatement($2);
}
;
expression_statement
- : SEMICOLON { $$ = 0; }
- | expression SEMICOLON { $$ = static_cast<TIntermNode*>($1); }
+ : SEMICOLON { $$ = context->addEmptyStatement(@$); }
+ | expression SEMICOLON { $$ = $1; }
;
selection_statement
: IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement {
- if (context->boolErrorCheck(@1, $3))
- context->recover();
- $$ = context->intermediate.addSelection($3, $5, @1);
+ $$ = context->addIfElse($3, $5, @1);
}
;
@@ -1438,12 +1333,14 @@ selection_rest_statement
}
| statement_with_scope {
$$.node1 = $1;
- $$.node2 = 0;
+ $$.node2 = nullptr;
}
;
+// Note that we've diverged from the spec grammar here a bit for the sake of simplicity.
+// We're reusing compound_statement_with_scope instead of having separate rules for switch.
switch_statement
- : SWITCH LEFT_PAREN expression RIGHT_PAREN { context->incrSwitchNestingLevel(); } compound_statement {
+ : SWITCH LEFT_PAREN expression RIGHT_PAREN { context->incrSwitchNestingLevel(); } compound_statement_with_scope {
$$ = context->addSwitch($3, $6, @1);
context->decrSwitchNestingLevel();
}
@@ -1459,42 +1356,28 @@ case_label
;
condition
- // In 1996 c++ draft, conditions can include single declarations
: expression {
$$ = $1;
- if (context->boolErrorCheck($1->getLine(), $1))
- context->recover();
+ context->checkIsScalarBool($1->getLine(), $1);
}
| fully_specified_type identifier EQUAL initializer {
- TIntermNode *intermNode;
- if (context->boolErrorCheck(@2, $1))
- context->recover();
-
- if (!context->executeInitializer(@2, *$2.string, $1, $4, &intermNode))
- $$ = $4;
- else {
- context->recover();
- $$ = 0;
- }
+ $$ = context->addConditionInitializer($1, *$2.string, $4, @2);
}
;
iteration_statement
: WHILE LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } condition RIGHT_PAREN statement_no_new_scope {
context->symbolTable.pop();
- $$ = context->intermediate.addLoop(ELoopWhile, 0, $4, 0, $6, @1);
+ $$ = context->addLoop(ELoopWhile, 0, $4, 0, $6, @1);
context->decrLoopNestingLevel();
}
| DO { context->incrLoopNestingLevel(); } statement_with_scope WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON {
- if (context->boolErrorCheck(@8, $6))
- context->recover();
-
- $$ = context->intermediate.addLoop(ELoopDoWhile, 0, $6, 0, $3, @4);
+ $$ = context->addLoop(ELoopDoWhile, 0, $6, 0, $3, @4);
context->decrLoopNestingLevel();
}
| FOR LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope {
context->symbolTable.pop();
- $$ = context->intermediate.addLoop(ELoopFor, $4, reinterpret_cast<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($5.node2), $7, @1);
+ $$ = context->addLoop(ELoopFor, $4, $5.node1, reinterpret_cast<TIntermTyped*>($5.node2), $7, @1);
context->decrLoopNestingLevel();
}
;
@@ -1513,7 +1396,7 @@ conditionopt
$$ = $1;
}
| /* May be null */ {
- $$ = 0;
+ $$ = nullptr;
}
;
@@ -1542,7 +1425,6 @@ jump_statement
$$ = context->addBranch(EOpReturn, $2, @1);
}
| DISCARD SEMICOLON {
- FRAG_ONLY("discard", @1);
$$ = context->addBranch(EOpKill, @1);
}
;
@@ -1551,12 +1433,13 @@ jump_statement
translation_unit
: external_declaration {
- $$ = $1;
+ $$ = new TIntermBlock();
+ $$->setLine(@$);
+ $$->appendStatement($1);
context->setTreeRoot($$);
}
| translation_unit external_declaration {
- $$ = context->intermediate.growAggregate($1, $2, @$);
- context->setTreeRoot($$);
+ $$->appendStatement($2);
}
;
@@ -1571,10 +1454,10 @@ external_declaration
function_definition
: function_prototype {
- context->parseFunctionPrototype(@1, $1.function, &$1.intermAggregate);
+ context->parseFunctionDefinitionHeader(@1, &($1.function), &($1.intermFunctionPrototype));
}
compound_statement_no_new_scope {
- $$ = context->addFunctionDefinition(*($1.function), $1.intermAggregate, $3, @1);
+ $$ = context->addFunctionDefinition($1.intermFunctionPrototype, $3, @1);
}
;
diff --git a/src/3rdparty/angle/src/compiler/translator/intermOut.cpp b/src/3rdparty/angle/src/compiler/translator/intermOut.cpp
deleted file mode 100644
index 6dca547f08..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/intermOut.cpp
+++ /dev/null
@@ -1,626 +0,0 @@
-//
-// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/Intermediate.h"
-#include "compiler/translator/SymbolTable.h"
-
-namespace
-{
-
-void OutputFunction(TInfoSinkBase &out, const char *str, TIntermAggregate *node)
-{
- const char *internal = node->getNameObj().isInternal() ? " (internal function)" : "";
- out << str << internal << ": " << node->getNameObj().getString();
-}
-
-//
-// Two purposes:
-// 1. Show an example of how to iterate tree. Functions can
-// also directly call Traverse() on children themselves to
-// have finer grained control over the process than shown here.
-// See the last function for how to get started.
-// 2. Print out a text based description of the tree.
-//
-
-//
-// Use this class to carry along data from node to node in
-// the traversal
-//
-class TOutputTraverser : public TIntermTraverser
-{
- public:
- TOutputTraverser(TInfoSinkBase &i)
- : TIntermTraverser(true, false, false),
- sink(i)
- {
- }
- TInfoSinkBase& sink;
-
- protected:
- void visitSymbol(TIntermSymbol *) override;
- void visitConstantUnion(TIntermConstantUnion *) override;
- bool visitBinary(Visit visit, TIntermBinary *) override;
- bool visitUnary(Visit visit, TIntermUnary *) override;
- bool visitSelection(Visit visit, TIntermSelection *) override;
- bool visitAggregate(Visit visit, TIntermAggregate *) override;
- bool visitLoop(Visit visit, TIntermLoop *) override;
- bool visitBranch(Visit visit, TIntermBranch *) override;
-};
-
-//
-// Helper functions for printing, not part of traversing.
-//
-void OutputTreeText(TInfoSinkBase &sink, TIntermNode *node, const int depth)
-{
- int i;
-
- sink.location(node->getLine());
-
- for (i = 0; i < depth; ++i)
- sink << " ";
-}
-
-} // namespace anonymous
-
-//
-// The rest of the file are the traversal functions. The last one
-// is the one that starts the traversal.
-//
-// Return true from interior nodes to have the external traversal
-// continue on to children. If you process children yourself,
-// return false.
-//
-
-void TOutputTraverser::visitSymbol(TIntermSymbol *node)
-{
- OutputTreeText(sink, node, mDepth);
-
- sink << "'" << node->getSymbol() << "' ";
- sink << "(" << node->getCompleteString() << ")\n";
-}
-
-bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary *node)
-{
- TInfoSinkBase& out = sink;
-
- OutputTreeText(out, node, mDepth);
-
- switch (node->getOp())
- {
- case EOpAssign:
- out << "move second child to first child";
- break;
- case EOpInitialize:
- out << "initialize first child with second child";
- break;
- case EOpAddAssign:
- out << "add second child into first child";
- break;
- case EOpSubAssign:
- out << "subtract second child into first child";
- break;
- case EOpMulAssign:
- out << "multiply second child into first child";
- break;
- case EOpVectorTimesMatrixAssign:
- out << "matrix mult second child into first child";
- break;
- case EOpVectorTimesScalarAssign:
- out << "vector scale second child into first child";
- break;
- case EOpMatrixTimesScalarAssign:
- out << "matrix scale second child into first child";
- break;
- case EOpMatrixTimesMatrixAssign:
- out << "matrix mult second child into first child";
- break;
- case EOpDivAssign:
- out << "divide second child into first child";
- break;
- case EOpIModAssign:
- out << "modulo second child into first child";
- break;
- case EOpBitShiftLeftAssign:
- out << "bit-wise shift first child left by second child";
- break;
- case EOpBitShiftRightAssign:
- out << "bit-wise shift first child right by second child";
- break;
- case EOpBitwiseAndAssign:
- out << "bit-wise and second child into first child";
- break;
- case EOpBitwiseXorAssign:
- out << "bit-wise xor second child into first child";
- break;
- case EOpBitwiseOrAssign:
- out << "bit-wise or second child into first child";
- break;
-
- case EOpIndexDirect:
- out << "direct index";
- break;
- case EOpIndexIndirect:
- out << "indirect index";
- break;
- case EOpIndexDirectStruct:
- out << "direct index for structure";
- break;
- case EOpIndexDirectInterfaceBlock:
- out << "direct index for interface block";
- break;
- case EOpVectorSwizzle:
- out << "vector swizzle";
- break;
-
- case EOpAdd:
- out << "add";
- break;
- case EOpSub:
- out << "subtract";
- break;
- case EOpMul:
- out << "component-wise multiply";
- break;
- case EOpDiv:
- out << "divide";
- break;
- case EOpIMod:
- out << "modulo";
- break;
- case EOpBitShiftLeft:
- out << "bit-wise shift left";
- break;
- case EOpBitShiftRight:
- out << "bit-wise shift right";
- break;
- case EOpBitwiseAnd:
- out << "bit-wise and";
- break;
- case EOpBitwiseXor:
- out << "bit-wise xor";
- break;
- case EOpBitwiseOr:
- out << "bit-wise or";
- break;
-
- case EOpEqual:
- out << "Compare Equal";
- break;
- case EOpNotEqual:
- out << "Compare Not Equal";
- break;
- case EOpLessThan:
- out << "Compare Less Than";
- break;
- case EOpGreaterThan:
- out << "Compare Greater Than";
- break;
- case EOpLessThanEqual:
- out << "Compare Less Than or Equal";
- break;
- case EOpGreaterThanEqual:
- out << "Compare Greater Than or Equal";
- break;
-
- case EOpVectorTimesScalar:
- out << "vector-scale";
- break;
- case EOpVectorTimesMatrix:
- out << "vector-times-matrix";
- break;
- case EOpMatrixTimesVector:
- out << "matrix-times-vector";
- break;
- case EOpMatrixTimesScalar:
- out << "matrix-scale";
- break;
- case EOpMatrixTimesMatrix:
- out << "matrix-multiply";
- break;
-
- case EOpLogicalOr:
- out << "logical-or";
- break;
- case EOpLogicalXor:
- out << "logical-xor";
- break;
- case EOpLogicalAnd:
- out << "logical-and";
- break;
- default:
- out << "<unknown op>";
- }
-
- out << " (" << node->getCompleteString() << ")";
-
- out << "\n";
-
- // Special handling for direct indexes. Because constant
- // unions are not aware they are struct indexes, treat them
- // here where we have that contextual knowledge.
- if (node->getOp() == EOpIndexDirectStruct ||
- node->getOp() == EOpIndexDirectInterfaceBlock)
- {
- mDepth++;
- node->getLeft()->traverse(this);
- mDepth--;
-
- TIntermConstantUnion *intermConstantUnion = node->getRight()->getAsConstantUnion();
- ASSERT(intermConstantUnion);
-
- OutputTreeText(out, intermConstantUnion, mDepth + 1);
-
- // The following code finds the field name from the constant union
- const TConstantUnion *constantUnion = intermConstantUnion->getUnionArrayPointer();
- const TStructure *structure = node->getLeft()->getType().getStruct();
- const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock();
- ASSERT(structure || interfaceBlock);
-
- const TFieldList &fields = structure ? structure->fields() : interfaceBlock->fields();
-
- const TField *field = fields[constantUnion->getIConst()];
-
- out << constantUnion->getIConst() << " (field '" << field->name() << "')";
-
- return false;
- }
-
- return true;
-}
-
-bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary *node)
-{
- TInfoSinkBase& out = sink;
-
- OutputTreeText(out, node, mDepth);
-
- switch (node->getOp())
- {
- case EOpNegative: out << "Negate value"; break;
- case EOpPositive: out << "Positive sign"; break;
- case EOpVectorLogicalNot:
- case EOpLogicalNot: out << "Negate conditional"; break;
- case EOpBitwiseNot: out << "bit-wise not"; break;
-
- case EOpPostIncrement: out << "Post-Increment"; break;
- case EOpPostDecrement: out << "Post-Decrement"; break;
- case EOpPreIncrement: out << "Pre-Increment"; break;
- case EOpPreDecrement: out << "Pre-Decrement"; break;
-
- case EOpRadians: out << "radians"; break;
- case EOpDegrees: out << "degrees"; break;
- case EOpSin: out << "sine"; break;
- case EOpCos: out << "cosine"; break;
- case EOpTan: out << "tangent"; break;
- case EOpAsin: out << "arc sine"; break;
- case EOpAcos: out << "arc cosine"; break;
- case EOpAtan: out << "arc tangent"; break;
-
- case EOpSinh: out << "hyperbolic sine"; break;
- case EOpCosh: out << "hyperbolic cosine"; break;
- case EOpTanh: out << "hyperbolic tangent"; break;
- case EOpAsinh: out << "arc hyperbolic sine"; break;
- case EOpAcosh: out << "arc hyperbolic cosine"; break;
- case EOpAtanh: out << "arc hyperbolic tangent"; break;
-
- case EOpExp: out << "exp"; break;
- case EOpLog: out << "log"; break;
- case EOpExp2: out << "exp2"; break;
- case EOpLog2: out << "log2"; break;
- case EOpSqrt: out << "sqrt"; break;
- case EOpInverseSqrt: out << "inverse sqrt"; break;
-
- case EOpAbs: out << "Absolute value"; break;
- case EOpSign: out << "Sign"; break;
- case EOpFloor: out << "Floor"; break;
- case EOpTrunc: out << "Truncate"; break;
- case EOpRound: out << "Round"; break;
- case EOpRoundEven: out << "Round half even"; break;
- case EOpCeil: out << "Ceiling"; break;
- case EOpFract: out << "Fraction"; break;
- case EOpIsNan: out << "Is not a number"; break;
- case EOpIsInf: out << "Is infinity"; break;
-
- case EOpFloatBitsToInt: out << "float bits to int"; break;
- case EOpFloatBitsToUint: out << "float bits to uint"; break;
- case EOpIntBitsToFloat: out << "int bits to float"; break;
- case EOpUintBitsToFloat: out << "uint bits to float"; break;
-
- case EOpPackSnorm2x16: out << "pack Snorm 2x16"; break;
- case EOpPackUnorm2x16: out << "pack Unorm 2x16"; break;
- case EOpPackHalf2x16: out << "pack half 2x16"; break;
-
- case EOpUnpackSnorm2x16: out << "unpack Snorm 2x16"; break;
- case EOpUnpackUnorm2x16: out << "unpack Unorm 2x16"; break;
- case EOpUnpackHalf2x16: out << "unpack half 2x16"; break;
-
- case EOpLength: out << "length"; break;
- case EOpNormalize: out << "normalize"; break;
- // case EOpDPdx: out << "dPdx"; break;
- // case EOpDPdy: out << "dPdy"; break;
- // case EOpFwidth: out << "fwidth"; break;
-
- case EOpDeterminant: out << "determinant"; break;
- case EOpTranspose: out << "transpose"; break;
- case EOpInverse: out << "inverse"; break;
-
- case EOpAny: out << "any"; break;
- case EOpAll: out << "all"; break;
-
- default:
- out.prefix(EPrefixError);
- out << "Bad unary op";
- }
-
- out << " (" << node->getCompleteString() << ")";
-
- out << "\n";
-
- return true;
-}
-
-bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
-{
- TInfoSinkBase &out = sink;
-
- if (node->getOp() == EOpNull)
- {
- out.prefix(EPrefixError);
- out << "node is still EOpNull!";
- return true;
- }
-
- OutputTreeText(out, node, mDepth);
-
- switch (node->getOp())
- {
- case EOpSequence: out << "Sequence\n"; return true;
- case EOpComma: out << "Comma\n"; return true;
- case EOpFunction: OutputFunction(out, "Function Definition", node); break;
- case EOpFunctionCall: OutputFunction(out, "Function Call", node); break;
- case EOpParameters: out << "Function Parameters: "; break;
- case EOpPrototype: OutputFunction(out, "Function Prototype", node); break;
-
- case EOpConstructFloat: out << "Construct float"; break;
- case EOpConstructVec2: out << "Construct vec2"; break;
- case EOpConstructVec3: out << "Construct vec3"; break;
- case EOpConstructVec4: out << "Construct vec4"; break;
- case EOpConstructBool: out << "Construct bool"; break;
- case EOpConstructBVec2: out << "Construct bvec2"; break;
- case EOpConstructBVec3: out << "Construct bvec3"; break;
- case EOpConstructBVec4: out << "Construct bvec4"; break;
- case EOpConstructInt: out << "Construct int"; break;
- case EOpConstructIVec2: out << "Construct ivec2"; break;
- case EOpConstructIVec3: out << "Construct ivec3"; break;
- case EOpConstructIVec4: out << "Construct ivec4"; break;
- case EOpConstructUInt: out << "Construct uint"; break;
- case EOpConstructUVec2: out << "Construct uvec2"; break;
- case EOpConstructUVec3: out << "Construct uvec3"; break;
- case EOpConstructUVec4: out << "Construct uvec4"; break;
- case EOpConstructMat2: out << "Construct mat2"; break;
- case EOpConstructMat2x3: out << "Construct mat2x3"; break;
- case EOpConstructMat2x4: out << "Construct mat2x4"; break;
- case EOpConstructMat3x2: out << "Construct mat3x2"; break;
- case EOpConstructMat3: out << "Construct mat3"; break;
- case EOpConstructMat3x4: out << "Construct mat3x4"; break;
- case EOpConstructMat4x2: out << "Construct mat4x2"; break;
- case EOpConstructMat4x3: out << "Construct mat4x3"; break;
- case EOpConstructMat4: out << "Construct mat4"; break;
- case EOpConstructStruct: out << "Construct structure"; break;
-
- case EOpLessThan: out << "Compare Less Than"; break;
- case EOpGreaterThan: out << "Compare Greater Than"; break;
- case EOpLessThanEqual: out << "Compare Less Than or Equal"; break;
- case EOpGreaterThanEqual: out << "Compare Greater Than or Equal"; break;
- case EOpVectorEqual: out << "Equal"; break;
- case EOpVectorNotEqual: out << "NotEqual"; break;
-
- case EOpMod: out << "mod"; break;
- case EOpModf: out << "modf"; break;
- case EOpPow: out << "pow"; break;
-
- case EOpAtan: out << "arc tangent"; break;
-
- case EOpMin: out << "min"; break;
- case EOpMax: out << "max"; break;
- case EOpClamp: out << "clamp"; break;
- case EOpMix: out << "mix"; break;
- case EOpStep: out << "step"; break;
- case EOpSmoothStep: out << "smoothstep"; break;
-
- case EOpDistance: out << "distance"; break;
- case EOpDot: out << "dot-product"; break;
- case EOpCross: out << "cross-product"; break;
- case EOpFaceForward: out << "face-forward"; break;
- case EOpReflect: out << "reflect"; break;
- case EOpRefract: out << "refract"; break;
- case EOpMul: out << "component-wise multiply"; break;
-
- case EOpOuterProduct: out << "outer product"; break;
-
- case EOpDeclaration: out << "Declaration: "; break;
- case EOpInvariantDeclaration: out << "Invariant Declaration: "; break;
-
- default:
- out.prefix(EPrefixError);
- out << "Bad aggregation op";
- }
-
- if (node->getOp() != EOpSequence && node->getOp() != EOpParameters)
- out << " (" << node->getCompleteString() << ")";
-
- out << "\n";
-
- return true;
-}
-
-bool TOutputTraverser::visitSelection(Visit visit, TIntermSelection *node)
-{
- TInfoSinkBase &out = sink;
-
- OutputTreeText(out, node, mDepth);
-
- out << "Test condition and select";
- out << " (" << node->getCompleteString() << ")\n";
-
- ++mDepth;
-
- OutputTreeText(sink, node, mDepth);
- out << "Condition\n";
- node->getCondition()->traverse(this);
-
- OutputTreeText(sink, node, mDepth);
- if (node->getTrueBlock())
- {
- out << "true case\n";
- node->getTrueBlock()->traverse(this);
- }
- else
- {
- out << "true case is null\n";
- }
-
- if (node->getFalseBlock())
- {
- OutputTreeText(sink, node, mDepth);
- out << "false case\n";
- node->getFalseBlock()->traverse(this);
- }
-
- --mDepth;
-
- return false;
-}
-
-void TOutputTraverser::visitConstantUnion(TIntermConstantUnion *node)
-{
- TInfoSinkBase &out = sink;
-
- size_t size = node->getType().getObjectSize();
-
- for (size_t i = 0; i < size; i++)
- {
- OutputTreeText(out, node, mDepth);
- switch (node->getUnionArrayPointer()[i].getType())
- {
- case EbtBool:
- if (node->getUnionArrayPointer()[i].getBConst())
- out << "true";
- else
- out << "false";
-
- out << " (" << "const bool" << ")";
- out << "\n";
- break;
- case EbtFloat:
- out << node->getUnionArrayPointer()[i].getFConst();
- out << " (const float)\n";
- break;
- case EbtInt:
- out << node->getUnionArrayPointer()[i].getIConst();
- out << " (const int)\n";
- break;
- case EbtUInt:
- out << node->getUnionArrayPointer()[i].getUConst();
- out << " (const uint)\n";
- break;
- default:
- out.message(EPrefixInternalError, node->getLine(), "Unknown constant");
- break;
- }
- }
-}
-
-bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop *node)
-{
- TInfoSinkBase &out = sink;
-
- OutputTreeText(out, node, mDepth);
-
- out << "Loop with condition ";
- if (node->getType() == ELoopDoWhile)
- out << "not ";
- out << "tested first\n";
-
- ++mDepth;
-
- OutputTreeText(sink, node, mDepth);
- if (node->getCondition())
- {
- out << "Loop Condition\n";
- node->getCondition()->traverse(this);
- }
- else
- {
- out << "No loop condition\n";
- }
-
- OutputTreeText(sink, node, mDepth);
- if (node->getBody())
- {
- out << "Loop Body\n";
- node->getBody()->traverse(this);
- }
- else
- {
- out << "No loop body\n";
- }
-
- if (node->getExpression())
- {
- OutputTreeText(sink, node, mDepth);
- out << "Loop Terminal Expression\n";
- node->getExpression()->traverse(this);
- }
-
- --mDepth;
-
- return false;
-}
-
-bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch *node)
-{
- TInfoSinkBase &out = sink;
-
- OutputTreeText(out, node, mDepth);
-
- switch (node->getFlowOp())
- {
- case EOpKill: out << "Branch: Kill"; break;
- case EOpBreak: out << "Branch: Break"; break;
- case EOpContinue: out << "Branch: Continue"; break;
- case EOpReturn: out << "Branch: Return"; break;
- default: out << "Branch: Unknown Branch"; break;
- }
-
- if (node->getExpression())
- {
- out << " with expression\n";
- ++mDepth;
- node->getExpression()->traverse(this);
- --mDepth;
- }
- else
- {
- out << "\n";
- }
-
- return false;
-}
-
-//
-// This function is the one to call externally to start the traversal.
-// Individual functions can be initialized to 0 to skip processing of that
-// type of node. Its children will still be processed.
-//
-void TIntermediate::outputTree(TIntermNode *root, TInfoSinkBase &infoSink)
-{
- TOutputTraverser it(infoSink);
-
- ASSERT(root);
-
- root->traverse(&it);
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/length_limits.h b/src/3rdparty/angle/src/compiler/translator/length_limits.h
index 88634381fa..fcda639d71 100644
--- a/src/3rdparty/angle/src/compiler/translator/length_limits.h
+++ b/src/3rdparty/angle/src/compiler/translator/length_limits.h
@@ -16,6 +16,11 @@
// These constants are factored out from the rest of the headers to
// make it easier to reference them from the compiler sources.
+namespace sh
+{
+
size_t GetGlobalMaxTokenSize(ShShaderSpec spec);
-#endif // COMPILER_TRANSLATOR_LENGTHLIMITS_H_
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_LENGTHLIMITS_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/parseConst.cpp b/src/3rdparty/angle/src/compiler/translator/parseConst.cpp
deleted file mode 100644
index 1897ed151c..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/parseConst.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-//
-// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/ParseContext.h"
-
-//
-// Use this class to carry along data from node to node in
-// the traversal
-//
-class TConstTraverser : public TIntermTraverser
-{
- public:
- TConstTraverser(ConstantUnion *cUnion, bool singleConstParam,
- TOperator constructType, TInfoSink &sink, TType &t)
- : error(false),
- mIndex(0),
- mUnionArray(cUnion),
- mType(t),
- mConstructorType(constructType),
- mSingleConstantParam(singleConstParam),
- mInfoSink(sink),
- mSize(0),
- mIsDiagonalMatrixInit(false),
- mMatrixCols(0),
- mMatrixRows(0)
- {
- }
-
- bool error;
-
- protected:
- void visitSymbol(TIntermSymbol *);
- void visitConstantUnion(TIntermConstantUnion *);
- bool visitBinary(Visit visit, TIntermBinary *);
- bool visitUnary(Visit visit, TIntermUnary *);
- bool visitSelection(Visit visit, TIntermSelection *);
- bool visitAggregate(Visit visit, TIntermAggregate *);
- bool visitLoop(Visit visit, TIntermLoop *);
- bool visitBranch(Visit visit, TIntermBranch *);
-
- size_t mIndex;
- ConstantUnion *mUnionArray;
- TType mType;
- TOperator mConstructorType;
- bool mSingleConstantParam;
- TInfoSink &mInfoSink;
- size_t mSize; // size of the constructor ( 4 for vec4)
- bool mIsDiagonalMatrixInit;
- int mMatrixCols; // columns of the matrix
- int mMatrixRows; // rows of the matrix
-};
-
-//
-// The rest of the file are the traversal functions. The last one
-// is the one that starts the traversal.
-//
-// Return true from interior nodes to have the external traversal
-// continue on to children. If you process children yourself,
-// return false.
-//
-void TConstTraverser::visitSymbol(TIntermSymbol *node)
-{
- mInfoSink.info.message(EPrefixInternalError, node->getLine(),
- "Symbol Node found in constant constructor");
- return;
-}
-
-bool TConstTraverser::visitBinary(Visit visit, TIntermBinary *node)
-{
- TQualifier qualifier = node->getType().getQualifier();
-
- if (qualifier != EvqConst)
- {
- TString buf;
- buf.append("'constructor' : assigning non-constant to ");
- buf.append(mType.getCompleteString());
- mInfoSink.info.message(EPrefixError, node->getLine(), buf.c_str());
- error = true;
- return false;
- }
-
- mInfoSink.info.message(EPrefixInternalError, node->getLine(),
- "Binary Node found in constant constructor");
- return false;
-}
-
-bool TConstTraverser::visitUnary(Visit visit, TIntermUnary *node)
-{
- TString buf;
- buf.append("'constructor' : assigning non-constant to ");
- buf.append(mType.getCompleteString());
- mInfoSink.info.message(EPrefixError, node->getLine(), buf.c_str());
- error = true;
- return false;
-}
-
-bool TConstTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
-{
- if (!node->isConstructor() && node->getOp() != EOpComma)
- {
- TString buf;
- buf.append("'constructor' : assigning non-constant to ");
- buf.append(mType.getCompleteString());
- mInfoSink.info.message(EPrefixError, node->getLine(), buf.c_str());
- error = true;
- return false;
- }
-
- if (node->getSequence()->size() == 0)
- {
- error = true;
- return false;
- }
-
- bool flag = node->getSequence()->size() == 1 &&
- (*node->getSequence())[0]->getAsTyped()->getAsConstantUnion();
- if (flag)
- {
- mSingleConstantParam = true;
- mConstructorType = node->getOp();
- mSize = node->getType().getObjectSize();
-
- if (node->getType().isMatrix())
- {
- mIsDiagonalMatrixInit = true;
- mMatrixCols = node->getType().getCols();
- mMatrixRows = node->getType().getRows();
- }
- }
-
- for (TIntermSequence::iterator p = node->getSequence()->begin();
- p != node->getSequence()->end(); p++)
- {
- if (node->getOp() == EOpComma)
- mIndex = 0;
- (*p)->traverse(this);
- }
- if (flag)
- {
- mSingleConstantParam = false;
- mConstructorType = EOpNull;
- mSize = 0;
- mIsDiagonalMatrixInit = false;
- mMatrixCols = 0;
- mMatrixRows = 0;
- }
- return false;
-}
-
-bool TConstTraverser::visitSelection(Visit visit, TIntermSelection *node)
-{
- mInfoSink.info.message(EPrefixInternalError, node->getLine(),
- "Selection Node found in constant constructor");
- error = true;
- return false;
-}
-
-void TConstTraverser::visitConstantUnion(TIntermConstantUnion *node)
-{
- if (!node->getUnionArrayPointer())
- {
- // The constant was not initialized, this should already have been logged
- ASSERT(mInfoSink.info.size() != 0);
- return;
- }
-
- ConstantUnion *leftUnionArray = mUnionArray;
- size_t instanceSize = mType.getObjectSize();
- TBasicType basicType = mType.getBasicType();
-
- if (mIndex >= instanceSize)
- return;
-
- if (!mSingleConstantParam)
- {
- size_t objectSize = node->getType().getObjectSize();
- ConstantUnion *rightUnionArray = node->getUnionArrayPointer();
- for (size_t i=0; i < objectSize; i++)
- {
- if (mIndex >= instanceSize)
- return;
- leftUnionArray[mIndex].cast(basicType, rightUnionArray[i]);
- mIndex++;
- }
- }
- else
- {
- size_t totalSize = mIndex + mSize;
- ConstantUnion *rightUnionArray = node->getUnionArrayPointer();
- if (!mIsDiagonalMatrixInit)
- {
- int count = 0;
- for (size_t i = mIndex; i < totalSize; i++)
- {
- if (i >= instanceSize)
- return;
- leftUnionArray[i].cast(basicType, rightUnionArray[count]);
- mIndex++;
- if (node->getType().getObjectSize() > 1)
- count++;
- }
- }
- else
- {
- // for matrix diagonal constructors from a single scalar
- for (int i = 0, col = 0; col < mMatrixCols; col++)
- {
- for (int row = 0; row < mMatrixRows; row++, i++)
- {
- if (col == row)
- {
- leftUnionArray[i].cast(basicType, rightUnionArray[0]);
- }
- else
- {
- leftUnionArray[i].setFConst(0.0f);
- }
- mIndex++;
- }
- }
- }
- }
-}
-
-bool TConstTraverser::visitLoop(Visit visit, TIntermLoop *node)
-{
- mInfoSink.info.message(EPrefixInternalError, node->getLine(),
- "Loop Node found in constant constructor");
- error = true;
- return false;
-}
-
-bool TConstTraverser::visitBranch(Visit visit, TIntermBranch *node)
-{
- mInfoSink.info.message(EPrefixInternalError, node->getLine(),
- "Branch Node found in constant constructor");
- error = true;
- return false;
-}
-
-//
-// This function is the one to call externally to start the traversal.
-// Individual functions can be initialized to 0 to skip processing of that
-// type of node. It's children will still be processed.
-//
-bool TIntermediate::parseConstTree(
- const TSourceLoc &line, TIntermNode *root, ConstantUnion *unionArray,
- TOperator constructorType, TType t, bool singleConstantParam)
-{
- if (root == 0)
- return false;
-
- TConstTraverser it(unionArray, singleConstantParam, constructorType,
- mInfoSink, t);
-
- root->traverse(&it);
- if (it.error)
- return true;
- else
- return false;
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp b/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp
deleted file mode 100644
index 790974a2bf..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/InfoSink.h"
-#include "compiler/translator/ParseContext.h"
-#include "compiler/translator/depgraph/DependencyGraphOutput.h"
-#include "compiler/translator/timing/RestrictFragmentShaderTiming.h"
-
-RestrictFragmentShaderTiming::RestrictFragmentShaderTiming(TInfoSinkBase& sink)
- : mSink(sink)
- , mNumErrors(0)
-{
- // Sampling ops found only in fragment shaders.
- mSamplingOps.insert("texture2D(s21;vf2;f1;");
- mSamplingOps.insert("texture2DProj(s21;vf3;f1;");
- mSamplingOps.insert("texture2DProj(s21;vf4;f1;");
- mSamplingOps.insert("textureCube(sC1;vf3;f1;");
- // Sampling ops found in both vertex and fragment shaders.
- mSamplingOps.insert("texture2D(s21;vf2;");
- mSamplingOps.insert("texture2DProj(s21;vf3;");
- mSamplingOps.insert("texture2DProj(s21;vf4;");
- mSamplingOps.insert("textureCube(sC1;vf3;");
- // Sampling ops provided by OES_EGL_image_external.
- mSamplingOps.insert("texture2D(1;vf2;");
- mSamplingOps.insert("texture2DProj(1;vf3;");
- mSamplingOps.insert("texture2DProj(1;vf4;");
- // Sampling ops provided by ARB_texture_rectangle.
- mSamplingOps.insert("texture2DRect(1;vf2;");
- mSamplingOps.insert("texture2DRectProj(1;vf3;");
- mSamplingOps.insert("texture2DRectProj(1;vf4;");
- // Sampling ops provided by EXT_shader_texture_lod.
- mSamplingOps.insert("texture2DLodEXT(1;vf2;f1;");
- mSamplingOps.insert("texture2DProjLodEXT(1;vf3;f1;");
- mSamplingOps.insert("texture2DProjLodEXT(1;vf4;f1;");
- mSamplingOps.insert("textureCubeLodEXT(1;vf4;f1;");
- mSamplingOps.insert("texture2DGradEXT(1;vf2;vf2;vf2;");
- mSamplingOps.insert("texture2DProjGradEXT(1;vf3;vf2;vf2;");
- mSamplingOps.insert("texture2DProjGradEXT(1;vf4;vf2;vf2;");
- mSamplingOps.insert("textureCubeGradEXT(1;vf3;vf3;vf3;");
-}
-
-// FIXME(mvujovic): We do not know if the execution time of built-in operations like sin, pow, etc.
-// can vary based on the value of the input arguments. If so, we should restrict those as well.
-void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& graph)
-{
- mNumErrors = 0;
-
- // FIXME(mvujovic): The dependency graph does not support user defined function calls right now,
- // so we generate errors for them.
- validateUserDefinedFunctionCallUsage(graph);
-
- // Starting from each sampler, traverse the dependency graph and generate an error each time we
- // hit a node where sampler dependent values are not allowed.
- for (auto samplerSymbol : graph.samplerSymbols())
- {
- clearVisited();
- samplerSymbol->traverse(this);
- }
-}
-
-void RestrictFragmentShaderTiming::validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph)
-{
- for (const auto* functionCall : graph.userDefinedFunctionCalls())
- {
- beginError(functionCall->getIntermFunctionCall());
- mSink << "A call to a user defined function is not permitted.\n";
- }
-}
-
-void RestrictFragmentShaderTiming::beginError(const TIntermNode* node)
-{
- ++mNumErrors;
- mSink.prefix(EPrefixError);
- mSink.location(node->getLine());
-}
-
-bool RestrictFragmentShaderTiming::isSamplingOp(const TIntermAggregate* intermFunctionCall) const
-{
- return !intermFunctionCall->isUserDefined() &&
- mSamplingOps.find(intermFunctionCall->getName()) != mSamplingOps.end();
-}
-
-void RestrictFragmentShaderTiming::visitArgument(TGraphArgument* parameter)
-{
- // Texture cache access time might leak sensitive information.
- // Thus, we restrict sampler dependent values from affecting the coordinate or LOD bias of a
- // sampling operation.
- if (isSamplingOp(parameter->getIntermFunctionCall())) {
- switch (parameter->getArgumentNumber()) {
- case 1:
- // Second argument (coord)
- beginError(parameter->getIntermFunctionCall());
- mSink << "An expression dependent on a sampler is not permitted to be the"
- << " coordinate argument of a sampling operation.\n";
- break;
- case 2:
- // Third argument (bias)
- beginError(parameter->getIntermFunctionCall());
- mSink << "An expression dependent on a sampler is not permitted to be the"
- << " bias argument of a sampling operation.\n";
- break;
- default:
- // First argument (sampler)
- break;
- }
- }
-}
-
-void RestrictFragmentShaderTiming::visitSelection(TGraphSelection* selection)
-{
- beginError(selection->getIntermSelection());
- mSink << "An expression dependent on a sampler is not permitted in a conditional statement.\n";
-}
-
-void RestrictFragmentShaderTiming::visitLoop(TGraphLoop* loop)
-{
- beginError(loop->getIntermLoop());
- mSink << "An expression dependent on a sampler is not permitted in a loop condition.\n";
-}
-
-void RestrictFragmentShaderTiming::visitLogicalOp(TGraphLogicalOp* logicalOp)
-{
- beginError(logicalOp->getIntermLogicalOp());
- mSink << "An expression dependent on a sampler is not permitted on the left hand side of a logical "
- << logicalOp->getOpString()
- << " operator.\n";
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h b/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h
deleted file mode 100644
index b8c7e82956..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_TIMING_RESTRICTFRAGMENTSHADERTIMING_H_
-#define COMPILER_TRANSLATOR_TIMING_RESTRICTFRAGMENTSHADERTIMING_H_
-
-#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/depgraph/DependencyGraph.h"
-
-class TInfoSinkBase;
-
-class RestrictFragmentShaderTiming : TDependencyGraphTraverser
-{
- public:
- RestrictFragmentShaderTiming(TInfoSinkBase &sink);
- void enforceRestrictions(const TDependencyGraph &graph);
- int numErrors() const { return mNumErrors; }
-
- void visitArgument(TGraphArgument *parameter) override;
- void visitSelection(TGraphSelection *selection) override;
- void visitLoop(TGraphLoop *loop) override;
- void visitLogicalOp(TGraphLogicalOp *logicalOp) override;
-
- private:
- void beginError(const TIntermNode *node);
- void validateUserDefinedFunctionCallUsage(const TDependencyGraph &graph);
- bool isSamplingOp(const TIntermAggregate *intermFunctionCall) const;
-
- TInfoSinkBase &mSink;
- int mNumErrors;
-
- typedef std::set<TString> StringSet;
- StringSet mSamplingOps;
-};
-
-#endif // COMPILER_TRANSLATOR_TIMING_RESTRICTFRAGMENTSHADERTIMING_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp b/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp
deleted file mode 100644
index 7c1208a298..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "compiler/translator/timing/RestrictVertexShaderTiming.h"
-
-void RestrictVertexShaderTiming::visitSymbol(TIntermSymbol* node)
-{
- if (IsSampler(node->getBasicType())) {
- ++mNumErrors;
- mSink.message(EPrefixError,
- node->getLine(),
- "Samplers are not permitted in vertex shaders.\n");
- }
-}
diff --git a/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h b/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h
deleted file mode 100644
index 23a8217722..0000000000
--- a/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPILER_TRANSLATOR_TIMING_RESTRICTVERTEXSHADERTIMING_H_
-#define COMPILER_TRANSLATOR_TIMING_RESTRICTVERTEXSHADERTIMING_H_
-
-#include "compiler/translator/IntermNode.h"
-#include "compiler/translator/InfoSink.h"
-
-class TInfoSinkBase;
-
-class RestrictVertexShaderTiming : public TIntermTraverser {
-public:
- RestrictVertexShaderTiming(TInfoSinkBase& sink)
- : TIntermTraverser(true, false, false)
- , mSink(sink)
- , mNumErrors(0) {}
-
- void enforceRestrictions(TIntermNode* root) { root->traverse(this); }
- int numErrors() { return mNumErrors; }
-
- void visitSymbol(TIntermSymbol *) override;
-
-private:
- TInfoSinkBase& mSink;
- int mNumErrors;
-};
-
-#endif // COMPILER_TRANSLATOR_TIMING_RESTRICTVERTEXSHADERTIMING_H_
diff --git a/src/3rdparty/angle/src/compiler/translator/util.cpp b/src/3rdparty/angle/src/compiler/translator/util.cpp
index 0131137206..9738370c47 100644
--- a/src/3rdparty/angle/src/compiler/translator/util.cpp
+++ b/src/3rdparty/angle/src/compiler/translator/util.cpp
@@ -8,17 +8,9 @@
#include <limits>
+#include "common/utilities.h"
#include "compiler/preprocessor/numeric_lex.h"
#include "compiler/translator/SymbolTable.h"
-#include "common/utilities.h"
-
-bool strtof_clamp(const std::string &str, float *value)
-{
- bool success = pp::numeric_lex_float(str, value);
- if (!success)
- *value = std::numeric_limits<float>::max();
- return success;
-}
bool atoi_clamp(const char *str, unsigned int *value)
{
@@ -31,135 +23,392 @@ bool atoi_clamp(const char *str, unsigned int *value)
namespace sh
{
-GLenum GLVariableType(const TType &type)
+namespace
{
- if (type.getBasicType() == EbtFloat)
+
+bool IsInterpolationIn(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqSmoothIn:
+ case EvqFlatIn:
+ case EvqCentroidIn:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // anonymous namespace
+
+float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
+{
+ // Parses a decimal string using scientific notation into a floating point number.
+ // Out-of-range values are converted to infinity. Values that are too small to be
+ // represented are converted to zero.
+
+ // The mantissa in decimal scientific notation. The magnitude of the mantissa integer does not
+ // matter.
+ unsigned int decimalMantissa = 0;
+ size_t i = 0;
+ bool decimalPointSeen = false;
+ bool nonZeroSeenInMantissa = false;
+
+ // The exponent offset reflects the position of the decimal point.
+ int exponentOffset = -1;
+ while (i < str.length())
{
- if (type.isScalar())
+ const char c = str[i];
+ if (c == 'e' || c == 'E')
{
- return GL_FLOAT;
+ break;
+ }
+ if (c == '.')
+ {
+ decimalPointSeen = true;
+ ++i;
+ continue;
+ }
+
+ unsigned int digit = static_cast<unsigned int>(c - '0');
+ ASSERT(digit < 10u);
+ if (digit != 0u)
+ {
+ nonZeroSeenInMantissa = true;
}
- else if (type.isVector())
+ if (nonZeroSeenInMantissa)
+ {
+ // Add bits to the mantissa until space runs out in 32-bit int. This should be
+ // enough precision to make the resulting binary mantissa accurate to 1 ULP.
+ if (decimalMantissa <= (std::numeric_limits<unsigned int>::max() - 9u) / 10u)
+ {
+ decimalMantissa = decimalMantissa * 10u + digit;
+ }
+ if (!decimalPointSeen)
+ {
+ ++exponentOffset;
+ }
+ }
+ else if (decimalPointSeen)
+ {
+ --exponentOffset;
+ }
+ ++i;
+ }
+ if (decimalMantissa == 0)
+ {
+ return 0.0f;
+ }
+ int exponent = 0;
+ if (i < str.length())
+ {
+ ASSERT(str[i] == 'e' || str[i] == 'E');
+ ++i;
+ bool exponentOutOfRange = false;
+ bool negativeExponent = false;
+ if (str[i] == '-')
+ {
+ negativeExponent = true;
+ ++i;
+ }
+ else if (str[i] == '+')
+ {
+ ++i;
+ }
+ while (i < str.length())
+ {
+ const char c = str[i];
+ unsigned int digit = static_cast<unsigned int>(c - '0');
+ ASSERT(digit < 10u);
+ if (exponent <= (std::numeric_limits<int>::max() - 9) / 10)
+ {
+ exponent = exponent * 10 + digit;
+ }
+ else
+ {
+ exponentOutOfRange = true;
+ }
+ ++i;
+ }
+ if (negativeExponent)
+ {
+ exponent = -exponent;
+ }
+ if (exponentOutOfRange)
+ {
+ if (negativeExponent)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ return std::numeric_limits<float>::infinity();
+ }
+ }
+ }
+ // Do the calculation in 64-bit to avoid overflow.
+ long long exponentLong =
+ static_cast<long long>(exponent) + static_cast<long long>(exponentOffset);
+ if (exponentLong > std::numeric_limits<float>::max_exponent10)
+ {
+ return std::numeric_limits<float>::infinity();
+ }
+ else if (exponentLong < std::numeric_limits<float>::min_exponent10)
+ {
+ return 0.0f;
+ }
+ // The exponent is in range, so we need to actually evaluate the float.
+ exponent = static_cast<int>(exponentLong);
+ double value = decimalMantissa;
+
+ // Calculate the exponent offset to normalize the mantissa.
+ int normalizationExponentOffset = 0;
+ while (decimalMantissa >= 10u)
+ {
+ --normalizationExponentOffset;
+ decimalMantissa /= 10u;
+ }
+ // Apply the exponent.
+ value *= std::pow(10.0, static_cast<double>(exponent + normalizationExponentOffset));
+ if (value > static_cast<double>(std::numeric_limits<float>::max()))
+ {
+ return std::numeric_limits<float>::infinity();
+ }
+ if (value < static_cast<double>(std::numeric_limits<float>::min()))
+ {
+ return 0.0f;
+ }
+ return static_cast<float>(value);
+}
+
+bool strtof_clamp(const std::string &str, float *value)
+{
+ // Try the standard float parsing path first.
+ bool success = pp::numeric_lex_float(str, value);
+
+ // If the standard path doesn't succeed, take the path that can handle the following corner
+ // cases:
+ // 1. The decimal mantissa is very small but the exponent is very large, putting the resulting
+ // number inside the float range.
+ // 2. The decimal mantissa is very large but the exponent is very small, putting the resulting
+ // number inside the float range.
+ // 3. The value is out-of-range and should be evaluated as infinity.
+ // 4. The value is too small and should be evaluated as zero.
+ // See ESSL 3.00.6 section 4.1.4 for the relevant specification.
+ if (!success)
+ *value = NumericLexFloat32OutOfRangeToInfinity(str);
+ return !gl::isInf(*value);
+}
+
+GLenum GLVariableType(const TType &type)
+{
+ if (type.getBasicType() == EbtFloat)
+ {
+ if (type.isVector())
{
switch (type.getNominalSize())
{
- case 2: return GL_FLOAT_VEC2;
- case 3: return GL_FLOAT_VEC3;
- case 4: return GL_FLOAT_VEC4;
- default: UNREACHABLE();
+ case 2:
+ return GL_FLOAT_VEC2;
+ case 3:
+ return GL_FLOAT_VEC3;
+ case 4:
+ return GL_FLOAT_VEC4;
+ default:
+ UNREACHABLE();
}
}
else if (type.isMatrix())
{
switch (type.getCols())
{
- case 2:
- switch (type.getRows())
- {
- case 2: return GL_FLOAT_MAT2;
- case 3: return GL_FLOAT_MAT2x3;
- case 4: return GL_FLOAT_MAT2x4;
- default: UNREACHABLE();
- }
-
- case 3:
- switch (type.getRows())
- {
- case 2: return GL_FLOAT_MAT3x2;
- case 3: return GL_FLOAT_MAT3;
- case 4: return GL_FLOAT_MAT3x4;
- default: UNREACHABLE();
- }
-
- case 4:
- switch (type.getRows())
- {
- case 2: return GL_FLOAT_MAT4x2;
- case 3: return GL_FLOAT_MAT4x3;
- case 4: return GL_FLOAT_MAT4;
- default: UNREACHABLE();
- }
-
- default: UNREACHABLE();
+ case 2:
+ switch (type.getRows())
+ {
+ case 2:
+ return GL_FLOAT_MAT2;
+ case 3:
+ return GL_FLOAT_MAT2x3;
+ case 4:
+ return GL_FLOAT_MAT2x4;
+ default:
+ UNREACHABLE();
+ }
+
+ case 3:
+ switch (type.getRows())
+ {
+ case 2:
+ return GL_FLOAT_MAT3x2;
+ case 3:
+ return GL_FLOAT_MAT3;
+ case 4:
+ return GL_FLOAT_MAT3x4;
+ default:
+ UNREACHABLE();
+ }
+
+ case 4:
+ switch (type.getRows())
+ {
+ case 2:
+ return GL_FLOAT_MAT4x2;
+ case 3:
+ return GL_FLOAT_MAT4x3;
+ case 4:
+ return GL_FLOAT_MAT4;
+ default:
+ UNREACHABLE();
+ }
+
+ default:
+ UNREACHABLE();
}
}
- else UNREACHABLE();
+ else
+ {
+ return GL_FLOAT;
+ }
}
else if (type.getBasicType() == EbtInt)
{
- if (type.isScalar())
- {
- return GL_INT;
- }
- else if (type.isVector())
+ if (type.isVector())
{
switch (type.getNominalSize())
{
- case 2: return GL_INT_VEC2;
- case 3: return GL_INT_VEC3;
- case 4: return GL_INT_VEC4;
- default: UNREACHABLE();
+ case 2:
+ return GL_INT_VEC2;
+ case 3:
+ return GL_INT_VEC3;
+ case 4:
+ return GL_INT_VEC4;
+ default:
+ UNREACHABLE();
}
}
- else UNREACHABLE();
+ else
+ {
+ ASSERT(!type.isMatrix());
+ return GL_INT;
+ }
}
else if (type.getBasicType() == EbtUInt)
{
- if (type.isScalar())
- {
- return GL_UNSIGNED_INT;
- }
- else if (type.isVector())
+ if (type.isVector())
{
switch (type.getNominalSize())
{
- case 2: return GL_UNSIGNED_INT_VEC2;
- case 3: return GL_UNSIGNED_INT_VEC3;
- case 4: return GL_UNSIGNED_INT_VEC4;
- default: UNREACHABLE();
+ case 2:
+ return GL_UNSIGNED_INT_VEC2;
+ case 3:
+ return GL_UNSIGNED_INT_VEC3;
+ case 4:
+ return GL_UNSIGNED_INT_VEC4;
+ default:
+ UNREACHABLE();
}
}
- else UNREACHABLE();
+ else
+ {
+ ASSERT(!type.isMatrix());
+ return GL_UNSIGNED_INT;
+ }
}
else if (type.getBasicType() == EbtBool)
{
- if (type.isScalar())
- {
- return GL_BOOL;
- }
- else if (type.isVector())
+ if (type.isVector())
{
switch (type.getNominalSize())
{
- case 2: return GL_BOOL_VEC2;
- case 3: return GL_BOOL_VEC3;
- case 4: return GL_BOOL_VEC4;
- default: UNREACHABLE();
+ case 2:
+ return GL_BOOL_VEC2;
+ case 3:
+ return GL_BOOL_VEC3;
+ case 4:
+ return GL_BOOL_VEC4;
+ default:
+ UNREACHABLE();
}
}
- else UNREACHABLE();
+ else
+ {
+ ASSERT(!type.isMatrix());
+ return GL_BOOL;
+ }
}
switch (type.getBasicType())
{
- case EbtSampler2D: return GL_SAMPLER_2D;
- case EbtSampler3D: return GL_SAMPLER_3D;
- case EbtSamplerCube: return GL_SAMPLER_CUBE;
- case EbtSamplerExternalOES: return GL_SAMPLER_EXTERNAL_OES;
- case EbtSampler2DRect: return GL_SAMPLER_2D_RECT_ARB;
- case EbtSampler2DArray: return GL_SAMPLER_2D_ARRAY;
- case EbtISampler2D: return GL_INT_SAMPLER_2D;
- case EbtISampler3D: return GL_INT_SAMPLER_3D;
- case EbtISamplerCube: return GL_INT_SAMPLER_CUBE;
- case EbtISampler2DArray: return GL_INT_SAMPLER_2D_ARRAY;
- case EbtUSampler2D: return GL_UNSIGNED_INT_SAMPLER_2D;
- case EbtUSampler3D: return GL_UNSIGNED_INT_SAMPLER_3D;
- case EbtUSamplerCube: return GL_UNSIGNED_INT_SAMPLER_CUBE;
- case EbtUSampler2DArray: return GL_UNSIGNED_INT_SAMPLER_2D_ARRAY;
- case EbtSampler2DShadow: return GL_SAMPLER_2D_SHADOW;
- case EbtSamplerCubeShadow: return GL_SAMPLER_CUBE_SHADOW;
- case EbtSampler2DArrayShadow: return GL_SAMPLER_2D_ARRAY_SHADOW;
- default: UNREACHABLE();
+ case EbtSampler2D:
+ return GL_SAMPLER_2D;
+ case EbtSampler3D:
+ return GL_SAMPLER_3D;
+ case EbtSamplerCube:
+ return GL_SAMPLER_CUBE;
+ case EbtSamplerExternalOES:
+ return GL_SAMPLER_EXTERNAL_OES;
+ case EbtSamplerExternal2DY2YEXT:
+ return GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT;
+ case EbtSampler2DRect:
+ return GL_SAMPLER_2D_RECT_ANGLE;
+ case EbtSampler2DArray:
+ return GL_SAMPLER_2D_ARRAY;
+ case EbtSampler2DMS:
+ return GL_SAMPLER_2D_MULTISAMPLE;
+ case EbtISampler2D:
+ return GL_INT_SAMPLER_2D;
+ case EbtISampler3D:
+ return GL_INT_SAMPLER_3D;
+ case EbtISamplerCube:
+ return GL_INT_SAMPLER_CUBE;
+ case EbtISampler2DArray:
+ return GL_INT_SAMPLER_2D_ARRAY;
+ case EbtISampler2DMS:
+ return GL_INT_SAMPLER_2D_MULTISAMPLE;
+ case EbtUSampler2D:
+ return GL_UNSIGNED_INT_SAMPLER_2D;
+ case EbtUSampler3D:
+ return GL_UNSIGNED_INT_SAMPLER_3D;
+ case EbtUSamplerCube:
+ return GL_UNSIGNED_INT_SAMPLER_CUBE;
+ case EbtUSampler2DArray:
+ return GL_UNSIGNED_INT_SAMPLER_2D_ARRAY;
+ case EbtUSampler2DMS:
+ return GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE;
+ case EbtSampler2DShadow:
+ return GL_SAMPLER_2D_SHADOW;
+ case EbtSamplerCubeShadow:
+ return GL_SAMPLER_CUBE_SHADOW;
+ case EbtSampler2DArrayShadow:
+ return GL_SAMPLER_2D_ARRAY_SHADOW;
+ case EbtImage2D:
+ return GL_IMAGE_2D;
+ case EbtIImage2D:
+ return GL_INT_IMAGE_2D;
+ case EbtUImage2D:
+ return GL_UNSIGNED_INT_IMAGE_2D;
+ case EbtImage2DArray:
+ return GL_IMAGE_2D_ARRAY;
+ case EbtIImage2DArray:
+ return GL_INT_IMAGE_2D_ARRAY;
+ case EbtUImage2DArray:
+ return GL_UNSIGNED_INT_IMAGE_2D_ARRAY;
+ case EbtImage3D:
+ return GL_IMAGE_3D;
+ case EbtIImage3D:
+ return GL_INT_IMAGE_3D;
+ case EbtUImage3D:
+ return GL_UNSIGNED_INT_IMAGE_3D;
+ case EbtImageCube:
+ return GL_IMAGE_CUBE;
+ case EbtIImageCube:
+ return GL_INT_IMAGE_CUBE;
+ case EbtUImageCube:
+ return GL_UNSIGNED_INT_IMAGE_CUBE;
+ case EbtAtomicCounter:
+ return GL_UNSIGNED_INT_ATOMIC_COUNTER;
+ default:
+ UNREACHABLE();
}
return GL_NONE;
@@ -171,32 +420,32 @@ GLenum GLVariablePrecision(const TType &type)
{
switch (type.getPrecision())
{
- case EbpHigh:
- return GL_HIGH_FLOAT;
- case EbpMedium:
- return GL_MEDIUM_FLOAT;
- case EbpLow:
- return GL_LOW_FLOAT;
- case EbpUndefined:
- // Should be defined as the default precision by the parser
- default:
- UNREACHABLE();
+ case EbpHigh:
+ return GL_HIGH_FLOAT;
+ case EbpMedium:
+ return GL_MEDIUM_FLOAT;
+ case EbpLow:
+ return GL_LOW_FLOAT;
+ case EbpUndefined:
+ // Should be defined as the default precision by the parser
+ default:
+ UNREACHABLE();
}
}
else if (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt)
{
switch (type.getPrecision())
{
- case EbpHigh:
- return GL_HIGH_INT;
- case EbpMedium:
- return GL_MEDIUM_INT;
- case EbpLow:
- return GL_LOW_INT;
- case EbpUndefined:
- // Should be defined as the default precision by the parser
- default:
- UNREACHABLE();
+ case EbpHigh:
+ return GL_HIGH_INT;
+ case EbpMedium:
+ return GL_MEDIUM_INT;
+ case EbpLow:
+ return GL_LOW_INT;
+ case EbpUndefined:
+ // Should be defined as the default precision by the parser
+ default:
+ UNREACHABLE();
}
}
@@ -206,26 +455,46 @@ GLenum GLVariablePrecision(const TType &type)
TString ArrayString(const TType &type)
{
+ TStringStream arrayString;
if (!type.isArray())
+ return arrayString.str();
+
+ const TVector<unsigned int> &arraySizes = *type.getArraySizes();
+ for (auto arraySizeIter = arraySizes.rbegin(); arraySizeIter != arraySizes.rend();
+ ++arraySizeIter)
{
- return "";
+ arrayString << "[";
+ if (*arraySizeIter > 0)
+ {
+ arrayString << (*arraySizeIter);
+ }
+ arrayString << "]";
}
+ return arrayString.str();
+}
- return "[" + str(type.getArraySize()) + "]";
+TString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap)
+{
+ if (type.getBasicType() == EbtStruct)
+ return HashName(TName(type.getStruct()->name()), hashFunction, nameMap);
+ else
+ return type.getBuiltInTypeNameString();
}
bool IsVaryingOut(TQualifier qualifier)
{
switch (qualifier)
{
- case EvqVaryingOut:
- case EvqSmoothOut:
- case EvqFlatOut:
- case EvqCentroidOut:
- case EvqVertexOut:
- return true;
-
- default: break;
+ case EvqVaryingOut:
+ case EvqSmoothOut:
+ case EvqFlatOut:
+ case EvqCentroidOut:
+ case EvqVertexOut:
+ case EvqGeometryOut:
+ return true;
+
+ default:
+ break;
}
return false;
@@ -235,14 +504,16 @@ bool IsVaryingIn(TQualifier qualifier)
{
switch (qualifier)
{
- case EvqVaryingIn:
- case EvqSmoothIn:
- case EvqFlatIn:
- case EvqCentroidIn:
- case EvqFragmentIn:
- return true;
-
- default: break;
+ case EvqVaryingIn:
+ case EvqSmoothIn:
+ case EvqFlatIn:
+ case EvqCentroidIn:
+ case EvqFragmentIn:
+ case EvqGeometryIn:
+ return true;
+
+ default:
+ break;
}
return false;
@@ -253,108 +524,191 @@ bool IsVarying(TQualifier qualifier)
return IsVaryingIn(qualifier) || IsVaryingOut(qualifier);
}
+bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier)
+{
+ return (qualifier == EvqGeometryIn) ||
+ ((shaderType == GL_GEOMETRY_SHADER_OES) && IsInterpolationIn(qualifier));
+}
+
InterpolationType GetInterpolationType(TQualifier qualifier)
{
switch (qualifier)
{
- case EvqFlatIn:
- case EvqFlatOut:
- return INTERPOLATION_FLAT;
-
- case EvqSmoothIn:
- case EvqSmoothOut:
- case EvqVertexOut:
- case EvqFragmentIn:
- case EvqVaryingIn:
- case EvqVaryingOut:
- return INTERPOLATION_SMOOTH;
-
- case EvqCentroidIn:
- case EvqCentroidOut:
- return INTERPOLATION_CENTROID;
-
- default: UNREACHABLE();
- return INTERPOLATION_SMOOTH;
+ case EvqFlatIn:
+ case EvqFlatOut:
+ return INTERPOLATION_FLAT;
+
+ case EvqSmoothIn:
+ case EvqSmoothOut:
+ case EvqVertexOut:
+ case EvqFragmentIn:
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ case EvqGeometryIn:
+ case EvqGeometryOut:
+ return INTERPOLATION_SMOOTH;
+
+ case EvqCentroidIn:
+ case EvqCentroidOut:
+ return INTERPOLATION_CENTROID;
+
+ default:
+ UNREACHABLE();
+ return INTERPOLATION_SMOOTH;
+ }
+}
+
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var)
+{
+ switch (var.type)
+ {
+ case GL_BOOL:
+ return TType(EbtBool);
+ case GL_BOOL_VEC2:
+ return TType(EbtBool, 2);
+ case GL_BOOL_VEC3:
+ return TType(EbtBool, 3);
+ case GL_BOOL_VEC4:
+ return TType(EbtBool, 4);
+ case GL_FLOAT:
+ return TType(EbtFloat);
+ case GL_FLOAT_VEC2:
+ return TType(EbtFloat, 2);
+ case GL_FLOAT_VEC3:
+ return TType(EbtFloat, 3);
+ case GL_FLOAT_VEC4:
+ return TType(EbtFloat, 4);
+ case GL_FLOAT_MAT2:
+ return TType(EbtFloat, 2, 2);
+ case GL_FLOAT_MAT3:
+ return TType(EbtFloat, 3, 3);
+ case GL_FLOAT_MAT4:
+ return TType(EbtFloat, 4, 4);
+ case GL_FLOAT_MAT2x3:
+ return TType(EbtFloat, 2, 3);
+ case GL_FLOAT_MAT2x4:
+ return TType(EbtFloat, 2, 4);
+ case GL_FLOAT_MAT3x2:
+ return TType(EbtFloat, 3, 2);
+ case GL_FLOAT_MAT3x4:
+ return TType(EbtFloat, 3, 4);
+ case GL_FLOAT_MAT4x2:
+ return TType(EbtFloat, 4, 2);
+ case GL_FLOAT_MAT4x3:
+ return TType(EbtFloat, 4, 3);
+ case GL_INT:
+ return TType(EbtInt);
+ case GL_INT_VEC2:
+ return TType(EbtInt, 2);
+ case GL_INT_VEC3:
+ return TType(EbtInt, 3);
+ case GL_INT_VEC4:
+ return TType(EbtInt, 4);
+ case GL_UNSIGNED_INT:
+ return TType(EbtUInt);
+ case GL_UNSIGNED_INT_VEC2:
+ return TType(EbtUInt, 2);
+ case GL_UNSIGNED_INT_VEC3:
+ return TType(EbtUInt, 3);
+ case GL_UNSIGNED_INT_VEC4:
+ return TType(EbtUInt, 4);
+ default:
+ UNREACHABLE();
+ return TType();
}
}
-GetVariableTraverser::GetVariableTraverser(const TSymbolTable &symbolTable)
- : mSymbolTable(symbolTable)
+// GLSL ES 1.0.17 4.6.1 The Invariant Qualifier
+bool CanBeInvariantESSL1(TQualifier qualifier)
{
+ return IsVaryingIn(qualifier) || IsVaryingOut(qualifier) ||
+ IsBuiltinOutputVariable(qualifier) ||
+ (IsBuiltinFragmentInputVariable(qualifier) && qualifier != EvqFrontFacing);
}
-template void GetVariableTraverser::setTypeSpecificInfo(
- const TType &type, const TString& name, InterfaceBlockField *variable);
-template void GetVariableTraverser::setTypeSpecificInfo(
- const TType &type, const TString& name, ShaderVariable *variable);
-template void GetVariableTraverser::setTypeSpecificInfo(
- const TType &type, const TString& name, Uniform *variable);
+// GLSL ES 3.00 Revision 6, 4.6.1 The Invariant Qualifier
+// GLSL ES 3.10 Revision 4, 4.8.1 The Invariant Qualifier
+bool CanBeInvariantESSL3OrGreater(TQualifier qualifier)
+{
+ return IsVaryingOut(qualifier) || qualifier == EvqFragmentOut ||
+ IsBuiltinOutputVariable(qualifier);
+}
-template<>
-void GetVariableTraverser::setTypeSpecificInfo(
- const TType &type, const TString& name, Varying *variable)
+bool IsBuiltinOutputVariable(TQualifier qualifier)
{
- ASSERT(variable);
- switch (type.getQualifier())
+ switch (qualifier)
{
- case EvqVaryingIn:
- case EvqVaryingOut:
- case EvqVertexOut:
- case EvqSmoothOut:
- case EvqFlatOut:
- case EvqCentroidOut:
- if (mSymbolTable.isVaryingInvariant(std::string(name.c_str())) || type.isInvariant())
- {
- variable->isInvariant = true;
- }
- break;
- default:
- break;
+ case EvqPosition:
+ case EvqPointSize:
+ case EvqFragDepth:
+ case EvqFragDepthEXT:
+ case EvqFragColor:
+ case EvqSecondaryFragColorEXT:
+ case EvqFragData:
+ case EvqSecondaryFragDataEXT:
+ return true;
+ default:
+ break;
}
-
- variable->interpolation = GetInterpolationType(type.getQualifier());
+ return false;
}
-template <typename VarT>
-void GetVariableTraverser::traverse(const TType &type,
- const TString &name,
- std::vector<VarT> *output)
+bool IsBuiltinFragmentInputVariable(TQualifier qualifier)
{
- const TStructure *structure = type.getStruct();
+ switch (qualifier)
+ {
+ case EvqFragCoord:
+ case EvqPointCoord:
+ case EvqFrontFacing:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
- VarT variable;
- variable.name = name.c_str();
- variable.arraySize = static_cast<unsigned int>(type.getArraySize());
+bool IsOutputESSL(ShShaderOutput output)
+{
+ return output == SH_ESSL_OUTPUT;
+}
- if (!structure)
+bool IsOutputGLSL(ShShaderOutput output)
+{
+ switch (output)
{
- variable.type = GLVariableType(type);
- variable.precision = GLVariablePrecision(type);
+ case SH_GLSL_130_OUTPUT:
+ case SH_GLSL_140_OUTPUT:
+ case SH_GLSL_150_CORE_OUTPUT:
+ case SH_GLSL_330_CORE_OUTPUT:
+ case SH_GLSL_400_CORE_OUTPUT:
+ case SH_GLSL_410_CORE_OUTPUT:
+ case SH_GLSL_420_CORE_OUTPUT:
+ case SH_GLSL_430_CORE_OUTPUT:
+ case SH_GLSL_440_CORE_OUTPUT:
+ case SH_GLSL_450_CORE_OUTPUT:
+ case SH_GLSL_COMPATIBILITY_OUTPUT:
+ return true;
+ default:
+ break;
}
- else
+ return false;
+}
+bool IsOutputHLSL(ShShaderOutput output)
+{
+ switch (output)
{
- // 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);
- }
+ case SH_HLSL_3_0_OUTPUT:
+ case SH_HLSL_4_1_OUTPUT:
+ case SH_HLSL_4_0_FL9_3_OUTPUT:
+ return true;
+ default:
+ break;
}
- setTypeSpecificInfo(type, name, &variable);
- visitVariable(&variable);
-
- ASSERT(output);
- output->push_back(variable);
+ return false;
}
-
-template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<InterfaceBlockField> *);
-template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<ShaderVariable> *);
-template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Uniform> *);
-template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Varying> *);
-
+bool IsOutputVulkan(ShShaderOutput output)
+{
+ return output == SH_GLSL_VULKAN_OUTPUT;
}
+
+} // namespace sh
diff --git a/src/3rdparty/angle/src/compiler/translator/util.h b/src/3rdparty/angle/src/compiler/translator/util.h
index ea7a35a352..6d6dc95b3b 100644
--- a/src/3rdparty/angle/src/compiler/translator/util.h
+++ b/src/3rdparty/angle/src/compiler/translator/util.h
@@ -12,54 +12,52 @@
#include "angle_gl.h"
#include <GLSLANG/ShaderLang.h>
+#include "compiler/translator/HashNames.h"
+#include "compiler/translator/Operator.h"
#include "compiler/translator/Types.h"
-// strtof_clamp is like strtof but
-// 1. it forces C locale, i.e. forcing '.' as decimal point.
-// 2. it clamps the value to -FLT_MAX or FLT_MAX if overflow happens.
-// Return false if overflow happens.
-bool strtof_clamp(const std::string &str, float *value);
-
// If overflow happens, clamp the value to UINT_MIN or UINT_MAX.
// Return false if overflow happens.
bool atoi_clamp(const char *str, unsigned int *value);
-class TSymbolTable;
-
namespace sh
{
+class TSymbolTable;
+
+float NumericLexFloat32OutOfRangeToInfinity(const std::string &str);
+
+// strtof_clamp is like strtof but
+// 1. it forces C locale, i.e. forcing '.' as decimal point.
+// 2. it sets the value to infinity if overflow happens.
+// 3. str should be guaranteed to be in the valid format for a floating point number as defined
+// by the grammar in the ESSL 3.00.6 spec section 4.1.4.
+// Return false if overflow happens.
+bool strtof_clamp(const std::string &str, float *value);
GLenum GLVariableType(const TType &type);
GLenum GLVariablePrecision(const TType &type);
bool IsVaryingIn(TQualifier qualifier);
bool IsVaryingOut(TQualifier qualifier);
bool IsVarying(TQualifier qualifier);
+bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier);
InterpolationType GetInterpolationType(TQualifier qualifier);
-TString ArrayString(const TType &type);
-class GetVariableTraverser : angle::NonCopyable
-{
- public:
- GetVariableTraverser(const TSymbolTable &symbolTable);
- virtual ~GetVariableTraverser() {}
-
- template <typename VarT>
- void traverse(const TType &type, const TString &name, std::vector<VarT> *output);
-
- protected:
- // May be overloaded
- virtual void visitVariable(ShaderVariable *newVar) {}
+// Returns array brackets including size with outermost array size first, as specified in GLSL ES
+// 3.10 section 4.1.9.
+TString ArrayString(const TType &type);
- private:
- // Helper function called by traverse() to fill specific fields
- // for attributes/varyings/uniforms.
- template <typename VarT>
- void setTypeSpecificInfo(
- const TType &type, const TString &name, VarT *variable) {}
+TString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap);
- const TSymbolTable &mSymbolTable;
-};
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var);
-}
+bool IsBuiltinOutputVariable(TQualifier qualifier);
+bool IsBuiltinFragmentInputVariable(TQualifier qualifier);
+bool CanBeInvariantESSL1(TQualifier qualifier);
+bool CanBeInvariantESSL3OrGreater(TQualifier qualifier);
+bool IsOutputESSL(ShShaderOutput output);
+bool IsOutputGLSL(ShShaderOutput output);
+bool IsOutputHLSL(ShShaderOutput output);
+bool IsOutputVulkan(ShShaderOutput output);
+} // namespace sh
-#endif // COMPILER_TRANSLATOR_UTIL_H_
+#endif // COMPILER_TRANSLATOR_UTIL_H_