From 0a7aebadfbb3534284546aa3ca8612314c08f136 Mon Sep 17 00:00:00 2001 From: Miguel Costa Date: Tue, 26 Jun 2018 16:56:45 +0200 Subject: Update ANGLE to chromium/3280 Change-Id: I0802c0d7486f772d361f87a544d6c5af937f4ca1 Reviewed-by: Friedemann Kleint --- .../src/compiler/translator/ASTMetadataHLSL.cpp | 155 +- .../src/compiler/translator/ASTMetadataHLSL.h | 17 +- .../translator/AddAndTrueToLoopCondition.cpp | 59 + .../translator/AddAndTrueToLoopCondition.h | 20 + .../translator/AddDefaultReturnStatements.cpp | 58 + .../translator/AddDefaultReturnStatements.h | 22 + .../translator/ArrayReturnValueToOutParameter.cpp | 227 +- .../translator/ArrayReturnValueToOutParameter.h | 12 +- .../angle/src/compiler/translator/BaseTypes.h | 958 ++- .../BreakVariableAliasingInInnerLoops.cpp | 107 + .../translator/BreakVariableAliasingInInnerLoops.h | 23 + .../translator/BuiltInFunctionEmulator.cpp | 308 +- .../compiler/translator/BuiltInFunctionEmulator.h | 192 +- .../translator/BuiltInFunctionEmulatorGLSL.cpp | 166 +- .../translator/BuiltInFunctionEmulatorGLSL.h | 21 +- .../translator/BuiltInFunctionEmulatorHLSL.cpp | 551 +- .../translator/BuiltInFunctionEmulatorHLSL.h | 11 + .../angle/src/compiler/translator/Cache.cpp | 41 +- src/3rdparty/angle/src/compiler/translator/Cache.h | 36 +- .../angle/src/compiler/translator/CallDAG.cpp | 261 +- .../angle/src/compiler/translator/CallDAG.h | 16 +- .../src/compiler/translator/ClampPointSize.cpp | 47 + .../angle/src/compiler/translator/ClampPointSize.h | 22 + .../angle/src/compiler/translator/CodeGen.cpp | 81 +- .../src/compiler/translator/CollectVariables.cpp | 869 +++ .../src/compiler/translator/CollectVariables.h | 37 + .../angle/src/compiler/translator/Common.h | 73 +- .../angle/src/compiler/translator/Compiler.cpp | 1111 ++-- .../angle/src/compiler/translator/Compiler.h | 221 +- .../src/compiler/translator/ConstantUnion.cpp | 681 +++ .../angle/src/compiler/translator/ConstantUnion.h | 399 +- ...DeclareAndInitBuiltinsForInstancedMultiview.cpp | 221 + .../DeclareAndInitBuiltinsForInstancedMultiview.h | 48 + .../translator/DeferGlobalInitializers.cpp | 147 + .../compiler/translator/DeferGlobalInitializers.h | 32 + .../src/compiler/translator/DetectCallDepth.cpp | 185 - .../src/compiler/translator/DetectCallDepth.h | 78 - .../compiler/translator/DetectDiscontinuity.cpp | 191 - .../src/compiler/translator/DetectDiscontinuity.h | 71 - .../angle/src/compiler/translator/Diagnostics.cpp | 105 +- .../angle/src/compiler/translator/Diagnostics.h | 46 +- .../src/compiler/translator/DirectiveHandler.cpp | 139 +- .../src/compiler/translator/DirectiveHandler.h | 14 +- .../translator/EmulateGLFragColorBroadcast.cpp | 129 + .../translator/EmulateGLFragColorBroadcast.h | 31 + .../src/compiler/translator/EmulatePrecision.cpp | 868 ++- .../src/compiler/translator/EmulatePrecision.h | 25 +- .../translator/ExpandIntegerPowExpressions.cpp | 153 + .../translator/ExpandIntegerPowExpressions.h | 29 + .../src/compiler/translator/ExtensionBehavior.cpp | 96 + .../src/compiler/translator/ExtensionBehavior.h | 65 +- .../src/compiler/translator/ExtensionGLSL.cpp | 5 + .../angle/src/compiler/translator/ExtensionGLSL.h | 7 +- .../angle/src/compiler/translator/FindMain.cpp | 38 + .../angle/src/compiler/translator/FindMain.h | 23 + .../src/compiler/translator/FindSymbolNode.cpp | 58 + .../angle/src/compiler/translator/FindSymbolNode.h | 27 + .../src/compiler/translator/FlagStd140Structs.cpp | 88 +- .../src/compiler/translator/FlagStd140Structs.h | 37 +- .../src/compiler/translator/ForLoopUnroll.cpp | 97 - .../angle/src/compiler/translator/ForLoopUnroll.h | 53 - .../angle/src/compiler/translator/HashNames.cpp | 72 + .../angle/src/compiler/translator/HashNames.h | 14 +- .../src/compiler/translator/ImageFunctionHLSL.cpp | 304 + .../src/compiler/translator/ImageFunctionHLSL.h | 76 + .../angle/src/compiler/translator/InfoSink.cpp | 38 +- .../angle/src/compiler/translator/InfoSink.h | 78 +- .../angle/src/compiler/translator/Initialize.cpp | 1252 ++-- .../angle/src/compiler/translator/Initialize.h | 23 +- .../src/compiler/translator/InitializeDll.cpp | 15 +- .../angle/src/compiler/translator/InitializeDll.h | 6 +- .../src/compiler/translator/InitializeGlobals.h | 2 +- .../compiler/translator/InitializeParseContext.cpp | 42 - .../compiler/translator/InitializeParseContext.h | 17 - .../compiler/translator/InitializeVariables.cpp | 325 +- .../src/compiler/translator/InitializeVariables.h | 76 +- .../angle/src/compiler/translator/IntermNode.cpp | 3832 +++++++----- .../angle/src/compiler/translator/IntermNode.h | 847 ++- .../translator/IntermNodePatternMatcher.cpp | 157 + .../compiler/translator/IntermNodePatternMatcher.h | 75 + .../src/compiler/translator/IntermNode_util.cpp | 254 + .../src/compiler/translator/IntermNode_util.h | 60 + .../src/compiler/translator/IntermTraverse.cpp | 630 +- .../angle/src/compiler/translator/IntermTraverse.h | 355 ++ .../angle/src/compiler/translator/Intermediate.cpp | 508 -- .../angle/src/compiler/translator/Intermediate.h | 75 - .../compiler/translator/IsASTDepthBelowLimit.cpp | 51 + .../src/compiler/translator/IsASTDepthBelowLimit.h | 20 + .../angle/src/compiler/translator/LoopInfo.cpp | 211 - .../angle/src/compiler/translator/LoopInfo.h | 80 - src/3rdparty/angle/src/compiler/translator/MMap.h | 56 - .../angle/src/compiler/translator/NodeSearch.h | 19 +- .../angle/src/compiler/translator/Operator.cpp | 554 +- .../angle/src/compiler/translator/Operator.h | 126 +- .../angle/src/compiler/translator/OutputESSL.cpp | 17 +- .../angle/src/compiler/translator/OutputESSL.h | 23 +- .../angle/src/compiler/translator/OutputGLSL.cpp | 80 +- .../angle/src/compiler/translator/OutputGLSL.h | 17 +- .../src/compiler/translator/OutputGLSLBase.cpp | 1846 +++--- .../angle/src/compiler/translator/OutputGLSLBase.h | 76 +- .../angle/src/compiler/translator/OutputHLSL.cpp | 3606 +++++------ .../angle/src/compiler/translator/OutputHLSL.h | 186 +- .../angle/src/compiler/translator/OutputTree.cpp | 682 +++ .../angle/src/compiler/translator/OutputTree.h | 22 + .../src/compiler/translator/OutputVulkanGLSL.cpp | 80 + .../src/compiler/translator/OutputVulkanGLSL.h | 34 + .../angle/src/compiler/translator/ParamType.h | 102 + .../angle/src/compiler/translator/ParseContext.cpp | 6236 +++++++++++++------- .../angle/src/compiler/translator/ParseContext.h | 767 ++- .../angle/src/compiler/translator/PoolAlloc.cpp | 227 +- .../angle/src/compiler/translator/PoolAlloc.h | 220 +- .../angle/src/compiler/translator/Pragma.h | 9 +- .../compiler/translator/PruneEmptyDeclarations.cpp | 81 - .../compiler/translator/PruneEmptyDeclarations.h | 15 - .../angle/src/compiler/translator/PruneNoOps.cpp | 156 + .../angle/src/compiler/translator/PruneNoOps.h | 24 + .../src/compiler/translator/QualifierAlive.cpp | 58 - .../angle/src/compiler/translator/QualifierAlive.h | 12 - .../src/compiler/translator/QualifierTypes.cpp | 784 +++ .../angle/src/compiler/translator/QualifierTypes.h | 191 + .../translator/RecordConstantPrecision.cpp | 94 +- .../compiler/translator/RecordConstantPrecision.h | 23 +- .../compiler/translator/RegenerateStructNames.cpp | 34 +- .../compiler/translator/RegenerateStructNames.h | 20 +- .../translator/RemoveArrayLengthMethod.cpp | 83 + .../compiler/translator/RemoveArrayLengthMethod.h | 29 + .../compiler/translator/RemoveDynamicIndexing.cpp | 319 +- .../compiler/translator/RemoveDynamicIndexing.h | 12 +- .../translator/RemoveEmptySwitchStatements.cpp | 56 + .../translator/RemoveEmptySwitchStatements.h | 18 + .../translator/RemoveInvariantDeclaration.cpp | 43 + .../translator/RemoveInvariantDeclaration.h | 18 + .../RemoveNoOpCasesFromEndOfSwitchStatements.cpp | 116 + .../RemoveNoOpCasesFromEndOfSwitchStatements.h | 21 + .../angle/src/compiler/translator/RemovePow.cpp | 36 +- .../angle/src/compiler/translator/RemovePow.h | 5 +- .../translator/RemoveSwitchFallThrough.cpp | 187 +- .../compiler/translator/RemoveSwitchFallThrough.h | 42 +- .../translator/RemoveUnreferencedVariables.cpp | 358 ++ .../translator/RemoveUnreferencedVariables.h | 24 + .../angle/src/compiler/translator/RenameFunction.h | 36 - .../src/compiler/translator/RewriteDoWhile.cpp | 62 +- .../angle/src/compiler/translator/RewriteDoWhile.h | 9 +- .../src/compiler/translator/RewriteElseBlocks.cpp | 112 +- .../src/compiler/translator/RewriteElseBlocks.h | 8 +- .../translator/RewriteTexelFetchOffset.cpp | 154 + .../compiler/translator/RewriteTexelFetchOffset.h | 28 + .../translator/RewriteUnaryMinusOperatorFloat.cpp | 94 + .../translator/RewriteUnaryMinusOperatorFloat.h | 19 + .../translator/RewriteUnaryMinusOperatorInt.cpp | 112 + .../translator/RewriteUnaryMinusOperatorInt.h | 20 + .../compiler/translator/RunAtTheEndOfShader.cpp | 112 + .../src/compiler/translator/RunAtTheEndOfShader.h | 23 + .../ScalarizeVecAndMatConstructorArgs.cpp | 275 +- .../translator/ScalarizeVecAndMatConstructorArgs.h | 49 +- .../angle/src/compiler/translator/SearchSymbol.cpp | 3 +- .../angle/src/compiler/translator/SearchSymbol.h | 4 +- .../translator/SeparateArrayInitialization.cpp | 76 +- .../translator/SeparateArrayInitialization.h | 6 +- .../compiler/translator/SeparateDeclarations.cpp | 52 +- .../src/compiler/translator/SeparateDeclarations.h | 7 +- .../SeparateExpressionsReturningArrays.cpp | 145 +- .../SeparateExpressionsReturningArrays.h | 12 +- .../angle/src/compiler/translator/Severity.h | 22 + .../angle/src/compiler/translator/ShaderLang.cpp | 426 +- .../angle/src/compiler/translator/ShaderVars.cpp | 397 +- .../translator/SimplifyArrayAssignment.cpp | 38 - .../compiler/translator/SimplifyArrayAssignment.h | 25 - .../compiler/translator/SimplifyLoopConditions.cpp | 300 + .../compiler/translator/SimplifyLoopConditions.h | 25 + .../compiler/translator/SplitSequenceOperator.cpp | 171 + .../compiler/translator/SplitSequenceOperator.h | 28 + .../src/compiler/translator/StructureHLSL.cpp | 448 +- .../angle/src/compiler/translator/StructureHLSL.h | 41 +- .../angle/src/compiler/translator/SymbolTable.cpp | 448 +- .../angle/src/compiler/translator/SymbolTable.h | 494 +- .../src/compiler/translator/SymbolUniqueId.cpp | 28 + .../angle/src/compiler/translator/SymbolUniqueId.h | 36 + .../compiler/translator/TextureFunctionHLSL.cpp | 1322 +++++ .../src/compiler/translator/TextureFunctionHLSL.h | 76 + .../src/compiler/translator/TranslatorESSL.cpp | 146 +- .../angle/src/compiler/translator/TranslatorESSL.h | 15 +- .../src/compiler/translator/TranslatorGLSL.cpp | 214 +- .../angle/src/compiler/translator/TranslatorGLSL.h | 17 +- .../src/compiler/translator/TranslatorHLSL.cpp | 119 +- .../angle/src/compiler/translator/TranslatorHLSL.h | 21 +- .../src/compiler/translator/TranslatorVulkan.cpp | 173 + .../src/compiler/translator/TranslatorVulkan.h | 34 + .../angle/src/compiler/translator/Types.cpp | 1045 +++- src/3rdparty/angle/src/compiler/translator/Types.h | 675 +-- .../src/compiler/translator/UnfoldShortCircuit.cpp | 185 - .../src/compiler/translator/UnfoldShortCircuit.h | 38 - .../compiler/translator/UnfoldShortCircuitAST.cpp | 44 +- .../compiler/translator/UnfoldShortCircuitAST.h | 12 +- .../compiler/translator/UnfoldShortCircuitToIf.cpp | 318 +- .../compiler/translator/UnfoldShortCircuitToIf.h | 13 +- .../angle/src/compiler/translator/UniformHLSL.cpp | 434 +- .../angle/src/compiler/translator/UniformHLSL.h | 85 +- .../translator/UseInterfaceBlockFields.cpp | 105 + .../compiler/translator/UseInterfaceBlockFields.h | 30 + .../angle/src/compiler/translator/UtilsHLSL.cpp | 794 ++- .../angle/src/compiler/translator/UtilsHLSL.h | 85 +- .../translator/ValidateGlobalInitializer.cpp | 62 +- .../translator/ValidateGlobalInitializer.h | 11 +- .../compiler/translator/ValidateLimitations.cpp | 393 +- .../src/compiler/translator/ValidateLimitations.h | 58 +- .../compiler/translator/ValidateMaxParameters.cpp | 29 + .../compiler/translator/ValidateMaxParameters.h | 21 + .../src/compiler/translator/ValidateOutputs.cpp | 116 +- .../src/compiler/translator/ValidateOutputs.h | 34 +- .../src/compiler/translator/ValidateSwitch.cpp | 179 +- .../angle/src/compiler/translator/ValidateSwitch.h | 52 +- .../translator/ValidateVaryingLocations.cpp | 174 + .../compiler/translator/ValidateVaryingLocations.h | 25 + .../angle/src/compiler/translator/VariableInfo.cpp | 673 --- .../angle/src/compiler/translator/VariableInfo.h | 77 - .../src/compiler/translator/VariablePacker.cpp | 394 +- .../angle/src/compiler/translator/VariablePacker.h | 39 +- .../translator/VectorizeVectorScalarArithmetic.cpp | 284 + .../translator/VectorizeVectorScalarArithmetic.h | 25 + .../angle/src/compiler/translator/VersionGLSL.cpp | 145 +- .../angle/src/compiler/translator/VersionGLSL.h | 14 +- .../translator/WrapSwitchStatementsInBlocks.cpp | 132 + .../translator/WrapSwitchStatementsInBlocks.h | 22 + .../angle/src/compiler/translator/blocklayout.cpp | 191 +- .../angle/src/compiler/translator/blocklayout.h | 75 +- .../src/compiler/translator/blocklayoutHLSL.cpp | 63 +- .../src/compiler/translator/blocklayoutHLSL.h | 32 +- .../src/compiler/translator/compilerdebug.cpp | 37 - .../angle/src/compiler/translator/compilerdebug.h | 53 - .../translator/depgraph/DependencyGraph.cpp | 95 - .../compiler/translator/depgraph/DependencyGraph.h | 199 - .../translator/depgraph/DependencyGraphBuilder.cpp | 255 - .../translator/depgraph/DependencyGraphBuilder.h | 199 - .../translator/depgraph/DependencyGraphOutput.cpp | 64 - .../translator/depgraph/DependencyGraphOutput.h | 31 - .../depgraph/DependencyGraphTraverse.cpp | 69 - .../emulated_builtin_function_data_hlsl.json | 1382 +++++ .../emulated_builtin_functions_hlsl_autogen.cpp | 859 +++ .../angle/src/compiler/translator/glslang.h | 16 +- .../angle/src/compiler/translator/glslang.l | 183 +- .../angle/src/compiler/translator/glslang.y | 1027 ++-- .../angle/src/compiler/translator/intermOut.cpp | 626 -- .../angle/src/compiler/translator/length_limits.h | 7 +- .../angle/src/compiler/translator/parseConst.cpp | 264 - .../timing/RestrictFragmentShaderTiming.cpp | 130 - .../timing/RestrictFragmentShaderTiming.h | 39 - .../timing/RestrictVertexShaderTiming.cpp | 17 - .../translator/timing/RestrictVertexShaderTiming.h | 32 - .../angle/src/compiler/translator/util.cpp | 780 ++- src/3rdparty/angle/src/compiler/translator/util.h | 60 +- 251 files changed, 36768 insertions(+), 20568 deletions(-) create mode 100644 src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/AddAndTrueToLoopCondition.h create mode 100644 src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/AddDefaultReturnStatements.h create mode 100644 src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ClampPointSize.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ClampPointSize.h create mode 100644 src/3rdparty/angle/src/compiler/translator/CollectVariables.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/CollectVariables.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ConstantUnion.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h create mode 100644 src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/DeferGlobalInitializers.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/DetectCallDepth.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/DetectCallDepth.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/DetectDiscontinuity.h create mode 100644 src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ExpandIntegerPowExpressions.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/FindMain.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/FindMain.h create mode 100644 src/3rdparty/angle/src/compiler/translator/FindSymbolNode.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/FindSymbolNode.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h create mode 100644 src/3rdparty/angle/src/compiler/translator/HashNames.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ImageFunctionHLSL.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/InitializeParseContext.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h create mode 100644 src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/IntermNodePatternMatcher.h create mode 100644 src/3rdparty/angle/src/compiler/translator/IntermNode_util.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/IntermNode_util.h create mode 100644 src/3rdparty/angle/src/compiler/translator/IntermTraverse.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/Intermediate.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/Intermediate.h create mode 100644 src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/IsASTDepthBelowLimit.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/LoopInfo.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/LoopInfo.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/MMap.h create mode 100644 src/3rdparty/angle/src/compiler/translator/OutputTree.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/OutputTree.h create mode 100644 src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/OutputVulkanGLSL.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ParamType.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h create mode 100644 src/3rdparty/angle/src/compiler/translator/PruneNoOps.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/PruneNoOps.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/QualifierAlive.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/QualifierAlive.h create mode 100644 src/3rdparty/angle/src/compiler/translator/QualifierTypes.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/QualifierTypes.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveArrayLengthMethod.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveEmptySwitchStatements.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveInvariantDeclaration.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveUnreferencedVariables.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/RenameFunction.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteTexelFetchOffset.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorFloat.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RunAtTheEndOfShader.h create mode 100644 src/3rdparty/angle/src/compiler/translator/Severity.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/SimplifyArrayAssignment.h create mode 100644 src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/SimplifyLoopConditions.h create mode 100644 src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/SplitSequenceOperator.h create mode 100644 src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/SymbolUniqueId.h create mode 100644 src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/TextureFunctionHLSL.h create mode 100644 src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/TranslatorVulkan.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuit.h create mode 100644 src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/UseInterfaceBlockFields.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ValidateMaxParameters.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ValidateVaryingLocations.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/VariableInfo.h create mode 100644 src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/VectorizeVectorScalarArithmetic.h create mode 100644 src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/compilerdebug.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/compilerdebug.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/emulated_builtin_function_data_hlsl.json create mode 100644 src/3rdparty/angle/src/compiler/translator/emulated_builtin_functions_hlsl_autogen.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/intermOut.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/parseConst.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h delete mode 100644 src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp delete mode 100644 src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h (limited to 'src/3rdparty/angle/src/compiler/translator') 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 mParents; + std::vector mParents; + + // A list of builtin functions that use gradients + std::set 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 mLoopsAndSwitches; - std::vector mIfs; + std::vector mLoopsAndSwitches; + std::vector 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 #include +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 mControlFlowsContainingGradient; + std::set mControlFlowsContainingGradient; // Remember information about the discontinuous loops and which functions // are called in such loops. bool mCalledInDiscontinuousLoop; bool mHasGradientLoopInCallGraph; - std::set mDiscontinuousLoops; - std::set mIfsContainingGradientLoop; + std::set mDiscontinuousLoops; + std::set mIfsContainingGradientLoop; // Will we need to generate a Lod0 version of the function. bool mNeedsLod0; @@ -55,4 +58,6 @@ typedef std::vector 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 + +#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 ¶m : *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 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 +#include + #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(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 ¶m) +bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, const TType ¶m) { - return SetFunctionCalled(FunctionId(op, ¶m)); + return setFunctionCalled(FunctionId(op, ¶m)); } -bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2) +bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, + const TType ¶m1, + const TType ¶m2) { - return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2)); + return setFunctionCalled(FunctionId(op, ¶m1, ¶m2)); } -bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, - const TType ¶m1, const TType ¶m2, const TType ¶m3) +bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, + const TType ¶m1, + const TType ¶m2, + const TType ¶m3) { - return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3)); + return setFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3)); } -bool BuiltInFunctionEmulator::SetFunctionCalled(const FunctionId &functionId) +bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, + const TType ¶m1, + const TType ¶m2, + const TType ¶m3, + const TType ¶m4) { - if (mEmulatedFunctions.find(functionId) != mEmulatedFunctions.end()) + return setFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3, ¶m4)); +} + +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 ¶m); - bool SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2); - bool SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2, const TType ¶m3); - - 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 ¶m); + bool setFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2); + bool setFunctionCalled(TOperator op, + const TType ¶m1, + const TType ¶m2, + const TType ¶m3); + bool setFunctionCalled(TOperator op, + const TType ¶m1, + const TType ¶m2, + const TType ¶m3, + const TType ¶m4); + + bool setFunctionCalled(const FunctionId &functionId); + + const char *findEmulatedFunction(const FunctionId &functionId) const; // Map from function id to emulated function definition std::map mEmulatedFunctions; + // Map from dependent functions to their dependencies. This structure allows each function to + // have at most one dependency. + std::map mFunctionDependencies; + // Called function ids std::vector mFunctions; + + // Constexpr function tables. + std::vector 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(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::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(basicType); - components.precision = static_cast(precision); - components.qualifier = static_cast(qualifier); - components.primarySize = primarySize; + value = 0; + components.basicType = static_cast(basicType); + components.precision = static_cast(precision); + components.qualifier = static_cast(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 TypeMap; + typedef std::map 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(callee->index)); } - (*idToIndex)[data.node->getFunctionId()] = static_cast(data.index); + (*idToIndex)[data.node->getFunctionSymbolInfo()->getId().get()] = + static_cast(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 callees; - TIntermAggregate *node; + std::set 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 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 mFunctions; + std::map mFunctions; CreatorFunctionData *mCurrentFunction; size_t mCurrentIndex; }; @@ -232,13 +287,9 @@ CallDAG::~CallDAG() const size_t CallDAG::InvalidIndex = std::numeric_limits::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 #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 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 mRecords; std::map 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 +VarT *FindVariable(const TString &name, std::vector *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 *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 *attribs, + std::vector *outputVariables, + std::vector *uniforms, + std::vector *inputVaryings, + std::vector *outputVaryings, + std::vector *uniformBlocks, + std::vector *shaderStorageBlocks, + std::vector *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 *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 *mAttribs; + std::vector *mOutputVariables; + std::vector *mUniforms; + std::vector *mInputVaryings; + std::vector *mOutputVaryings; + std::vector *mUniformBlocks; + std::vector *mShaderStorageBlocks; + std::vector *mInBlocks; + + std::map 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 *attribs, + std::vector *outputVariables, + std::vector *uniforms, + std::vector *inputVaryings, + std::vector *outputVaryings, + std::vector *uniformBlocks, + std::vector *shaderStorageBlocks, + std::vector *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(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 *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(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 *attributes, + std::vector *outputVariables, + std::vector *uniforms, + std::vector *inputVaryings, + std::vector *outputVaryings, + std::vector *uniformBlocks, + std::vector *shaderStorageBlocks, + std::vector *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 + +#include "compiler/translator/ExtensionBehavior.h" + +namespace sh +{ + +class TIntermBlock; +class TSymbolTable; + +void CollectVariables(TIntermBlock *root, + std::vector *attributes, + std::vector *outputVariables, + std::vector *uniforms, + std::vector *inputVaryings, + std::vector *outputVaryings, + std::vector *uniformBlocks, + std::vector *shaderStorageBlocks, + std::vector *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 #include #include +#include #include #include #include #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 TStringAllocator; -typedef std::basic_string , TStringAllocator> TString; +typedef std::basic_string, TStringAllocator> TString; typedef std::basic_ostringstream, 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 TVector : public std::vector> { public: + POOL_ALLOCATOR_NEW_DELETE(); + typedef typename std::vector>::size_type size_type; TVector() : std::vector>() {} TVector(const pool_allocator &a) : std::vector>(a) {} TVector(size_type i) : std::vector>(i) {} }; +template , class CMP = std::equal_to> +class TUnorderedMap : public std::unordered_map>> +{ + public: + POOL_ALLOCATOR_NEW_DELETE(); + typedef pool_allocator> tAllocator; + + TUnorderedMap() : std::unordered_map() {} + // use correct two-stage name lookup supported in gcc 3.4 and above + TUnorderedMap(const tAllocator &a) + : std::unordered_map( + std::unordered_map::key_compare(), + a) + { + } +}; + template > class TMap : public std::map>> { public: + POOL_ALLOCATOR_NEW_DELETE(); typedef pool_allocator> tAllocator; TMap() : std::map() {} // use correct two-stage name lookup supported in gcc 3.4 and above - TMap(const tAllocator& a) : std::map(std::map::key_compare(), a) {} + TMap(const tAllocator &a) + : std::map(std::map::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 +{ + size_t operator()(const sh::TString &s) const + { + return angle::PMurHash32(0, s.data(), static_cast(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 + +#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(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 *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 *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(gl::VariableColumnCount(varying.type)); - unsigned char secondarySize = static_cast(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 + #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 &getAttributes() const { return attributes; } const std::vector &getOutputVariables() const { return outputVariables; } const std::vector &getUniforms() const { return uniforms; } - const std::vector &getVaryings() const { return varyings; } + const std::vector &getInputVaryings() const { return inputVaryings; } + const std::vector &getOutputVaryings() const { return outputVaryings; } const std::vector &getInterfaceBlocks() const { return interfaceBlocks; } + const std::vector &getUniformBlocks() const { return uniformBlocks; } + const std::vector &getShaderStorageBlocks() const + { + return shaderStorageBlocks; + } + const std::vector &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 attributes; std::vector outputVariables; std::vector uniforms; - std::vector expandedUniforms; - std::vector varyings; + std::vector inputVaryings; + std::vector outputVaryings; std::vector interfaceBlocks; - - virtual bool shouldCollectVariables(int compileOptions) - { - return (compileOptions & SH_VARIABLES) != 0; - } + std::vector uniformBlocks; + std::vector shaderStorageBlocks; + std::vector 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(constant.getIConst())); + break; + case EbtUInt: + setFConst(static_cast(constant.getUConst())); + break; + case EbtBool: + setFConst(static_cast(constant.getBConst())); + break; + case EbtFloat: + setFConst(static_cast(constant.getFConst())); + break; + default: + return false; + } + break; + case EbtInt: + switch (constant.type) + { + case EbtInt: + setIConst(static_cast(constant.getIConst())); + break; + case EbtUInt: + setIConst(static_cast(constant.getUConst())); + break; + case EbtBool: + setIConst(static_cast(constant.getBConst())); + break; + case EbtFloat: + setIConst(static_cast(constant.getFConst())); + break; + default: + return false; + } + break; + case EbtUInt: + switch (constant.type) + { + case EbtInt: + setUConst(static_cast(constant.getIConst())); + break; + case EbtUInt: + setUConst(static_cast(constant.getUConst())); + break; + case EbtBool: + setUConst(static_cast(constant.getBConst())); + break; + case EbtFloat: + setUConst(static_cast(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(lhs.iConst, rhs.iConst)); + break; + case EbtUInt: + returnValue.setUConst(gl::WrappingSum(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(lhs.iConst, rhs.iConst)); + break; + case EbtUInt: + returnValue.setUConst(gl::WrappingDiff(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(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::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(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(static_cast(lhs.iConst) << rhs.iConst)); + break; + case EbtUInt: + returnValue.setIConst( + static_cast(static_cast(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 +#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(constant.getIConst())); break; - case EbtUInt: setFConst(static_cast(constant.getUConst())); break; - case EbtBool: setFConst(static_cast(constant.getBConst())); break; - case EbtFloat: setFConst(static_cast(constant.getFConst())); break; - default: return false; - } - break; - case EbtInt: - switch (constant.type) - { - case EbtInt: setIConst(static_cast(constant.getIConst())); break; - case EbtUInt: setIConst(static_cast(constant.getUConst())); break; - case EbtBool: setIConst(static_cast(constant.getBConst())); break; - case EbtFloat: setIConst(static_cast(constant.getFConst())); break; - default: return false; - } - break; - case EbtUInt: - switch (constant.type) - { - case EbtInt: setUConst(static_cast(constant.getIConst())); break; - case EbtUInt: setUConst(static_cast(constant.getUConst())); break; - case EbtBool: setUConst(static_cast(constant.getBConst())); break; - case EbtFloat: setUConst(static_cast(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 -#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 callees; - - Visit visit; - }; - - ErrorCode detectCallDepthForFunction(FunctionNode* func); - FunctionNode* findFunctionByName(const TString& name); - void resetFunctionNodes(); - - TInfoSink& getInfoSink() { return infoSink; } - - TVector 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 *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 + +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 *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 -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::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 +#include + +#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(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 + +#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 -#include -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 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 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 #include -#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 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 getMappedStructs() const { return mMappedStructs; } + + protected: + bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; + + private: + void mapBlockStructMembers(TIntermSymbol *blockDeclarator, TInterfaceBlock *block); + + std::vector 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 FlagStd140ValueStructs(TIntermNode *node) +} // anonymous namespace + +std::vector 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 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 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 mFlaggedNodes; +struct MappedStruct +{ + TIntermSymbol *blockDeclarator; + TField *field; }; -std::vector FlagStd140ValueStructs(TIntermNode *node); - +std::vector 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 -#include "compiler/translator/IntermNode.h" +#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/Common.h" -#define HASHED_NAME_PREFIX "webgl_" +namespace sh +{ typedef std::map 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 + +#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; + 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 #include #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 - 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(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 +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 - -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(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(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(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 + +#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 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 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 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(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(elements, cols, rows).transpose(); } angle::Matrix GetMatrix(const TConstantUnion *paramArray, const unsigned int &size) @@ -163,7 +136,7 @@ void SetUnionArrayFromMatrix(const angle::Matrix &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 result = m.transpose(); + angle::Matrix result = m.transpose(); std::vector 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 &m, TConstantUnion *resu } // namespace anonymous - //////////////////////////////////////////////////////////////// // // Member functions of the nodes used for building the tree. @@ -181,90 +153,214 @@ void SetUnionArrayFromMatrix(const angle::Matrix &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(replacement); \ - return true; \ + if (node == original) \ + { \ + node = static_cast(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) +{ + return replaceChildNodeInternal(original, replacement); +} + +bool TIntermFunctionPrototype::replaceChildNode(TIntermNode *original, TIntermNode *replacement) +{ + return replaceChildNodeInternal(original, replacement); +} + +bool TIntermDeclaration::replaceChildNode(TIntermNode *original, TIntermNode *replacement) { - for (size_t ii = 0; ii < mSequence.size(); ++ii) + 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(mTrueExpression, TIntermTyped, original, replacement); + REPLACE_IF_IS(mFalseExpression, TIntermTyped, original, replacement); + return false; +} + +bool TIntermIfElse::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(mTrueBlock, TIntermBlock, original, replacement); + REPLACE_IF_IS(mFalseBlock, TIntermBlock, original, replacement); return false; } -bool TIntermSwitch::replaceChildNode( - TIntermNode *original, TIntermNode *replacement) +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,176 +661,422 @@ 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; } } -// -// Make sure the type of a unary operator is appropriate for its -// combination of operation and operand type. -// -void TIntermUnary::promote(const TType *funcReturnType) +TOperator TIntermBinary::GetMulOpBasedOnOperands(const TType &left, const TType &right) { - switch (mOp) + if (left.isMatrix()) { - 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()); + if (right.isMatrix()) + { + return EOpMatrixTimesMatrix; + } + else + { + if (right.isVector()) + { + return EOpMatrixTimesVector; + } + else + { + return EOpMatrixTimesScalar; + } + } } - - if (funcReturnType != nullptr) + else { - if (funcReturnType->getBasicType() == EbtBool) + if (right.isMatrix()) { - // Bool types should not have precision. - setType(*funcReturnType); + if (left.isVector()) + { + return EOpVectorTimesMatrix; + } + else + { + return EOpMatrixTimesScalar; + } } else { - // Precision of the node has been set based on the operand. - setTypePreservePrecision(*funcReturnType); + // Neither operand is a matrix. + if (left.isVector() == right.isVector()) + { + // Leave as component product. + return EOpMul; + } + else + { + return EOpVectorTimesScalar; + } } } +} - if (mOperand->getQualifier() == EvqConst) - mType.setQualifier(EvqConst); +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 - mType.setQualifier(EvqTemporary); + { + 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; + } + } + } } // -// 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. +// Make sure the type of a unary operator is appropriate for its +// combination of operation and operand type. // -bool TIntermBinary::promote(TInfoSink &infoSink) +void TIntermUnary::promote() { - ASSERT(mLeft->isArray() == mRight->isArray()); + if (mOp == EOpArrayLength) + { + // Special case: the qualifier of .length() doesn't depend on the operand qualifier. + setType(TType(EbtInt, EbpUndefined, EvqConst)); + return; + } - // - // Base assumption: just make the type the same as the left - // operand. Then only deviations from this need be coded. - // - setType(mLeft->getType()); + TQualifier resultQualifier = EvqTemporary; + if (mOperand->getQualifier() == EvqConst) + resultQualifier = EvqConst; - // The result gets promoted to the highest precision. - TPrecision higherPrecision = GetHigherPrecision( - mLeft->getPrecision(), mRight->getPrecision()); - getTypePointer()->setPrecision(higherPrecision); + unsigned char operandPrimarySize = + static_cast(mOperand->getType().getNominalSize()); + switch (mOp) + { + 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(mOperand->getType().getRows()), + static_cast(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 &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; +} + +TIntermTyped *TIntermTernary::fold() +{ + if (mCondition->getAsConstantUnion()) + { + if (mCondition->getAsConstantUnion()->getBConst(0)) + { + mTrueExpression->getTypePointer()->setQualifier(mType.getQualifier()); + return mTrueExpression; + } + else + { + mFalseExpression->getTypePointer()->setQualifier(mType.getQualifier()); + return mFalseExpression; + } + } + return this; +} + +void TIntermSwizzle::promote() +{ + TQualifier resultQualifier = EvqTemporary; + if (mOperand->getQualifier() == EvqConst) + resultQualifier = EvqConst; + + auto numFields = mSwizzleOffsets.size(); + setType(TType(mOperand->getBasicType(), mOperand->getPrecision(), resultQualifier, + static_cast(numFields))); +} + +bool TIntermSwizzle::hasDuplicateOffsets() const +{ + 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()); TQualifier resultQualifier = EvqConst; // Binary operations results in temporary variables unless both @@ -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(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(mRight->getCols()), 1)); - } - else + case EOpMul: + break; + case EOpMatrixTimesScalar: + if (mRight->isMatrix()) { - mOp = EOpMatrixTimesScalar; setType(TType(basicType, higherPrecision, resultQualifier, static_cast(mRight->getCols()), static_cast(mRight->getRows()))); } - } - else if (mLeft->isMatrix() && !mRight->isMatrix()) - { - if (mRight->isVector()) - { - mOp = EOpMatrixTimesVector; - setType(TType(basicType, higherPrecision, resultQualifier, - static_cast(mLeft->getRows()), 1)); - } - else - { - mOp = EOpMatrixTimesScalar; - } - } - else if (mLeft->isMatrix() && mRight->isMatrix()) - { - mOp = EOpMatrixTimesMatrix; + break; + case EOpMatrixTimesVector: + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mLeft->getRows()), 1)); + break; + case EOpMatrixTimesMatrix: setType(TType(basicType, higherPrecision, resultQualifier, static_cast(mRight->getCols()), static_cast(mLeft->getRows()))); + break; + case EOpVectorTimesScalar: + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(nominalSize), 1)); + break; + case EOpVectorTimesMatrix: + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(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(nominalSize), + static_cast(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(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; + } +} - case EOpMulAssign: - if (!mLeft->isMatrix() && mRight->isMatrix()) +const TConstantUnion *TIntermConstantUnion::foldIndexing(int index) +{ + if (isArray()) + { + ASSERT(index < static_cast(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; + } +} + +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(mRight->getCols()), - static_cast(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(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(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; + const TConstantUnion *constArray = leftConstant->getUnionArrayPointer(); + return CreateFoldedNode(constArray + previousFieldsSize, this, mType.getQualifier()); } - 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; - } - + 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(nominalSize), - static_cast(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::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::infinity() + : std::numeric_limits::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::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 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 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,304 +1962,320 @@ TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator resultArray[1].setFConst(f2); break; } - else + + case EOpPackHalf2x16: + ASSERT(getType().getBasicType() == EbtFloat); + ASSERT(getType().getNominalSize() == 2); + resultArray = new TConstantUnion(); + resultArray->setUConst( + gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + break; + + case EOpUnpackHalf2x16: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; + ASSERT(getType().getBasicType() == EbtUInt); + resultArray = new TConstantUnion[2]; + float f1, f2; + gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2); + resultArray[0].setFConst(f1); + resultArray[1].setFConst(f2); + break; } - case EOpPackHalf2x16: - if (getType().getBasicType() == EbtFloat) + case EOpPackUnorm4x8: { - ASSERT(getType().getNominalSize() == 2); + ASSERT(getType().getBasicType() == EbtFloat); resultArray = new TConstantUnion(); - resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + resultArray->setUConst( + gl::PackUnorm4x8(operandArray[0].getFConst(), operandArray[1].getFConst(), + operandArray[2].getFConst(), operandArray[3].getFConst())); break; } - else + case EOpPackSnorm4x8: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; + 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 EOpUnpackHalf2x16: - if (getType().getBasicType() == EbtUInt) + case EOpUnpackUnorm4x8: { - resultArray = new TConstantUnion[2]; - float f1, f2; - gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2); - resultArray[0].setFConst(f1); - resultArray[1].setFConst(f2); + 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; } - else + case EOpUnpackSnorm4x8: { - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return nullptr; + 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( - -static_cast(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::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::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( + -static_cast(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( - static_cast(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( + static_cast(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(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(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(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(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(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(result)); + } + else + { + resultArray[i].setUConst(result); + } + break; + } + case EOpBitCount: + { + uint32_t value; + if (getType().getBasicType() == EbtInt) + { + value = static_cast(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(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(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 ¶meter, FloatTypeUnaryFunc builtinFunc, - TInfoSink &infoSink, TConstantUnion *result) const +void TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion ¶meter, + 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(sequence->size()); - std::vector unionArrays(paramsCount); - std::vector objectSizes(paramsCount); + TOperator op = aggregate->getOp(); + TIntermSequence *arguments = aggregate->getSequence(); + unsigned int argsCount = static_cast(arguments->size()); + std::vector unionArrays(argsCount); + std::vector 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 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 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 result = - GetMatrix(unionArrays[0], 1, static_cast(numCols)) - .outerProduct(GetMatrix(unionArrays[1], static_cast(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 result = + GetMatrix(unionArrays[0], static_cast(numRows), 1) + .outerProduct(GetMatrix(unionArrays[1], 1, static_cast(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(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(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(unionArrays[0][i].getIConst()); + uint32_t insert = static_cast(unionArrays[1][i].getIConst()); + uint32_t resultUnsigned = + (base & baseMask) | ((insert << offset) & insertMask); + resultArray[i].setIConst(static_cast(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 ¶meter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const; + typedef float (*FloatTypeUnaryFunc)(float); + void foldFloatTypeUnary(const TConstantUnion ¶meter, + 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 &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 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 TIntermSequence; typedef TVector 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 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 mReplacements; - std::vector mMultiReplacements; - std::vector 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 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 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(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(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(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(sym); - ASSERT(builtInFunc->getParamCount() == sequence->size()); - } + builtInFunc = static_cast( + 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 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 mInsertions; + std::vector mReplacements; + + // All the nodes from root to the current node during traversing. + TVector mPath; + + // All the code blocks from the root to the current node's parent during traversal. + std::vector 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 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 -#include -#include - -#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 -{ - 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 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 { 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 -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(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(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 -#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 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(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 &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 &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 &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 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 &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 &OutputHLSL::getInterfaceBlockRegisterMap() const +const std::map &OutputHLSL::getUniformBlockRegisterMap() const { - return mUniformHLSL->getInterfaceBlockRegisterMap(); + return mUniformHLSL->getUniformBlockRegisterMap(); } const std::map &OutputHLSL::getUniformRegisterMap() const @@ -275,95 +275,161 @@ const std::map &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; +} - const TFieldList &fields = structure.fields(); - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - const TField &field = *fields[fieldIndex]; - const TString &fieldName = rhsStructName + "." + Decorate(field.name()); - const TType &fieldType = *field.type(); +TString OutputHLSL::generateStructMapping(const std::vector &std140Structs) const +{ + TString mappedStructs; - if (fieldType.getStruct()) + for (auto &mappedStruct : std140Structs) + { + 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 &std140Structs, + const BuiltInFunctionEmulator *builtInFunctionEmulator) const { TString varyings; TString attributes; - TString flaggedStructs; - - for (std::map::const_iterator flaggedStructIt = mFlaggedStructMappedNames.begin(); flaggedStructIt != mFlaggedStructMappedNames.end(); flaggedStructIt++) - { - TIntermTyped *structNode = flaggedStructIt->first; - const TString &mappedName = flaggedStructIt->second; - const TStructure &structure = *structNode->getType().getStruct(); - const TString &originalName = mFlaggedStructOriginalNames[structNode]; + TString mappedStructs = generateStructMapping(std140Structs); - 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++) + 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,769 +729,126 @@ 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(); - } + ASSERT(mShaderType == GL_COMPUTE_SHADER); - 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 + out << "cbuffer DriverConstants : register(b1)\n" + "{\n"; + if (mUsesNumWorkGroups) { - 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"; - } + out << " uint3 gl_NumWorkGroups : packoffset(c0);\n"; } + ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); + mUniformHLSL->samplerMetadataUniforms(out, "c1"); + out << "};\n"; - 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) + // Follow built-in variables would be initialized in + // DynamicHLSL::generateComputeShaderLinkHLSL, if they + // are used in compute shader. + if (mUsesWorkGroupID) { - 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(); - } + out << "static uint3 gl_WorkGroupID = uint3(0, 0, 0);\n"; } - if (textureFunction->method == TextureFunction::GRAD) + if (mUsesLocalInvocationID) { - 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(); - } + out << "static uint3 gl_LocalInvocationID = uint3(0, 0, 0);\n"; } - switch(textureFunction->method) + if (mUsesGlobalInvocationID) { - 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 << "static uint3 gl_GlobalInvocationID = uint3(0, 0, 0);\n"; } - if (textureFunction->offset) + if (mUsesLocalInvocationIndex) { - 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 uint gl_LocalInvocationIndex = uint(0);\n"; } + } - if (textureFunction->method == TextureFunction::BIAS || - textureFunction->method == TextureFunction::LOD0BIAS) - { - out << ", float bias"; - } + bool getDimensionsIgnoresBaseLevel = + (mCompileOptions & SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL) != 0; + mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel); + mImageFunctionHLSL->imageFunctionHeader(out); - out << ")\n" - "{\n"; + if (mUsesFragCoord) + { + out << "#define GL_USES_FRAG_COORD\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) - { - 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]"; - } - } + if (mUsesPointCoord) + { + out << "#define GL_USES_POINT_COORD\n"; + } - if (textureFunction->method == TextureFunction::SIZE) - { - 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(); + if (mUsesFrontFacing) + { + out << "#define GL_USES_FRONT_FACING\n"; + } - 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"; + if (mUsesPointSize) + { + out << "#define GL_USES_POINT_SIZE\n"; + } - out << " uint mip = 0;\n"; + if (mHasMultiviewExtensionEnabled) + { + out << "#define GL_ANGLE_MULTIVIEW_ENABLED\n"; + } - out << " " << textureReference - << ".GetDimensions(mip, width, height, layers, levels);\n"; + if (mUsesViewID) + { + out << "#define GL_USES_VIEW_ID\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"; + if (mUsesFragDepth) + { + out << "#define GL_USES_FRAG_DEPTH\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"; + if (mUsesDepthRange) + { + out << "#define GL_USES_DEPTH_RANGE\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"; + if (mUsesNumWorkGroups) + { + out << "#define GL_USES_NUM_WORK_GROUPS\n"; + } - out << " t.x = (u * 0.5f / m) + 0.5f;\n"; - out << " t.y = (v * 0.5f / m) + 0.5f;\n"; + if (mUsesWorkGroupID) + { + out << "#define GL_USES_WORK_GROUP_ID\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 (mUsesLocalInvocationID) + { + out << "#define GL_USES_LOCAL_INVOCATION_ID\n"; + } - if (textureFunction->method == TextureFunction::LOD0) - { - out << " uint mip = 0;\n"; - } - else if (textureFunction->method == TextureFunction::LOD0BIAS) - { - out << " uint mip = bias;\n"; - } - else - { + if (mUsesGlobalInvocationID) + { + out << "#define GL_USES_GLOBAL_INVOCATION_ID\n"; + } - 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 << "\n" - "}\n" - "\n"; - } - - if (mUsesFragCoord) - { - out << "#define GL_USES_FRAG_COORD\n"; - } - - if (mUsesPointCoord) - { - out << "#define GL_USES_POINT_COORD\n"; - } - - if (mUsesFrontFacing) - { - out << "#define GL_USES_FRONT_FACING\n"; - } - - if (mUsesPointSize) - { - out << "#define GL_USES_POINT_SIZE\n"; - } - - if (mUsesFragDepth) - { - out << "#define GL_USES_FRAG_DEPTH\n"; - } - - if (mUsesDepthRange) - { - out << "#define GL_USES_DEPTH_RANGE\n"; - } + if (mUsesLocalInvocationIndex) + { + out << "#define GL_USES_LOCAL_INVOCATION_INDEX\n"; + } if (mUsesXor) { @@ -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) + { + return false; + } + switch (ancestorBinary->getOp()) { - TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); - if (rightAgg != nullptr && rightAgg->isConstructor()) + 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; - } - // 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 - return false; + break; } - else if (writeConstantInitialization(out, symbolNode, expression)) - { + case EOpIndexDirect: + break; + default: + // Returning a sampler from indirect indexing is not supported. return false; - } - } - 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 + } + 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); + } + 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) + { + 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 + { + 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,534 +1475,485 @@ 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()); - - size_t index = mCallDag.findIndex(node); - ASSERT(index != CallDAG::InvalidIndex); - mCurrentFunctionMetadata = &mASTMetadataList[index]; + TIntermSymbol *symbol = variable->getAsSymbolNode(); + ASSERT(symbol); // Varying declarations can't have initializers. - out << TypeString(node->getType()) << " "; - - if (name == "main") - { - out << "gl_main("; - } - else - { - out << DecorateFunctionIfNeeded(node->getNameObj()) - << (mOutputLod0Function ? "Lod0(" : "("); - } - - TIntermSequence *sequence = node->getSequence(); - TIntermSequence *arguments = (*sequence)[0]->getAsAggregate()->getSequence(); - - for (unsigned int i = 0; i < arguments->size(); i++) - { - TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); + // Vertex outputs which are declared but not written to should still be declared to + // allow successful linking. + mReferencedVaryings[symbol->getSymbol()] = symbol; + } + } + return false; +} - if (symbol) - { - ensureStructDefined(symbol->getType()); +bool OutputHLSL::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) +{ + // Do not do any translation + return false; +} - out << argumentString(symbol); +bool OutputHLSL::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node) +{ + TInfoSinkBase &out = getInfoSink(); - if (i < arguments->size() - 1) - { - out << ", "; - } - } - else UNREACHABLE(); - } + 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; + } - out << ")\n"; + TIntermSequence *arguments = node->getSequence(); - 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"; - } + TString name = DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()); + out << TypeString(node->getType()) << " " << name << DisambiguateFunctionName(arguments) + << (mOutputLod0Function ? "Lod0(" : "("); - mCurrentFunctionMetadata = nullptr; + for (unsigned int i = 0; i < arguments->size(); i++) + { + TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode(); + ASSERT(symbol != nullptr); - bool needsLod0 = mASTMetadataList[index].mNeedsLod0; - if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) - { - ASSERT(name != "main"); - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } + out << argumentString(symbol); - return false; - } - break; - case EOpFunctionCall: + if (i < arguments->size() - 1) { - TIntermSequence *arguments = node->getSequence(); - - bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; - if (node->isUserDefined()) - { - if (node->isArray()) - { - UNIMPLEMENTED(); - } - size_t index = mCallDag.findIndex(node); - ASSERT(index != CallDAG::InvalidIndex); - lod0 &= mASTMetadataList[index].mNeedsLod0; - - out << DecorateFunctionIfNeeded(node->getNameObj()) << (lod0 ? "Lod0(" : "("); - } - else - { - TString name = TFunction::unmangleName(node->getNameObj().getString()); - TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType(); + out << ", "; + } + } - TextureFunction textureFunction; - textureFunction.sampler = samplerType; - textureFunction.coords = (*arguments)[1]->getAsTyped()->getNominalSize(); - textureFunction.method = TextureFunction::IMPLICIT; - textureFunction.proj = false; - textureFunction.offset = false; + out << ");\n"; - 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(); + // 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; + } - if (textureFunction.method == TextureFunction::IMPLICIT) // Could require lod 0 or have a bias argument - { - unsigned int mandatoryArgumentCount = 2; // All functions have sampler and coordinate arguments + return false; +} - if (textureFunction.offset) - { - mandatoryArgumentCount++; - } +bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) +{ + TInfoSinkBase &out = getInfoSink(); - bool bias = (arguments->size() > mandatoryArgumentCount); // Bias argument is optional + switch (node->getOp()) + { + case EOpCallBuiltInFunction: + case EOpCallFunctionInAST: + case EOpCallInternalRawFunction: + { + TIntermSequence *arguments = node->getSequence(); - if (lod0 || mShaderType == GL_VERTEX_SHADER) - { - if (bias) - { - textureFunction.method = TextureFunction::LOD0BIAS; - } - else - { - textureFunction.method = TextureFunction::LOD0; - } - } - else if (bias) - { - textureFunction.method = TextureFunction::BIAS; - } + bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; + if (node->getOp() == EOpCallFunctionInAST) + { + if (node->isArray()) + { + UNIMPLEMENTED(); } + size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo()); + ASSERT(index != CallDAG::InvalidIndex); + lod0 &= mASTMetadataList[index].mNeedsLod0; - mUsesTexture.insert(textureFunction); - - out << textureFunction.name(); + 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 + { + const TString &name = node->getFunctionSymbolInfo()->getName(); + TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType(); + int coords = 0; // textureSize(gsampler2DMS) doesn't have a second argument. + if (arguments->size() > 1) + { + coords = (*arguments)[1]->getAsTyped()->getNominalSize(); + } + TString textureFunctionName = mTextureFunctionHLSL->useTextureFunction( + name, samplerType, coords, arguments->size(), lod0, mShaderType); + out << textureFunctionName << "("; } for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++) { - if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && - IsSampler((*arg)->getAsTyped()->getBasicType())) + TIntermTyped *typedArg = (*arg)->getAsTyped(); + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(typedArg->getBasicType())) { out << "texture_"; (*arg)->traverse(this); @@ -2469,6 +1962,29 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) (*arg)->traverse(this); + if (typedArg->getType().isStructureContainingSamplers()) + { + const TType &argType = typedArg->getType(); + TVector samplerSymbols; + TString structName = samplerNamePrefixFromStruct(typedArg); + argType.createSamplerSymbols("angle_" + structName, "", &samplerSymbols, + nullptr, mSymbolTable); + for (const TIntermSymbol *sampler : samplerSymbols) + { + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) + { + out << ", texture_" << sampler->getSymbol(); + out << ", sampler_" << sampler->getSymbol(); + } + else + { + // 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(); + } + } + } + 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()); - break; - case EOpConstructUVec4: - outputConstructor(out, visit, node->getType(), "uvec4", node->getSequence()); - break; - case EOpConstructMat2: - outputConstructor(out, visit, node->getType(), "mat2", node->getSequence()); + case EOpConstruct: + outputConstructor(out, visit, node); 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(); - } + TInfoSinkBase &out = getInfoSink(); - return true; -} - -bool OutputHLSL::isSingleStatement(TIntermNode *node) -{ - TIntermAggregate *aggregate = node->getAsAggregate(); - - if (aggregate) - { - if (aggregate->getOp() == EOpSequence) - { - 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 + switch (node->getFlowOp()) { - 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 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; + function->type = type; - TInfoSinkBase fnNameOut; - fnNameOut << "angle_eq_" << type.getArraySize() << "_" << typeName; - function->functionName = fnNameOut.c_str(); - - 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 -#include #include #include #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 ReferencedSymbols; +typedef std::map 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 &uniforms, - int compileOptions); + OutputHLSL(sh::GLenum shaderType, + int shaderVersion, + const TExtensionBehavior &extensionBehavior, + const char *sourcePath, + ShShaderOutput outputType, + int numRenderTargets, + const std::vector &uniforms, + ShCompileOptions compileOptions, + TSymbolTable *symbolTable, + PerformanceDiagnostics *perfDiagnostics); ~OutputHLSL(); void output(TIntermNode *treeRoot, TInfoSinkBase &objSink); - const std::map &getInterfaceBlockRegisterMap() const; + const std::map &getUniformBlockRegisterMap() const; const std::map &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 &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 &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 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 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 mFlaggedStructMappedNames; - std::map 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 mEqualityFunctions; + std::vector mEqualityFunctions; struct StructEqualityFunction : public HelperFunction { const TStructure *structure; }; - std::vector mStructEqualityFunctions; + std::vector mStructEqualityFunctions; struct ArrayHelperFunction : public HelperFunction { TType type; }; - std::vector mArrayEqualityFunctions; + std::vector mArrayEqualityFunctions; std::vector 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 mArrayConstructIntoFunctions; -}; + PerformanceDiagnostics *mPerfDiagnostics; + + private: + TString generateStructMapping(const std::vector &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 << ""; + } + + 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(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 #include +#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 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(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 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 *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; @@ -132,52 +350,31 @@ 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 ¶m = 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(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(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 (arrayArg && op != EOpConstructStruct) - { - error(line, "constructing from a non-dereferenced array", "constructor"); - return true; - } - - if (matrixInMatrix && !type->isArray()) - { - if (function.getParamCount() != 1) + if (argTyped->getBasicType() == EbtVoid) { - error(line, "constructing matrix from matrix can only take one argument", - "constructor"); - return true; + error(line, "cannot convert a void", "constructor"); + return false; } } - if (overFull) + if (type.isArray()) { - error(line, "too many arguments", "constructor"); - return true; - } - - if (op == EOpConstructStruct && !type->isArray() && - type->getStruct()->fields().size() != function.getParamCount()) - { - error(line, - "Number of constructor parameters does not match the number of structure fields", - "constructor"); - return true; + // The size of an unsized constructor should already have been determined. + ASSERT(!type.isUnsizedArray()); + if (static_cast(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 (!type->isMatrix() || !matrixInMatrix) + else if (type.getBasicType() == EbtStruct) { - if ((op != EOpConstructStruct && size != 1 && size < type->getObjectSize()) || - (op == EOpConstructStruct && size < type->getObjectSize())) + const TFieldList &fields = type.getStruct()->fields(); + if (fields.size() != arguments->size()) { - error(line, "not enough data provided for construction", "constructor"); - return true; + error(line, + "Number of constructor parameters does not match the number of structure fields", + "constructor"); + return false; } - } - if (argumentsNode == nullptr) - { - error(line, "constructor does not have any 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; + } + } } - - TIntermAggregate *argumentsAgg = argumentsNode->getAsAggregate(); - for (TIntermNode *&argNode : *argumentsAgg->getSequence()) + else { - TIntermTyped *argTyped = argNode->getAsTyped(); - ASSERT(argTyped != nullptr); - if (op != EOpConstructStruct && IsSampler(argTyped->getBasicType())) + // We're constructing a scalar, vector, or matrix. + + // 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, "cannot convert a sampler", "constructor"); - return true; + const TIntermTyped *argTyped = arg->getAsTyped(); + ASSERT(argTyped != nullptr); + + 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; + } + + size += argTyped->getType().getObjectSize(); + if (full) + { + overFull = true; + } + if (size >= type.getObjectSize()) + { + full = true; + } } - if (argTyped->getBasicType() == EbtVoid) + + if (type.isMatrix() && matrixArg) { - error(line, "cannot convert a void", "constructor"); - return true; + if (arguments->size() != 1) + { + error(line, "constructing matrix from matrix can only take one argument", + "constructor"); + return false; + } + } + else + { + 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(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(size); + size = static_cast(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( 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(type.getOutermostArraySize()) == + maxDrawBuffers->getConstPointer()->getIConst()) { if (TSymbol *builtInSymbol = symbolTable.findBuiltIn(identifier, mShaderVersion)) { - needsReservedErrorCheck = extensionErrorCheck(line, builtInSymbol->getExtension()); + needsReservedCheck = !checkCanUseExtension(line, builtInSymbol->getExtension()); } } else @@ -931,184 +1136,602 @@ 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) { - if (qualifier != EvqConst && qualifier != EvqTemporary) + // 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) { - error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier)); - return true; + checkOutParameterIsNotOpaqueType(line, typeQualifier.qualifier, *type); } - if (qualifier == EvqConst && paramQualifier != EvqIn) + + if (!IsImage(type->getBasicType())) { - error(line, "qualifier not allowed with ", getQualifierString(qualifier), - getQualifierString(paramQualifier)); - return true; + checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, line); } - - if (qualifier == EvqConst) - type->setQualifier(EvqConstReadOnly); else - type->setQualifier(paramQualifier); + { + type->setMemoryQualifier(typeQualifier.memoryQualifier); + } - return false; + type->setQualifier(typeQualifier.qualifier); + + if (typeQualifier.precision != EbpUndefined) + { + type->setPrecision(typeQualifier.precision); + } } -bool TParseContext::extensionErrorCheck(const TSourceLoc &line, const TString &extension) +template +bool TParseContext::checkCanUseOneOfExtensions(const TSourceLoc &line, + const std::array &extensions) { - const TExtensionBehavior &extBehavior = extensionBehavior(); - TExtensionBehavior::const_iterator iter = extBehavior.find(extension.c_str()); - if (iter == extBehavior.end()) + ASSERT(!extensions.empty()); + const TExtensionBehavior &extBehavior = extensionBehavior(); + + bool canUseWithWarning = false; + bool canUseWithoutWarning = false; + + const char *errorMsgString = ""; + TExtension errorMsgExtension = TExtension::UNDEFINED; + + for (TExtension extension : extensions) { - error(line, "extension", extension.c_str(), "is not supported"); - return true; + 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; + } } - // In GLSL ES, an extension's default behavior is "disable". - if (iter->second == EBhDisable || iter->second == EBhUndefined) + + if (canUseWithoutWarning) { - error(line, "extension", extension.c_str(), "is disabled"); return true; } - if (iter->second == EBhWarn) + if (canUseWithWarning) { - warning(line, "extension", extension.c_str(), "is being used"); - return false; + warning(line, "extension is being used", GetExtensionNameString(errorMsgExtension)); + return true; } - + error(line, errorMsgString, GetExtensionNameString(errorMsgExtension)); return false; } -// 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) +template bool TParseContext::checkCanUseOneOfExtensions( + const TSourceLoc &line, + const std::array &extensions); +template bool TParseContext::checkCanUseOneOfExtensions( + const TSourceLoc &line, + const std::array &extensions); +template bool TParseContext::checkCanUseOneOfExtensions( + const TSourceLoc &line, + const std::array &extensions); + +bool TParseContext::checkCanUseExtension(const TSourceLoc &line, TExtension extension) { - switch (publicType.qualifier) + ASSERT(extension != TExtension::UNDEFINED); + ASSERT(extension != TExtension::EXT_geometry_shader); + if (extension == TExtension::OES_geometry_shader) { - case EvqVaryingIn: - case EvqVaryingOut: - case EvqAttribute: - case EvqVertexIn: - case EvqFragmentOut: - if (publicType.type == EbtStruct) - { - error(identifierLocation, "cannot be used with a structure", - getQualifierString(publicType.qualifier)); - return true; - } - - default: - break; + // OES_geometry_shader and EXT_geometry_shader are always interchangeable. + constexpr std::array extensions{ + {TExtension::EXT_geometry_shader, TExtension::OES_geometry_shader}}; + return checkCanUseOneOfExtensions(line, extensions); } + return checkCanUseOneOfExtensions(line, std::array{{extension}}); +} - if (publicType.qualifier != EvqUniform && - samplerErrorCheck(identifierLocation, publicType, "samplers must be uniform")) +// 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()) { - return true; + error(location, "Shared memory declarations cannot have layout specified", "layout"); } - // check for layout qualifier issues - const TLayoutQualifier layoutQualifier = publicType.layoutQualifier; - if (layoutQualifier.matrixPacking != EmpUnspecified) { - error(identifierLocation, "layout qualifier", - getMatrixPackingString(layoutQualifier.matrixPacking), - "only valid for interface blocks"); - return true; + error(location, "layout qualifier only valid for interface blocks", + getMatrixPackingString(layoutQualifier.matrixPacking)); + return; } if (layoutQualifier.blockStorage != EbsUnspecified) { - error(identifierLocation, "layout qualifier", - getBlockStorageString(layoutQualifier.blockStorage), - "only valid for interface blocks"); - return true; + error(location, "layout qualifier only valid for interface blocks", + getBlockStorageString(layoutQualifier.blockStorage)); + return; } - if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut && - layoutLocationErrorCheck(identifierLocation, publicType.layoutQualifier)) + if (qualifier == EvqFragmentOut) { - return true; + if (layoutQualifier.location != -1 && layoutQualifier.yuv == true) + { + error(location, "invalid layout qualifier combination", "yuv"); + return; + } + } + else + { + checkYuvIsNotSpecified(location, layoutQualifier.yuv); } - return false; -} - -bool TParseContext::layoutLocationErrorCheck(const TSourceLoc &location, - const TLayoutQualifier &layoutQualifier) -{ - if (layoutQualifier.location != -1) + // 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, "invalid layout qualifier:", "location", - "only valid on program inputs and outputs"); - return true; + error(location, "storage qualifier supported in GLSL ES 3.00 and above only", "in"); } - return false; + 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::functionCallLValueErrorCheck(const TFunction *fnCandidate, - TIntermAggregate *aggregate) +void TParseContext::atomicCounterQualifierErrorCheck(const TPublicType &publicType, + const TSourceLoc &location) { - for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) + if (publicType.precision != EbpHigh) { - TQualifier qual = fnCandidate->getParam(i).type->getQualifier(); - if (qual == EvqOut || qual == EvqInOut) - { - TIntermTyped *node = (*(aggregate->getSequence()))[i]->getAsTyped(); - if (lValueErrorCheck(node->getLine(), "assign", node)) + error(location, "Can only be highp", "atomic counter"); + } + // dEQP enforces compile error if location is specified. See uniform_location.test. + if (publicType.layoutQualifier.location != -1) + { + error(location, "location must not be set for atomic_uint", "layout"); + } + if (publicType.layoutQualifier.binding == -1) + { + error(location, "no binding specified", "atomic counter"); + } +} + +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 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) + { + case EvqVaryingIn: + case EvqVaryingOut: + case EvqAttribute: + case EvqVertexIn: + case EvqFragmentOut: + case EvqComputeIn: + if (publicType.getBasicType() == EbtStruct) { - error(node->getLine(), - "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error"); - recover(); - return true; + error(identifierLocation, "cannot be used with a structure", + getQualifierString(publicType.qualifier)); + 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 && + !checkIsNotOpaqueType(identifierLocation, publicType.typeSpecifierNonArray, reason.c_str())) + { + 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); } } - return false; + + // check for layout qualifier issues + const TLayoutQualifier layoutQualifier = publicType.layoutQualifier; + + if (IsImage(publicType.getBasicType())) + { + + 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 (IsAtomicCounter(publicType.getBasicType())) + { + atomicCounterQualifierErrorCheck(publicType, identifierLocation); + } + else + { + checkOffsetIsNotSpecified(identifierLocation, layoutQualifier.offset); + } +} + +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())) + { + 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); + } +} + +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::checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location, + const TLayoutQualifier &layoutQualifier) +{ + const sh::WorkGroupSize &localSize = layoutQualifier.localSize; + for (size_t i = 0u; i < localSize.size(); ++i) + { + 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 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::es3InvariantErrorCheck(const TQualifier qualifier, - const TSourceLoc &invariantLocation) +void TParseContext::checkSamplerBindingIsValid(const TSourceLoc &location, + int binding, + int arrayTotalElementCount) { - if (!sh::IsVaryingOut(qualifier) && qualifier != EvqFragmentOut) + // Expects arraySize to be 1 when setting binding for only a single variable. + if (binding >= 0 && binding + arrayTotalElementCount > mMaxCombinedTextureImageUnits) { - error(invariantLocation, "Only out variables can be invariant.", "invariant"); - recover(); + error(location, "sampler binding greater than maximum texture units", "binding"); } } -bool TParseContext::supportsExtension(const char *extension) +void TParseContext::checkBlockBindingIsValid(const TSourceLoc &location, + const TQualifier &qualifier, + int binding, + int arraySize) { - const TExtensionBehavior &extbehavior = extensionBehavior(); - TExtensionBehavior::const_iterator iter = extbehavior.find(extension); - return (iter != extbehavior.end()); + 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"); + } +} + +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) + { + if (!checkCanBeLValue(argument->getLine(), "assign", argument)) + { + error(argument->getLine(), + "Constant value cannot be passed for 'out' or 'inout' parameters.", + fnCall->getFunctionSymbolInfo()->getName().c_str()); + return; + } + } + } +} + +void TParseContext::checkInvariantVariableQualifier(bool invariant, + const TQualifier qualifier, + const TSourceLoc &invariantLocation) +{ + if (!invariant) + return; + + if (mShaderVersion < 300) + { + // 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::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(symbol); - if (symbolTable.findBuiltIn(variable->getName(), mShaderVersion) && - !variable->getExtension().empty() && - extensionErrorCheck(location, variable->getExtension())) - { - recover(); - } + const TVariable *variable = static_cast(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(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(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; } -TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, - bool invariant, - TLayoutQualifier layoutQualifier, +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(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(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); + } + } +} - mDeferredSingleDeclarationErrorCheck = emptyDeclaration; +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); + } + } + + 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 &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); + + TIntermDeclaration *declaration = new TIntermDeclaration(); + declaration->setLine(identifierLocation); - TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation); - if (variable && symbol) - symbol->setId(variable->getUniqueId()); + if (variable) + { + TIntermSymbol *symbol = new TIntermSymbol(variable->getUniqueId(), identifier, arrayType); + symbol->setLine(identifierLocation); + declaration->appendDeclarator(symbol); + } - return intermediate.makeAggregate(symbol, identifierLocation); + 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 &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); + + checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), &type); + + checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &type); - TIntermSymbol *symbol = - intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation); - if (variable && symbol) - symbol->setId(variable->getUniqueId()); + checkAtomicCounterOffsetDoesNotOverlap(true, identifierLocation, &type); - return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation); + 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 &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(); + checkDeclaratorLocationIsNotSpecified(identifierLocation, elementType); - if (nonInitErrorCheck(identifierLocation, identifier, &publicType)) - recover(); - - 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 &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"); + } +} - 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)) +bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier) +{ + ASSERT(typeQualifier.qualifier == EvqGeometryIn); + + const TLayoutQualifier &layoutQualifier = typeQualifier.layoutQualifier; + + 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)) { - return intermediate.growAggregate(aggregateDeclaration, initNode, initLocation); + error(typeQualifier.line, "invalid primitive type for 'in' layout", "layout"); + return false; } - else + + if (mGeometryShaderInputPrimitiveType == EptUndefined) { - return aggregateDeclaration; + mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType; + setGeometryShaderInputArraySize( + GetGeometryShaderInputArraySize(mGeometryShaderInputPrimitiveType), + typeQualifier.line); + } + else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType) + { + 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( + 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(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 ¶m = 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( + 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(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( + 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 ¶m = 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(symbolTable.find(function->getMangledName(), getShaderVersion())); - if (prevDec) + + for (size_t i = 0u; i < function->getParamCount(); ++i) + { + auto ¶m = 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; - - 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; - } + // 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); - 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 &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(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) { - recover(); + checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line); } + // add array index + unsigned int arraySize = 0; + if (arrayIndex != nullptr) + { + 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(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(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 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; } } } @@ -3109,265 +4205,608 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre { if (mShaderVersion < 300) { - error(dotLocation, " field selection requires structure or vector on left hand side", - fieldString.c_str()); + error(dotLocation, " field selection requires structure or vector on left hand side", + fieldString.c_str()); + } + else + { + error(dotLocation, + " field selection requires structure, vector, or interface block on left hand " + "side", + fieldString.c_str()); + } + return baseExpression; + } +} + +TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, + const TSourceLoc &qualifierTypeLine) +{ + 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; + } + else if (qualifierType == "row_major") + { + qualifier.matrixPacking = EmpRowMajor; + } + else if (qualifierType == "column_major") + { + qualifier.matrixPacking = EmpColumnMajor; + } + else if (qualifierType == "location") + { + 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()); + } + + 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, + int intValue, + const TSourceLoc &intValueLine) +{ + TLayoutQualifier qualifier = TLayoutQualifier::Create(); + + std::string intValueString = Str(intValue); + + 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") + { + 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 if (qualifierType == "offset") + { + checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310); + if (intValue < 0) + { + error(intValueLine, "out of range: offset must be non-negative", + intValueString.c_str()); } else { - error(dotLocation, - " field selection requires structure, vector, or interface block on left hand " - "side", - fieldString.c_str()); + qualifier.offset = intValue; } - recover(); - indexedExpression = baseExpression; - } - - if (baseExpression->getQualifier() == EvqConst) - { - indexedExpression->getTypePointer()->setQualifier(EvqConst); - } - else - { - indexedExpression->getTypePointer()->setQualifier(EvqTemporary); } - - return indexedExpression; -} - -TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, - const TSourceLoc &qualifierTypeLine) -{ - TLayoutQualifier qualifier; - - qualifier.location = -1; - qualifier.matrixPacking = EmpUnspecified; - qualifier.blockStorage = EbsUnspecified; - - if (qualifierType == "shared") + else if (qualifierType == "local_size_x") { - qualifier.blockStorage = EbsShared; + parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u, + &qualifier.localSize); } - else if (qualifierType == "packed") + else if (qualifierType == "local_size_y") { - qualifier.blockStorage = EbsPacked; + parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 1u, + &qualifier.localSize); } - else if (qualifierType == "std140") + else if (qualifierType == "local_size_z") { - qualifier.blockStorage = EbsStd140; + parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 2u, + &qualifier.localSize); } - else if (qualifierType == "row_major") + else if (qualifierType == "num_views" && mShaderType == GL_VERTEX_SHADER) { - qualifier.matrixPacking = EmpRowMajor; + if (checkCanUseExtension(qualifierTypeLine, TExtension::OVR_multiview)) + { + parseNumViews(intValue, intValueLine, intValueString, &qualifier.numViews); + } } - else if (qualifierType == "column_major") + else if (qualifierType == "invocations" && mShaderType == GL_GEOMETRY_SHADER_OES && + checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader)) { - qualifier.matrixPacking = EmpColumnMajor; + parseInvocations(intValue, intValueLine, intValueString, &qualifier.invocations); } - else if (qualifierType == "location") + else if (qualifierType == "max_vertices" && mShaderType == GL_GEOMETRY_SHADER_OES && + checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader)) { - error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), - "location requires an argument"); - recover(); + parseMaxVertices(intValue, intValueLine, intValueString, &qualifier.maxVertices); } + else { error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str()); - recover(); } return qualifier; } -TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, - const TSourceLoc &qualifierTypeLine, - const TString &intValueString, - int intValue, - const TSourceLoc &intValueLine) +TTypeQualifierBuilder *TParseContext::createTypeQualifierBuilder(const TSourceLoc &loc) { - TLayoutQualifier qualifier; + return new TTypeQualifierBuilder( + new TStorageQualifierWrapper(symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary, loc), + mShaderVersion); +} - qualifier.location = -1; - qualifier.matrixPacking = EmpUnspecified; - qualifier.blockStorage = EbsUnspecified; +TStorageQualifierWrapper *TParseContext::parseGlobalStorageQualifier(TQualifier qualifier, + const TSourceLoc &loc) +{ + checkIsAtGlobalLevel(loc, getQualifierString(qualifier)); + return new TStorageQualifierWrapper(qualifier, loc); +} - if (qualifierType != "location") +TStorageQualifierWrapper *TParseContext::parseVaryingQualifier(const TSourceLoc &loc) +{ + if (getShaderType() == GL_VERTEX_SHADER) { - error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), - "only location may have arguments"); - recover(); + return parseGlobalStorageQualifier(EvqVaryingOut, loc); } - else + return parseGlobalStorageQualifier(EvqVaryingIn, loc); +} + +TStorageQualifierWrapper *TParseContext::parseInQualifier(const TSourceLoc &loc) +{ + if (declaringFunction()) { - // must check that location is non-negative - if (intValue < 0) + return new TStorageQualifierWrapper(EvqIn, loc); + } + + switch (getShaderType()) + { + case GL_VERTEX_SHADER: { - error(intValueLine, "out of range:", intValueString.c_str(), - "location must be non-negative"); - recover(); + 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); } - else + 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: { - qualifier.location = intValue; + UNREACHABLE(); + return new TStorageQualifierWrapper(EvqLast, loc); } } - - return qualifier; } -TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier, - TLayoutQualifier rightQualifier) +TStorageQualifierWrapper *TParseContext::parseOutQualifier(const TSourceLoc &loc) { - TLayoutQualifier joinedQualifier = leftQualifier; - - if (rightQualifier.location != -1) + if (declaringFunction()) { - joinedQualifier.location = rightQualifier.location; + return new TStorageQualifierWrapper(EvqOut, loc); } - if (rightQualifier.matrixPacking != EmpUnspecified) + switch (getShaderType()) { - joinedQualifier.matrixPacking = rightQualifier.matrixPacking; + 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); + } } - if (rightQualifier.blockStorage != EbsUnspecified) +} + +TStorageQualifierWrapper *TParseContext::parseInOutQualifier(const TSourceLoc &loc) +{ + if (!declaringFunction()) { - joinedQualifier.blockStorage = rightQualifier.blockStorage; + error(loc, "invalid qualifier: can be only used with function parameters", "inout"); } + return new TStorageQualifierWrapper(EvqInOut, loc); +} - return joinedQualifier; +TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier, + TLayoutQualifier rightQualifier, + const TSourceLoc &rightQualifierLocation) +{ + return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation, + mDiagnostics); } -TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, - TQualifier interpolationQualifier, - const TSourceLoc &storageLoc, - TQualifier storageQualifier) +TField *TParseContext::parseStructDeclarator(TString *identifier, const TSourceLoc &loc) { - TQualifier mergedQualifier = EvqSmoothIn; + checkIsNotReserved(loc, *identifier); + TType *type = new TType(EbtVoid, EbpUndefined); + return new TField(type, identifier, loc); +} - if (storageQualifier == EvqFragmentIn) - { - if (interpolationQualifier == EvqSmooth) - mergedQualifier = EvqSmoothIn; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatIn; - else - UNREACHABLE(); - } - else if (storageQualifier == EvqCentroidIn) +TField *TParseContext::parseStructArrayDeclarator(TString *identifier, + const TSourceLoc &loc, + const TVector &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 = EvqCentroidIn; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatIn; - else - UNREACHABLE(); + if ((*fieldIter)->name() == name) + { + error(location, "duplicate field name in structure", name.c_str()); + } } - else if (storageQualifier == EvqVertexOut) +} + +TFieldList *TParseContext::addStructFieldList(TFieldList *fields, const TSourceLoc &location) +{ + for (TFieldList::const_iterator fieldIter = fields->begin(); fieldIter != fields->end(); + ++fieldIter) { - if (interpolationQualifier == EvqSmooth) - mergedQualifier = EvqSmoothOut; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatOut; - else - UNREACHABLE(); + checkDoesNotHaveDuplicateFieldName(fields->begin(), fieldIter, (*fieldIter)->name(), + location); } - else if (storageQualifier == EvqCentroidOut) + return fields; +} + +TFieldList *TParseContext::combineStructFieldLists(TFieldList *processedFields, + const TFieldList *newlyAddedFields, + const TSourceLoc &location) +{ + for (TField *field : *newlyAddedFields) { - if (interpolationQualifier == EvqSmooth) - mergedQualifier = EvqCentroidOut; - else if (interpolationQualifier == EvqFlat) - mergedQualifier = EvqFlatOut; - else - UNREACHABLE(); + checkDoesNotHaveDuplicateFieldName(processedFields->begin(), processedFields->end(), + field->name(), location); + processedFields->push_back(field); } - else - { - error(interpolationLoc, - "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier", - getInterpolationString(interpolationQualifier)); - recover(); + return processedFields; +} - mergedQualifier = storageQualifier; - } +TFieldList *TParseContext::addStructDeclaratorListWithQualifiers( + const TTypeQualifierBuilder &typeQualifierBuilder, + TPublicType *typeSpecifier, + TFieldList *fieldList) +{ + TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics); - TPublicType type; - type.setBasic(EbtVoid, mergedQualifier, storageLoc); - return type; + typeSpecifier->qualifier = typeQualifier.qualifier; + typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier; + typeSpecifier->memoryQualifier = typeQualifier.memoryQualifier; + typeSpecifier->invariant = typeQualifier.invariant; + if (typeQualifier.precision != EbpUndefined) + { + typeSpecifier->precision = typeQualifier.precision; + } + return addStructDeclaratorList(*typeSpecifier, fieldList); } TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier, - TFieldList *fieldList) + TFieldList *declaratorList) { - if (voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type)) - { - recover(); - } + checkPrecisionSpecified(typeSpecifier.getLine(), typeSpecifier.precision, + typeSpecifier.getBasicType()); - for (unsigned int i = 0; i < fieldList->size(); ++i) - { - // - // 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); + checkIsNonVoid(typeSpecifier.getLine(), (*declaratorList)[0]->name(), + typeSpecifier.getBasicType()); - // 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) + 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)) + checkIsNotReserved(nameLine, *structName); + if (!symbolTable.declareStructType(structure)) { - recover(); - } - TVariable *userTypeDef = new TVariable(structName, *structureType, true); - if (!symbolTable.declare(userTypeDef)) - { - 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) - { - if (!ValidateSwitch::validate(switchType, this, statementList, loc)) - { - recover(); - return nullptr; - } - } - - TIntermSwitch *node = intermediate.addSwitch(init, statementList, loc); - if (node == nullptr) + ASSERT(statementList); + if (!ValidateSwitchStatementList(switchType, mShaderVersion, mDiagnostics, statementList, loc)) { - 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,49 +4954,134 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op, break; } - return intermediate.addUnaryMath(op, child, loc, funcReturnType); -} - -TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc) -{ - TIntermTyped *node = createUnaryMath(op, child, loc, nullptr); - if (node == nullptr) + 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) +{ + ASSERT(op != EOpNull); + TIntermTyped *node = createUnaryMath(op, child, loc); + if (node == nullptr) + { + return child; + } + return node; +} + +TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op, + TIntermTyped *child, + const TSourceLoc &loc) +{ + checkCanBeLValue(loc, GetOperatorString(op), child); + return addUnaryMath(op, child, loc); +} + +bool TParseContext::binaryOpCommonCheck(TOperator op, + TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) +{ + // 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())) + { + switch (op) + { + 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->getMemoryQualifier().writeonly) + { + switch (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()) { - unaryOpError(loc, GetOperatorString(op), child->getCompleteString()); - recover(); - return child; + error(loc, "array / non-array mismatch", GetOperatorString(op)); + return false; } - return node; -} - -TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op, - TIntermTyped *child, - const TSourceLoc &loc) -{ - if (lValueErrorCheck(loc, GetOperatorString(op), child)) - recover(); - return addUnaryMath(op, child, loc); -} -bool TParseContext::binaryOpCommonCheck(TOperator op, - TIntermTyped *left, - TIntermTyped *right, - const TSourceLoc &loc) -{ - if (left->isArray() || right->isArray()) + if (left->isArray()) { + ASSERT(right->isArray()); if (mShaderVersion < 300) { error(loc, "Invalid operation for arrays", GetOperatorString(op)); return false; } - if (left->isArray() != right->isArray()) - { - error(loc, "array / non-array mismatch", GetOperatorString(op)); - return false; - } - switch (op) { case EOpEqual: @@ -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(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(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); + + callNode->setLine(loc); - functionCallLValueErrorCheck(fnCandidate, aggregate); + 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; } - // ESSL1 sections 5.2 and 5.7: - // ESSL3 section 5.7: + + if (cond->getMemoryQualifier().writeonly || trueExpression->getMemoryQualifier().writeonly || + falseExpression->getMemoryQualifier().writeonly) + { + error(loc, "ternary operator is not allowed for variables with writeonly", "?:"); + return falseExpression; + } + + // 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 *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 + bool checkCanUseOneOfExtensions(const TSourceLoc &line, + const std::array &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 &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 &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 &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 &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 &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 &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 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 #include #include +#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(GetTLSValue(PoolIndex)); + return static_cast(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(inUseList); + delete[] reinterpret_cast(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(freeList); + while (freeList) + { + tHeader *next = freeList->nextPage; + delete[] reinterpret_cast(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(inUseList); - else { + delete[] reinterpret_cast(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(inUseList) + currentPageOffset; + unsigned char *memory = reinterpret_cast(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(::new char[numBytesToAlloc]); + tHeader *memory = reinterpret_cast(::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(reinterpret_cast(memory) + headerSkip); + return reinterpret_cast(reinterpret_cast(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(::new char[pageSize]); + } + else + { + memory = reinterpret_cast(::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(inUseList) + headerSkip; - currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask; + + unsigned char *ret = reinterpret_cast(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(alloc); + intAlloc = (intAlloc + alignmentMask) & ~alignmentMask; + return reinterpret_cast(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 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(memory); + new (memory) TAllocation(numBytes, memory, block->lastAllocation); + block->lastAllocation = reinterpret_cast(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> 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 pool_allocator { -public: +template +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 - struct rebind { + template + struct rebind + { typedef pool_allocator other; }; pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } - pool_allocator() { } + pool_allocator() {} - template - pool_allocator(const pool_allocator& p) { } + template + pool_allocator(const pool_allocator &p) + { + } template - pool_allocator& operator=(const pool_allocator& p) { return *this; } + pool_allocator &operator=(const pool_allocator &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(getAllocator().allocate(n * sizeof(T))); } - pointer allocate(size_type n, const void*) { + pointer allocate(size_type n, const void *) + { return reinterpret_cast(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(-1) / sizeof(T); } size_type max_size(int size) const { return static_cast(-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 + +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(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 ¤tQualifier = + static_cast(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(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(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(qualifiers[i])->getQualifier(); + for (size_t j = 1; j < i; ++j) + { + if (qualifiers[j]->getType() == QtMemory) + { + const TMemoryQualifierWrapper *previousQualifierWrapper = + static_cast(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(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(qualifier) + ->getQualifier(); + break; + default: + isQualifierValid = false; + } + break; + } + case QtLayout: + { + const TLayoutQualifierWrapper *layoutQualifierWrapper = + static_cast(qualifier); + isQualifierValid = true; + typeQualifier.layoutQualifier = sh::JoinLayoutQualifiers( + typeQualifier.layoutQualifier, layoutQualifierWrapper->getQualifier(), + layoutQualifierWrapper->getLine(), diagnostics); + break; + } + case QtStorage: + isQualifierValid = JoinVariableStorageQualifier( + &typeQualifier.qualifier, + static_cast(qualifier)->getQualifier()); + break; + case QtPrecision: + isQualifierValid = true; + typeQualifier.precision = + static_cast(qualifier)->getQualifier(); + ASSERT(typeQualifier.precision != EbpUndefined); + break; + case QtMemory: + isQualifierValid = JoinMemoryQualifier( + &typeQualifier.memoryQualifier, + static_cast(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(qualifier)->getQualifier()); + break; + case QtStorage: + isQualifierValid = JoinParameterStorageQualifier( + &typeQualifier.qualifier, + static_cast(qualifier)->getQualifier()); + break; + case QtPrecision: + isQualifierValid = true; + typeQualifier.precision = + static_cast(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(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(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; + + 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 +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 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 = ; +// func(); +// int j = ; +// +// 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 = ; +// func(); +// int j = ; +// +// 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 mIndexedVecAndMatrixTypes; - std::set 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 mIndexedVecAndMatrixTypes; + std::map 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 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 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; + 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 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(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 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 const std::vector *GetVariableList(const TCompiler *compiler); template <> -const std::vector *GetVariableList(const TCompiler *compiler) +const std::vector *GetVariableList(const TCompiler *compiler) { return &compiler->getUniforms(); } template <> -const std::vector *GetVariableList(const TCompiler *compiler) +const std::vector *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 *GetVariableList(const TCompiler *compiler) +const std::vector *GetVariableList(const TCompiler *compiler) { return &compiler->getAttributes(); } template <> -const std::vector *GetVariableList(const TCompiler *compiler) +const std::vector *GetVariableList(const TCompiler *compiler) { return &compiler->getOutputVariables(); } template <> -const std::vector *GetVariableList(const TCompiler *compiler) +const std::vector *GetVariableList(const TCompiler *compiler) { return &compiler->getInterfaceBlocks(); } -template -const std::vector *GetShaderVariables(const ShHandle handle) +TCompiler *GetCompilerFromHandle(ShHandle handle) { if (!handle) { - return NULL; + return nullptr; } - TShHandleBase* base = static_cast(handle); - TCompiler* compiler = base->getAsCompiler(); + TShHandleBase *base = static_cast(handle); + return base->getAsCompiler(); +} + +template +const std::vector *GetShaderVariables(const ShHandle handle) +{ + TCompiler *compiler = GetCompilerFromHandle(handle); if (!compiler) { - return NULL; + return nullptr; } return GetVariableList(compiler); } -TCompiler *GetCompilerFromHandle(ShHandle handle) -{ - if (!handle) - return NULL; - TShHandleBase *base = static_cast(handle); - return base->getAsCompiler(); -} - #ifdef ANGLE_ENABLE_HLSL TranslatorHLSL *GetTranslatorHLSLFromHandle(ShHandle handle) { if (!handle) - return NULL; + return nullptr; TShHandleBase *base = static_cast(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(ConstructCompiler(type, spec, output)); + TShHandleBase *base = static_cast(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(base); + return reinterpret_cast(base); } -void ShDestruct(ShHandle handle) +void Destruct(ShHandle handle) { if (handle == 0) return; - TShHandleBase* base = static_cast(handle); + TShHandleBase *base = static_cast(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 *ShGetNameHashingMap( - const ShHandle handle) +const std::map *GetNameHashingMap(const ShHandle handle) { TCompiler *compiler = GetCompilerFromHandle(handle); ASSERT(compiler); return &(compiler->getNameMap()); } -const std::vector *ShGetUniforms(const ShHandle handle) +const std::vector *GetUniforms(const ShHandle handle) { - return GetShaderVariables(handle); + return GetShaderVariables(handle); } -const std::vector *ShGetVaryings(const ShHandle handle) +const std::vector *GetInputVaryings(const ShHandle handle) { - return GetShaderVariables(handle); + TCompiler *compiler = GetCompilerFromHandle(handle); + if (compiler == nullptr) + { + return nullptr; + } + return &compiler->getInputVaryings(); } -const std::vector *ShGetAttributes(const ShHandle handle) +const std::vector *GetOutputVaryings(const ShHandle handle) { - return GetShaderVariables(handle); + TCompiler *compiler = GetCompilerFromHandle(handle); + if (compiler == nullptr) + { + return nullptr; + } + return &compiler->getOutputVaryings(); } -const std::vector *ShGetOutputVariables(const ShHandle handle) +const std::vector *GetVaryings(const ShHandle handle) { - return GetShaderVariables(handle); + return GetShaderVariables(handle); } -const std::vector *ShGetInterfaceBlocks(const ShHandle handle) +const std::vector *GetAttributes(const ShHandle handle) { - return GetShaderVariables(handle); + return GetShaderVariables(handle); } -bool ShCheckVariablesWithinPackingLimits( - int maxVectors, ShVariableInfo *varInfoArray, size_t varInfoArraySize) +const std::vector *GetOutputVariables(const ShHandle handle) { - if (varInfoArraySize == 0) - return true; - ASSERT(varInfoArray); - std::vector 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(handle); } -bool ShGetInterfaceBlockRegister(const ShHandle handle, - const std::string &interfaceBlockName, - unsigned int *indexOut) +const std::vector *GetInterfaceBlocks(const ShHandle handle) +{ + return GetShaderVariables(handle); +} + +const std::vector *GetUniformBlocks(const ShHandle handle) +{ + ASSERT(handle); + TShHandleBase *base = static_cast(handle); + TCompiler *compiler = base->getAsCompiler(); + ASSERT(compiler); + + return &compiler->getUniformBlocks(); +} + +const std::vector *GetShaderStorageBlocks(const ShHandle handle) +{ + ASSERT(handle); + TShHandleBase *base = static_cast(handle); + TCompiler *compiler = base->getAsCompiler(); + ASSERT(compiler); + + return &compiler->getShaderStorageBlocks(); +} + +WorkGroupSize GetComputeShaderLocalGroupSize(const ShHandle handle) +{ + ASSERT(handle); + + TShHandleBase *base = static_cast(handle); + TCompiler *compiler = base->getAsCompiler(); + ASSERT(compiler); + + return compiler->getComputeShaderLocalSize(); +} + +int GetVertexShaderNumViews(const ShHandle handle) +{ + ASSERT(handle); + TShHandleBase *base = static_cast(handle); + TCompiler *compiler = base->getAsCompiler(); + ASSERT(compiler); + + return compiler->getNumViews(); +} + +bool CheckVariablesWithinPackingLimits(int maxVectors, const std::vector &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 *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(handle); + TCompiler *compiler = base->getAsCompiler(); + ASSERT(compiler); + + return GetGeometryShaderPrimitiveTypeEnum(compiler->getGeometryShaderInputPrimitiveType()); +} + +GLenum GetGeometryShaderOutputPrimitiveType(const ShHandle handle) +{ + ASSERT(handle); + + TShHandleBase *base = static_cast(handle); + TCompiler *compiler = base->getAsCompiler(); + ASSERT(compiler); + + return GetGeometryShaderPrimitiveTypeEnum(compiler->getGeometryShaderOutputPrimitiveType()); } + +int GetGeometryShaderInvocations(const ShHandle handle) +{ + ASSERT(handle); + + TShHandleBase *base = static_cast(handle); + TCompiler *compiler = base->getAsCompiler(); + ASSERT(compiler); + + return compiler->getGeometryShaderInvocations(); +} + +int GetGeometryShaderMaxVertices(const ShHandle handle) +{ + ASSERT(handle); + + TShHandleBase *base = static_cast(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 #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 ¶meters) +{ + TString parameterList; + for (size_t parameter = 0u; parameter < parameters.size(); parameter++) + { + const TType ¶mType = 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 &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 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 ctorParameters; + const TFieldList &fields = structure.fields(); + for (const TField *field : fields) { - const TType ¶mType = 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 ctorParameters; + for (auto parameter : *parameters) { - constructor += " return " + TypeString(ctorType) + "("; + const TType ¶mType = 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 ¶meter = 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 ¶meter = ctorParameters[parameterIndex]; + const TType ¶meter = 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 mStd140StructElementIndexes; - typedef std::set StructNames; - StructNames mStructNames; + struct TStructProperties : public angle::NonCopyable + { + POOL_ALLOCATOR_NEW_DELETE(); + + TStructProperties() {} - typedef std::set 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 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 StructDeclarations; StructDeclarations mStructDeclarations; + typedef std::set 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 #include -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 ¶metersSource) +{ + 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(size)); - case EbtGenIType: return TCache::getType(EbtInt, static_cast(size)); - case EbtGenUType: return TCache::getType(EbtUInt, static_cast(size)); - case EbtGenBType: return TCache::getType(EbtBool, static_cast(size)); - default: return type; + case EbtGenType: + return TCache::getType(EbtFloat, type->getQualifier(), + static_cast(size)); + case EbtGenIType: + return TCache::getType(EbtInt, type->getQualifier(), static_cast(size)); + case EbtGenUType: + return TCache::getType(EbtUInt, type->getQualifier(), static_cast(size)); + case EbtGenBType: + return TCache::getType(EbtBool, type->getQualifier(), static_cast(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(size)); + case EbtIVec: + return TCache::getType(EbtInt, static_cast(size)); + case EbtUVec: + return TCache::getType(EbtUInt, static_cast(size)); + case EbtBVec: + return TCache::getType(EbtBool, static_cast(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(size)); - case EbtIVec: return TCache::getType(EbtInt, static_cast(size)); - case EbtUVec: return TCache::getType(EbtUInt, static_cast(size)); - case EbtBVec: return TCache::getType(EbtBool, static_cast(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(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(table.size())); + table[level]->insertUnmangledBuiltInName(std::string(name)); +} + +bool TSymbolTable::hasUnmangledBuiltInAtLevel(const char *name, ESymbolLevel level) +{ + ASSERT(level >= 0 && level < static_cast(table.size())); + return table[level]->hasUnmangledBuiltIn(std::string(name)); +} + +bool TSymbolTable::hasUnmangledBuiltInForShaderVersion(const char *name, int shaderVersion) +{ + ASSERT(static_cast(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 #include #include #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 ¶metersSource); + 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 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 tLevel; + typedef TUnorderedMap tLevel; typedef tLevel::const_iterator const_iterator; typedef const tLevel::value_type tLevelPair; typedef std::pair 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 mInvariantVaryings; + bool mGlobalInvariant; + + private: + std::set 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 &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(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(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(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 table; typedef TMap PrecisionStackLevel; - std::vector< PrecisionStackLevel *> precisionStack; + std::vector precisionStack; - std::set 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 + +#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 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 *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 *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 mInterfaceBlockRegisterMap; + std::map mUniformBlockRegisterMap; std::map 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 #include -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(*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(*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(*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('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(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(std::numeric_limits::max() / count)) + { + count = std::numeric_limits::max(); + } + else + { + count *= static_cast(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 *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(); + + mArraySizes->push_back(s); + invalidateMangledName(); +} + +void TType::makeArrays(const TVector &sizes) +{ + if (!mArraySizes) + mArraySizes = new TVector(); + + 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 *outputSymbols, + TMap *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 *outputSymbols, + TMap *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::max() - count) + { + count = std::numeric_limits::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 *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 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 *outputSymbols, + TMap *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(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 *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 &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 *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 *outputSymbols, + TMap *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 *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 *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 *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 &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 &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, ®isterCount); + // 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 &group, - unsigned int *groupTextureRegisterIndex) +void UniformHLSL::outputHLSLSamplerUniformGroup( + TInfoSinkBase &out, + const HLSLTextureGroup textureGroup, + const TVector &group, + const TMap &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, ®isterCount); + + // 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, ®isterCount); + } + else + { + ASSERT(samplerInStructSymbolsToAPINames.find(uniform) != + samplerInStructSymbolsToAPINames.end()); + samplerArrayIndex = assignSamplerInStructUniformRegister( + type, samplerInStructSymbolsToAPINames.at(uniform), ®isterCount); + } 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 ®isterString = + 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> groupedSamplerUniforms; - groupedSamplerUniforms.resize(HLSL_TEXTURE_MAX + 1); + TVector> groupedSamplerUniforms(HLSL_TEXTURE_MAX + 1); + TMap samplerInStructSymbolsToAPINames; + TVector 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 ®isterString = TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")"; - - out << "uniform " << typeName << " " << DecorateUniform(name, type) << ArrayString(type) - << " : " << registerString << ";\n"; + if (type.isStructureContainingSamplers()) + { + TVector samplerSymbols; + TMap 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(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 &uniforms); + UniformHLSL(sh::GLenum shaderType, + StructureHLSL *structureHLSL, + ShShaderOutput outputType, + const std::vector &uniforms); void reserveUniformRegisters(unsigned int registerCount); - void reserveInterfaceBlockRegisters(unsigned int registerCount); - void outputHLSLSamplerUniformGroup(TInfoSinkBase &out, - const HLSLTextureSamplerGroup textureGroup, - const TVector &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 &getInterfaceBlockRegisterMap() const + const std::map &getUniformBlockRegisterMap() const { - return mInterfaceBlockRegisterMap; + return mUniformBlockRegisterMap; } const std::map &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 &group, + const TMap &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 &mUniforms; - std::map mInterfaceBlockRegisterMap; + std::map mUniformBlockRegisterMap; std::map 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 + +namespace sh +{ + +class TIntermBlock; +class TSymbolTable; + +using InterfaceBlockList = std::vector; + +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"; case HLSL_TEXTURE_CUBE: - return "TextureCube"; + return "TextureCube"; case HLSL_TEXTURE_2D_ARRAY: - return "Texture2DArray"; + return "Texture2DArray"; case HLSL_TEXTURE_3D: - return "Texture3D"; + return "Texture3D"; + case HLSL_TEXTURE_2D_UNORM: + return "Texture2D"; + case HLSL_TEXTURE_CUBE_UNORM: + return "TextureCube"; + case HLSL_TEXTURE_2D_ARRAY_UNORN: + return "Texture2DArray"; + case HLSL_TEXTURE_3D_UNORM: + return "Texture3D"; + case HLSL_TEXTURE_2D_SNORM: + return "Texture2D"; + case HLSL_TEXTURE_CUBE_SNORM: + return "TextureCube"; + case HLSL_TEXTURE_2D_ARRAY_SNORM: + return "Texture2DArray"; + case HLSL_TEXTURE_3D_SNORM: + return "Texture3D"; + case HLSL_TEXTURE_2D_MS: + return "Texture2DMS"; case HLSL_TEXTURE_2D_INT4: return "Texture2D"; case HLSL_TEXTURE_3D_INT4: return "Texture3D"; case HLSL_TEXTURE_2D_ARRAY_INT4: return "Texture2DArray"; + case HLSL_TEXTURE_2D_MS_INT4: + return "Texture2DMS"; case HLSL_TEXTURE_2D_UINT4: return "Texture2D"; case HLSL_TEXTURE_3D_UINT4: return "Texture3D"; case HLSL_TEXTURE_2D_ARRAY_UINT4: return "Texture2DArray"; + case HLSL_TEXTURE_2D_MS_UINT4: + return "Texture2DMS"; case HLSL_TEXTURE_2D_COMPARISON: return "Texture2D"; case HLSL_TEXTURE_CUBE_COMPARISON: @@ -115,15 +274,15 @@ TString TextureString(const HLSLTextureSamplerGroup type) UNREACHABLE(); } - return ""; + return ""; } -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 ""; } -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"; + case HLSL_RWTEXTURE_2D_ARRAY_FLOAT4: + return "RWTexture2DArray"; + case HLSL_RWTEXTURE_3D_FLOAT4: + return "RWTexture3D"; + case HLSL_RWTEXTURE_2D_UNORM: + return "RWTexture2D"; + case HLSL_RWTEXTURE_2D_ARRAY_UNORN: + return "RWTexture2DArray"; + case HLSL_RWTEXTURE_3D_UNORM: + return "RWTexture3D"; + case HLSL_RWTEXTURE_2D_SNORM: + return "RWTexture2D"; + case HLSL_RWTEXTURE_2D_ARRAY_SNORM: + return "RWTexture2DArray"; + case HLSL_RWTEXTURE_3D_SNORM: + return "RWTexture3D"; + case HLSL_RWTEXTURE_2D_UINT4: + return "RWTexture2D"; + case HLSL_RWTEXTURE_2D_ARRAY_UINT4: + return "RWTexture2DArray"; + case HLSL_RWTEXTURE_3D_UINT4: + return "RWTexture3D"; + case HLSL_RWTEXTURE_2D_INT4: + return "RWTexture2D"; + case HLSL_RWTEXTURE_2D_ARRAY_INT4: + return "RWTexture2DArray"; + case HLSL_RWTEXTURE_3D_INT4: + return "RWTexture3D"; + default: + UNREACHABLE(); } - return Decorate(string); + return ""; +} + +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 ""; +} + +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 ¶mType = 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 +#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(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 &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 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 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 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(symbol); - for (ParamIndex::const_iterator i = pIndex.begin(); - i != pIndex.end(); ++i) - { - const TConstParameter ¶m = 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 + #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 OutputVector; + OutputVector mOutputs; + OutputVector mUnspecifiedLocationOutputs; + OutputVector mYuvOutputs; + std::set 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(type.isArray() ? type.getArraySize() : 1); + ASSERT(!type.isArrayOfArrays()); // Disallowed in GLSL ES 3.10 section 4.3.6. + const size_t elementCount = + static_cast(type.isArray() ? type.getOutermostArraySize() : 1u); const size_t location = static_cast(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 -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 OutputVector; - OutputVector mOutputs; - OutputVector mUnspecifiedLocationOutputs; - std::set 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 mCasesSigned; + std::set 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 mCasesSigned; - std::set 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(varyingType.getArraySizeProduct()); + } +} + +using VaryingVector = std::vector; + +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 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 *expanded); - -void ExpandVariable(const ShaderVariable &variable, - const std::string &name, - const std::string &mappedName, - bool markStaticUse, - std::vector *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 *expanded) -{ - ASSERT(variable.isStruct()); - - const std::vector &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 -VarT *FindVariable(const TString &name, - std::vector *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 *attribs, - std::vector *outputVariables, - std::vector *uniforms, - std::vector *varyings, - std::vector *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(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( - 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( - 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( - 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 *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(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 *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(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 *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 -void CollectVariables::visitVariable(const TIntermSymbol *variable, - std::vector *infoList) const -{ - NameHashingTraverser traverser(mHashFunction, mSymbolTable); - traverser.traverse(variable->getType(), variable->getSymbol(), infoList); -} - -template -void CollectVariables::visitInfoList(const TIntermSequence &sequence, - std::vector *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 &compact, - std::vector *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 - -#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 *attribs, - std::vector *outputVariables, - std::vector *uniforms, - std::vector *varyings, - std::vector *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 - void visitVariable(const TIntermSymbol *variable, std::vector *infoList) const; - - template - void visitInfoList(const TIntermSequence &sequence, std::vector *infoList) const; - - std::vector *mAttribs; - std::vector *mOutputVariables; - std::vector *mUniforms; - std::vector *mVaryings; - std::vector *mInterfaceBlocks; - - std::map 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 &compact, - std::vector *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 @@ -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 *expanded); + +void ExpandStructVariable(const ShaderVariable &variable, + const std::string &name, + std::vector *expanded) +{ + ASSERT(variable.isStruct()); + + const std::vector &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 *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 *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 *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 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 -bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int maxVectors, - const std::vector &in_variables) +bool VariablePacker::checkExpandedVariablesWithinPackingLimits( + unsigned int maxVectors, + std::vector *variables) { ASSERT(maxVectors > 0); - maxRows_ = maxVectors; - topNonFullRow_ = 0; + maxRows_ = maxVectors; + topNonFullRow_ = 0; bottomNonFullRow_ = maxRows_ - 1; - std::vector 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 &); -template bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int, const std::vector &); -template bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int, const std::vector &); -template bool VariablePacker::CheckVariablesWithinPackingLimits(unsigned int, const std::vector &); +} // 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 +bool CheckVariablesInPackingLimits(unsigned int maxVectors, const std::vector &variables) +{ + VariablePacker packer; + std::vector expandedVariables; + for (const ShaderVariable &variable : variables) + { + ExpandVariable(variable, variable.name, &expandedVariables); + } + return packer.checkExpandedVariablesWithinPackingLimits(maxVectors, &expandedVariables); +} + +template bool CheckVariablesInPackingLimits( + unsigned int maxVectors, + const std::vector &variables); +template bool CheckVariablesInPackingLimits(unsigned int maxVectors, + const std::vector &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 -#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 - bool CheckVariablesWithinPackingLimits(unsigned int maxVectors, - const std::vector &in_variables); +#include - // 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 +bool CheckVariablesInPackingLimits(unsigned int maxVectors, const std::vector &variables); - int topNonFullRow_; - int bottomNonFullRow_; - int maxRows_; - std::vector 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 + +#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 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(node->getType().getNominalSize())); + TIntermTyped *leftVectorized = Vectorize(left, leftVectorizedType, nullptr); + TType rightVectorizedType = right->getType(); + rightVectorizedType.setPrimarySize( + static_cast(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 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 ¶ms = *(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 ¶ms = *(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 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 +void GetUniformBlockStructMemberInfo(const std::vector &fields, + const std::string &fieldName, + sh::BlockLayoutEncoder *encoder, + bool inRowMajorLayout, + BlockLayoutMap *blockInfoOut) +{ + encoder->enterAggregateType(); + GetUniformBlockInfo(fields, fieldName, encoder, inRowMajorLayout, blockInfoOut); + encoder->exitAggregateType(); +} + +template +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 +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 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 &arraySizes, + bool isRowMajorMatrix) { int arrayStride; int matrixStride; - getBlockLayoutInfo(type, arraySize, isRowMajorMatrix, &arrayStride, &matrixStride); + getBlockLayoutInfo(type, arraySizes, isRowMajorMatrix, &arrayStride, &matrixStride); const BlockMemberInfo memberInfo(static_cast(mCurrentOffset * BytesPerComponent), static_cast(arrayStride * BytesPerComponent), static_cast(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 &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(numComponents)); + baseAlignment = (numComponents == 3 ? 4u : static_cast(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 &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 +void GetUniformBlockInfo(const std::vector &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 &, + const std::string &, + sh::BlockLayoutEncoder *, + bool, + BlockLayoutMap *); + +template void GetUniformBlockInfo(const std::vector &, + const std::string &, + sh::BlockLayoutEncoder *, + bool, + BlockLayoutMap *); + +template void GetUniformBlockInfo(const std::vector &, + 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 +#include #include #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 &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 &arraySizes, + bool isRowMajorMatrix, + int *arrayStrideOut, + int *matrixStrideOut) = 0; + virtual void advanceOffset(GLenum type, + const std::vector &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 &arraySizes, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut) override; void advanceOffset(GLenum type, - unsigned int arraySize, + const std::vector &arraySizes, bool isRowMajorMatrix, int arrayStride, int matrixStride) override; }; -} +using BlockLayoutMap = std::map; + +// Only valid to call with ShaderVariable, InterfaceBlockField and Uniform. +template +void GetUniformBlockInfo(const std::vector &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 &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 &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(rx::roundUp(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(rx::roundUp(encoder.getBlockSize(), registerBytes) / registerBytes); -} - + return static_cast(rx::roundUp(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 &arraySizes, + bool isRowMajorMatrix, + int *arrayStrideOut, + int *matrixStrideOut) override; + void advanceOffset(GLenum type, + const std::vector &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 -#include - -#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 - -#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 -#include - -class TGraphNode; -class TGraphParentNode; -class TGraphArgument; -class TGraphFunctionCall; -class TGraphSymbol; -class TGraphSelection; -class TGraphLoop; -class TGraphLogicalOp; -class TDependencyGraphTraverser; -class TDependencyGraphOutput; - -typedef std::set TGraphNodeSet; -typedef std::vector TGraphNodeVector; -typedef std::vector TGraphSymbolVector; -typedef std::vector 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 TSymbolIdMap; - typedef std::pair 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 TSymbolStack; - typedef std::set 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 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; } [ \t\v\f\r] {} +. { + yyextra->error(*yylloc, "Illegal character at fieldname start", yytext); + return 0; +} [ \t\v\n\f\r] { } <*><> { 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(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 *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 ATTRIBUTE CONST_QUAL BOOL_TYPE FLOAT_TYPE INT_TYPE UINT_TYPE %token BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT %token BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4 UVEC2 UVEC3 UVEC4 -%token MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM VARYING +%token MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM BUFFER VARYING %token MATRIX2x3 MATRIX3x2 MATRIX2x4 MATRIX4x2 MATRIX3x4 MATRIX4x3 %token CENTROID FLAT SMOOTH +%token READONLY WRITEONLY COHERENT RESTRICT VOLATILE SHARED %token STRUCT VOID_TYPE WHILE %token SAMPLER2D SAMPLERCUBE SAMPLER_EXTERNAL_OES SAMPLER2DRECT SAMPLER2DARRAY %token ISAMPLER2D ISAMPLER3D ISAMPLERCUBE ISAMPLER2DARRAY %token USAMPLER2D USAMPLER3D USAMPLERCUBE USAMPLER2DARRAY +%token SAMPLER2DMS ISAMPLER2DMS USAMPLER2DMS %token SAMPLER3D SAMPLER3DRECT SAMPLER2DSHADOW SAMPLERCUBESHADOW SAMPLER2DARRAYSHADOW +%token SAMPLEREXTERNAL2DY2YEXT +%token IMAGE2D IIMAGE2D UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY +%token IMAGECUBE IIMAGECUBE UIMAGECUBE +%token ATOMICUINT %token LAYOUT +%token YUVCSCSTANDARDEXT YUVCSCSTANDARDEXTCONSTANT %token IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT %token FIELD_SELECTION @@ -166,7 +193,7 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons %token LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION %type identifier -%type assignment_operator unary_operator +%type assignment_operator unary_operator %type variable_identifier primary_expression postfix_expression %type expression integer_expression assignment_expression %type unary_expression multiplicative_expression additive_expression @@ -174,11 +201,12 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons %type conditional_expression constant_expression %type logical_or_expression logical_xor_expression logical_and_expression %type shift_expression and_expression exclusive_or_expression inclusive_or_expression -%type function_call initializer condition conditionopt +%type function_call initializer -%type translation_unit function_definition -%type statement simple_statement -%type statement_list compound_statement compound_statement_no_new_scope +%type condition conditionopt +%type translation_unit +%type function_definition statement simple_statement +%type statement_list compound_statement_with_scope compound_statement_no_new_scope %type declaration_statement selection_statement expression_statement %type declaration external_declaration %type for_init_statement @@ -188,14 +216,22 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons %type iteration_statement jump_statement statement_no_new_scope statement_with_scope %type single_declaration init_declarator_list -%type parameter_declaration parameter_declarator parameter_type_specifier -%type parameter_qualifier parameter_type_qualifier -%type layout_qualifier layout_qualifier_id_list layout_qualifier_id +%type parameter_declaration parameter_declarator parameter_type_specifier +%type layout_qualifier_id_list layout_qualifier_id + +// Note: array_specifier guaranteed to be non-null. +%type array_specifier + +%type fully_specified_type type_specifier %type precision_qualifier -%type type_qualifier fully_specified_type type_specifier storage_qualifier interpolation_qualifier -%type type_specifier_no_prec type_specifier_nonarray -%type struct_specifier +%type layout_qualifier +%type interpolation_qualifier +%type storage_qualifier single_type_qualifier invariant_qualifier +%type type_qualifier + +%type type_specifier_nonarray struct_specifier +%type type_specifier_no_prec %type struct_declarator %type struct_declarator_list struct_declaration struct_declaration_list %type 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(); + $$->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 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 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($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($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($5.node1), reinterpret_cast($5.node2), $7, @1); + $$ = context->addLoop(ELoopFor, $4, $5.node1, reinterpret_cast($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 << ""; - } - - 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 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 +#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::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(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::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(c - '0'); + ASSERT(digit < 10u); + if (exponent <= (std::numeric_limits::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::infinity(); + } + } + } + // Do the calculation in 64-bit to avoid overflow. + long long exponentLong = + static_cast(exponent) + static_cast(exponentOffset); + if (exponentLong > std::numeric_limits::max_exponent10) + { + return std::numeric_limits::infinity(); + } + else if (exponentLong < std::numeric_limits::min_exponent10) + { + return 0.0f; + } + // The exponent is in range, so we need to actually evaluate the float. + exponent = static_cast(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(exponent + normalizationExponentOffset)); + if (value > static_cast(std::numeric_limits::max())) + { + return std::numeric_limits::infinity(); + } + if (value < static_cast(std::numeric_limits::min())) + { + return 0.0f; + } + return static_cast(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 &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 -void GetVariableTraverser::traverse(const TType &type, - const TString &name, - std::vector *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(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 *); -template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector *); -template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector *); -template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector *); - +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 +#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 - void traverse(const TType &type, const TString &name, std::vector *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 - 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_ -- cgit v1.2.3