| // | |
| // Copyright 2002 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/RemoveDynamicIndexing.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.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 GetMaxShaderVersionForSpec(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_GL_CORE_SPEC: | |
| case SH_GL_COMPATIBILITY_SPEC: | |
| return 460; | |
| 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), | |
| mCompileOptions(0) | |
| {} | |
| 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) | |
| { | |
| // Remember the compile options for helper functions such as validateAST. | |
| mCompileOptions = 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(), getOutputType()); | |
| 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 (GetMaxShaderVersionForSpec(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(); | |
| } | |
| } | |
| unsigned int TCompiler::getSharedMemorySize() const | |
| { | |
| unsigned int sharedMemSize = 0; | |
| for (const sh::ShaderVariable &var : mSharedVariables) | |
| { | |
| sharedMemSize += var.getExternalSize(); | |
| } | |
| return sharedMemSize; | |
| } | |
| bool TCompiler::validateAST(TIntermNode *root) | |
| { | |
| if ((mCompileOptions & SH_VALIDATE_AST) != 0) | |
| { | |
| bool valid = ValidateAST(root, &mDiagnostics, mValidateASTOptions); | |
| // In debug, assert validation. In release, validation errors will be returned back to the | |
| // application as internal ANGLE errors. | |
| ASSERT(valid); | |
| return valid; | |
| } | |
| return true; | |
| } | |
| 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. | |
| if (!FoldExpressions(this, root, &mDiagnostics)) | |
| { | |
| return false; | |
| } | |
| // Folding should only be able to generate warnings. | |
| ASSERT(mDiagnostics.numErrors() == 0); | |
| // 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. | |
| if (!PruneNoOps(this, root, &mSymbolTable)) | |
| { | |
| 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 (IsSpecWithFunctionBodyNewScope(mShaderSpec, mShaderVersion)) | |
| { | |
| if (!ReplaceShadowingVariables(this, root, &mSymbolTable)) | |
| { | |
| return false; | |
| } | |
| } | |
| 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) | |
| { | |
| if (!DeclareAndInitBuiltinsForInstancedMultiview( | |
| this, root, mNumViews, mShaderType, compileOptions, mOutputType, &mSymbolTable)) | |
| { | |
| return false; | |
| } | |
| } | |
| // This pass might emit short circuits so keep it before the short circuit unfolding | |
| if (compileOptions & SH_REWRITE_DO_WHILE_LOOPS) | |
| { | |
| if (!RewriteDoWhile(this, root, &mSymbolTable)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION) | |
| { | |
| if (!AddAndTrueToLoopCondition(this, root)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (compileOptions & SH_UNFOLD_SHORT_CIRCUIT) | |
| { | |
| if (!UnfoldShortCircuitAST(this, root)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT) | |
| { | |
| if (!RemovePow(this, root, &mSymbolTable)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (compileOptions & SH_REGENERATE_STRUCT_NAMES) | |
| { | |
| RegenerateStructNames gen(&mSymbolTable); | |
| root->traverse(&gen); | |
| if (!validateAST(root)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (mShaderType == GL_VERTEX_SHADER && | |
| IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw)) | |
| { | |
| if ((compileOptions & SH_EMULATE_GL_DRAW_ID) != 0u) | |
| { | |
| if (!EmulateGLDrawID(this, root, &mSymbolTable, &mUniforms, | |
| shouldCollectVariables(compileOptions))) | |
| { | |
| 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) | |
| { | |
| if (!EmulateGLBaseVertexBaseInstance(this, root, &mSymbolTable, &mUniforms, | |
| shouldCollectVariables(compileOptions), | |
| compileOptions & SH_ADD_BASE_VERTEX_TO_VERTEX_ID)) | |
| { | |
| return false; | |
| } | |
| } | |
| } | |
| if (mShaderType == GL_FRAGMENT_SHADER && mShaderVersion == 100 && mResources.EXT_draw_buffers && | |
| mResources.MaxDrawBuffers > 1 && | |
| IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers)) | |
| { | |
| if (!EmulateGLFragColorBroadcast(this, root, mResources.MaxDrawBuffers, &mOutputVariables, | |
| &mSymbolTable, mShaderVersion)) | |
| { | |
| 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. | |
| if (!SimplifyLoopConditions(this, root, | |
| IntermNodePatternMatcher::kMultiDeclaration | | |
| IntermNodePatternMatcher::kArrayLengthMethod | | |
| simplifyScalarized, | |
| &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| // Note that separate declarations need to be run before other AST transformations that | |
| // generate new statements from expressions. | |
| if (!SeparateDeclarations(this, root)) | |
| { | |
| return false; | |
| } | |
| mValidateASTOptions.validateMultiDeclarations = true; | |
| if (!SplitSequenceOperator(this, root, | |
| IntermNodePatternMatcher::kArrayLengthMethod | simplifyScalarized, | |
| &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| if (!RemoveArrayLengthMethod(this, root)) | |
| { | |
| return false; | |
| } | |
| if (!RemoveUnreferencedVariables(this, root, &mSymbolTable)) | |
| { | |
| 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. | |
| if (!PruneEmptyCases(this, root)) | |
| { | |
| 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) | |
| { | |
| if (!ScalarizeVecAndMatConstructorArgs(this, root, mShaderType, highPrecisionSupported, | |
| &mSymbolTable)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (shouldCollectVariables(compileOptions)) | |
| { | |
| ASSERT(!mVariablesCollected); | |
| CollectVariables(root, &mAttributes, &mOutputVariables, &mUniforms, &mInputVaryings, | |
| &mOutputVaryings, &mSharedVariables, &mUniformBlocks, | |
| &mShaderStorageBlocks, &mInBlocks, mResources.HashFunction, &mSymbolTable, | |
| mShaderType, mExtensionBehavior); | |
| collectInterfaceBlocks(); | |
| mVariablesCollected = true; | |
| if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS) | |
| { | |
| if (!useAllMembersInUnusedStandardAndSharedBlocks(root)) | |
| { | |
| return false; | |
| } | |
| } | |
| 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)) | |
| { | |
| if (!initializeOutputVariables(root)) | |
| { | |
| 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)) | |
| { | |
| if (!RemoveInvariantDeclaration(this, root)) | |
| { | |
| 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))) | |
| { | |
| if (!initializeGLPosition(root)) | |
| { | |
| return false; | |
| } | |
| 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); | |
| if (!DeferGlobalInitializers(this, root, initializeLocalsAndGlobals, canUseLoopsToInitialize, | |
| highPrecisionSupported, &mSymbolTable)) | |
| { | |
| 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)) | |
| { | |
| if (!SimplifyLoopConditions(this, root, | |
| IntermNodePatternMatcher::kArrayDeclaration | | |
| IntermNodePatternMatcher::kNamelessStructDeclaration, | |
| &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| } | |
| if (!InitializeUninitializedLocals(this, root, getShaderVersion(), canUseLoopsToInitialize, | |
| highPrecisionSupported, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| } | |
| if (getShaderType() == GL_VERTEX_SHADER && (compileOptions & SH_CLAMP_POINT_SIZE)) | |
| { | |
| if (!ClampPointSize(this, root, mResources.MaxPointSize, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| } | |
| if (getShaderType() == GL_FRAGMENT_SHADER && (compileOptions & SH_CLAMP_FRAG_DEPTH)) | |
| { | |
| if (!ClampFragDepth(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| } | |
| if (compileOptions & SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED) | |
| { | |
| if (!sh::RewriteRepeatedAssignToSwizzled(this, root)) | |
| { | |
| return false; | |
| } | |
| } | |
| if (compileOptions & SH_REWRITE_VECTOR_SCALAR_ARITHMETIC) | |
| { | |
| if (!VectorizeVectorScalarArithmetic(this, root, &getSymbolTable())) | |
| { | |
| return false; | |
| } | |
| } | |
| if (compileOptions & SH_REMOVE_DYNAMIC_INDEXING_OF_SWIZZLED_VECTOR) | |
| { | |
| if (!sh::RemoveDynamicIndexingOfSwizzledVector(this, root, &getSymbolTable(), nullptr)) | |
| { | |
| 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); | |
| if (!translate(root, compileOptions, &perfDiagnostics)) | |
| { | |
| return false; | |
| } | |
| } | |
| 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(); | |
| mSharedVariables.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; | |
| } | |
| bool TCompiler::initializeGLPosition(TIntermBlock *root) | |
| { | |
| InitVariableList list; | |
| sh::ShaderVariable var(GL_FLOAT_VEC4); | |
| var.name = "gl_Position"; | |
| list.push_back(var); | |
| return InitializeVariables(this, root, list, &mSymbolTable, mShaderVersion, mExtensionBehavior, | |
| false, false); | |
| } | |
| bool 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); | |
| } | |
| } | |
| return sh::UseInterfaceBlockFields(this, root, list, mSymbolTable); | |
| } | |
| bool TCompiler::initializeOutputVariables(TIntermBlock *root) | |
| { | |
| InitVariableList list; | |
| if (mShaderType == GL_VERTEX_SHADER || mShaderType == GL_GEOMETRY_SHADER_EXT) | |
| { | |
| for (const sh::ShaderVariable &var : mOutputVaryings) | |
| { | |
| list.push_back(var); | |
| if (var.name == "gl_Position") | |
| { | |
| ASSERT(!mGLPositionInitialized); | |
| mGLPositionInitialized = true; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| ASSERT(mShaderType == GL_FRAGMENT_SHADER); | |
| for (const sh::ShaderVariable &var : mOutputVariables) | |
| { | |
| list.push_back(var); | |
| } | |
| } | |
| return InitializeVariables(this, 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 |