diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/Compiler.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/Compiler.cpp | 1478 |
1 files changed, 1478 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/Compiler.cpp b/gfx/angle/checkout/src/compiler/translator/Compiler.cpp new file mode 100644 index 0000000000..42a556f363 --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/Compiler.cpp @@ -0,0 +1,1478 @@ +// +// 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/Compiler.h" + +#include <sstream> + +#include "angle_gl.h" +#include "common/utilities.h" +#include "compiler/translator/CallDAG.h" +#include "compiler/translator/CollectVariables.h" +#include "compiler/translator/Initialize.h" +#include "compiler/translator/IsASTDepthBelowLimit.h" +#include "compiler/translator/OutputTree.h" +#include "compiler/translator/ParseContext.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/tree_ops/AddAndTrueToLoopCondition.h" +#include "compiler/translator/tree_ops/ClampFragDepth.h" +#include "compiler/translator/tree_ops/ClampPointSize.h" +#include "compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h" +#include "compiler/translator/tree_ops/DeferGlobalInitializers.h" +#include "compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h" +#include "compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.h" +#include "compiler/translator/tree_ops/EmulatePrecision.h" +#include "compiler/translator/tree_ops/FoldExpressions.h" +#include "compiler/translator/tree_ops/InitializeVariables.h" +#include "compiler/translator/tree_ops/PruneEmptyCases.h" +#include "compiler/translator/tree_ops/PruneNoOps.h" +#include "compiler/translator/tree_ops/RegenerateStructNames.h" +#include "compiler/translator/tree_ops/RemoveArrayLengthMethod.h" +#include "compiler/translator/tree_ops/RemoveInvariantDeclaration.h" +#include "compiler/translator/tree_ops/RemovePow.h" +#include "compiler/translator/tree_ops/RemoveUnreferencedVariables.h" +#include "compiler/translator/tree_ops/RewriteDoWhile.h" +#include "compiler/translator/tree_ops/RewriteRepeatedAssignToSwizzled.h" +#include "compiler/translator/tree_ops/ScalarizeVecAndMatConstructorArgs.h" +#include "compiler/translator/tree_ops/SeparateDeclarations.h" +#include "compiler/translator/tree_ops/SimplifyLoopConditions.h" +#include "compiler/translator/tree_ops/SplitSequenceOperator.h" +#include "compiler/translator/tree_ops/UnfoldShortCircuitAST.h" +#include "compiler/translator/tree_ops/UseInterfaceBlockFields.h" +#include "compiler/translator/tree_ops/VectorizeVectorScalarArithmetic.h" +#include "compiler/translator/tree_util/BuiltIn_autogen.h" +#include "compiler/translator/tree_util/IntermNodePatternMatcher.h" +#include "compiler/translator/tree_util/ReplaceShadowingVariables.h" +#include "compiler/translator/util.h" +#include "third_party/compiler/ArrayBoundsClamper.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 = sh::InitializeStream<std::ostringstream>(); + 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 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); +} + +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 (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 length of 256, while ES2 leaves max token + // size undefined. ES3 defines a max size of 1024 characters. + switch (spec) + { + case SH_WEBGL_SPEC: + return 256; + default: + return 1024; + } +} + +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_EXT: + return resources.MaxGeometryUniformComponents / 4; + default: + UNREACHABLE(); + return -1; + } +} + +namespace +{ + +class TScopedPoolAllocator +{ + public: + TScopedPoolAllocator(angle::PoolAllocator *allocator) : mAllocator(allocator) + { + mAllocator->push(); + SetGlobalPoolAllocator(mAllocator); + } + ~TScopedPoolAllocator() + { + SetGlobalPoolAllocator(nullptr); + mAllocator->pop(); + } + + private: + angle::PoolAllocator *mAllocator; +}; + +class TScopedSymbolTableLevel +{ + public: + TScopedSymbolTableLevel(TSymbolTable *table) : mTable(table) + { + ASSERT(mTable->isEmpty()); + mTable->push(); + } + ~TScopedSymbolTableLevel() + { + while (!mTable->isEmpty()) + mTable->pop(); + } + + private: + TSymbolTable *mTable; +}; + +int MapSpecToShaderVersion(ShShaderSpec spec) +{ + switch (spec) + { + 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; + case SH_GL3_3_SPEC: + return 330; + default: + UNREACHABLE(); + return 0; + } +} + +bool ValidateFragColorAndFragData(GLenum shaderType, + int shaderVersion, + const TSymbolTable &symbolTable, + TDiagnostics *diagnostics) +{ + if (shaderVersion > 100 || shaderType != GL_FRAGMENT_SHADER) + { + return true; + } + + bool usesFragColor = false; + bool usesFragData = false; + // This validation is a bit stricter than the spec - it's only an error to write to + // both FragData and FragColor. But because it's better not to have reads from undefined + // variables, we always return an error if they are both referenced, rather than only if they + // are written. + if (symbolTable.isStaticallyUsed(*BuiltInVariable::gl_FragColor()) || + symbolTable.isStaticallyUsed(*BuiltInVariable::gl_SecondaryFragColorEXT())) + { + usesFragColor = true; + } + // Extension variables may not always be initialized (saves some time at symbol table init). + bool secondaryFragDataUsed = + symbolTable.gl_SecondaryFragDataEXT() != nullptr && + symbolTable.isStaticallyUsed(*symbolTable.gl_SecondaryFragDataEXT()); + if (symbolTable.isStaticallyUsed(*symbolTable.gl_FragData()) || secondaryFragDataUsed) + { + usesFragData = true; + } + if (usesFragColor && usesFragData) + { + const char *errorMessage = "cannot use both gl_FragData and gl_FragColor"; + if (symbolTable.isStaticallyUsed(*BuiltInVariable::gl_SecondaryFragColorEXT()) || + secondaryFragDataUsed) + { + errorMessage = + "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)" + " and (gl_FragColor, gl_SecondaryFragColorEXT)"; + } + diagnostics->globalError(errorMessage); + return false; + } + return true; +} + +} // namespace + +TShHandleBase::TShHandleBase() +{ + allocator.push(); + SetGlobalPoolAllocator(&allocator); +} + +TShHandleBase::~TShHandleBase() +{ + SetGlobalPoolAllocator(nullptr); + allocator.popAll(); +} + +TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) + : mVariablesCollected(false), + mGLPositionInitialized(false), + mShaderType(type), + mShaderSpec(spec), + mOutputType(output), + mBuiltInFunctionEmulator(), + mDiagnostics(mInfoSink.info), + mSourcePath(nullptr), + mComputeShaderLocalSizeDeclared(false), + mComputeShaderLocalSize(1), + mGeometryShaderMaxVertices(-1), + mGeometryShaderInvocations(0), + mGeometryShaderInputPrimitiveType(EptUndefined), + mGeometryShaderOutputPrimitiveType(EptUndefined) +{} + +TCompiler::~TCompiler() {} + +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 + // of ESSL 1.00 as in Appendix A of the spec). + return (IsWebGLBasedSpec(mShaderSpec) && mShaderVersion == 100) || + (compileOptions & SH_VALIDATE_LOOP_INDEXING); +} + +bool TCompiler::Init(const ShBuiltInResources &resources) +{ + SetGlobalPoolAllocator(&allocator); + + // Generate built-in symbol table. + if (!initBuiltInSymbolTable(resources)) + return false; + + mResources = resources; + setResourceString(); + + InitExtensionBehavior(resources, mExtensionBehavior); + mArrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy); + return true; +} + +TIntermBlock *TCompiler::compileTreeForTesting(const char *const shaderStrings[], + size_t numStrings, + ShCompileOptions compileOptions) +{ + return compileTreeImpl(shaderStrings, numStrings, compileOptions); +} + +TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], + size_t numStrings, + const ShCompileOptions compileOptions) +{ + clearResults(); + + ASSERT(numStrings > 0); + ASSERT(GetGlobalPoolAllocator()); + + // Reset the extension behavior for each compilation unit. + ResetExtensionBehavior(mExtensionBehavior); + + // If gl_DrawID is not supported, remove it from the available extensions + // Currently we only allow emulation of gl_DrawID + const bool glDrawIDSupported = (compileOptions & SH_EMULATE_GL_DRAW_ID) != 0u; + if (!glDrawIDSupported) + { + auto it = mExtensionBehavior.find(TExtension::ANGLE_multi_draw); + if (it != mExtensionBehavior.end()) + { + mExtensionBehavior.erase(it); + } + } + + const bool glBaseVertexBaseInstanceSupported = + (compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0u; + if (!glBaseVertexBaseInstanceSupported) + { + auto it = mExtensionBehavior.find(TExtension::ANGLE_base_vertex_base_instance); + if (it != mExtensionBehavior.end()) + { + mExtensionBehavior.erase(it); + } + } + + // First string is path of source file if flag is set. The actual source follows. + size_t firstSource = 0; + if (compileOptions & SH_SOURCE_PATH) + { + mSourcePath = shaderStrings[0]; + ++firstSource; + } + + TParseContext parseContext(mSymbolTable, mExtensionBehavior, mShaderType, mShaderSpec, + compileOptions, !IsDesktopGLSpec(mShaderSpec), &mDiagnostics, + getResources()); + + parseContext.setFragmentPrecisionHighOnESSL1(mResources.FragmentPrecisionHigh == 1); + + // We preserve symbols at the built-in level from compile-to-compile. + // Start pushing the user-defined symbols at global level. + TScopedSymbolTableLevel globalLevel(&mSymbolTable); + ASSERT(mSymbolTable.atGlobalLevel()); + + // Parse shader. + if (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, + &parseContext) != 0) + { + return nullptr; + } + + if (parseContext.getTreeRoot() == nullptr) + { + return nullptr; + } + + setASTMetadata(parseContext); + + if (!checkShaderVersion(&parseContext)) + { + return nullptr; + } + + TIntermBlock *root = parseContext.getTreeRoot(); + if (!checkAndSimplifyAST(root, parseContext, compileOptions)) + { + return nullptr; + } + + return root; +} + +bool TCompiler::checkShaderVersion(TParseContext *parseContext) +{ + if (MapSpecToShaderVersion(mShaderSpec) < mShaderVersion) + { + mDiagnostics.globalError("unsupported shader version"); + return false; + } + + ASSERT(parseContext); + switch (mShaderType) + { + case GL_COMPUTE_SHADER: + if (mShaderVersion < 310) + { + mDiagnostics.globalError("Compute shader is not supported in this shader version."); + return false; + } + break; + + case GL_GEOMETRY_SHADER_EXT: + if (mShaderVersion < 310) + { + mDiagnostics.globalError( + "Geometry shader is not supported in this shader version."); + return false; + } + else + { + ASSERT(mShaderVersion == 310); + if (!parseContext->checkCanUseExtension(sh::TSourceLoc(), + TExtension::EXT_geometry_shader)) + { + return false; + } + } + break; + + default: + break; + } + + return true; +} + +void TCompiler::setASTMetadata(const TParseContext &parseContext) +{ + mShaderVersion = parseContext.getShaderVersion(); + + mPragma = parseContext.pragma(); + mSymbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll); + + mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared(); + mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize(); + + mNumViews = parseContext.getNumViews(); + + if (mShaderType == GL_GEOMETRY_SHADER_EXT) + { + mGeometryShaderInputPrimitiveType = parseContext.getGeometryShaderInputPrimitiveType(); + mGeometryShaderOutputPrimitiveType = parseContext.getGeometryShaderOutputPrimitiveType(); + mGeometryShaderMaxVertices = parseContext.getGeometryShaderMaxVertices(); + mGeometryShaderInvocations = parseContext.getGeometryShaderInvocations(); + } +} + +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 (shouldRunLoopAndIndexingValidation(compileOptions) && + !ValidateLimitations(root, mShaderType, &mSymbolTable, &mDiagnostics)) + { + return false; + } + + if (!ValidateFragColorAndFragData(mShaderType, mShaderVersion, mSymbolTable, &mDiagnostics)) + { + return false; + } + + // Fold expressions that could not be folded before validation that was done as a part of + // parsing. + FoldExpressions(root, &mDiagnostics); + // Folding should only be able to generate warnings. + ASSERT(mDiagnostics.numErrors() == 0); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + // 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, &mSymbolTable); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + // Create the function DAG and check there is no recursion + if (!initCallDag(root)) + { + return false; + } + + if ((compileOptions & SH_LIMIT_CALL_STACK_DEPTH) && !checkCallDepth()) + { + return false; + } + + // Checks which functions are used and if "main" exists + mFunctionMetadata.clear(); + mFunctionMetadata.resize(mCallDag.size()); + if (!tagUsedFunctions()) + { + return false; + } + + if (!(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS)) + { + pruneUnusedFunctions(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + if (IsSpecWithFunctionBodyNewScope(mShaderSpec, mShaderVersion)) + { + ReplaceShadowingVariables(root, &mSymbolTable); + } + + if (mShaderVersion >= 310 && !ValidateVaryingLocations(root, &mDiagnostics, mShaderType)) + { + return false; + } + + if (mShaderVersion >= 300 && mShaderType == GL_FRAGMENT_SHADER && + !ValidateOutputs(root, getExtensionBehavior(), mResources.MaxDrawBuffers, &mDiagnostics)) + { + return false; + } + + // Fail compilation if precision emulation not supported. + if (getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision && + !EmulatePrecision::SupportedInLanguage(mOutputType)) + { + mDiagnostics.globalError("Precision emulation not supported for this output type."); + return false; + } + + // Clamping uniform array bounds needs to happen after validateLimitations pass. + if (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS) + { + mArrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); + } + + if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) && + (parseContext.isExtensionEnabled(TExtension::OVR_multiview2) || + parseContext.isExtensionEnabled(TExtension::OVR_multiview)) && + getShaderType() != GL_COMPUTE_SHADER) + { + DeclareAndInitBuiltinsForInstancedMultiview(root, mNumViews, mShaderType, compileOptions, + mOutputType, &mSymbolTable); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + // This pass might emit short circuits so keep it before the short circuit unfolding + if (compileOptions & SH_REWRITE_DO_WHILE_LOOPS) + { + RewriteDoWhile(root, &mSymbolTable); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION) + { + AddAndTrueToLoopCondition(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (compileOptions & SH_UNFOLD_SHORT_CIRCUIT) + { + UnfoldShortCircuitAST(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT) + { + RemovePow(root, &mSymbolTable); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (compileOptions & SH_REGENERATE_STRUCT_NAMES) + { + RegenerateStructNames gen(&mSymbolTable); + root->traverse(&gen); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (mShaderType == GL_VERTEX_SHADER && + IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw)) + { + if ((compileOptions & SH_EMULATE_GL_DRAW_ID) != 0u) + { + EmulateGLDrawID(root, &mSymbolTable, &mUniforms, + shouldCollectVariables(compileOptions)); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + } + + if (mShaderType == GL_VERTEX_SHADER && + IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_base_vertex_base_instance)) + { + if ((compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0u) + { + EmulateGLBaseVertex(root, &mSymbolTable, &mUniforms, + shouldCollectVariables(compileOptions)); + EmulateGLBaseInstance(root, &mSymbolTable, &mUniforms, + shouldCollectVariables(compileOptions)); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + } + + if (mShaderType == GL_FRAGMENT_SHADER && mShaderVersion == 100 && mResources.EXT_draw_buffers && + mResources.MaxDrawBuffers > 1 && + IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers)) + { + EmulateGLFragColorBroadcast(root, mResources.MaxDrawBuffers, &mOutputVariables, + &mSymbolTable, mShaderVersion); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + int simplifyScalarized = (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS) + ? IntermNodePatternMatcher::kScalarizedVecOrMatConstructor + : 0; + + // 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 | simplifyScalarized, + &getSymbolTable()); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + // Note that separate declarations need to be run before other AST transformations that + // generate new statements from expressions. + SeparateDeclarations(root); + mValidateASTOptions.validateMultiDeclarations = true; + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + SplitSequenceOperator(root, IntermNodePatternMatcher::kArrayLengthMethod | simplifyScalarized, + &getSymbolTable()); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + RemoveArrayLengthMethod(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + RemoveUnreferencedVariables(root, &mSymbolTable); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + // 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 or RemoveUnreferencedVariables may have + // left switch statements that only contained an empty declaration inside the final case in an + // invalid state. Relies on that PruneNoOps and RemoveUnreferencedVariables have already been + // run. + PruneEmptyCases(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + // Built-in function emulation needs to happen after validateLimitations pass. + // TODO(jmadill): Remove global pool allocator. + GetGlobalPoolAllocator()->lock(); + initBuiltInFunctionEmulator(&mBuiltInFunctionEmulator, compileOptions); + GetGlobalPoolAllocator()->unlock(); + mBuiltInFunctionEmulator.markBuiltInFunctionsForEmulation(root); + + bool highPrecisionSupported = mShaderVersion > 100 || mShaderType != GL_FRAGMENT_SHADER || + mResources.FragmentPrecisionHigh == 1; + if (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS) + { + ScalarizeVecAndMatConstructorArgs(root, mShaderType, highPrecisionSupported, &mSymbolTable); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (shouldCollectVariables(compileOptions)) + { + ASSERT(!mVariablesCollected); + CollectVariables(root, &mAttributes, &mOutputVariables, &mUniforms, &mInputVaryings, + &mOutputVaryings, &mUniformBlocks, &mShaderStorageBlocks, &mInBlocks, + mResources.HashFunction, &mSymbolTable, mShaderType, mExtensionBehavior); + collectInterfaceBlocks(); + mVariablesCollected = true; + if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS) + { + useAllMembersInUnusedStandardAndSharedBlocks(root); + } + if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) + { + int maxUniformVectors = GetMaxUniformVectorsForShaderType(mShaderType, mResources); + // 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, mUniforms)) + { + mDiagnostics.globalError("too many uniforms"); + return false; + } + } + if ((compileOptions & SH_INIT_OUTPUT_VARIABLES) && (mShaderType != GL_COMPUTE_SHADER)) + { + initializeOutputVariables(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + } + + // Removing invariant declarations must be done after collecting variables. + // Otherwise, built-in invariant declarations don't apply. + if (RemoveInvariant(mShaderType, mShaderVersion, mOutputType, compileOptions)) + { + RemoveInvariantDeclaration(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + // 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 (mShaderType == GL_VERTEX_SHADER && !mGLPositionInitialized && + ((compileOptions & SH_INIT_GL_POSITION) || (mOutputType == SH_GLSL_COMPATIBILITY_OUTPUT))) + { + initializeGLPosition(root); + mGLPositionInitialized = true; + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + // 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, + highPrecisionSupported, &mSymbolTable); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + + 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)) + { + SimplifyLoopConditions(root, + IntermNodePatternMatcher::kArrayDeclaration | + IntermNodePatternMatcher::kNamelessStructDeclaration, + &getSymbolTable()); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + InitializeUninitializedLocals(root, getShaderVersion(), canUseLoopsToInitialize, + highPrecisionSupported, &getSymbolTable()); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (getShaderType() == GL_VERTEX_SHADER && (compileOptions & SH_CLAMP_POINT_SIZE)) + { + ClampPointSize(root, mResources.MaxPointSize, &getSymbolTable()); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (getShaderType() == GL_FRAGMENT_SHADER && (compileOptions & SH_CLAMP_FRAG_DEPTH)) + { + ClampFragDepth(root, &getSymbolTable()); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (compileOptions & SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED) + { + sh::RewriteRepeatedAssignToSwizzled(root); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + if (compileOptions & SH_REWRITE_VECTOR_SCALAR_ARITHMETIC) + { + VectorizeVectorScalarArithmetic(root, &getSymbolTable()); + if (!ValidateAST(root, &mDiagnostics, mValidateASTOptions)) + { + return false; + } + } + + return true; +} + +bool TCompiler::compile(const char *const shaderStrings[], + size_t numStrings, + ShCompileOptions compileOptionsIn) +{ +#if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) + DumpFuzzerCase(shaderStrings, numStrings, mShaderType, mShaderSpec, mOutputType, + 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); + TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions); + + if (root) + { + if (compileOptions & SH_INTERMEDIATE_TREE) + OutputTree(root, mInfoSink.info); + + if (compileOptions & SH_OBJECT_CODE) + { + PerformanceDiagnostics perfDiagnostics(&mDiagnostics); + translate(root, compileOptions, &perfDiagnostics); + } + + if (mShaderType == GL_VERTEX_SHADER) + { + bool lookForDrawID = + IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw) && + ((compileOptions & SH_EMULATE_GL_DRAW_ID) != 0u); + bool lookForBaseVertexBaseInstance = + IsExtensionEnabled(mExtensionBehavior, + TExtension::ANGLE_base_vertex_base_instance) && + ((compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0u); + + if (lookForDrawID || lookForBaseVertexBaseInstance) + { + for (auto &uniform : mUniforms) + { + if (lookForDrawID && uniform.name == "angle_DrawID" && + uniform.mappedName == "angle_DrawID") + { + uniform.name = "gl_DrawID"; + } + else if (lookForBaseVertexBaseInstance && uniform.name == "angle_BaseVertex" && + uniform.mappedName == "angle_BaseVertex") + { + uniform.name = "gl_BaseVertex"; + } + else if (lookForBaseVertexBaseInstance && + uniform.name == "angle_BaseInstance" && + uniform.mappedName == "angle_BaseInstance") + { + uniform.name = "gl_BaseInstance"; + } + } + } + } + + // The IntermNode tree doesn't need to be deleted here, since the + // memory will be freed in a big chunk by the PoolAllocator. + return true; + } + return false; +} + +bool TCompiler::initBuiltInSymbolTable(const ShBuiltInResources &resources) +{ + if (resources.MaxDrawBuffers < 1) + { + return false; + } + if (resources.EXT_blend_func_extended && resources.MaxDualSourceDrawBuffers < 1) + { + return false; + } + + mSymbolTable.initializeBuiltIns(mShaderType, mShaderSpec, resources); + + return true; +} + +void TCompiler::setResourceString() +{ + std::ostringstream strstream = sh::InitializeStream<std::ostringstream>(); + + // clang-format off + strstream << ":MaxVertexAttribs:" << mResources.MaxVertexAttribs + << ":MaxVertexUniformVectors:" << mResources.MaxVertexUniformVectors + << ":MaxVaryingVectors:" << mResources.MaxVaryingVectors + << ":MaxVertexTextureImageUnits:" << mResources.MaxVertexTextureImageUnits + << ":MaxCombinedTextureImageUnits:" << mResources.MaxCombinedTextureImageUnits + << ":MaxTextureImageUnits:" << mResources.MaxTextureImageUnits + << ":MaxFragmentUniformVectors:" << mResources.MaxFragmentUniformVectors + << ":MaxDrawBuffers:" << mResources.MaxDrawBuffers + << ":OES_standard_derivatives:" << mResources.OES_standard_derivatives + << ":OES_EGL_image_external:" << mResources.OES_EGL_image_external + << ":OES_EGL_image_external_essl3:" << mResources.OES_EGL_image_external_essl3 + << ":NV_EGL_stream_consumer_external:" << mResources.NV_EGL_stream_consumer_external + << ":ARB_texture_rectangle:" << mResources.ARB_texture_rectangle + << ":EXT_draw_buffers:" << mResources.EXT_draw_buffers + << ":FragmentPrecisionHigh:" << mResources.FragmentPrecisionHigh + << ":MaxExpressionComplexity:" << mResources.MaxExpressionComplexity + << ":MaxCallStackDepth:" << mResources.MaxCallStackDepth + << ":MaxFunctionParameters:" << mResources.MaxFunctionParameters + << ":EXT_blend_func_extended:" << mResources.EXT_blend_func_extended + << ":EXT_frag_depth:" << mResources.EXT_frag_depth + << ":EXT_shader_texture_lod:" << mResources.EXT_shader_texture_lod + << ":EXT_shader_framebuffer_fetch:" << mResources.EXT_shader_framebuffer_fetch + << ":NV_shader_framebuffer_fetch:" << mResources.NV_shader_framebuffer_fetch + << ":ARM_shader_framebuffer_fetch:" << mResources.ARM_shader_framebuffer_fetch + << ":OVR_multiview2:" << mResources.OVR_multiview2 + << ":OVR_multiview:" << mResources.OVR_multiview + << ":EXT_YUV_target:" << mResources.EXT_YUV_target + << ":EXT_geometry_shader:" << mResources.EXT_geometry_shader + << ":OES_texture_3D:" << mResources.OES_texture_3D + << ":MaxVertexOutputVectors:" << mResources.MaxVertexOutputVectors + << ":MaxFragmentInputVectors:" << mResources.MaxFragmentInputVectors + << ":MinProgramTexelOffset:" << mResources.MinProgramTexelOffset + << ":MaxProgramTexelOffset:" << mResources.MaxProgramTexelOffset + << ":MaxDualSourceDrawBuffers:" << mResources.MaxDualSourceDrawBuffers + << ":MaxViewsOVR:" << mResources.MaxViewsOVR + << ":NV_draw_buffers:" << mResources.NV_draw_buffers + << ":WEBGL_debug_shader_precision:" << mResources.WEBGL_debug_shader_precision + << ":ANGLE_multi_draw:" << mResources.ANGLE_multi_draw + << ":ANGLE_base_vertex_base_instance:" << mResources.ANGLE_base_vertex_base_instance + << ":MinProgramTextureGatherOffset:" << mResources.MinProgramTextureGatherOffset + << ":MaxProgramTextureGatherOffset:" << mResources.MaxProgramTextureGatherOffset + << ":MaxImageUnits:" << mResources.MaxImageUnits + << ":MaxVertexImageUniforms:" << mResources.MaxVertexImageUniforms + << ":MaxFragmentImageUniforms:" << mResources.MaxFragmentImageUniforms + << ":MaxComputeImageUniforms:" << mResources.MaxComputeImageUniforms + << ":MaxCombinedImageUniforms:" << mResources.MaxCombinedImageUniforms + << ":MaxCombinedShaderOutputResources:" << mResources.MaxCombinedShaderOutputResources + << ":MaxComputeWorkGroupCountX:" << mResources.MaxComputeWorkGroupCount[0] + << ":MaxComputeWorkGroupCountY:" << mResources.MaxComputeWorkGroupCount[1] + << ":MaxComputeWorkGroupCountZ:" << mResources.MaxComputeWorkGroupCount[2] + << ":MaxComputeWorkGroupSizeX:" << mResources.MaxComputeWorkGroupSize[0] + << ":MaxComputeWorkGroupSizeY:" << mResources.MaxComputeWorkGroupSize[1] + << ":MaxComputeWorkGroupSizeZ:" << mResources.MaxComputeWorkGroupSize[2] + << ":MaxComputeUniformComponents:" << mResources.MaxComputeUniformComponents + << ":MaxComputeTextureImageUnits:" << mResources.MaxComputeTextureImageUnits + << ":MaxComputeAtomicCounters:" << mResources.MaxComputeAtomicCounters + << ":MaxComputeAtomicCounterBuffers:" << mResources.MaxComputeAtomicCounterBuffers + << ":MaxVertexAtomicCounters:" << mResources.MaxVertexAtomicCounters + << ":MaxFragmentAtomicCounters:" << mResources.MaxFragmentAtomicCounters + << ":MaxCombinedAtomicCounters:" << mResources.MaxCombinedAtomicCounters + << ":MaxAtomicCounterBindings:" << mResources.MaxAtomicCounterBindings + << ":MaxVertexAtomicCounterBuffers:" << mResources.MaxVertexAtomicCounterBuffers + << ":MaxFragmentAtomicCounterBuffers:" << mResources.MaxFragmentAtomicCounterBuffers + << ":MaxCombinedAtomicCounterBuffers:" << mResources.MaxCombinedAtomicCounterBuffers + << ":MaxAtomicCounterBufferSize:" << mResources.MaxAtomicCounterBufferSize + << ":MaxGeometryUniformComponents:" << mResources.MaxGeometryUniformComponents + << ":MaxGeometryUniformBlocks:" << mResources.MaxGeometryUniformBlocks + << ":MaxGeometryInputComponents:" << mResources.MaxGeometryInputComponents + << ":MaxGeometryOutputComponents:" << mResources.MaxGeometryOutputComponents + << ":MaxGeometryOutputVertices:" << mResources.MaxGeometryOutputVertices + << ":MaxGeometryTotalOutputComponents:" << mResources.MaxGeometryTotalOutputComponents + << ":MaxGeometryTextureImageUnits:" << mResources.MaxGeometryTextureImageUnits + << ":MaxGeometryAtomicCounterBuffers:" << mResources.MaxGeometryAtomicCounterBuffers + << ":MaxGeometryAtomicCounters:" << mResources.MaxGeometryAtomicCounters + << ":MaxGeometryShaderStorageBlocks:" << mResources.MaxGeometryShaderStorageBlocks + << ":MaxGeometryShaderInvocations:" << mResources.MaxGeometryShaderInvocations + << ":MaxGeometryImageUniforms:" << mResources.MaxGeometryImageUniforms; + // clang-format on + + mBuiltInResourcesString = strstream.str(); +} + +void TCompiler::collectInterfaceBlocks() +{ + ASSERT(mInterfaceBlocks.empty()); + mInterfaceBlocks.reserve(mUniformBlocks.size() + mShaderStorageBlocks.size() + + mInBlocks.size()); + mInterfaceBlocks.insert(mInterfaceBlocks.end(), mUniformBlocks.begin(), mUniformBlocks.end()); + mInterfaceBlocks.insert(mInterfaceBlocks.end(), mShaderStorageBlocks.begin(), + mShaderStorageBlocks.end()); + mInterfaceBlocks.insert(mInterfaceBlocks.end(), mInBlocks.begin(), mInBlocks.end()); +} + +void TCompiler::clearResults() +{ + mArrayBoundsClamper.Cleanup(); + mInfoSink.info.erase(); + mInfoSink.obj.erase(); + mInfoSink.debug.erase(); + mDiagnostics.resetErrorCount(); + + mAttributes.clear(); + mOutputVariables.clear(); + mUniforms.clear(); + mInputVaryings.clear(); + mOutputVaryings.clear(); + mInterfaceBlocks.clear(); + mUniformBlocks.clear(); + mShaderStorageBlocks.clear(); + mInBlocks.clear(); + mVariablesCollected = false; + mGLPositionInitialized = false; + + mNumViews = -1; + + mGeometryShaderInputPrimitiveType = EptUndefined; + mGeometryShaderOutputPrimitiveType = EptUndefined; + mGeometryShaderInvocations = 0; + mGeometryShaderMaxVertices = -1; + + mBuiltInFunctionEmulator.cleanup(); + + mNameMap.clear(); + + mSourcePath = nullptr; + + mSymbolTable.clearCompilationResults(); +} + +bool TCompiler::initCallDag(TIntermNode *root) +{ + mCallDag.clear(); + + switch (mCallDag.init(root, &mDiagnostics)) + { + 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(); + return true; +} + +bool TCompiler::checkCallDepth() +{ + std::vector<int> depths(mCallDag.size()); + + for (size_t i = 0; i < mCallDag.size(); i++) + { + int depth = 0; + const CallDAG::Record &record = mCallDag.getRecordFromIndex(i); + + for (const int &calleeIndex : record.callees) + { + depth = std::max(depth, depths[calleeIndex] + 1); + } + + depths[i] = depth; + + if (depth >= mResources.MaxCallStackDepth) + { + // Trace back the function chain to have a meaningful info log. + std::stringstream errorStream = sh::InitializeStream<std::stringstream>(); + errorStream << "Call stack too deep (larger than " << mResources.MaxCallStackDepth + << ") with the following call chain: " + << record.node->getFunction()->name(); + + int currentFunction = static_cast<int>(i); + int currentDepth = depth; + + while (currentFunction != -1) + { + errorStream + << " -> " + << mCallDag.getRecordFromIndex(currentFunction).node->getFunction()->name(); + + int nextFunction = -1; + for (const int &calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees) + { + if (depths[calleeIndex] == currentDepth - 1) + { + currentDepth--; + nextFunction = calleeIndex; + } + } + + currentFunction = nextFunction; + } + + std::string errorStr = errorStream.str(); + mDiagnostics.globalError(errorStr.c_str()); + + return false; + } + } + + return true; +} + +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).node->getFunction()->isMain()) + { + internalTagUsedFunction(i); + return true; + } + } + + mDiagnostics.globalError("Missing main()"); + return false; +} + +void TCompiler::internalTagUsedFunction(size_t index) +{ + if (mFunctionMetadata[index].used) + { + return; + } + + mFunctionMetadata[index].used = true; + + for (int calleeIndex : mCallDag.getRecordFromIndex(index).callees) + { + internalTagUsedFunction(calleeIndex); + } +} + +// A predicate for the stl that returns if a top-level node is unused +class TCompiler::UnusedPredicate +{ + public: + UnusedPredicate(const CallDAG *callDag, const std::vector<FunctionMetadata> *metadatas) + : mCallDag(callDag), mMetadatas(metadatas) + {} + + bool operator()(TIntermNode *node) + { + const TIntermFunctionPrototype *asFunctionPrototype = node->getAsFunctionPrototypeNode(); + const TIntermFunctionDefinition *asFunctionDefinition = node->getAsFunctionDefinition(); + + const TFunction *func = nullptr; + + if (asFunctionDefinition) + { + func = asFunctionDefinition->getFunction(); + } + else if (asFunctionPrototype) + { + func = asFunctionPrototype->getFunction(); + } + if (func == nullptr) + { + return false; + } + + size_t callDagIndex = mCallDag->findIndex(func->uniqueId()); + if (callDagIndex == CallDAG::InvalidIndex) + { + // This happens only for unimplemented prototypes which are thus unused + ASSERT(asFunctionPrototype); + return true; + } + + ASSERT(callDagIndex < mMetadatas->size()); + return !(*mMetadatas)[callDagIndex].used; + } + + private: + const CallDAG *mCallDag; + const std::vector<FunctionMetadata> *mMetadatas; +}; + +void TCompiler::pruneUnusedFunctions(TIntermBlock *root) +{ + UnusedPredicate isUnused(&mCallDag, &mFunctionMetadata); + TIntermSequence *sequence = root->getSequence(); + + if (!sequence->empty()) + { + sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused), + sequence->end()); + } +} + +bool TCompiler::limitExpressionComplexity(TIntermBlock *root) +{ + if (!IsASTDepthBelowLimit(root, mResources.MaxExpressionComplexity)) + { + mDiagnostics.globalError("Expression too complex."); + return false; + } + + if (!ValidateMaxParameters(root, mResources.MaxFunctionParameters)) + { + mDiagnostics.globalError("Function has too many parameters."); + return false; + } + + return true; +} + +bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions) +{ + return (compileOptions & SH_VARIABLES) != 0; +} + +bool TCompiler::wereVariablesCollected() const +{ + return mVariablesCollected; +} + +void TCompiler::initializeGLPosition(TIntermBlock *root) +{ + InitVariableList list; + sh::ShaderVariable var(GL_FLOAT_VEC4); + var.name = "gl_Position"; + list.push_back(var); + InitializeVariables(root, list, &mSymbolTable, mShaderVersion, mExtensionBehavior, false, + false); +} + +void TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermBlock *root) +{ + sh::InterfaceBlockList list; + + for (const sh::InterfaceBlock &block : mUniformBlocks) + { + if (!block.staticUse && + (block.layout == sh::BLOCKLAYOUT_STD140 || block.layout == sh::BLOCKLAYOUT_SHARED)) + { + list.push_back(block); + } + } + + sh::UseInterfaceBlockFields(root, list, mSymbolTable); +} + +void TCompiler::initializeOutputVariables(TIntermBlock *root) +{ + InitVariableList list; + if (mShaderType == GL_VERTEX_SHADER || mShaderType == GL_GEOMETRY_SHADER_EXT) + { + for (const sh::Varying &var : mOutputVaryings) + { + list.push_back(var); + if (var.name == "gl_Position") + { + ASSERT(!mGLPositionInitialized); + mGLPositionInitialized = true; + } + } + } + else + { + ASSERT(mShaderType == GL_FRAGMENT_SHADER); + for (const sh::OutputVariable &var : mOutputVariables) + { + list.push_back(var); + } + } + InitializeVariables(root, list, &mSymbolTable, mShaderVersion, mExtensionBehavior, false, + false); +} + +const TExtensionBehavior &TCompiler::getExtensionBehavior() const +{ + return mExtensionBehavior; +} + +const char *TCompiler::getSourcePath() const +{ + return mSourcePath; +} + +const ShBuiltInResources &TCompiler::getResources() const +{ + return mResources; +} + +const ArrayBoundsClamper &TCompiler::getArrayBoundsClamper() const +{ + return mArrayBoundsClamper; +} + +ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const +{ + return mResources.ArrayIndexClampingStrategy; +} + +const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const +{ + return mBuiltInFunctionEmulator; +} + +void TCompiler::writePragma(ShCompileOptions compileOptions) +{ + if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL)) + { + TInfoSinkBase &sink = mInfoSink.obj; + if (mPragma.stdgl.invariantAll) + sink << "#pragma STDGL invariant(all)\n"; + } +} + +bool TCompiler::isVaryingDefined(const char *varyingName) +{ + ASSERT(mVariablesCollected); + for (size_t ii = 0; ii < mInputVaryings.size(); ++ii) + { + if (mInputVaryings[ii].name == varyingName) + { + return true; + } + } + for (size_t ii = 0; ii < mOutputVaryings.size(); ++ii) + { + if (mOutputVaryings[ii].name == varyingName) + { + return true; + } + } + + return false; +} + +void EmitWorkGroupSizeGLSL(const TCompiler &compiler, TInfoSinkBase &sink) +{ + if (compiler.isComputeShaderLocalSizeDeclared()) + { + const sh::WorkGroupSize &localSize = compiler.getComputeShaderLocalSize(); + sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1] + << ", local_size_z=" << localSize[2] << ") in;\n"; + } +} + +void EmitMultiviewGLSL(const TCompiler &compiler, + const ShCompileOptions &compileOptions, + const TBehavior behavior, + TInfoSinkBase &sink) +{ + ASSERT(behavior != EBhUndefined); + if (behavior == EBhDisable) + return; + + const bool isVertexShader = (compiler.getShaderType() == GL_VERTEX_SHADER); + if (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) + { + // Emit ARB_shader_viewport_layer_array/NV_viewport_array2 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. + if (isVertexShader && (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER)) + { + sink << "#if defined(GL_ARB_shader_viewport_layer_array)\n" + << "#extension GL_ARB_shader_viewport_layer_array : require\n" + << "#elif defined(GL_NV_viewport_array2)\n" + << "#extension GL_NV_viewport_array2 : require\n" + << "#endif\n"; + } + } + else + { + sink << "#extension GL_OVR_multiview2 : " << GetBehaviorString(behavior) << "\n"; + + const auto &numViews = compiler.getNumViews(); + if (isVertexShader && numViews != -1) + { + sink << "layout(num_views=" << numViews << ") in;\n"; + } + } +} + +} // namespace sh |