tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

renderer_utils.cpp (62537B)


      1 //
      2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 // renderer_utils:
      7 //   Helper methods pertaining to most or all back-ends.
      8 //
      9 
     10 #include "libANGLE/renderer/renderer_utils.h"
     11 
     12 #include "common/string_utils.h"
     13 #include "common/system_utils.h"
     14 #include "common/utilities.h"
     15 #include "image_util/copyimage.h"
     16 #include "image_util/imageformats.h"
     17 #include "libANGLE/AttributeMap.h"
     18 #include "libANGLE/Context.h"
     19 #include "libANGLE/Context.inl.h"
     20 #include "libANGLE/Display.h"
     21 #include "libANGLE/formatutils.h"
     22 #include "libANGLE/renderer/ContextImpl.h"
     23 #include "libANGLE/renderer/Format.h"
     24 #include "platform/Feature.h"
     25 
     26 #include <string.h>
     27 #include <cctype>
     28 
     29 namespace angle
     30 {
     31 namespace
     32 {
     33 // For the sake of feature name matching, underscore is ignored, and the names are matched
     34 // case-insensitive.  This allows feature names to be overriden both in snake_case (previously used
     35 // by ANGLE) and camelCase.  The second string (user-provided name) can end in `*` for wildcard
     36 // matching.
     37 bool FeatureNameMatch(const std::string &a, const std::string &b)
     38 {
     39    size_t ai = 0;
     40    size_t bi = 0;
     41 
     42    while (ai < a.size() && bi < b.size())
     43    {
     44        if (a[ai] == '_')
     45        {
     46            ++ai;
     47        }
     48        if (b[bi] == '_')
     49        {
     50            ++bi;
     51        }
     52        if (b[bi] == '*' && bi + 1 == b.size())
     53        {
     54            // If selected feature name ends in wildcard, match it.
     55            return true;
     56        }
     57        if (std::tolower(a[ai++]) != std::tolower(b[bi++]))
     58        {
     59            return false;
     60        }
     61    }
     62 
     63    return ai == a.size() && bi == b.size();
     64 }
     65 }  // anonymous namespace
     66 
     67 // FeatureSetBase implementation
     68 void FeatureSetBase::overrideFeatures(const std::vector<std::string> &featureNames, bool enabled)
     69 {
     70    for (const std::string &name : featureNames)
     71    {
     72        const bool hasWildcard = name.back() == '*';
     73        for (auto iter : members)
     74        {
     75            const std::string &featureName = iter.first;
     76            FeatureInfo *feature           = iter.second;
     77 
     78            if (!FeatureNameMatch(featureName, name))
     79            {
     80                continue;
     81            }
     82 
     83            feature->enabled = enabled;
     84 
     85            // If name has a wildcard, try to match it with all features.  Otherwise, bail on first
     86            // match, as names are unique.
     87            if (!hasWildcard)
     88            {
     89                break;
     90            }
     91        }
     92    }
     93 }
     94 
     95 void FeatureSetBase::populateFeatureList(FeatureList *features) const
     96 {
     97    for (FeatureMap::const_iterator it = members.begin(); it != members.end(); it++)
     98    {
     99        features->push_back(it->second);
    100    }
    101 }
    102 }  // namespace angle
    103 
    104 namespace rx
    105 {
    106 
    107 namespace
    108 {
    109 // Both D3D and Vulkan support the same set of standard sample positions for 1, 2, 4, 8, and 16
    110 // samples.  See:
    111 //
    112 // - https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218.aspx
    113 //
    114 // -
    115 // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#primsrast-multisampling
    116 using SamplePositionsArray                                     = std::array<float, 32>;
    117 constexpr std::array<SamplePositionsArray, 5> kSamplePositions = {
    118    {{{0.5f, 0.5f}},
    119     {{0.75f, 0.75f, 0.25f, 0.25f}},
    120     {{0.375f, 0.125f, 0.875f, 0.375f, 0.125f, 0.625f, 0.625f, 0.875f}},
    121     {{0.5625f, 0.3125f, 0.4375f, 0.6875f, 0.8125f, 0.5625f, 0.3125f, 0.1875f, 0.1875f, 0.8125f,
    122       0.0625f, 0.4375f, 0.6875f, 0.9375f, 0.9375f, 0.0625f}},
    123     {{0.5625f, 0.5625f, 0.4375f, 0.3125f, 0.3125f, 0.625f,  0.75f,   0.4375f,
    124       0.1875f, 0.375f,  0.625f,  0.8125f, 0.8125f, 0.6875f, 0.6875f, 0.1875f,
    125       0.375f,  0.875f,  0.5f,    0.0625f, 0.25f,   0.125f,  0.125f,  0.75f,
    126       0.0f,    0.5f,    0.9375f, 0.25f,   0.875f,  0.9375f, 0.0625f, 0.0f}}}};
    127 
    128 struct IncompleteTextureParameters
    129 {
    130    GLenum sizedInternalFormat;
    131    GLenum format;
    132    GLenum type;
    133    GLubyte clearColor[4];
    134 };
    135 
    136 // Note that for gl::SamplerFormat::Shadow, the clearColor datatype needs to be GLushort and as such
    137 // we will reinterpret GLubyte[4] as GLushort[2].
    138 constexpr angle::PackedEnumMap<gl::SamplerFormat, IncompleteTextureParameters>
    139    kIncompleteTextureParameters = {
    140        {gl::SamplerFormat::Float, {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, {0, 0, 0, 255}}},
    141        {gl::SamplerFormat::Unsigned,
    142         {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, {0, 0, 0, 255}}},
    143        {gl::SamplerFormat::Signed, {GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, {0, 0, 0, 127}}},
    144        {gl::SamplerFormat::Shadow,
    145         {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, {0, 0, 0, 0}}}};
    146 
    147 void CopyColor(gl::ColorF *color)
    148 {
    149    // No-op
    150 }
    151 
    152 void PremultiplyAlpha(gl::ColorF *color)
    153 {
    154    color->red *= color->alpha;
    155    color->green *= color->alpha;
    156    color->blue *= color->alpha;
    157 }
    158 
    159 void UnmultiplyAlpha(gl::ColorF *color)
    160 {
    161    if (color->alpha != 0.0f)
    162    {
    163        float invAlpha = 1.0f / color->alpha;
    164        color->red *= invAlpha;
    165        color->green *= invAlpha;
    166        color->blue *= invAlpha;
    167    }
    168 }
    169 
    170 void ClipChannelsR(gl::ColorF *color)
    171 {
    172    color->green = 0.0f;
    173    color->blue  = 0.0f;
    174    color->alpha = 1.0f;
    175 }
    176 
    177 void ClipChannelsRG(gl::ColorF *color)
    178 {
    179    color->blue  = 0.0f;
    180    color->alpha = 1.0f;
    181 }
    182 
    183 void ClipChannelsRGB(gl::ColorF *color)
    184 {
    185    color->alpha = 1.0f;
    186 }
    187 
    188 void ClipChannelsLuminance(gl::ColorF *color)
    189 {
    190    color->alpha = 1.0f;
    191 }
    192 
    193 void ClipChannelsAlpha(gl::ColorF *color)
    194 {
    195    color->red   = 0.0f;
    196    color->green = 0.0f;
    197    color->blue  = 0.0f;
    198 }
    199 
    200 void ClipChannelsNoOp(gl::ColorF *color) {}
    201 
    202 void WriteUintColor(const gl::ColorF &color,
    203                    PixelWriteFunction colorWriteFunction,
    204                    uint8_t *destPixelData)
    205 {
    206    gl::ColorUI destColor(
    207        static_cast<unsigned int>(color.red * 255), static_cast<unsigned int>(color.green * 255),
    208        static_cast<unsigned int>(color.blue * 255), static_cast<unsigned int>(color.alpha * 255));
    209    colorWriteFunction(reinterpret_cast<const uint8_t *>(&destColor), destPixelData);
    210 }
    211 
    212 void WriteFloatColor(const gl::ColorF &color,
    213                     PixelWriteFunction colorWriteFunction,
    214                     uint8_t *destPixelData)
    215 {
    216    colorWriteFunction(reinterpret_cast<const uint8_t *>(&color), destPixelData);
    217 }
    218 
    219 template <int cols, int rows, bool IsColumnMajor>
    220 inline int GetFlattenedIndex(int col, int row)
    221 {
    222    if (IsColumnMajor)
    223    {
    224        return col * rows + row;
    225    }
    226    else
    227    {
    228        return row * cols + col;
    229    }
    230 }
    231 
    232 template <typename T,
    233          bool IsSrcColumnMajor,
    234          int colsSrc,
    235          int rowsSrc,
    236          bool IsDstColumnMajor,
    237          int colsDst,
    238          int rowsDst>
    239 void ExpandMatrix(T *target, const GLfloat *value)
    240 {
    241    static_assert(colsSrc <= colsDst && rowsSrc <= rowsDst, "Can only expand!");
    242 
    243    constexpr int kDstFlatSize = colsDst * rowsDst;
    244    T staging[kDstFlatSize]    = {0};
    245 
    246    for (int r = 0; r < rowsSrc; r++)
    247    {
    248        for (int c = 0; c < colsSrc; c++)
    249        {
    250            int srcIndex = GetFlattenedIndex<colsSrc, rowsSrc, IsSrcColumnMajor>(c, r);
    251            int dstIndex = GetFlattenedIndex<colsDst, rowsDst, IsDstColumnMajor>(c, r);
    252 
    253            staging[dstIndex] = static_cast<T>(value[srcIndex]);
    254        }
    255    }
    256 
    257    memcpy(target, staging, kDstFlatSize * sizeof(T));
    258 }
    259 
    260 template <bool IsSrcColumMajor,
    261          int colsSrc,
    262          int rowsSrc,
    263          bool IsDstColumnMajor,
    264          int colsDst,
    265          int rowsDst>
    266 void SetFloatUniformMatrix(unsigned int arrayElementOffset,
    267                           unsigned int elementCount,
    268                           GLsizei countIn,
    269                           const GLfloat *value,
    270                           uint8_t *targetData)
    271 {
    272    unsigned int count =
    273        std::min(elementCount - arrayElementOffset, static_cast<unsigned int>(countIn));
    274 
    275    const unsigned int targetMatrixStride = colsDst * rowsDst;
    276    GLfloat *target                       = reinterpret_cast<GLfloat *>(
    277        targetData + arrayElementOffset * sizeof(GLfloat) * targetMatrixStride);
    278 
    279    for (unsigned int i = 0; i < count; i++)
    280    {
    281        ExpandMatrix<GLfloat, IsSrcColumMajor, colsSrc, rowsSrc, IsDstColumnMajor, colsDst,
    282                     rowsDst>(target, value);
    283 
    284        target += targetMatrixStride;
    285        value += colsSrc * rowsSrc;
    286    }
    287 }
    288 
    289 void SetFloatUniformMatrixFast(unsigned int arrayElementOffset,
    290                               unsigned int elementCount,
    291                               GLsizei countIn,
    292                               size_t matrixSize,
    293                               const GLfloat *value,
    294                               uint8_t *targetData)
    295 {
    296    const unsigned int count =
    297        std::min(elementCount - arrayElementOffset, static_cast<unsigned int>(countIn));
    298 
    299    const uint8_t *valueData = reinterpret_cast<const uint8_t *>(value);
    300    targetData               = targetData + arrayElementOffset * matrixSize;
    301 
    302    memcpy(targetData, valueData, matrixSize * count);
    303 }
    304 }  // anonymous namespace
    305 
    306 bool IsRotatedAspectRatio(SurfaceRotation rotation)
    307 {
    308    switch (rotation)
    309    {
    310        case SurfaceRotation::Rotated90Degrees:
    311        case SurfaceRotation::Rotated270Degrees:
    312        case SurfaceRotation::FlippedRotated90Degrees:
    313        case SurfaceRotation::FlippedRotated270Degrees:
    314            return true;
    315        default:
    316            return false;
    317    }
    318 }
    319 
    320 void RotateRectangle(const SurfaceRotation rotation,
    321                     const bool flipY,
    322                     const int framebufferWidth,
    323                     const int framebufferHeight,
    324                     const gl::Rectangle &incoming,
    325                     gl::Rectangle *outgoing)
    326 {
    327    // GLES's y-axis points up; Vulkan's points down.
    328    switch (rotation)
    329    {
    330        case SurfaceRotation::Identity:
    331            // Do not rotate gl_Position (surface matches the device's orientation):
    332            outgoing->x     = incoming.x;
    333            outgoing->y     = flipY ? framebufferHeight - incoming.y - incoming.height : incoming.y;
    334            outgoing->width = incoming.width;
    335            outgoing->height = incoming.height;
    336            break;
    337        case SurfaceRotation::Rotated90Degrees:
    338            // Rotate gl_Position 90 degrees:
    339            outgoing->x      = incoming.y;
    340            outgoing->y      = flipY ? incoming.x : framebufferWidth - incoming.x - incoming.width;
    341            outgoing->width  = incoming.height;
    342            outgoing->height = incoming.width;
    343            break;
    344        case SurfaceRotation::Rotated180Degrees:
    345            // Rotate gl_Position 180 degrees:
    346            outgoing->x     = framebufferWidth - incoming.x - incoming.width;
    347            outgoing->y     = flipY ? incoming.y : framebufferHeight - incoming.y - incoming.height;
    348            outgoing->width = incoming.width;
    349            outgoing->height = incoming.height;
    350            break;
    351        case SurfaceRotation::Rotated270Degrees:
    352            // Rotate gl_Position 270 degrees:
    353            outgoing->x      = framebufferHeight - incoming.y - incoming.height;
    354            outgoing->y      = flipY ? framebufferWidth - incoming.x - incoming.width : incoming.x;
    355            outgoing->width  = incoming.height;
    356            outgoing->height = incoming.width;
    357            break;
    358        default:
    359            UNREACHABLE();
    360            break;
    361    }
    362 }
    363 
    364 PackPixelsParams::PackPixelsParams()
    365    : destFormat(nullptr),
    366      outputPitch(0),
    367      packBuffer(nullptr),
    368      offset(0),
    369      rotation(SurfaceRotation::Identity)
    370 {}
    371 
    372 PackPixelsParams::PackPixelsParams(const gl::Rectangle &areaIn,
    373                                   const angle::Format &destFormat,
    374                                   GLuint outputPitchIn,
    375                                   bool reverseRowOrderIn,
    376                                   gl::Buffer *packBufferIn,
    377                                   ptrdiff_t offsetIn)
    378    : area(areaIn),
    379      destFormat(&destFormat),
    380      outputPitch(outputPitchIn),
    381      packBuffer(packBufferIn),
    382      reverseRowOrder(reverseRowOrderIn),
    383      offset(offsetIn),
    384      rotation(SurfaceRotation::Identity)
    385 {}
    386 
    387 void PackPixels(const PackPixelsParams &params,
    388                const angle::Format &sourceFormat,
    389                int inputPitchIn,
    390                const uint8_t *sourceIn,
    391                uint8_t *destWithoutOffset)
    392 {
    393    uint8_t *destWithOffset = destWithoutOffset + params.offset;
    394 
    395    const uint8_t *source = sourceIn;
    396    int inputPitch        = inputPitchIn;
    397    int destWidth         = params.area.width;
    398    int destHeight        = params.area.height;
    399    int xAxisPitch        = 0;
    400    int yAxisPitch        = 0;
    401    switch (params.rotation)
    402    {
    403        case SurfaceRotation::Identity:
    404            // The source image is not rotated (i.e. matches the device's orientation), and may or
    405            // may not be y-flipped.  The image is row-major.  Each source row (one step along the
    406            // y-axis for each step in the dest y-axis) is inputPitch past the previous row.  Along
    407            // a row, each source pixel (one step along the x-axis for each step in the dest
    408            // x-axis) is sourceFormat.pixelBytes past the previous pixel.
    409            xAxisPitch = sourceFormat.pixelBytes;
    410            if (params.reverseRowOrder)
    411            {
    412                // The source image is y-flipped, which means we start at the last row, and each
    413                // source row is BEFORE the previous row.
    414                source += inputPitchIn * (params.area.height - 1);
    415                inputPitch = -inputPitch;
    416                yAxisPitch = -inputPitchIn;
    417            }
    418            else
    419            {
    420                yAxisPitch = inputPitchIn;
    421            }
    422            break;
    423        case SurfaceRotation::Rotated90Degrees:
    424            // The source image is rotated 90 degrees counter-clockwise.  Y-flip is always applied
    425            // to rotated images.  The image is column-major.  Each source column (one step along
    426            // the source x-axis for each step in the dest y-axis) is inputPitch past the previous
    427            // column.  Along a column, each source pixel (one step along the y-axis for each step
    428            // in the dest x-axis) is sourceFormat.pixelBytes past the previous pixel.
    429            xAxisPitch = inputPitchIn;
    430            yAxisPitch = sourceFormat.pixelBytes;
    431            destWidth  = params.area.height;
    432            destHeight = params.area.width;
    433            break;
    434        case SurfaceRotation::Rotated180Degrees:
    435            // The source image is rotated 180 degrees.  Y-flip is always applied to rotated
    436            // images.  The image is row-major, but upside down.  Each source row (one step along
    437            // the y-axis for each step in the dest y-axis) is inputPitch after the previous row.
    438            // Along a row, each source pixel (one step along the x-axis for each step in the dest
    439            // x-axis) is sourceFormat.pixelBytes BEFORE the previous pixel.
    440            xAxisPitch = -static_cast<int>(sourceFormat.pixelBytes);
    441            yAxisPitch = inputPitchIn;
    442            source += sourceFormat.pixelBytes * (params.area.width - 1);
    443            break;
    444        case SurfaceRotation::Rotated270Degrees:
    445            // The source image is rotated 270 degrees counter-clockwise (or 90 degrees clockwise).
    446            // Y-flip is always applied to rotated images.  The image is column-major, where each
    447            // column (one step in the source x-axis for one step in the dest y-axis) is inputPitch
    448            // BEFORE the previous column.  Along a column, each source pixel (one step along the
    449            // y-axis for each step in the dest x-axis) is sourceFormat.pixelBytes BEFORE the
    450            // previous pixel.  The first pixel is at the end of the source.
    451            xAxisPitch = -inputPitchIn;
    452            yAxisPitch = -static_cast<int>(sourceFormat.pixelBytes);
    453            destWidth  = params.area.height;
    454            destHeight = params.area.width;
    455            source += inputPitch * (params.area.height - 1) +
    456                      sourceFormat.pixelBytes * (params.area.width - 1);
    457            break;
    458        default:
    459            UNREACHABLE();
    460            break;
    461    }
    462 
    463    if (params.rotation == SurfaceRotation::Identity && sourceFormat == *params.destFormat)
    464    {
    465        // Direct copy possible
    466        for (int y = 0; y < params.area.height; ++y)
    467        {
    468            memcpy(destWithOffset + y * params.outputPitch, source + y * inputPitch,
    469                   params.area.width * sourceFormat.pixelBytes);
    470        }
    471        return;
    472    }
    473 
    474    FastCopyFunction fastCopyFunc = sourceFormat.fastCopyFunctions.get(params.destFormat->id);
    475 
    476    if (fastCopyFunc)
    477    {
    478        // Fast copy is possible through some special function
    479        fastCopyFunc(source, xAxisPitch, yAxisPitch, destWithOffset, params.destFormat->pixelBytes,
    480                     params.outputPitch, destWidth, destHeight);
    481        return;
    482    }
    483 
    484    PixelWriteFunction pixelWriteFunction = params.destFormat->pixelWriteFunction;
    485    ASSERT(pixelWriteFunction != nullptr);
    486 
    487    // Maximum size of any Color<T> type used.
    488    uint8_t temp[16];
    489    static_assert(sizeof(temp) >= sizeof(gl::ColorF) && sizeof(temp) >= sizeof(gl::ColorUI) &&
    490                      sizeof(temp) >= sizeof(gl::ColorI) &&
    491                      sizeof(temp) >= sizeof(angle::DepthStencil),
    492                  "Unexpected size of pixel struct.");
    493 
    494    PixelReadFunction pixelReadFunction = sourceFormat.pixelReadFunction;
    495    ASSERT(pixelReadFunction != nullptr);
    496 
    497    for (int y = 0; y < destHeight; ++y)
    498    {
    499        for (int x = 0; x < destWidth; ++x)
    500        {
    501            uint8_t *dest =
    502                destWithOffset + y * params.outputPitch + x * params.destFormat->pixelBytes;
    503            const uint8_t *src = source + y * yAxisPitch + x * xAxisPitch;
    504 
    505            // readFunc and writeFunc will be using the same type of color, CopyTexImage
    506            // will not allow the copy otherwise.
    507            pixelReadFunction(src, temp);
    508            pixelWriteFunction(temp, dest);
    509        }
    510    }
    511 }
    512 
    513 bool FastCopyFunctionMap::has(angle::FormatID formatID) const
    514 {
    515    return (get(formatID) != nullptr);
    516 }
    517 
    518 namespace
    519 {
    520 
    521 const FastCopyFunctionMap::Entry *getEntry(const FastCopyFunctionMap::Entry *entry,
    522                                           size_t numEntries,
    523                                           angle::FormatID formatID)
    524 {
    525    const FastCopyFunctionMap::Entry *end = entry + numEntries;
    526    while (entry != end)
    527    {
    528        if (entry->formatID == formatID)
    529        {
    530            return entry;
    531        }
    532        ++entry;
    533    }
    534 
    535    return nullptr;
    536 }
    537 
    538 }  // namespace
    539 
    540 FastCopyFunction FastCopyFunctionMap::get(angle::FormatID formatID) const
    541 {
    542    const FastCopyFunctionMap::Entry *entry = getEntry(mData, mSize, formatID);
    543    return entry ? entry->func : nullptr;
    544 }
    545 
    546 bool ShouldUseDebugLayers(const egl::AttributeMap &attribs)
    547 {
    548    EGLAttrib debugSetting =
    549        attribs.get(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE, EGL_DONT_CARE);
    550 
    551    // Prefer to enable debug layers when available.
    552 #if defined(ANGLE_ENABLE_ASSERTS)
    553    return (debugSetting != EGL_FALSE);
    554 #else
    555    return (debugSetting == EGL_TRUE);
    556 #endif  // defined(ANGLE_ENABLE_ASSERTS)
    557 }
    558 
    559 void CopyImageCHROMIUM(const uint8_t *sourceData,
    560                       size_t sourceRowPitch,
    561                       size_t sourcePixelBytes,
    562                       size_t sourceDepthPitch,
    563                       PixelReadFunction pixelReadFunction,
    564                       uint8_t *destData,
    565                       size_t destRowPitch,
    566                       size_t destPixelBytes,
    567                       size_t destDepthPitch,
    568                       PixelWriteFunction pixelWriteFunction,
    569                       GLenum destUnsizedFormat,
    570                       GLenum destComponentType,
    571                       size_t width,
    572                       size_t height,
    573                       size_t depth,
    574                       bool unpackFlipY,
    575                       bool unpackPremultiplyAlpha,
    576                       bool unpackUnmultiplyAlpha)
    577 {
    578    using ConversionFunction              = void (*)(gl::ColorF *);
    579    ConversionFunction conversionFunction = CopyColor;
    580    if (unpackPremultiplyAlpha != unpackUnmultiplyAlpha)
    581    {
    582        if (unpackPremultiplyAlpha)
    583        {
    584            conversionFunction = PremultiplyAlpha;
    585        }
    586        else
    587        {
    588            conversionFunction = UnmultiplyAlpha;
    589        }
    590    }
    591 
    592    auto clipChannelsFunction = ClipChannelsNoOp;
    593    switch (destUnsizedFormat)
    594    {
    595        case GL_RED:
    596            clipChannelsFunction = ClipChannelsR;
    597            break;
    598        case GL_RG:
    599            clipChannelsFunction = ClipChannelsRG;
    600            break;
    601        case GL_RGB:
    602            clipChannelsFunction = ClipChannelsRGB;
    603            break;
    604        case GL_LUMINANCE:
    605            clipChannelsFunction = ClipChannelsLuminance;
    606            break;
    607        case GL_ALPHA:
    608            clipChannelsFunction = ClipChannelsAlpha;
    609            break;
    610    }
    611 
    612    auto writeFunction = (destComponentType == GL_UNSIGNED_INT) ? WriteUintColor : WriteFloatColor;
    613 
    614    for (size_t z = 0; z < depth; z++)
    615    {
    616        for (size_t y = 0; y < height; y++)
    617        {
    618            for (size_t x = 0; x < width; x++)
    619            {
    620                const uint8_t *sourcePixelData =
    621                    sourceData + y * sourceRowPitch + x * sourcePixelBytes + z * sourceDepthPitch;
    622 
    623                gl::ColorF sourceColor;
    624                pixelReadFunction(sourcePixelData, reinterpret_cast<uint8_t *>(&sourceColor));
    625 
    626                conversionFunction(&sourceColor);
    627                clipChannelsFunction(&sourceColor);
    628 
    629                size_t destY = 0;
    630                if (unpackFlipY)
    631                {
    632                    destY += (height - 1);
    633                    destY -= y;
    634                }
    635                else
    636                {
    637                    destY += y;
    638                }
    639 
    640                uint8_t *destPixelData =
    641                    destData + destY * destRowPitch + x * destPixelBytes + z * destDepthPitch;
    642                writeFunction(sourceColor, pixelWriteFunction, destPixelData);
    643            }
    644        }
    645    }
    646 }
    647 
    648 // IncompleteTextureSet implementation.
    649 IncompleteTextureSet::IncompleteTextureSet() : mIncompleteTextureBufferAttachment(nullptr) {}
    650 
    651 IncompleteTextureSet::~IncompleteTextureSet() {}
    652 
    653 void IncompleteTextureSet::onDestroy(const gl::Context *context)
    654 {
    655    // Clear incomplete textures.
    656    for (auto &incompleteTextures : mIncompleteTextures)
    657    {
    658        for (auto &incompleteTexture : incompleteTextures)
    659        {
    660            if (incompleteTexture.get() != nullptr)
    661            {
    662                incompleteTexture->onDestroy(context);
    663                incompleteTexture.set(context, nullptr);
    664            }
    665        }
    666    }
    667    if (mIncompleteTextureBufferAttachment != nullptr)
    668    {
    669        mIncompleteTextureBufferAttachment->onDestroy(context);
    670        mIncompleteTextureBufferAttachment = nullptr;
    671    }
    672 }
    673 
    674 angle::Result IncompleteTextureSet::getIncompleteTexture(
    675    const gl::Context *context,
    676    gl::TextureType type,
    677    gl::SamplerFormat format,
    678    MultisampleTextureInitializer *multisampleInitializer,
    679    gl::Texture **textureOut)
    680 {
    681    *textureOut = mIncompleteTextures[format][type].get();
    682    if (*textureOut != nullptr)
    683    {
    684        return angle::Result::Continue;
    685    }
    686 
    687    ContextImpl *implFactory = context->getImplementation();
    688 
    689    gl::Extents colorSize(1, 1, 1);
    690    gl::PixelUnpackState unpack;
    691    unpack.alignment = 1;
    692    gl::Box area(0, 0, 0, 1, 1, 1);
    693    const IncompleteTextureParameters &incompleteTextureParam =
    694        kIncompleteTextureParameters[format];
    695 
    696    // Cube map arrays are expected to have layer counts that are multiples of 6
    697    constexpr int kCubeMapArraySize = 6;
    698    if (type == gl::TextureType::CubeMapArray)
    699    {
    700        // From the GLES 3.2 spec:
    701        //   8.18. IMMUTABLE-FORMAT TEXTURE IMAGES
    702        //   TexStorage3D Errors
    703        //   An INVALID_OPERATION error is generated if any of the following conditions hold:
    704        //     * target is TEXTURE_CUBE_MAP_ARRAY and depth is not a multiple of 6
    705        // Since ANGLE treats incomplete textures as immutable, respect that here.
    706        colorSize.depth = kCubeMapArraySize;
    707        area.depth      = kCubeMapArraySize;
    708    }
    709 
    710    // If a texture is external use a 2D texture for the incomplete texture
    711    gl::TextureType createType = (type == gl::TextureType::External) ? gl::TextureType::_2D : type;
    712 
    713    gl::Texture *tex =
    714        new gl::Texture(implFactory, {std::numeric_limits<GLuint>::max()}, createType);
    715    angle::UniqueObjectPointer<gl::Texture, gl::Context> t(tex, context);
    716 
    717    // This is a bit of a kludge but is necessary to consume the error.
    718    gl::Context *mutableContext = const_cast<gl::Context *>(context);
    719 
    720    if (createType == gl::TextureType::Buffer)
    721    {
    722        constexpr uint32_t kBufferInitData = 0;
    723        mIncompleteTextureBufferAttachment =
    724            new gl::Buffer(implFactory, {std::numeric_limits<GLuint>::max()});
    725        ANGLE_TRY(mIncompleteTextureBufferAttachment->bufferData(
    726            mutableContext, gl::BufferBinding::Texture, &kBufferInitData, sizeof(kBufferInitData),
    727            gl::BufferUsage::StaticDraw));
    728    }
    729    else if (createType == gl::TextureType::_2DMultisample)
    730    {
    731        ANGLE_TRY(t->setStorageMultisample(mutableContext, createType, 1,
    732                                           incompleteTextureParam.sizedInternalFormat, colorSize,
    733                                           true));
    734    }
    735    else
    736    {
    737        ANGLE_TRY(t->setStorage(mutableContext, createType, 1,
    738                                incompleteTextureParam.sizedInternalFormat, colorSize));
    739    }
    740 
    741    if (type == gl::TextureType::CubeMap)
    742    {
    743        for (gl::TextureTarget face : gl::AllCubeFaceTextureTargets())
    744        {
    745            ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr, face, 0, area,
    746                                     incompleteTextureParam.format, incompleteTextureParam.type,
    747                                     incompleteTextureParam.clearColor));
    748        }
    749    }
    750    else if (type == gl::TextureType::CubeMapArray)
    751    {
    752        // We need to provide enough pixel data to fill the array of six faces
    753        GLubyte incompleteCubeArrayPixels[kCubeMapArraySize][4];
    754        for (int i = 0; i < kCubeMapArraySize; ++i)
    755        {
    756            incompleteCubeArrayPixels[i][0] = incompleteTextureParam.clearColor[0];
    757            incompleteCubeArrayPixels[i][1] = incompleteTextureParam.clearColor[1];
    758            incompleteCubeArrayPixels[i][2] = incompleteTextureParam.clearColor[2];
    759            incompleteCubeArrayPixels[i][3] = incompleteTextureParam.clearColor[3];
    760        }
    761 
    762        ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr,
    763                                 gl::NonCubeTextureTypeToTarget(createType), 0, area,
    764                                 incompleteTextureParam.format, incompleteTextureParam.type,
    765                                 *incompleteCubeArrayPixels));
    766    }
    767    else if (type == gl::TextureType::_2DMultisample)
    768    {
    769        // Call a specialized clear function to init a multisample texture.
    770        ANGLE_TRY(multisampleInitializer->initializeMultisampleTextureToBlack(context, t.get()));
    771    }
    772    else if (type == gl::TextureType::Buffer)
    773    {
    774        ANGLE_TRY(t->setBuffer(context, mIncompleteTextureBufferAttachment,
    775                               incompleteTextureParam.sizedInternalFormat));
    776    }
    777    else
    778    {
    779        ANGLE_TRY(t->setSubImage(mutableContext, unpack, nullptr,
    780                                 gl::NonCubeTextureTypeToTarget(createType), 0, area,
    781                                 incompleteTextureParam.format, incompleteTextureParam.type,
    782                                 incompleteTextureParam.clearColor));
    783    }
    784 
    785    if (format == gl::SamplerFormat::Shadow)
    786    {
    787        // To avoid the undefined spec behavior for shadow samplers with a depth texture, we set the
    788        // compare mode to GL_COMPARE_REF_TO_TEXTURE
    789        ASSERT(!t->hasObservers());
    790        t->setCompareMode(context, GL_COMPARE_REF_TO_TEXTURE);
    791    }
    792 
    793    ANGLE_TRY(t->syncState(context, gl::Command::Other));
    794 
    795    mIncompleteTextures[format][type].set(context, t.release());
    796    *textureOut = mIncompleteTextures[format][type].get();
    797    return angle::Result::Continue;
    798 }
    799 
    800 #define ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(api, cols, rows) \
    801    template void SetFloatUniformMatrix##api<cols, rows>::Run(     \
    802        unsigned int, unsigned int, GLsizei, GLboolean, const GLfloat *, uint8_t *)
    803 
    804 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 2);
    805 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 3);
    806 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 3);
    807 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 2);
    808 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 2);
    809 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 3);
    810 
    811 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 2);
    812 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 3);
    813 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 3);
    814 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 2);
    815 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 2, 4);
    816 ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC(HLSL, 3, 4);
    817 
    818 #undef ANGLE_INSTANTIATE_SET_UNIFORM_MATRIX_FUNC
    819 
    820 #define ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(api, cols, rows)                      \
    821    template void SetFloatUniformMatrix##api<cols, 4>::Run(unsigned int, unsigned int, GLsizei, \
    822                                                           GLboolean, const GLfloat *, uint8_t *)
    823 
    824 template <int cols>
    825 struct SetFloatUniformMatrixGLSL<cols, 4>
    826 {
    827    static void Run(unsigned int arrayElementOffset,
    828                    unsigned int elementCount,
    829                    GLsizei countIn,
    830                    GLboolean transpose,
    831                    const GLfloat *value,
    832                    uint8_t *targetData);
    833 };
    834 
    835 ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 2, 4);
    836 ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 3, 4);
    837 ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC(GLSL, 4, 4);
    838 
    839 #undef ANGLE_SPECIALIZATION_ROWS_SET_UNIFORM_MATRIX_FUNC
    840 
    841 #define ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(api, cols, rows)                      \
    842    template void SetFloatUniformMatrix##api<4, rows>::Run(unsigned int, unsigned int, GLsizei, \
    843                                                           GLboolean, const GLfloat *, uint8_t *)
    844 
    845 template <int rows>
    846 struct SetFloatUniformMatrixHLSL<4, rows>
    847 {
    848    static void Run(unsigned int arrayElementOffset,
    849                    unsigned int elementCount,
    850                    GLsizei countIn,
    851                    GLboolean transpose,
    852                    const GLfloat *value,
    853                    uint8_t *targetData);
    854 };
    855 
    856 ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 2);
    857 ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 3);
    858 ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC(HLSL, 4, 4);
    859 
    860 #undef ANGLE_SPECIALIZATION_COLS_SET_UNIFORM_MATRIX_FUNC
    861 
    862 template <int cols>
    863 void SetFloatUniformMatrixGLSL<cols, 4>::Run(unsigned int arrayElementOffset,
    864                                             unsigned int elementCount,
    865                                             GLsizei countIn,
    866                                             GLboolean transpose,
    867                                             const GLfloat *value,
    868                                             uint8_t *targetData)
    869 {
    870    const bool isSrcColumnMajor = !transpose;
    871    if (isSrcColumnMajor)
    872    {
    873        // Both src and dst matrixs are has same layout,
    874        // a single memcpy updates all the matrices
    875        constexpr size_t srcMatrixSize = sizeof(GLfloat) * cols * 4;
    876        SetFloatUniformMatrixFast(arrayElementOffset, elementCount, countIn, srcMatrixSize, value,
    877                                  targetData);
    878    }
    879    else
    880    {
    881        // fallback to general cases
    882        SetFloatUniformMatrix<false, cols, 4, true, cols, 4>(arrayElementOffset, elementCount,
    883                                                             countIn, value, targetData);
    884    }
    885 }
    886 
    887 template <int cols, int rows>
    888 void SetFloatUniformMatrixGLSL<cols, rows>::Run(unsigned int arrayElementOffset,
    889                                                unsigned int elementCount,
    890                                                GLsizei countIn,
    891                                                GLboolean transpose,
    892                                                const GLfloat *value,
    893                                                uint8_t *targetData)
    894 {
    895    const bool isSrcColumnMajor = !transpose;
    896    // GLSL expects matrix uniforms to be column-major, and each column is padded to 4 rows.
    897    if (isSrcColumnMajor)
    898    {
    899        SetFloatUniformMatrix<true, cols, rows, true, cols, 4>(arrayElementOffset, elementCount,
    900                                                               countIn, value, targetData);
    901    }
    902    else
    903    {
    904        SetFloatUniformMatrix<false, cols, rows, true, cols, 4>(arrayElementOffset, elementCount,
    905                                                                countIn, value, targetData);
    906    }
    907 }
    908 
    909 template <int rows>
    910 void SetFloatUniformMatrixHLSL<4, rows>::Run(unsigned int arrayElementOffset,
    911                                             unsigned int elementCount,
    912                                             GLsizei countIn,
    913                                             GLboolean transpose,
    914                                             const GLfloat *value,
    915                                             uint8_t *targetData)
    916 {
    917    const bool isSrcColumnMajor = !transpose;
    918    if (!isSrcColumnMajor)
    919    {
    920        // Both src and dst matrixs are has same layout,
    921        // a single memcpy updates all the matrices
    922        constexpr size_t srcMatrixSize = sizeof(GLfloat) * 4 * rows;
    923        SetFloatUniformMatrixFast(arrayElementOffset, elementCount, countIn, srcMatrixSize, value,
    924                                  targetData);
    925    }
    926    else
    927    {
    928        // fallback to general cases
    929        SetFloatUniformMatrix<true, 4, rows, false, 4, rows>(arrayElementOffset, elementCount,
    930                                                             countIn, value, targetData);
    931    }
    932 }
    933 
    934 template <int cols, int rows>
    935 void SetFloatUniformMatrixHLSL<cols, rows>::Run(unsigned int arrayElementOffset,
    936                                                unsigned int elementCount,
    937                                                GLsizei countIn,
    938                                                GLboolean transpose,
    939                                                const GLfloat *value,
    940                                                uint8_t *targetData)
    941 {
    942    const bool isSrcColumnMajor = !transpose;
    943    // Internally store matrices as row-major to accomodate HLSL matrix indexing.  Each row is
    944    // padded to 4 columns.
    945    if (!isSrcColumnMajor)
    946    {
    947        SetFloatUniformMatrix<false, cols, rows, false, 4, rows>(arrayElementOffset, elementCount,
    948                                                                 countIn, value, targetData);
    949    }
    950    else
    951    {
    952        SetFloatUniformMatrix<true, cols, rows, false, 4, rows>(arrayElementOffset, elementCount,
    953                                                                countIn, value, targetData);
    954    }
    955 }
    956 
    957 template void GetMatrixUniform<GLint>(GLenum, GLint *, const GLint *, bool);
    958 template void GetMatrixUniform<GLuint>(GLenum, GLuint *, const GLuint *, bool);
    959 
    960 void GetMatrixUniform(GLenum type, GLfloat *dataOut, const GLfloat *source, bool transpose)
    961 {
    962    int columns = gl::VariableColumnCount(type);
    963    int rows    = gl::VariableRowCount(type);
    964    for (GLint col = 0; col < columns; ++col)
    965    {
    966        for (GLint row = 0; row < rows; ++row)
    967        {
    968            GLfloat *outptr = dataOut + ((col * rows) + row);
    969            const GLfloat *inptr =
    970                transpose ? source + ((row * 4) + col) : source + ((col * 4) + row);
    971            *outptr = *inptr;
    972        }
    973    }
    974 }
    975 
    976 template <typename NonFloatT>
    977 void GetMatrixUniform(GLenum type, NonFloatT *dataOut, const NonFloatT *source, bool transpose)
    978 {
    979    UNREACHABLE();
    980 }
    981 
    982 const angle::Format &GetFormatFromFormatType(GLenum format, GLenum type)
    983 {
    984    GLenum sizedInternalFormat    = gl::GetInternalFormatInfo(format, type).sizedInternalFormat;
    985    angle::FormatID angleFormatID = angle::Format::InternalFormatToID(sizedInternalFormat);
    986    return angle::Format::Get(angleFormatID);
    987 }
    988 
    989 angle::Result ComputeStartVertex(ContextImpl *contextImpl,
    990                                 const gl::IndexRange &indexRange,
    991                                 GLint baseVertex,
    992                                 GLint *firstVertexOut)
    993 {
    994    // The entire index range should be within the limits of a 32-bit uint because the largest
    995    // GL index type is GL_UNSIGNED_INT.
    996    ASSERT(indexRange.start <= std::numeric_limits<uint32_t>::max() &&
    997           indexRange.end <= std::numeric_limits<uint32_t>::max());
    998 
    999    // The base vertex is only used in DrawElementsIndirect. Given the assertion above and the
   1000    // type of mBaseVertex (GLint), adding them both as 64-bit ints is safe.
   1001    int64_t startVertexInt64 =
   1002        static_cast<int64_t>(baseVertex) + static_cast<int64_t>(indexRange.start);
   1003 
   1004    // OpenGL ES 3.2 spec section 10.5: "Behavior of DrawElementsOneInstance is undefined if the
   1005    // vertex ID is negative for any element"
   1006    ANGLE_CHECK_GL_MATH(contextImpl, startVertexInt64 >= 0);
   1007 
   1008    // OpenGL ES 3.2 spec section 10.5: "If the vertex ID is larger than the maximum value
   1009    // representable by type, it should behave as if the calculation were upconverted to 32-bit
   1010    // unsigned integers(with wrapping on overflow conditions)." ANGLE does not fully handle
   1011    // these rules, an overflow error is returned if the start vertex cannot be stored in a
   1012    // 32-bit signed integer.
   1013    ANGLE_CHECK_GL_MATH(contextImpl, startVertexInt64 <= std::numeric_limits<GLint>::max());
   1014 
   1015    *firstVertexOut = static_cast<GLint>(startVertexInt64);
   1016    return angle::Result::Continue;
   1017 }
   1018 
   1019 angle::Result GetVertexRangeInfo(const gl::Context *context,
   1020                                 GLint firstVertex,
   1021                                 GLsizei vertexOrIndexCount,
   1022                                 gl::DrawElementsType indexTypeOrInvalid,
   1023                                 const void *indices,
   1024                                 GLint baseVertex,
   1025                                 GLint *startVertexOut,
   1026                                 size_t *vertexCountOut)
   1027 {
   1028    if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
   1029    {
   1030        gl::IndexRange indexRange;
   1031        ANGLE_TRY(context->getState().getVertexArray()->getIndexRange(
   1032            context, indexTypeOrInvalid, vertexOrIndexCount, indices, &indexRange));
   1033        ANGLE_TRY(ComputeStartVertex(context->getImplementation(), indexRange, baseVertex,
   1034                                     startVertexOut));
   1035        *vertexCountOut = indexRange.vertexCount();
   1036    }
   1037    else
   1038    {
   1039        *startVertexOut = firstVertex;
   1040        *vertexCountOut = vertexOrIndexCount;
   1041    }
   1042    return angle::Result::Continue;
   1043 }
   1044 
   1045 gl::Rectangle ClipRectToScissor(const gl::State &glState, const gl::Rectangle &rect, bool invertY)
   1046 {
   1047    // If the scissor test isn't enabled, assume it has infinite size.  Its intersection with the
   1048    // rect would be the rect itself.
   1049    //
   1050    // Note that on Vulkan, returning this (as opposed to a fixed max-int-sized rect) could lead to
   1051    // unnecessary pipeline creations if two otherwise identical pipelines are used on framebuffers
   1052    // with different sizes.  If such usage is observed in an application, we should investigate
   1053    // possible optimizations.
   1054    if (!glState.isScissorTestEnabled())
   1055    {
   1056        return rect;
   1057    }
   1058 
   1059    gl::Rectangle clippedRect;
   1060    if (!gl::ClipRectangle(glState.getScissor(), rect, &clippedRect))
   1061    {
   1062        return gl::Rectangle();
   1063    }
   1064 
   1065    if (invertY)
   1066    {
   1067        clippedRect.y = rect.height - clippedRect.y - clippedRect.height;
   1068    }
   1069 
   1070    return clippedRect;
   1071 }
   1072 
   1073 void LogFeatureStatus(const angle::FeatureSetBase &features,
   1074                      const std::vector<std::string> &featureNames,
   1075                      bool enabled)
   1076 {
   1077    for (const std::string &name : featureNames)
   1078    {
   1079        const bool hasWildcard = name.back() == '*';
   1080        for (auto iter : features.getFeatures())
   1081        {
   1082            const std::string &featureName = iter.first;
   1083 
   1084            if (!angle::FeatureNameMatch(featureName, name))
   1085            {
   1086                continue;
   1087            }
   1088 
   1089            INFO() << "Feature: " << featureName << (enabled ? " enabled" : " disabled");
   1090 
   1091            if (!hasWildcard)
   1092            {
   1093                break;
   1094            }
   1095        }
   1096    }
   1097 }
   1098 
   1099 void ApplyFeatureOverrides(angle::FeatureSetBase *features, const egl::DisplayState &state)
   1100 {
   1101    features->overrideFeatures(state.featureOverridesEnabled, true);
   1102    features->overrideFeatures(state.featureOverridesDisabled, false);
   1103 
   1104    // Override with environment as well.
   1105    constexpr char kAngleFeatureOverridesEnabledEnvName[]  = "ANGLE_FEATURE_OVERRIDES_ENABLED";
   1106    constexpr char kAngleFeatureOverridesDisabledEnvName[] = "ANGLE_FEATURE_OVERRIDES_DISABLED";
   1107    constexpr char kAngleFeatureOverridesEnabledPropertyName[] =
   1108        "debug.angle.feature_overrides_enabled";
   1109    constexpr char kAngleFeatureOverridesDisabledPropertyName[] =
   1110        "debug.angle.feature_overrides_disabled";
   1111    std::vector<std::string> overridesEnabled =
   1112        angle::GetCachedStringsFromEnvironmentVarOrAndroidProperty(
   1113            kAngleFeatureOverridesEnabledEnvName, kAngleFeatureOverridesEnabledPropertyName, ":");
   1114    std::vector<std::string> overridesDisabled =
   1115        angle::GetCachedStringsFromEnvironmentVarOrAndroidProperty(
   1116            kAngleFeatureOverridesDisabledEnvName, kAngleFeatureOverridesDisabledPropertyName, ":");
   1117 
   1118    features->overrideFeatures(overridesEnabled, true);
   1119    LogFeatureStatus(*features, overridesEnabled, true);
   1120 
   1121    features->overrideFeatures(overridesDisabled, false);
   1122    LogFeatureStatus(*features, overridesDisabled, false);
   1123 }
   1124 
   1125 void GetSamplePosition(GLsizei sampleCount, size_t index, GLfloat *xy)
   1126 {
   1127    ASSERT(gl::isPow2(sampleCount));
   1128    if (sampleCount > 16)
   1129    {
   1130        // Vulkan (and D3D11) doesn't have standard sample positions for 32 and 64 samples (and no
   1131        // drivers are known to support that many samples)
   1132        xy[0] = 0.5f;
   1133        xy[1] = 0.5f;
   1134    }
   1135    else
   1136    {
   1137        size_t indexKey = static_cast<size_t>(gl::log2(sampleCount));
   1138        ASSERT(indexKey < kSamplePositions.size() &&
   1139               (2 * index + 1) < kSamplePositions[indexKey].size());
   1140 
   1141        xy[0] = kSamplePositions[indexKey][2 * index];
   1142        xy[1] = kSamplePositions[indexKey][2 * index + 1];
   1143    }
   1144 }
   1145 
   1146 // These macros are to avoid code too much duplication for variations of multi draw types
   1147 #define DRAW_ARRAYS__ contextImpl->drawArrays(context, mode, firsts[drawID], counts[drawID])
   1148 #define DRAW_ARRAYS_INSTANCED_                                                      \
   1149    contextImpl->drawArraysInstanced(context, mode, firsts[drawID], counts[drawID], \
   1150                                     instanceCounts[drawID])
   1151 #define DRAW_ELEMENTS__ \
   1152    contextImpl->drawElements(context, mode, counts[drawID], type, indices[drawID])
   1153 #define DRAW_ELEMENTS_INSTANCED_                                                             \
   1154    contextImpl->drawElementsInstanced(context, mode, counts[drawID], type, indices[drawID], \
   1155                                       instanceCounts[drawID])
   1156 #define DRAW_ARRAYS_INSTANCED_BASE_INSTANCE                                                     \
   1157    contextImpl->drawArraysInstancedBaseInstance(context, mode, firsts[drawID], counts[drawID], \
   1158                                                 instanceCounts[drawID], baseInstances[drawID])
   1159 #define DRAW_ELEMENTS_INSTANCED_BASE_VERTEX_BASE_INSTANCE                             \
   1160    contextImpl->drawElementsInstancedBaseVertexBaseInstance(                         \
   1161        context, mode, counts[drawID], type, indices[drawID], instanceCounts[drawID], \
   1162        baseVertices[drawID], baseInstances[drawID])
   1163 #define DRAW_CALL(drawType, instanced, bvbi) DRAW_##drawType##instanced##bvbi
   1164 
   1165 #define MULTI_DRAW_BLOCK(drawType, instanced, bvbi, hasDrawID, hasBaseVertex, hasBaseInstance) \
   1166    for (GLsizei drawID = 0; drawID < drawcount; ++drawID)                                     \
   1167    {                                                                                          \
   1168        if (ANGLE_NOOP_DRAW(instanced))                                                        \
   1169        {                                                                                      \
   1170            ANGLE_TRY(contextImpl->handleNoopDrawEvent());                                     \
   1171            continue;                                                                          \
   1172        }                                                                                      \
   1173        ANGLE_SET_DRAW_ID_UNIFORM(hasDrawID)(drawID);                                          \
   1174        ANGLE_SET_BASE_VERTEX_UNIFORM(hasBaseVertex)(baseVertices[drawID]);                    \
   1175        ANGLE_SET_BASE_INSTANCE_UNIFORM(hasBaseInstance)(baseInstances[drawID]);               \
   1176        ANGLE_TRY(DRAW_CALL(drawType, instanced, bvbi));                                       \
   1177        ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE(instanced);                                        \
   1178        gl::MarkShaderStorageUsage(context);                                                   \
   1179    }
   1180 
   1181 angle::Result MultiDrawArraysGeneral(ContextImpl *contextImpl,
   1182                                     const gl::Context *context,
   1183                                     gl::PrimitiveMode mode,
   1184                                     const GLint *firsts,
   1185                                     const GLsizei *counts,
   1186                                     GLsizei drawcount)
   1187 {
   1188    gl::Program *programObject = context->getState().getLinkedProgram(context);
   1189    const bool hasDrawID       = programObject && programObject->hasDrawIDUniform();
   1190    if (hasDrawID)
   1191    {
   1192        MULTI_DRAW_BLOCK(ARRAYS, _, _, 1, 0, 0)
   1193    }
   1194    else
   1195    {
   1196        MULTI_DRAW_BLOCK(ARRAYS, _, _, 0, 0, 0)
   1197    }
   1198 
   1199    return angle::Result::Continue;
   1200 }
   1201 
   1202 angle::Result MultiDrawArraysIndirectGeneral(ContextImpl *contextImpl,
   1203                                             const gl::Context *context,
   1204                                             gl::PrimitiveMode mode,
   1205                                             const void *indirect,
   1206                                             GLsizei drawcount,
   1207                                             GLsizei stride)
   1208 {
   1209    const GLubyte *indirectPtr = static_cast<const GLubyte *>(indirect);
   1210 
   1211    for (auto count = 0; count < drawcount; count++)
   1212    {
   1213        ANGLE_TRY(contextImpl->drawArraysIndirect(
   1214            context, mode, reinterpret_cast<const gl::DrawArraysIndirectCommand *>(indirectPtr)));
   1215        if (stride == 0)
   1216        {
   1217            indirectPtr += sizeof(gl::DrawArraysIndirectCommand);
   1218        }
   1219        else
   1220        {
   1221            indirectPtr += stride;
   1222        }
   1223    }
   1224 
   1225    return angle::Result::Continue;
   1226 }
   1227 
   1228 angle::Result MultiDrawArraysInstancedGeneral(ContextImpl *contextImpl,
   1229                                              const gl::Context *context,
   1230                                              gl::PrimitiveMode mode,
   1231                                              const GLint *firsts,
   1232                                              const GLsizei *counts,
   1233                                              const GLsizei *instanceCounts,
   1234                                              GLsizei drawcount)
   1235 {
   1236    gl::Program *programObject = context->getState().getLinkedProgram(context);
   1237    const bool hasDrawID       = programObject && programObject->hasDrawIDUniform();
   1238    if (hasDrawID)
   1239    {
   1240        MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _, 1, 0, 0)
   1241    }
   1242    else
   1243    {
   1244        MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _, 0, 0, 0)
   1245    }
   1246 
   1247    return angle::Result::Continue;
   1248 }
   1249 
   1250 angle::Result MultiDrawElementsGeneral(ContextImpl *contextImpl,
   1251                                       const gl::Context *context,
   1252                                       gl::PrimitiveMode mode,
   1253                                       const GLsizei *counts,
   1254                                       gl::DrawElementsType type,
   1255                                       const GLvoid *const *indices,
   1256                                       GLsizei drawcount)
   1257 {
   1258    gl::Program *programObject = context->getState().getLinkedProgram(context);
   1259    const bool hasDrawID       = programObject && programObject->hasDrawIDUniform();
   1260    if (hasDrawID)
   1261    {
   1262        MULTI_DRAW_BLOCK(ELEMENTS, _, _, 1, 0, 0)
   1263    }
   1264    else
   1265    {
   1266        MULTI_DRAW_BLOCK(ELEMENTS, _, _, 0, 0, 0)
   1267    }
   1268 
   1269    return angle::Result::Continue;
   1270 }
   1271 
   1272 angle::Result MultiDrawElementsIndirectGeneral(ContextImpl *contextImpl,
   1273                                               const gl::Context *context,
   1274                                               gl::PrimitiveMode mode,
   1275                                               gl::DrawElementsType type,
   1276                                               const void *indirect,
   1277                                               GLsizei drawcount,
   1278                                               GLsizei stride)
   1279 {
   1280    const GLubyte *indirectPtr = static_cast<const GLubyte *>(indirect);
   1281 
   1282    for (auto count = 0; count < drawcount; count++)
   1283    {
   1284        ANGLE_TRY(contextImpl->drawElementsIndirect(
   1285            context, mode, type,
   1286            reinterpret_cast<const gl::DrawElementsIndirectCommand *>(indirectPtr)));
   1287        if (stride == 0)
   1288        {
   1289            indirectPtr += sizeof(gl::DrawElementsIndirectCommand);
   1290        }
   1291        else
   1292        {
   1293            indirectPtr += stride;
   1294        }
   1295    }
   1296 
   1297    return angle::Result::Continue;
   1298 }
   1299 
   1300 angle::Result MultiDrawElementsInstancedGeneral(ContextImpl *contextImpl,
   1301                                                const gl::Context *context,
   1302                                                gl::PrimitiveMode mode,
   1303                                                const GLsizei *counts,
   1304                                                gl::DrawElementsType type,
   1305                                                const GLvoid *const *indices,
   1306                                                const GLsizei *instanceCounts,
   1307                                                GLsizei drawcount)
   1308 {
   1309    gl::Program *programObject = context->getState().getLinkedProgram(context);
   1310    const bool hasDrawID       = programObject && programObject->hasDrawIDUniform();
   1311    if (hasDrawID)
   1312    {
   1313        MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _, 1, 0, 0)
   1314    }
   1315    else
   1316    {
   1317        MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _, 0, 0, 0)
   1318    }
   1319 
   1320    return angle::Result::Continue;
   1321 }
   1322 
   1323 angle::Result MultiDrawArraysInstancedBaseInstanceGeneral(ContextImpl *contextImpl,
   1324                                                          const gl::Context *context,
   1325                                                          gl::PrimitiveMode mode,
   1326                                                          const GLint *firsts,
   1327                                                          const GLsizei *counts,
   1328                                                          const GLsizei *instanceCounts,
   1329                                                          const GLuint *baseInstances,
   1330                                                          GLsizei drawcount)
   1331 {
   1332    gl::Program *programObject = context->getState().getLinkedProgram(context);
   1333    const bool hasDrawID       = programObject && programObject->hasDrawIDUniform();
   1334    const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform();
   1335    ResetBaseVertexBaseInstance resetUniforms(programObject, false, hasBaseInstance);
   1336 
   1337    if (hasDrawID && hasBaseInstance)
   1338    {
   1339        MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 1, 0, 1)
   1340    }
   1341    else if (hasDrawID)
   1342    {
   1343        MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 1, 0, 0)
   1344    }
   1345    else if (hasBaseInstance)
   1346    {
   1347        MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 0, 0, 1)
   1348    }
   1349    else
   1350    {
   1351        MULTI_DRAW_BLOCK(ARRAYS, _INSTANCED, _BASE_INSTANCE, 0, 0, 0)
   1352    }
   1353 
   1354    return angle::Result::Continue;
   1355 }
   1356 
   1357 angle::Result MultiDrawElementsInstancedBaseVertexBaseInstanceGeneral(ContextImpl *contextImpl,
   1358                                                                      const gl::Context *context,
   1359                                                                      gl::PrimitiveMode mode,
   1360                                                                      const GLsizei *counts,
   1361                                                                      gl::DrawElementsType type,
   1362                                                                      const GLvoid *const *indices,
   1363                                                                      const GLsizei *instanceCounts,
   1364                                                                      const GLint *baseVertices,
   1365                                                                      const GLuint *baseInstances,
   1366                                                                      GLsizei drawcount)
   1367 {
   1368    gl::Program *programObject = context->getState().getLinkedProgram(context);
   1369    const bool hasDrawID       = programObject && programObject->hasDrawIDUniform();
   1370    const bool hasBaseVertex   = programObject && programObject->hasBaseVertexUniform();
   1371    const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform();
   1372    ResetBaseVertexBaseInstance resetUniforms(programObject, hasBaseVertex, hasBaseInstance);
   1373 
   1374    if (hasDrawID)
   1375    {
   1376        if (hasBaseVertex)
   1377        {
   1378            if (hasBaseInstance)
   1379            {
   1380                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 1, 1)
   1381            }
   1382            else
   1383            {
   1384                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 1, 0)
   1385            }
   1386        }
   1387        else
   1388        {
   1389            if (hasBaseInstance)
   1390            {
   1391                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 0, 1)
   1392            }
   1393            else
   1394            {
   1395                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 1, 0, 0)
   1396            }
   1397        }
   1398    }
   1399    else
   1400    {
   1401        if (hasBaseVertex)
   1402        {
   1403            if (hasBaseInstance)
   1404            {
   1405                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 1, 1)
   1406            }
   1407            else
   1408            {
   1409                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 1, 0)
   1410            }
   1411        }
   1412        else
   1413        {
   1414            if (hasBaseInstance)
   1415            {
   1416                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 0, 1)
   1417            }
   1418            else
   1419            {
   1420                MULTI_DRAW_BLOCK(ELEMENTS, _INSTANCED, _BASE_VERTEX_BASE_INSTANCE, 0, 0, 0)
   1421            }
   1422        }
   1423    }
   1424 
   1425    return angle::Result::Continue;
   1426 }
   1427 
   1428 ResetBaseVertexBaseInstance::ResetBaseVertexBaseInstance(gl::Program *programObject,
   1429                                                         bool resetBaseVertex,
   1430                                                         bool resetBaseInstance)
   1431    : mProgramObject(programObject),
   1432      mResetBaseVertex(resetBaseVertex),
   1433      mResetBaseInstance(resetBaseInstance)
   1434 {}
   1435 
   1436 ResetBaseVertexBaseInstance::~ResetBaseVertexBaseInstance()
   1437 {
   1438    if (mProgramObject)
   1439    {
   1440        // Reset emulated uniforms to zero to avoid affecting other draw calls
   1441        if (mResetBaseVertex)
   1442        {
   1443            mProgramObject->setBaseVertexUniform(0);
   1444        }
   1445 
   1446        if (mResetBaseInstance)
   1447        {
   1448            mProgramObject->setBaseInstanceUniform(0);
   1449        }
   1450    }
   1451 }
   1452 
   1453 angle::FormatID ConvertToSRGB(angle::FormatID formatID)
   1454 {
   1455    switch (formatID)
   1456    {
   1457        case angle::FormatID::R8_UNORM:
   1458            return angle::FormatID::R8_UNORM_SRGB;
   1459        case angle::FormatID::R8G8_UNORM:
   1460            return angle::FormatID::R8G8_UNORM_SRGB;
   1461        case angle::FormatID::R8G8B8_UNORM:
   1462            return angle::FormatID::R8G8B8_UNORM_SRGB;
   1463        case angle::FormatID::R8G8B8A8_UNORM:
   1464            return angle::FormatID::R8G8B8A8_UNORM_SRGB;
   1465        case angle::FormatID::B8G8R8A8_UNORM:
   1466            return angle::FormatID::B8G8R8A8_UNORM_SRGB;
   1467        case angle::FormatID::BC1_RGB_UNORM_BLOCK:
   1468            return angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK;
   1469        case angle::FormatID::BC1_RGBA_UNORM_BLOCK:
   1470            return angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK;
   1471        case angle::FormatID::BC2_RGBA_UNORM_BLOCK:
   1472            return angle::FormatID::BC2_RGBA_UNORM_SRGB_BLOCK;
   1473        case angle::FormatID::BC3_RGBA_UNORM_BLOCK:
   1474            return angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK;
   1475        case angle::FormatID::BC7_RGBA_UNORM_BLOCK:
   1476            return angle::FormatID::BC7_RGBA_UNORM_SRGB_BLOCK;
   1477        case angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK:
   1478            return angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK;
   1479        case angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK:
   1480            return angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK;
   1481        case angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK:
   1482            return angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK;
   1483        case angle::FormatID::ASTC_4x4_UNORM_BLOCK:
   1484            return angle::FormatID::ASTC_4x4_SRGB_BLOCK;
   1485        case angle::FormatID::ASTC_5x4_UNORM_BLOCK:
   1486            return angle::FormatID::ASTC_5x4_SRGB_BLOCK;
   1487        case angle::FormatID::ASTC_5x5_UNORM_BLOCK:
   1488            return angle::FormatID::ASTC_5x5_SRGB_BLOCK;
   1489        case angle::FormatID::ASTC_6x5_UNORM_BLOCK:
   1490            return angle::FormatID::ASTC_6x5_SRGB_BLOCK;
   1491        case angle::FormatID::ASTC_6x6_UNORM_BLOCK:
   1492            return angle::FormatID::ASTC_6x6_SRGB_BLOCK;
   1493        case angle::FormatID::ASTC_8x5_UNORM_BLOCK:
   1494            return angle::FormatID::ASTC_8x5_SRGB_BLOCK;
   1495        case angle::FormatID::ASTC_8x6_UNORM_BLOCK:
   1496            return angle::FormatID::ASTC_8x6_SRGB_BLOCK;
   1497        case angle::FormatID::ASTC_8x8_UNORM_BLOCK:
   1498            return angle::FormatID::ASTC_8x8_SRGB_BLOCK;
   1499        case angle::FormatID::ASTC_10x5_UNORM_BLOCK:
   1500            return angle::FormatID::ASTC_10x5_SRGB_BLOCK;
   1501        case angle::FormatID::ASTC_10x6_UNORM_BLOCK:
   1502            return angle::FormatID::ASTC_10x6_SRGB_BLOCK;
   1503        case angle::FormatID::ASTC_10x8_UNORM_BLOCK:
   1504            return angle::FormatID::ASTC_10x8_SRGB_BLOCK;
   1505        case angle::FormatID::ASTC_10x10_UNORM_BLOCK:
   1506            return angle::FormatID::ASTC_10x10_SRGB_BLOCK;
   1507        case angle::FormatID::ASTC_12x10_UNORM_BLOCK:
   1508            return angle::FormatID::ASTC_12x10_SRGB_BLOCK;
   1509        case angle::FormatID::ASTC_12x12_UNORM_BLOCK:
   1510            return angle::FormatID::ASTC_12x12_SRGB_BLOCK;
   1511        default:
   1512            return angle::FormatID::NONE;
   1513    }
   1514 }
   1515 
   1516 angle::FormatID ConvertToLinear(angle::FormatID formatID)
   1517 {
   1518    switch (formatID)
   1519    {
   1520        case angle::FormatID::R8_UNORM_SRGB:
   1521            return angle::FormatID::R8_UNORM;
   1522        case angle::FormatID::R8G8_UNORM_SRGB:
   1523            return angle::FormatID::R8G8_UNORM;
   1524        case angle::FormatID::R8G8B8_UNORM_SRGB:
   1525            return angle::FormatID::R8G8B8_UNORM;
   1526        case angle::FormatID::R8G8B8A8_UNORM_SRGB:
   1527            return angle::FormatID::R8G8B8A8_UNORM;
   1528        case angle::FormatID::B8G8R8A8_UNORM_SRGB:
   1529            return angle::FormatID::B8G8R8A8_UNORM;
   1530        case angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK:
   1531            return angle::FormatID::BC1_RGB_UNORM_BLOCK;
   1532        case angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK:
   1533            return angle::FormatID::BC1_RGBA_UNORM_BLOCK;
   1534        case angle::FormatID::BC2_RGBA_UNORM_SRGB_BLOCK:
   1535            return angle::FormatID::BC2_RGBA_UNORM_BLOCK;
   1536        case angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK:
   1537            return angle::FormatID::BC3_RGBA_UNORM_BLOCK;
   1538        case angle::FormatID::BC7_RGBA_UNORM_SRGB_BLOCK:
   1539            return angle::FormatID::BC7_RGBA_UNORM_BLOCK;
   1540        case angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK:
   1541            return angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK;
   1542        case angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK:
   1543            return angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK;
   1544        case angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK:
   1545            return angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK;
   1546        case angle::FormatID::ASTC_4x4_SRGB_BLOCK:
   1547            return angle::FormatID::ASTC_4x4_UNORM_BLOCK;
   1548        case angle::FormatID::ASTC_5x4_SRGB_BLOCK:
   1549            return angle::FormatID::ASTC_5x4_UNORM_BLOCK;
   1550        case angle::FormatID::ASTC_5x5_SRGB_BLOCK:
   1551            return angle::FormatID::ASTC_5x5_UNORM_BLOCK;
   1552        case angle::FormatID::ASTC_6x5_SRGB_BLOCK:
   1553            return angle::FormatID::ASTC_6x5_UNORM_BLOCK;
   1554        case angle::FormatID::ASTC_6x6_SRGB_BLOCK:
   1555            return angle::FormatID::ASTC_6x6_UNORM_BLOCK;
   1556        case angle::FormatID::ASTC_8x5_SRGB_BLOCK:
   1557            return angle::FormatID::ASTC_8x5_UNORM_BLOCK;
   1558        case angle::FormatID::ASTC_8x6_SRGB_BLOCK:
   1559            return angle::FormatID::ASTC_8x6_UNORM_BLOCK;
   1560        case angle::FormatID::ASTC_8x8_SRGB_BLOCK:
   1561            return angle::FormatID::ASTC_8x8_UNORM_BLOCK;
   1562        case angle::FormatID::ASTC_10x5_SRGB_BLOCK:
   1563            return angle::FormatID::ASTC_10x5_UNORM_BLOCK;
   1564        case angle::FormatID::ASTC_10x6_SRGB_BLOCK:
   1565            return angle::FormatID::ASTC_10x6_UNORM_BLOCK;
   1566        case angle::FormatID::ASTC_10x8_SRGB_BLOCK:
   1567            return angle::FormatID::ASTC_10x8_UNORM_BLOCK;
   1568        case angle::FormatID::ASTC_10x10_SRGB_BLOCK:
   1569            return angle::FormatID::ASTC_10x10_UNORM_BLOCK;
   1570        case angle::FormatID::ASTC_12x10_SRGB_BLOCK:
   1571            return angle::FormatID::ASTC_12x10_UNORM_BLOCK;
   1572        case angle::FormatID::ASTC_12x12_SRGB_BLOCK:
   1573            return angle::FormatID::ASTC_12x12_UNORM_BLOCK;
   1574        default:
   1575            return angle::FormatID::NONE;
   1576    }
   1577 }
   1578 
   1579 bool IsOverridableLinearFormat(angle::FormatID formatID)
   1580 {
   1581    return ConvertToSRGB(formatID) != angle::FormatID::NONE;
   1582 }
   1583 }  // namespace rx