// // Copyright 2010 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // #include "compiler/translator/util.h" #include #include "common/utilities.h" #include "compiler/preprocessor/numeric_lex.h" #include "compiler/translator/ImmutableStringBuilder.h" #include "compiler/translator/SymbolTable.h" bool atoi_clamp(const char *str, unsigned int *value) { bool success = angle::pp::numeric_lex_int(str, value); if (!success) *value = std::numeric_limits::max(); return success; } namespace sh { namespace { // [primarySize-1][secondarySize-1] is the GL type with a basic type of float. constexpr GLenum kFloatGLType[4][4] = { // float1xS only makes sense for S == 1 { GL_FLOAT, GL_NONE, GL_NONE, GL_NONE, }, // float2xS is vec2 for S == 1, and mat2xS o.w. { GL_FLOAT_VEC2, GL_FLOAT_MAT2, GL_FLOAT_MAT2x3, GL_FLOAT_MAT2x4, }, // float3xS is vec3 for S == 1, and mat3xS o.w. { GL_FLOAT_VEC3, GL_FLOAT_MAT3x2, GL_FLOAT_MAT3, GL_FLOAT_MAT3x4, }, // float4xS is vec4 for S == 1, and mat4xS o.w. { GL_FLOAT_VEC4, GL_FLOAT_MAT4x2, GL_FLOAT_MAT4x3, GL_FLOAT_MAT4, }, }; // [primarySize-1] is the GL type with a basic type of int. constexpr GLenum kIntGLType[4] = {GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4}; // [primarySize-1] is the GL type with a basic type of uint. constexpr GLenum kUIntGLType[4] = {GL_UNSIGNED_INT, GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3, GL_UNSIGNED_INT_VEC4}; // [primarySize-1] is the GL type with a basic type of bool. constexpr GLenum kBoolGLType[4] = {GL_BOOL, GL_BOOL_VEC2, GL_BOOL_VEC3, GL_BOOL_VEC4}; bool IsInterpolationIn(TQualifier qualifier) { switch (qualifier) { case EvqSmoothIn: case EvqFlatIn: case EvqNoPerspectiveIn: case EvqCentroidIn: case EvqSampleIn: return true; default: return false; } } bool IsInterpolationOut(TQualifier qualifier) { switch (qualifier) { case EvqSmoothOut: case EvqFlatOut: case EvqNoPerspectiveOut: case EvqCentroidOut: case EvqSampleOut: return true; default: return false; } } } // anonymous namespace float NumericLexFloat32OutOfRangeToInfinity(const std::string &str) { // Parses a decimal string using scientific notation into a floating point number. // Out-of-range values are converted to infinity. Values that are too small to be // represented are converted to zero. // The mantissa in decimal scientific notation. The magnitude of the mantissa integer does not // matter. unsigned int decimalMantissa = 0; size_t i = 0; bool decimalPointSeen = false; bool nonZeroSeenInMantissa = false; // The exponent offset reflects the position of the decimal point. int exponentOffset = -1; // This is just a counter for how many decimal digits are written to decimalMantissa. int mantissaDecimalDigits = 0; while (i < str.length()) { const char c = str[i]; if (c == 'e' || c == 'E') { break; } if (c == '.') { decimalPointSeen = true; ++i; continue; } unsigned int digit = static_cast(c - '0'); ASSERT(digit < 10u); if (digit != 0u) { nonZeroSeenInMantissa = true; } if (nonZeroSeenInMantissa) { // Add bits to the mantissa until space runs out in 32-bit int. This should be // enough precision to make the resulting binary mantissa accurate to 1 ULP. if (decimalMantissa <= (std::numeric_limits::max() - 9u) / 10u) { decimalMantissa = decimalMantissa * 10u + digit; ++mantissaDecimalDigits; } if (!decimalPointSeen) { ++exponentOffset; } } else if (decimalPointSeen) { --exponentOffset; } ++i; } if (decimalMantissa == 0) { return 0.0f; } int exponent = 0; if (i < str.length()) { ASSERT(str[i] == 'e' || str[i] == 'E'); ++i; bool exponentOutOfRange = false; bool negativeExponent = false; if (str[i] == '-') { negativeExponent = true; ++i; } else if (str[i] == '+') { ++i; } while (i < str.length()) { const char c = str[i]; unsigned int digit = static_cast(c - '0'); ASSERT(digit < 10u); if (exponent <= (std::numeric_limits::max() - 9) / 10) { exponent = exponent * 10 + digit; } else { exponentOutOfRange = true; } ++i; } if (negativeExponent) { exponent = -exponent; } if (exponentOutOfRange) { if (negativeExponent) { return 0.0f; } else { return std::numeric_limits::infinity(); } } } // Do the calculation in 64-bit to avoid overflow. long long exponentLong = static_cast(exponent) + static_cast(exponentOffset); if (exponentLong > std::numeric_limits::max_exponent10) { return std::numeric_limits::infinity(); } else if (exponentLong < std::numeric_limits::min_exponent10) { return 0.0f; } // The exponent is in range, so we need to actually evaluate the float. exponent = static_cast(exponentLong); double value = decimalMantissa; // Calculate the exponent offset to normalize the mantissa. int normalizationExponentOffset = 1 - mantissaDecimalDigits; // Apply the exponent. value *= std::pow(10.0, static_cast(exponent + normalizationExponentOffset)); if (value > static_cast(std::numeric_limits::max())) { return std::numeric_limits::infinity(); } if (value < static_cast(std::numeric_limits::min())) { return 0.0f; } return static_cast(value); } bool strtof_clamp(const std::string &str, float *value) { // Custom float parsing that can handle the following corner cases: // 1. The decimal mantissa is very small but the exponent is very large, putting the resulting // number inside the float range. // 2. The decimal mantissa is very large but the exponent is very small, putting the resulting // number inside the float range. // 3. The value is out-of-range and should be evaluated as infinity. // 4. The value is too small and should be evaluated as zero. // See ESSL 3.00.6 section 4.1.4 for the relevant specification. *value = NumericLexFloat32OutOfRangeToInfinity(str); return !gl::isInf(*value); } GLenum GLVariableType(const TType &type) { switch (type.getBasicType()) { case EbtFloat: ASSERT(type.getNominalSize() >= 1 && type.getNominalSize() <= 4); ASSERT(type.getSecondarySize() >= 1 && type.getSecondarySize() <= 4); return kFloatGLType[type.getNominalSize() - 1][type.getSecondarySize() - 1]; case EbtInt: ASSERT(type.getNominalSize() >= 1 && type.getNominalSize() <= 4); ASSERT(type.getSecondarySize() == 1); return kIntGLType[type.getNominalSize() - 1]; case EbtUInt: ASSERT(type.getNominalSize() >= 1 && type.getNominalSize() <= 4); ASSERT(type.getSecondarySize() == 1); return kUIntGLType[type.getNominalSize() - 1]; case EbtBool: ASSERT(type.getNominalSize() >= 1 && type.getNominalSize() <= 4); ASSERT(type.getSecondarySize() == 1); return kBoolGLType[type.getNominalSize() - 1]; case EbtSampler2D: return GL_SAMPLER_2D; case EbtSampler3D: return GL_SAMPLER_3D; case EbtSamplerCube: return GL_SAMPLER_CUBE; case EbtSamplerExternalOES: return GL_SAMPLER_EXTERNAL_OES; case EbtSamplerExternal2DY2YEXT: return GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT; case EbtSampler2DRect: return GL_SAMPLER_2D_RECT_ANGLE; case EbtSampler2DArray: return GL_SAMPLER_2D_ARRAY; case EbtSampler2DMS: return GL_SAMPLER_2D_MULTISAMPLE; case EbtSampler2DMSArray: return GL_SAMPLER_2D_MULTISAMPLE_ARRAY; case EbtSamplerCubeArray: return GL_SAMPLER_CUBE_MAP_ARRAY; case EbtSamplerBuffer: return GL_SAMPLER_BUFFER; case EbtISampler2D: return GL_INT_SAMPLER_2D; case EbtISampler3D: return GL_INT_SAMPLER_3D; case EbtISamplerCube: return GL_INT_SAMPLER_CUBE; case EbtISampler2DArray: return GL_INT_SAMPLER_2D_ARRAY; case EbtISampler2DMS: return GL_INT_SAMPLER_2D_MULTISAMPLE; case EbtISampler2DMSArray: return GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY; case EbtISamplerCubeArray: return GL_INT_SAMPLER_CUBE_MAP_ARRAY; case EbtISamplerBuffer: return GL_INT_SAMPLER_BUFFER; case EbtUSampler2D: return GL_UNSIGNED_INT_SAMPLER_2D; case EbtUSampler3D: return GL_UNSIGNED_INT_SAMPLER_3D; case EbtUSamplerCube: return GL_UNSIGNED_INT_SAMPLER_CUBE; case EbtUSampler2DArray: return GL_UNSIGNED_INT_SAMPLER_2D_ARRAY; case EbtUSampler2DMS: return GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE; case EbtUSampler2DMSArray: return GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY; case EbtUSamplerCubeArray: return GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY; case EbtUSamplerBuffer: return GL_UNSIGNED_INT_SAMPLER_BUFFER; case EbtSampler2DShadow: return GL_SAMPLER_2D_SHADOW; case EbtSamplerCubeShadow: return GL_SAMPLER_CUBE_SHADOW; case EbtSampler2DArrayShadow: return GL_SAMPLER_2D_ARRAY_SHADOW; case EbtSamplerCubeArrayShadow: return GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW; case EbtImage2D: return GL_IMAGE_2D; case EbtIImage2D: return GL_INT_IMAGE_2D; case EbtUImage2D: return GL_UNSIGNED_INT_IMAGE_2D; case EbtImage2DArray: return GL_IMAGE_2D_ARRAY; case EbtIImage2DArray: return GL_INT_IMAGE_2D_ARRAY; case EbtUImage2DArray: return GL_UNSIGNED_INT_IMAGE_2D_ARRAY; case EbtImage3D: return GL_IMAGE_3D; case EbtIImage3D: return GL_INT_IMAGE_3D; case EbtUImage3D: return GL_UNSIGNED_INT_IMAGE_3D; case EbtImageCube: return GL_IMAGE_CUBE; case EbtIImageCube: return GL_INT_IMAGE_CUBE; case EbtUImageCube: return GL_UNSIGNED_INT_IMAGE_CUBE; case EbtImageCubeArray: return GL_IMAGE_CUBE_MAP_ARRAY; case EbtIImageCubeArray: return GL_INT_IMAGE_CUBE_MAP_ARRAY; case EbtUImageCubeArray: return GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY; case EbtImageBuffer: return GL_IMAGE_BUFFER; case EbtIImageBuffer: return GL_INT_IMAGE_BUFFER; case EbtUImageBuffer: return GL_UNSIGNED_INT_IMAGE_BUFFER; case EbtAtomicCounter: return GL_UNSIGNED_INT_ATOMIC_COUNTER; case EbtSamplerVideoWEBGL: return GL_SAMPLER_VIDEO_IMAGE_WEBGL; case EbtPixelLocalANGLE: case EbtIPixelLocalANGLE: case EbtUPixelLocalANGLE: // TODO(anglebug.com/7279): For now, we can expect PLS handles to be rewritten to images // before anyone calls into here. [[fallthrough]]; default: UNREACHABLE(); return GL_NONE; } } GLenum GLVariablePrecision(const TType &type) { if (type.getBasicType() == EbtFloat) { switch (type.getPrecision()) { case EbpHigh: return GL_HIGH_FLOAT; case EbpMedium: return GL_MEDIUM_FLOAT; case EbpLow: return GL_LOW_FLOAT; case EbpUndefined: // Desktop specs do not use precision return GL_NONE; default: UNREACHABLE(); } } else if (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt) { switch (type.getPrecision()) { case EbpHigh: return GL_HIGH_INT; case EbpMedium: return GL_MEDIUM_INT; case EbpLow: return GL_LOW_INT; case EbpUndefined: // Desktop specs do not use precision return GL_NONE; default: UNREACHABLE(); } } // Other types (boolean, sampler) don't have a precision return GL_NONE; } ImmutableString ArrayString(const TType &type) { if (!type.isArray()) return ImmutableString(""); const TSpan &arraySizes = type.getArraySizes(); constexpr const size_t kMaxDecimalDigitsPerSize = 10u; ImmutableStringBuilder arrayString(arraySizes.size() * (kMaxDecimalDigitsPerSize + 2u)); for (auto arraySizeIter = arraySizes.rbegin(); arraySizeIter != arraySizes.rend(); ++arraySizeIter) { arrayString << "["; if (*arraySizeIter > 0) { arrayString.appendDecimal(*arraySizeIter); } arrayString << "]"; } return arrayString; } ImmutableString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap) { if (type.getBasicType() == EbtStruct) return HashName(type.getStruct(), hashFunction, nameMap); else return ImmutableString(type.getBuiltInTypeNameString()); } bool IsVaryingOut(TQualifier qualifier) { switch (qualifier) { case EvqVaryingOut: case EvqSmoothOut: case EvqFlatOut: case EvqNoPerspectiveOut: case EvqCentroidOut: case EvqVertexOut: case EvqGeometryOut: case EvqTessControlOut: case EvqTessEvaluationOut: case EvqSampleOut: case EvqPatchOut: return true; default: break; } return false; } bool IsVaryingIn(TQualifier qualifier) { switch (qualifier) { case EvqVaryingIn: case EvqSmoothIn: case EvqFlatIn: case EvqNoPerspectiveIn: case EvqCentroidIn: case EvqFragmentIn: case EvqGeometryIn: case EvqTessControlIn: case EvqTessEvaluationIn: case EvqSampleIn: case EvqPatchIn: return true; default: break; } return false; } bool IsVarying(TQualifier qualifier) { return IsVaryingIn(qualifier) || IsVaryingOut(qualifier); } bool IsMatrixGLType(GLenum type) { switch (type) { case GL_FLOAT_MAT2: case GL_FLOAT_MAT3: case GL_FLOAT_MAT4: case GL_FLOAT_MAT2x3: case GL_FLOAT_MAT2x4: case GL_FLOAT_MAT3x2: case GL_FLOAT_MAT3x4: case GL_FLOAT_MAT4x2: case GL_FLOAT_MAT4x3: return true; default: return false; } } bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier) { return (qualifier == EvqGeometryIn) || ((shaderType == GL_GEOMETRY_SHADER_EXT) && IsInterpolationIn(qualifier)); } bool IsTessellationControlShaderInput(GLenum shaderType, TQualifier qualifier) { return qualifier == EvqTessControlIn || ((shaderType == GL_TESS_CONTROL_SHADER) && IsInterpolationIn(qualifier)); } bool IsTessellationControlShaderOutput(GLenum shaderType, TQualifier qualifier) { return qualifier == EvqTessControlOut || ((shaderType == GL_TESS_CONTROL_SHADER) && IsInterpolationOut(qualifier)); } bool IsTessellationEvaluationShaderInput(GLenum shaderType, TQualifier qualifier) { return qualifier == EvqTessEvaluationIn || ((shaderType == GL_TESS_EVALUATION_SHADER) && IsInterpolationIn(qualifier)); } InterpolationType GetInterpolationType(TQualifier qualifier) { switch (qualifier) { case EvqFlatIn: case EvqFlatOut: // The auxiliary storage qualifier patch is not used for interpolation // it is a compile-time error to use interpolation qualifiers with patch case EvqPatchIn: case EvqPatchOut: return INTERPOLATION_FLAT; case EvqNoPerspectiveIn: case EvqNoPerspectiveOut: return INTERPOLATION_NOPERSPECTIVE; case EvqSmoothIn: case EvqSmoothOut: case EvqVertexOut: case EvqFragmentIn: case EvqVaryingIn: case EvqVaryingOut: case EvqGeometryIn: case EvqGeometryOut: case EvqTessControlIn: case EvqTessControlOut: case EvqTessEvaluationIn: case EvqTessEvaluationOut: return INTERPOLATION_SMOOTH; case EvqCentroidIn: case EvqCentroidOut: return INTERPOLATION_CENTROID; case EvqSampleIn: case EvqSampleOut: return INTERPOLATION_SAMPLE; default: UNREACHABLE(); return INTERPOLATION_SMOOTH; } } // a field may not have qualifer without in or out. InterpolationType GetFieldInterpolationType(TQualifier qualifier) { switch (qualifier) { case EvqFlat: return INTERPOLATION_FLAT; case EvqNoPerspective: return INTERPOLATION_NOPERSPECTIVE; case EvqSmooth: return INTERPOLATION_SMOOTH; case EvqCentroid: return INTERPOLATION_CENTROID; default: return GetInterpolationType(qualifier); } } TType GetShaderVariableBasicType(const sh::ShaderVariable &var) { switch (var.type) { case GL_BOOL: return TType(EbtBool); case GL_BOOL_VEC2: return TType(EbtBool, 2); case GL_BOOL_VEC3: return TType(EbtBool, 3); case GL_BOOL_VEC4: return TType(EbtBool, 4); case GL_FLOAT: return TType(EbtFloat); case GL_FLOAT_VEC2: return TType(EbtFloat, 2); case GL_FLOAT_VEC3: return TType(EbtFloat, 3); case GL_FLOAT_VEC4: return TType(EbtFloat, 4); case GL_FLOAT_MAT2: return TType(EbtFloat, 2, 2); case GL_FLOAT_MAT3: return TType(EbtFloat, 3, 3); case GL_FLOAT_MAT4: return TType(EbtFloat, 4, 4); case GL_FLOAT_MAT2x3: return TType(EbtFloat, 2, 3); case GL_FLOAT_MAT2x4: return TType(EbtFloat, 2, 4); case GL_FLOAT_MAT3x2: return TType(EbtFloat, 3, 2); case GL_FLOAT_MAT3x4: return TType(EbtFloat, 3, 4); case GL_FLOAT_MAT4x2: return TType(EbtFloat, 4, 2); case GL_FLOAT_MAT4x3: return TType(EbtFloat, 4, 3); case GL_INT: return TType(EbtInt); case GL_INT_VEC2: return TType(EbtInt, 2); case GL_INT_VEC3: return TType(EbtInt, 3); case GL_INT_VEC4: return TType(EbtInt, 4); case GL_UNSIGNED_INT: return TType(EbtUInt); case GL_UNSIGNED_INT_VEC2: return TType(EbtUInt, 2); case GL_UNSIGNED_INT_VEC3: return TType(EbtUInt, 3); case GL_UNSIGNED_INT_VEC4: return TType(EbtUInt, 4); default: UNREACHABLE(); return TType(); } } void DeclareGlobalVariable(TIntermBlock *root, const TVariable *variable) { TIntermDeclaration *declaration = new TIntermDeclaration(); declaration->appendDeclarator(new TIntermSymbol(variable)); TIntermSequence *globalSequence = root->getSequence(); globalSequence->insert(globalSequence->begin(), declaration); } // GLSL ES 1.0.17 4.6.1 The Invariant Qualifier bool CanBeInvariantESSL1(TQualifier qualifier) { return IsVaryingIn(qualifier) || IsVaryingOut(qualifier) || IsBuiltinOutputVariable(qualifier) || (IsBuiltinFragmentInputVariable(qualifier) && qualifier != EvqFrontFacing); } // GLSL ES 3.00 Revision 6, 4.6.1 The Invariant Qualifier // GLSL ES 3.10 Revision 4, 4.8.1 The Invariant Qualifier bool CanBeInvariantESSL3OrGreater(TQualifier qualifier) { return IsVaryingOut(qualifier) || qualifier == EvqFragmentOut || IsBuiltinOutputVariable(qualifier) || qualifier == EvqFragmentInOut; } bool IsBuiltinOutputVariable(TQualifier qualifier) { switch (qualifier) { case EvqPosition: case EvqPointSize: case EvqFragDepth: case EvqFragColor: case EvqSecondaryFragColorEXT: case EvqFragData: case EvqSecondaryFragDataEXT: case EvqClipDistance: case EvqCullDistance: case EvqLastFragData: case EvqSampleMask: return true; default: break; } return false; } bool IsBuiltinFragmentInputVariable(TQualifier qualifier) { switch (qualifier) { case EvqFragCoord: case EvqPointCoord: case EvqFrontFacing: case EvqHelperInvocation: case EvqLastFragData: return true; default: break; } return false; } bool IsShaderOutput(TQualifier qualifier) { return IsVaryingOut(qualifier) || IsBuiltinOutputVariable(qualifier); } bool IsFragmentOutput(TQualifier qualifier) { switch (qualifier) { case EvqFragmentOut: case EvqFragmentInOut: return true; default: return false; } } bool IsOutputESSL(ShShaderOutput output) { return output == SH_ESSL_OUTPUT; } bool IsOutputGLSL(ShShaderOutput output) { switch (output) { case SH_GLSL_130_OUTPUT: case SH_GLSL_140_OUTPUT: case SH_GLSL_150_CORE_OUTPUT: case SH_GLSL_330_CORE_OUTPUT: case SH_GLSL_400_CORE_OUTPUT: case SH_GLSL_410_CORE_OUTPUT: case SH_GLSL_420_CORE_OUTPUT: case SH_GLSL_430_CORE_OUTPUT: case SH_GLSL_440_CORE_OUTPUT: case SH_GLSL_450_CORE_OUTPUT: case SH_GLSL_COMPATIBILITY_OUTPUT: return true; default: break; } return false; } bool IsOutputHLSL(ShShaderOutput output) { switch (output) { case SH_HLSL_3_0_OUTPUT: case SH_HLSL_4_1_OUTPUT: case SH_HLSL_4_0_FL9_3_OUTPUT: return true; default: break; } return false; } bool IsOutputVulkan(ShShaderOutput output) { return output == SH_SPIRV_VULKAN_OUTPUT; } bool IsOutputMetal(ShShaderOutput output) { return output == SH_SPIRV_METAL_OUTPUT; } bool IsOutputMetalDirect(ShShaderOutput output) { return output == SH_MSL_METAL_OUTPUT; } bool IsInShaderStorageBlock(TIntermTyped *node) { TIntermSwizzle *swizzleNode = node->getAsSwizzleNode(); if (swizzleNode) { return IsInShaderStorageBlock(swizzleNode->getOperand()); } TIntermBinary *binaryNode = node->getAsBinaryNode(); if (binaryNode) { switch (binaryNode->getOp()) { case EOpIndexDirectInterfaceBlock: case EOpIndexIndirect: case EOpIndexDirect: case EOpIndexDirectStruct: return IsInShaderStorageBlock(binaryNode->getLeft()); default: return false; } } const TType &type = node->getType(); return type.getQualifier() == EvqBuffer; } GLenum GetImageInternalFormatType(TLayoutImageInternalFormat iifq) { switch (iifq) { case EiifRGBA32F: return GL_RGBA32F; case EiifRGBA16F: return GL_RGBA16F; case EiifR32F: return GL_R32F; case EiifRGBA32UI: return GL_RGBA32UI; case EiifRGBA16UI: return GL_RGBA16UI; case EiifRGBA8UI: return GL_RGBA8UI; case EiifR32UI: return GL_R32UI; case EiifRGBA32I: return GL_RGBA32I; case EiifRGBA16I: return GL_RGBA16I; case EiifRGBA8I: return GL_RGBA8I; case EiifR32I: return GL_R32I; case EiifRGBA8: return GL_RGBA8; case EiifRGBA8_SNORM: return GL_RGBA8_SNORM; default: return GL_NONE; } } bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion) { return (shaderVersion == 100 && !sh::IsWebGLBasedSpec(shaderSpec)); } ImplicitTypeConversion GetConversion(TBasicType t1, TBasicType t2) { if (t1 == t2) return ImplicitTypeConversion::Same; switch (t1) { case EbtInt: switch (t2) { case EbtInt: UNREACHABLE(); break; case EbtUInt: return ImplicitTypeConversion::Invalid; case EbtFloat: return ImplicitTypeConversion::Left; default: return ImplicitTypeConversion::Invalid; } break; case EbtUInt: switch (t2) { case EbtInt: return ImplicitTypeConversion::Invalid; case EbtUInt: UNREACHABLE(); break; case EbtFloat: return ImplicitTypeConversion::Left; default: return ImplicitTypeConversion::Invalid; } break; case EbtFloat: switch (t2) { case EbtInt: case EbtUInt: return ImplicitTypeConversion::Right; case EbtFloat: UNREACHABLE(); break; default: return ImplicitTypeConversion::Invalid; } break; default: return ImplicitTypeConversion::Invalid; } return ImplicitTypeConversion::Invalid; } bool IsValidImplicitConversion(sh::ImplicitTypeConversion conversion, TOperator op) { switch (conversion) { case sh::ImplicitTypeConversion::Same: return true; case sh::ImplicitTypeConversion::Left: switch (op) { case EOpEqual: case EOpNotEqual: case EOpLessThan: case EOpGreaterThan: case EOpLessThanEqual: case EOpGreaterThanEqual: case EOpAdd: case EOpSub: case EOpMul: case EOpDiv: return true; default: break; } break; case sh::ImplicitTypeConversion::Right: switch (op) { case EOpAssign: case EOpInitialize: case EOpEqual: case EOpNotEqual: case EOpLessThan: case EOpGreaterThan: case EOpLessThanEqual: case EOpGreaterThanEqual: case EOpAdd: case EOpSub: case EOpMul: case EOpDiv: case EOpAddAssign: case EOpSubAssign: case EOpMulAssign: case EOpDivAssign: return true; default: break; } break; case sh::ImplicitTypeConversion::Invalid: break; } return false; } bool IsPrecisionApplicableToType(TBasicType type) { switch (type) { case EbtInt: case EbtUInt: case EbtFloat: // TODO: find all types where precision is applicable; for example samplers. // http://anglebug.com/6132 return true; default: return false; } } bool IsRedeclarableBuiltIn(const ImmutableString &name) { return name == "gl_ClipDistance" || name == "gl_CullDistance" || name == "gl_LastFragData" || name == "gl_PerVertex" || name == "gl_Position" || name == "gl_PointSize"; } size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName) { for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex) { if (strcmp(fieldList[fieldIndex]->name().data(), fieldName) == 0) { return fieldIndex; } } UNREACHABLE(); return 0; } } // namespace sh