| // |
| // 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/AddAndTrueToLoopCondition.h" |
| #include "compiler/translator/Cache.h" |
| #include "compiler/translator/CallDAG.h" |
| #include "compiler/translator/DeferGlobalInitializers.h" |
| #include "compiler/translator/EmulateGLFragColorBroadcast.h" |
| #include "compiler/translator/EmulatePrecision.h" |
| #include "compiler/translator/Initialize.h" |
| #include "compiler/translator/InitializeVariables.h" |
| #include "compiler/translator/ParseContext.h" |
| #include "compiler/translator/PruneEmptyDeclarations.h" |
| #include "compiler/translator/RegenerateStructNames.h" |
| #include "compiler/translator/RemoveInvariantDeclaration.h" |
| #include "compiler/translator/RemovePow.h" |
| #include "compiler/translator/RewriteDoWhile.h" |
| #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h" |
| #include "compiler/translator/UnfoldShortCircuitAST.h" |
| #include "compiler/translator/UseInterfaceBlockFields.h" |
| #include "compiler/translator/ValidateLimitations.h" |
| #include "compiler/translator/ValidateMaxParameters.h" |
| #include "compiler/translator/ValidateMultiviewWebGL.h" |
| #include "compiler/translator/ValidateOutputs.h" |
| #include "compiler/translator/VariablePacker.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; |
| o << "corpus/" << fileIndex++ << ".sample"; |
| std::string s = o.str(); |
| |
| // Must match the input format of the fuzzer |
| FILE *f = fopen(s.c_str(), "w"); |
| fwrite(&type, sizeof(type), 1, f); |
| fwrite(&spec, sizeof(spec), 1, f); |
| fwrite(&output, sizeof(output), 1, f); |
| fwrite(&options, sizeof(options), 1, f); |
| |
| char zero[128 - 20] = {0}; |
| fwrite(&zero, 128 - 20, 1, f); |
| |
| for (size_t i = 0; i < numStrings; i++) |
| { |
| fwrite(shaderStrings[i], sizeof(char), strlen(shaderStrings[i]), f); |
| } |
| fwrite(&zero, 1, 1, f); |
| |
| fclose(f); |
| } |
| #endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) |
| } // anonymous namespace |
| |
| bool IsWebGLBasedSpec(ShShaderSpec spec) |
| { |
| return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC); |
| } |
| |
| bool IsGLSL130OrNewer(ShShaderOutput output) |
| { |
| return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT || |
| output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT || |
| output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT || |
| output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT || |
| output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT); |
| } |
| |
| bool IsGLSL420OrNewer(ShShaderOutput output) |
| { |
| return (output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT || |
| output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT); |
| } |
| |
| bool IsGLSL410OrOlder(ShShaderOutput output) |
| { |
| return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT || |
| output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT || |
| output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT); |
| } |
| |
| bool RemoveInvariant(sh::GLenum shaderType, |
| int shaderVersion, |
| ShShaderOutput outputType, |
| ShCompileOptions compileOptions) |
| { |
| if ((compileOptions & SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT) == 0 && |
| shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType)) |
| return true; |
| |
| if ((compileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0 && |
| shaderVersion >= 300 && shaderType == GL_VERTEX_SHADER) |
| return true; |
| |
| return false; |
| } |
| |
| size_t GetGlobalMaxTokenSize(ShShaderSpec spec) |
| { |
| // WebGL defines a max token 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; |
| } |
| } |
| |
| namespace |
| { |
| |
| class TScopedPoolAllocator |
| { |
| public: |
| TScopedPoolAllocator(TPoolAllocator *allocator) : mAllocator(allocator) |
| { |
| mAllocator->push(); |
| SetGlobalPoolAllocator(mAllocator); |
| } |
| ~TScopedPoolAllocator() |
| { |
| SetGlobalPoolAllocator(nullptr); |
| mAllocator->pop(); |
| } |
| |
| private: |
| TPoolAllocator *mAllocator; |
| }; |
| |
| class TScopedSymbolTableLevel |
| { |
| public: |
| TScopedSymbolTableLevel(TSymbolTable *table) : mTable(table) |
| { |
| ASSERT(mTable->atBuiltInLevel()); |
| mTable->push(); |
| } |
| ~TScopedSymbolTableLevel() |
| { |
| while (!mTable->atBuiltInLevel()) |
| 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; |
| default: |
| UNREACHABLE(); |
| return 0; |
| } |
| } |
| |
| } // namespace |
| |
| TShHandleBase::TShHandleBase() |
| { |
| allocator.push(); |
| SetGlobalPoolAllocator(&allocator); |
| } |
| |
| TShHandleBase::~TShHandleBase() |
| { |
| SetGlobalPoolAllocator(nullptr); |
| allocator.popAll(); |
| } |
| |
| TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) |
| : variablesCollected(false), |
| shaderType(type), |
| shaderSpec(spec), |
| outputType(output), |
| maxUniformVectors(0), |
| maxExpressionComplexity(0), |
| maxCallStackDepth(0), |
| maxFunctionParameters(0), |
| fragmentPrecisionHigh(false), |
| clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC), |
| builtInFunctionEmulator(), |
| mDiagnostics(infoSink.info), |
| mSourcePath(nullptr), |
| mComputeShaderLocalSizeDeclared(false), |
| mTemporaryIndex(0) |
| { |
| mComputeShaderLocalSize.fill(1); |
| } |
| |
| 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(shaderSpec) && shaderVersion == 100) || |
| (compileOptions & SH_VALIDATE_LOOP_INDEXING); |
| } |
| |
| bool TCompiler::Init(const ShBuiltInResources &resources) |
| { |
| shaderVersion = 100; |
| maxUniformVectors = (shaderType == GL_VERTEX_SHADER) ? resources.MaxVertexUniformVectors |
| : resources.MaxFragmentUniformVectors; |
| maxExpressionComplexity = resources.MaxExpressionComplexity; |
| maxCallStackDepth = resources.MaxCallStackDepth; |
| maxFunctionParameters = resources.MaxFunctionParameters; |
| |
| SetGlobalPoolAllocator(&allocator); |
| |
| // Generate built-in symbol table. |
| if (!InitBuiltInSymbolTable(resources)) |
| return false; |
| InitExtensionBehavior(resources, extensionBehavior); |
| fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1; |
| |
| arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy); |
| clampingStrategy = resources.ArrayIndexClampingStrategy; |
| |
| hashFunction = resources.HashFunction; |
| |
| 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(extensionBehavior); |
| |
| // 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(symbolTable, extensionBehavior, shaderType, shaderSpec, |
| compileOptions, true, &mDiagnostics, getResources()); |
| |
| parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh); |
| |
| // We preserve symbols at the built-in level from compile-to-compile. |
| // Start pushing the user-defined symbols at global level. |
| TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable); |
| |
| // Parse shader. |
| bool success = (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, |
| &parseContext) == 0) && |
| (parseContext.getTreeRoot() != nullptr); |
| |
| shaderVersion = parseContext.getShaderVersion(); |
| if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion) |
| { |
| mDiagnostics.globalError("unsupported shader version"); |
| success = false; |
| } |
| |
| TIntermBlock *root = nullptr; |
| |
| if (success) |
| { |
| mPragma = parseContext.pragma(); |
| symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll); |
| |
| mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared(); |
| mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize(); |
| |
| mNumViews = parseContext.getNumViews(); |
| |
| root = parseContext.getTreeRoot(); |
| |
| // Highp might have been auto-enabled based on shader version |
| fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh(); |
| |
| if (success && (IsWebGLBasedSpec(shaderSpec) && |
| IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview") && |
| IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview2"))) |
| { |
| // Can't enable both extensions at the same time. |
| mDiagnostics.globalError("Can't enable both OVR_multiview and OVR_multiview2"); |
| success = false; |
| } |
| |
| // Disallow expressions deemed too complex. |
| if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) |
| success = limitExpressionComplexity(root); |
| |
| // Create the function DAG and check there is no recursion |
| if (success) |
| success = initCallDag(root); |
| |
| if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH)) |
| success = checkCallDepth(); |
| |
| // Checks which functions are used and if "main" exists |
| if (success) |
| { |
| functionMetadata.clear(); |
| functionMetadata.resize(mCallDag.size()); |
| success = tagUsedFunctions(); |
| } |
| |
| if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS)) |
| success = pruneUnusedFunctions(root); |
| |
| // Prune empty declarations to work around driver bugs and to keep declaration output |
| // simple. |
| if (success) |
| PruneEmptyDeclarations(root); |
| |
| if (success && shaderVersion >= 300 && shaderType == GL_FRAGMENT_SHADER) |
| success = validateOutputs(root); |
| |
| if (success && shouldRunLoopAndIndexingValidation(compileOptions)) |
| success = |
| ValidateLimitations(root, shaderType, symbolTable, shaderVersion, &mDiagnostics); |
| |
| bool multiview2 = IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview2"); |
| if (success && compileResources.OVR_multiview && IsWebGLBasedSpec(shaderSpec) && |
| (IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview") || multiview2)) |
| success = ValidateMultiviewWebGL(root, shaderType, symbolTable, shaderVersion, |
| multiview2, &mDiagnostics); |
| |
| // Fail compilation if precision emulation not supported. |
| if (success && getResources().WEBGL_debug_shader_precision && |
| getPragma().debugShaderPrecision) |
| { |
| if (!EmulatePrecision::SupportedInLanguage(outputType)) |
| { |
| mDiagnostics.globalError("Precision emulation not supported for this output type."); |
| success = false; |
| } |
| } |
| |
| // Built-in function emulation needs to happen after validateLimitations pass. |
| if (success) |
| { |
| // TODO(jmadill): Remove global pool allocator. |
| GetGlobalPoolAllocator()->lock(); |
| initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions); |
| GetGlobalPoolAllocator()->unlock(); |
| builtInFunctionEmulator.markBuiltInFunctionsForEmulation(root); |
| } |
| |
| // Clamping uniform array bounds needs to happen after validateLimitations pass. |
| if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)) |
| arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); |
| |
| // gl_Position is always written in compatibility output mode |
| if (success && shaderType == GL_VERTEX_SHADER && |
| ((compileOptions & SH_INIT_GL_POSITION) || |
| (outputType == SH_GLSL_COMPATIBILITY_OUTPUT))) |
| initializeGLPosition(root); |
| |
| // This pass might emit short circuits so keep it before the short circuit unfolding |
| if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS)) |
| RewriteDoWhile(root, getTemporaryIndex()); |
| |
| if (success && (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION)) |
| sh::AddAndTrueToLoopCondition(root); |
| |
| if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)) |
| { |
| UnfoldShortCircuitAST unfoldShortCircuit; |
| root->traverse(&unfoldShortCircuit); |
| unfoldShortCircuit.updateTree(); |
| } |
| |
| if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT)) |
| { |
| RemovePow(root); |
| } |
| |
| if (success && shouldCollectVariables(compileOptions)) |
| { |
| collectVariables(root); |
| if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS) |
| { |
| useAllMembersInUnusedStandardAndSharedBlocks(root); |
| } |
| if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) |
| { |
| success = enforcePackingRestrictions(); |
| if (!success) |
| { |
| mDiagnostics.globalError("too many uniforms"); |
| } |
| } |
| if (success && (compileOptions & SH_INIT_OUTPUT_VARIABLES)) |
| { |
| initializeOutputVariables(root); |
| } |
| } |
| |
| // Removing invariant declarations must be done after collecting variables. |
| // Otherwise, built-in invariant declarations don't apply. |
| if (success && RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions)) |
| sh::RemoveInvariantDeclaration(root); |
| |
| if (success && (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS)) |
| { |
| ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh, |
| &mTemporaryIndex); |
| } |
| |
| if (success && (compileOptions & SH_REGENERATE_STRUCT_NAMES)) |
| { |
| RegenerateStructNames gen(symbolTable, shaderVersion); |
| root->traverse(&gen); |
| } |
| |
| if (success && shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 && |
| compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 && |
| IsExtensionEnabled(extensionBehavior, "GL_EXT_draw_buffers")) |
| { |
| EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables); |
| } |
| |
| if (success) |
| { |
| DeferGlobalInitializers(root); |
| } |
| } |
| |
| if (success) |
| return root; |
| |
| return nullptr; |
| } |
| |
| bool TCompiler::compile(const char *const shaderStrings[], |
| size_t numStrings, |
| ShCompileOptions compileOptionsIn) |
| { |
| #if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) |
| DumpFuzzerCase(shaderStrings, numStrings, shaderType, shaderSpec, outputType, compileOptionsIn); |
| #endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) |
| |
| if (numStrings == 0) |
| return true; |
| |
| ShCompileOptions compileOptions = compileOptionsIn; |
| |
| // Apply key workarounds. |
| if (shouldFlattenPragmaStdglInvariantAll()) |
| { |
| // This should be harmless to do in all cases, but for the moment, do it only conditionally. |
| compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL; |
| } |
| |
| TScopedPoolAllocator scopedAlloc(&allocator); |
| TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions); |
| |
| if (root) |
| { |
| if (compileOptions & SH_INTERMEDIATE_TREE) |
| TIntermediate::outputTree(root, infoSink.info); |
| |
| if (compileOptions & SH_OBJECT_CODE) |
| translate(root, compileOptions); |
| |
| // 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; |
| } |
| |
| compileResources = resources; |
| setResourceString(); |
| |
| assert(symbolTable.isEmpty()); |
| symbolTable.push(); // COMMON_BUILTINS |
| symbolTable.push(); // ESSL1_BUILTINS |
| symbolTable.push(); // ESSL3_BUILTINS |
| symbolTable.push(); // ESSL3_1_BUILTINS |
| |
| TPublicType integer; |
| integer.initializeBasicType(EbtInt); |
| |
| TPublicType floatingPoint; |
| floatingPoint.initializeBasicType(EbtFloat); |
| |
| switch (shaderType) |
| { |
| case GL_FRAGMENT_SHADER: |
| symbolTable.setDefaultPrecision(integer, EbpMedium); |
| break; |
| case GL_VERTEX_SHADER: |
| symbolTable.setDefaultPrecision(integer, EbpHigh); |
| symbolTable.setDefaultPrecision(floatingPoint, EbpHigh); |
| break; |
| case GL_COMPUTE_SHADER: |
| symbolTable.setDefaultPrecision(integer, EbpHigh); |
| symbolTable.setDefaultPrecision(floatingPoint, EbpHigh); |
| break; |
| default: |
| assert(false && "Language not supported"); |
| } |
| // Set defaults for sampler types that have default precision, even those that are |
| // only available if an extension exists. |
| // New sampler types in ESSL3 don't have default precision. ESSL1 types do. |
| initSamplerDefaultPrecision(EbtSampler2D); |
| initSamplerDefaultPrecision(EbtSamplerCube); |
| // SamplerExternalOES is specified in the extension to have default precision. |
| initSamplerDefaultPrecision(EbtSamplerExternalOES); |
| // SamplerExternal2DY2YEXT is specified in the extension to have default precision. |
| initSamplerDefaultPrecision(EbtSamplerExternal2DY2YEXT); |
| // It isn't specified whether Sampler2DRect has default precision. |
| initSamplerDefaultPrecision(EbtSampler2DRect); |
| |
| InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable); |
| |
| IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable); |
| |
| return true; |
| } |
| |
| void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType) |
| { |
| ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd); |
| TPublicType sampler; |
| sampler.initializeBasicType(samplerType); |
| symbolTable.setDefaultPrecision(sampler, EbpLow); |
| } |
| |
| void TCompiler::setResourceString() |
| { |
| std::ostringstream strstream; |
| |
| // clang-format off |
| strstream << ":MaxVertexAttribs:" << compileResources.MaxVertexAttribs |
| << ":MaxVertexUniformVectors:" << compileResources.MaxVertexUniformVectors |
| << ":MaxVaryingVectors:" << compileResources.MaxVaryingVectors |
| << ":MaxVertexTextureImageUnits:" << compileResources.MaxVertexTextureImageUnits |
| << ":MaxCombinedTextureImageUnits:" << compileResources.MaxCombinedTextureImageUnits |
| << ":MaxTextureImageUnits:" << compileResources.MaxTextureImageUnits |
| << ":MaxFragmentUniformVectors:" << compileResources.MaxFragmentUniformVectors |
| << ":MaxDrawBuffers:" << compileResources.MaxDrawBuffers |
| << ":OES_standard_derivatives:" << compileResources.OES_standard_derivatives |
| << ":OES_EGL_image_external:" << compileResources.OES_EGL_image_external |
| << ":OES_EGL_image_external_essl3:" << compileResources.OES_EGL_image_external_essl3 |
| << ":NV_EGL_stream_consumer_external:" << compileResources.NV_EGL_stream_consumer_external |
| << ":ARB_texture_rectangle:" << compileResources.ARB_texture_rectangle |
| << ":EXT_draw_buffers:" << compileResources.EXT_draw_buffers |
| << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh |
| << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity |
| << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth |
| << ":MaxFunctionParameters:" << compileResources.MaxFunctionParameters |
| << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended |
| << ":EXT_frag_depth:" << compileResources.EXT_frag_depth |
| << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod |
| << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch |
| << ":NV_shader_framebuffer_fetch:" << compileResources.NV_shader_framebuffer_fetch |
| << ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch |
| << ":EXT_YUV_target:" << compileResources.EXT_YUV_target |
| << ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors |
| << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors |
| << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset |
| << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset |
| << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers |
| << ":NV_draw_buffers:" << compileResources.NV_draw_buffers |
| << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision |
| << ":MaxImageUnits:" << compileResources.MaxImageUnits |
| << ":MaxVertexImageUniforms:" << compileResources.MaxVertexImageUniforms |
| << ":MaxFragmentImageUniforms:" << compileResources.MaxFragmentImageUniforms |
| << ":MaxComputeImageUniforms:" << compileResources.MaxComputeImageUniforms |
| << ":MaxCombinedImageUniforms:" << compileResources.MaxCombinedImageUniforms |
| << ":MaxCombinedShaderOutputResources:" << compileResources.MaxCombinedShaderOutputResources |
| << ":MaxComputeWorkGroupCountX:" << compileResources.MaxComputeWorkGroupCount[0] |
| << ":MaxComputeWorkGroupCountY:" << compileResources.MaxComputeWorkGroupCount[1] |
| << ":MaxComputeWorkGroupCountZ:" << compileResources.MaxComputeWorkGroupCount[2] |
| << ":MaxComputeWorkGroupSizeX:" << compileResources.MaxComputeWorkGroupSize[0] |
| << ":MaxComputeWorkGroupSizeY:" << compileResources.MaxComputeWorkGroupSize[1] |
| << ":MaxComputeWorkGroupSizeZ:" << compileResources.MaxComputeWorkGroupSize[2] |
| << ":MaxComputeUniformComponents:" << compileResources.MaxComputeUniformComponents |
| << ":MaxComputeTextureImageUnits:" << compileResources.MaxComputeTextureImageUnits |
| << ":MaxComputeAtomicCounters:" << compileResources.MaxComputeAtomicCounters |
| << ":MaxComputeAtomicCounterBuffers:" << compileResources.MaxComputeAtomicCounterBuffers |
| << ":MaxVertexAtomicCounters:" << compileResources.MaxVertexAtomicCounters |
| << ":MaxFragmentAtomicCounters:" << compileResources.MaxFragmentAtomicCounters |
| << ":MaxCombinedAtomicCounters:" << compileResources.MaxCombinedAtomicCounters |
| << ":MaxAtomicCounterBindings:" << compileResources.MaxAtomicCounterBindings |
| << ":MaxVertexAtomicCounterBuffers:" << compileResources.MaxVertexAtomicCounterBuffers |
| << ":MaxFragmentAtomicCounterBuffers:" << compileResources.MaxFragmentAtomicCounterBuffers |
| << ":MaxCombinedAtomicCounterBuffers:" << compileResources.MaxCombinedAtomicCounterBuffers |
| << ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize; |
| // clang-format on |
| |
| builtInResourcesString = strstream.str(); |
| } |
| |
| void TCompiler::clearResults() |
| { |
| arrayBoundsClamper.Cleanup(); |
| infoSink.info.erase(); |
| infoSink.obj.erase(); |
| infoSink.debug.erase(); |
| mDiagnostics.resetErrorCount(); |
| |
| attributes.clear(); |
| outputVariables.clear(); |
| uniforms.clear(); |
| expandedUniforms.clear(); |
| varyings.clear(); |
| interfaceBlocks.clear(); |
| variablesCollected = false; |
| |
| mNumViews = -1; |
| |
| builtInFunctionEmulator.cleanup(); |
| |
| nameMap.clear(); |
| |
| mSourcePath = nullptr; |
| mTemporaryIndex = 0; |
| } |
| |
| 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; |
| auto &record = mCallDag.getRecordFromIndex(i); |
| |
| for (auto &calleeIndex : record.callees) |
| { |
| depth = std::max(depth, depths[calleeIndex] + 1); |
| } |
| |
| depths[i] = depth; |
| |
| if (depth >= maxCallStackDepth) |
| { |
| // Trace back the function chain to have a meaningful info log. |
| std::stringstream errorStream; |
| errorStream << "Call stack too deep (larger than " << maxCallStackDepth |
| << ") with the following call chain: " << record.name; |
| |
| int currentFunction = static_cast<int>(i); |
| int currentDepth = depth; |
| |
| while (currentFunction != -1) |
| { |
| errorStream << " -> " << mCallDag.getRecordFromIndex(currentFunction).name; |
| |
| int nextFunction = -1; |
| for (auto &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).name == "main") |
| { |
| internalTagUsedFunction(i); |
| return true; |
| } |
| } |
| |
| mDiagnostics.globalError("Missing main()"); |
| return false; |
| } |
| |
| void TCompiler::internalTagUsedFunction(size_t index) |
| { |
| if (functionMetadata[index].used) |
| { |
| return; |
| } |
| |
| functionMetadata[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 TFunctionSymbolInfo *functionInfo = nullptr; |
| |
| if (asFunctionDefinition) |
| { |
| functionInfo = asFunctionDefinition->getFunctionSymbolInfo(); |
| } |
| else if (asFunctionPrototype) |
| { |
| functionInfo = asFunctionPrototype->getFunctionSymbolInfo(); |
| } |
| if (functionInfo == nullptr) |
| { |
| return false; |
| } |
| |
| size_t callDagIndex = mCallDag->findIndex(functionInfo); |
| 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; |
| }; |
| |
| bool TCompiler::pruneUnusedFunctions(TIntermBlock *root) |
| { |
| UnusedPredicate isUnused(&mCallDag, &functionMetadata); |
| TIntermSequence *sequence = root->getSequence(); |
| |
| if (!sequence->empty()) |
| { |
| sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused), |
| sequence->end()); |
| } |
| |
| return true; |
| } |
| |
| bool TCompiler::validateOutputs(TIntermNode *root) |
| { |
| ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers); |
| root->traverse(&validateOutputs); |
| validateOutputs.validate(&mDiagnostics); |
| return (mDiagnostics.numErrors() == 0); |
| } |
| |
| bool TCompiler::limitExpressionComplexity(TIntermNode *root) |
| { |
| TMaxDepthTraverser traverser(maxExpressionComplexity + 1); |
| root->traverse(&traverser); |
| |
| if (traverser.getMaxDepth() > maxExpressionComplexity) |
| { |
| mDiagnostics.globalError("Expression too complex."); |
| return false; |
| } |
| |
| if (!ValidateMaxParameters::validate(root, maxFunctionParameters)) |
| { |
| mDiagnostics.globalError("Function has too many parameters."); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void TCompiler::collectVariables(TIntermNode *root) |
| { |
| if (!variablesCollected) |
| { |
| sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings, |
| &interfaceBlocks, hashFunction, symbolTable, |
| extensionBehavior); |
| root->traverse(&collect); |
| |
| // This is for enforcePackingRestriction(). |
| sh::ExpandUniforms(uniforms, &expandedUniforms); |
| variablesCollected = true; |
| } |
| } |
| |
| bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions) |
| { |
| return (compileOptions & SH_VARIABLES) != 0; |
| } |
| |
| bool TCompiler::wereVariablesCollected() const |
| { |
| return variablesCollected; |
| } |
| |
| bool TCompiler::enforcePackingRestrictions() |
| { |
| VariablePacker packer; |
| return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, expandedUniforms); |
| } |
| |
| void TCompiler::initializeGLPosition(TIntermBlock *root) |
| { |
| InitVariableList list; |
| sh::ShaderVariable var(GL_FLOAT_VEC4, 0); |
| var.name = "gl_Position"; |
| list.push_back(var); |
| InitializeVariables(root, list, symbolTable); |
| } |
| |
| void TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermBlock *root) |
| { |
| sh::InterfaceBlockList list; |
| |
| for (auto block : interfaceBlocks) |
| { |
| if (!block.staticUse && |
| (block.layout == sh::BLOCKLAYOUT_STANDARD || block.layout == sh::BLOCKLAYOUT_SHARED)) |
| { |
| list.push_back(block); |
| } |
| } |
| |
| sh::UseInterfaceBlockFields(root, list, symbolTable); |
| } |
| |
| void TCompiler::initializeOutputVariables(TIntermBlock *root) |
| { |
| InitVariableList list; |
| if (shaderType == GL_VERTEX_SHADER) |
| { |
| for (auto var : varyings) |
| { |
| list.push_back(var); |
| } |
| } |
| else |
| { |
| ASSERT(shaderType == GL_FRAGMENT_SHADER); |
| for (auto var : outputVariables) |
| { |
| list.push_back(var); |
| } |
| } |
| InitializeVariables(root, list, symbolTable); |
| } |
| |
| const TExtensionBehavior &TCompiler::getExtensionBehavior() const |
| { |
| return extensionBehavior; |
| } |
| |
| const char *TCompiler::getSourcePath() const |
| { |
| return mSourcePath; |
| } |
| |
| const ShBuiltInResources &TCompiler::getResources() const |
| { |
| return compileResources; |
| } |
| |
| const ArrayBoundsClamper &TCompiler::getArrayBoundsClamper() const |
| { |
| return arrayBoundsClamper; |
| } |
| |
| ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const |
| { |
| return clampingStrategy; |
| } |
| |
| const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const |
| { |
| return builtInFunctionEmulator; |
| } |
| |
| void TCompiler::writePragma(ShCompileOptions compileOptions) |
| { |
| if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL)) |
| { |
| TInfoSinkBase &sink = infoSink.obj; |
| if (mPragma.stdgl.invariantAll) |
| sink << "#pragma STDGL invariant(all)\n"; |
| } |
| } |
| |
| bool TCompiler::isVaryingDefined(const char *varyingName) |
| { |
| ASSERT(variablesCollected); |
| for (size_t ii = 0; ii < varyings.size(); ++ii) |
| { |
| if (varyings[ii].name == varyingName) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| } // namespace sh |