void GrGLCaps::applyDriverCorrectnessWorkarounds()

in src/gpu/ganesh/gl/GrGLCaps.cpp [3936:4838]


void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo,
                                                 const GrContextOptions& contextOptions,
                                                 const GrGLInterface* glInterface,
                                                 GrShaderCaps* shaderCaps,
                                                 FormatWorkarounds* formatWorkarounds) {
    GrGLDriverVersion driverVersion =
        ctxInfo.angleBackend() == GrGLANGLEBackend::kUnknown ? ctxInfo.driverVersion()
                                                             : ctxInfo.angleDriverVersion();

    // A driver bug on the nexus 6 causes incorrect dst copies when invalidate is called beforehand.
    // Thus we are disabling this extension for now on Adreno4xx devices.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno430       ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno4xx_other ||
        fDriverBugWorkarounds.disable_discard_framebuffer) {
        fInvalidateFBType = kNone_InvalidateFBType;
    }

    if (ctxInfo.renderer() == GrGLRenderer::kIntelCherryView) {
        // When running DMSAA_dst_read_with_existing_barrier with DMSAA disabled on linux Intel
        // HD405, the test fails when using texture barriers. Oddly the gpu doing the draw which
        // uses the barrier correctly. It is the next draw, which does not use or need a barrier,
        // that is blending with a dst as if the barrier draw didn't happen. Since this GPU is not
        // that important to us and this driver bug could probably manifest itself in the wild, we
        // are just disabling texture barrier support for the gpu.
        fTextureBarrierSupport = false;
    }

    // glClearTexImage seems to have a bug in NVIDIA drivers that was fixed sometime between
    // 340.96 and 367.57.
    if (GR_IS_GR_GL(ctxInfo.standard()) && ctxInfo.driver() == GrGLDriver::kNVIDIA &&
        ctxInfo.driverVersion() < GR_GL_DRIVER_VER(367, 57, 0)) {
        fClearTextureSupport = false;
    }

    // glBlitFramebuffer seems to produce incorrect results on QC, Mali400, and Tegra3 but
    // glCopyTexSubImage2D works (even though there is no extension that specifically allows it).
    if (ctxInfo.vendor()   == GrGLVendor::kQualcomm  ||
        ctxInfo.renderer() == GrGLRenderer::kMali4xx ||
        ctxInfo.renderer() == GrGLRenderer::kTegra_PreK1) {
        fAllowBGRA8CopyTexSubImage = true;
    }
    // glCopyTexSubImage2D works for sRGB with GLES 3.0 and on some GPUs with GLES 2.0
    if (ctxInfo.version() >= GR_GL_VER(3, 0) ||
        ctxInfo.renderer() == GrGLRenderer::kMali4xx ||
        ctxInfo.renderer() == GrGLRenderer::kTegra_PreK1) {
        fAllowSRGBCopyTexSubImage = true;
    }

    // http://anglebug.com/6030
    if (fMSFBOType == kES_EXT_MsToTexture_MSFBOType &&
        ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D11) {
        // As GL_EXT_multisampled_render_to_texture supporting issue,
        // fall back to default dmsaa path
        if ((ctxInfo.vendor()  == GrGLVendor::kIntel ||
             ctxInfo.angleVendor() == GrGLVendor::kIntel) &&
             ctxInfo.renderer() >= GrGLRenderer::kIntelIceLake) {
            fMSFBOType = kStandard_MSFBOType;
            fMSAAResolvesAutomatically = false;
        }
        else {
            fDisallowDynamicMSAA = true;
        }
    }

    // http://skbug.com/12081
    if (GR_IS_GR_WEBGL(ctxInfo.standard())) {
        fDisallowDynamicMSAA = true;
    }

    // Below we are aggressive about turning off all mapping/transfer functionality together. This
    // could be finer grained if code paths and tests were adjusted to check more specific caps.
    // For example it might be possible to support buffer to buffer transfers even if buffer mapping
    // or buffer to surface transfers don't work.
#if defined(__has_feature)
#if defined(SK_BUILD_FOR_MAC) && __has_feature(thread_sanitizer)
    // See skbug.com/7058
    fMapBufferType = kNone_MapBufferType;
    fMapBufferFlags = kNone_MapFlags;
    fTransferFromBufferToTextureSupport = false;
    fTransferFromSurfaceToBufferSupport = false;
    fTransferFromBufferToBufferSupport  = false;
    fTransferBufferType = TransferBufferType::kNone;
#endif
#endif

    // We found that the Galaxy J5 with an Adreno 306 running 6.0.1 has a bug where
    // GL_INVALID_OPERATION thrown by glDrawArrays when using a buffer that was mapped. The same bug
    // did not reproduce on a Nexus7 2013 with a 320 running Android M with driver 127.0. It's
    // unclear whether this really affects a wide range of devices.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno3xx &&
        ctxInfo.driverVersion() > GR_GL_DRIVER_VER(127, 0, 0)) {
        fMapBufferType = kNone_MapBufferType;
        fMapBufferFlags = kNone_MapFlags;
        fTransferFromBufferToTextureSupport = false;
        fTransferFromSurfaceToBufferSupport = false;
        fTransferFromBufferToBufferSupport  = false;
        fTransferBufferType = TransferBufferType::kNone;
    }

    // The TransferPixelsToTexture test fails on ANGLE D3D9 and D3D11 if this is enabled.
    // https://anglebug.com/5542
    if (angle_backend_is_d3d(ctxInfo.angleBackend())) {
        fTransferPixelsToRowBytesSupport = false;
    }

    // Using MIPs on this GPU seems to be a source of trouble.
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVR54x) {
        fMipmapSupport = false;
    }

#ifdef SK_BUILD_FOR_ANDROID
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVR54x) {
        // Flutter found glTexSubImage2D for GL_RED is much slower than GL_ALPHA on the
        // "MC18 PERSONAL SHOPPER"
        formatWorkarounds->fDisallowR8ForPowerVRSGX54x = true;
    }
#endif

    // Reported on skia-discuss as occurring with these GL strings:
    // GL_VERSION:  3.1.0 - Build 9.17.10.4459
    // GL_VENDOR:   Intel
    // GL_RENDERER: Intel(R) HD Graphics 2000
    // https://groups.google.com/g/skia-discuss/c/dYV1blEAda0/m/-zuZLXQKAwAJ?utm_medium=email&utm_source=footer
    // See also http://skbug.com/9286
    if (ctxInfo.renderer() == GrGLRenderer::kIntelSandyBridge &&
        ctxInfo.driver() == GrGLDriver::kIntel) {
        fMapBufferType  = kNone_MapBufferType;
        fMapBufferFlags = kNone_MapFlags;
        // On skia-discuss it was reported that after turning off mapping there was this
        // shader compilation error.
        // ERROR: 0:18: 'assign' :  cannot convert from '3-component vector of float' to 'varying 2-component vector of float'
        // for this line:
        // vTransformedCoords_5_S0 = mat3x2(umatrix_S1_c0_c1) * vec3(_tmp_2_inPosition, 1.0);
        fShaderCaps->fNonsquareMatrixSupport = false;
    }

    if (ctxInfo.isOverCommandBuffer() && ctxInfo.version() >= GR_GL_VER(3,0)) {
        formatWorkarounds->fDisallowTextureUnorm16 = true;  // http://crbug.com/1224108
        formatWorkarounds->fDisallowETC2Compression = true;  // http://crbug.com/1224111
        fTransferFromSurfaceToBufferSupport = false;  // http://crbug.com/1224138

        // http://crbug.com/1224117
        fMapBufferFlags = kNone_MapFlags;
        fMapBufferType = kNone_MapBufferType;
    }

    // https://b.corp.google.com/issues/143074513
    // https://skbug.com/11152
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno615 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno620) {
        fMSFBOType = kNone_MSFBOType;
        fMSAAResolvesAutomatically = false;
    }

#ifndef SK_BUILD_FOR_IOS
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVR54x   ||
        ctxInfo.renderer() == GrGLRenderer::kPowerVRRogue ||
        (ctxInfo.renderer() == GrGLRenderer::kAdreno3xx && !ctxInfo.isOverCommandBuffer())) {
        fPerformColorClearsAsDraws = true;
    }
#endif

    // A lot of GPUs have trouble with full screen clears (skbug.com/7195)
    if (ctxInfo.renderer() == GrGLRenderer::kAMDRadeonHD7xxx ||
        ctxInfo.renderer() == GrGLRenderer::kAMDRadeonR9M4xx) {
        fPerformColorClearsAsDraws = true;
    }

#ifdef SK_BUILD_FOR_MAC
    // crbug.com/768134 - On MacBook Pros, the Intel Iris Pro doesn't always perform
    // full screen clears
    // crbug.com/773107 - On MacBook Pros, a wide range of Intel GPUs don't always
    // perform full screen clears.
    // Update on 4/4/2018 - This appears to be fixed on driver 10.30.12 on a macOS 10.13.2 on a
    // Retina MBP Early 2015 with Iris 6100. It is possibly fixed on earlier drivers as well.
    // crbug.com/1039912 - Crash rate in glClear spiked after OS update, affecting mostly
    //   Broadwell on 10.13+
    if (ctxInfo.vendor() == GrGLVendor::kIntel &&
        (ctxInfo.driverVersion() < GR_GL_DRIVER_VER(10, 30, 12) ||
         ctxInfo.renderer() == GrGLRenderer::kIntelBroadwell)) {
        fPerformColorClearsAsDraws = true;
    }
    // crbug.com/969609 - NVIDIA on Mac sometimes segfaults during glClear in chrome. It seems
    // mostly concentrated in 10.13/14, GT 650Ms, driver 12+. But there are instances of older
    // drivers and GTX 775s, so we'll start with a broader workaround.
    if (ctxInfo.vendor() == GrGLVendor::kNVIDIA) {
        fPerformColorClearsAsDraws = true;
    }
#endif

    // See crbug.com/755871. This could probably be narrowed to just partial clears as the driver
    // bugs seems to involve clearing too much and not skipping the clear.
    // See crbug.com/768134. This is also needed for full clears and was seen on an nVidia K620
    // but only for D3D11 ANGLE.
    if (ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D11) {
        fPerformColorClearsAsDraws = true;
    }

    if (ctxInfo.renderer() == GrGLRenderer::kAdreno430 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno4xx_other) {
        // This is known to be fixed sometime between driver 145.0 and 219.0
        if (ctxInfo.driverVersion() <= GR_GL_DRIVER_VER(219, 0, 0)) {
            fPerformStencilClearsAsDraws = true;
        }
        // This is known to be fixed sometime between driver 129.0 and 145.0 on Nexus 6P.
        // On driver 129 on Android M it fails the unit tests called WritePixelsPendingIO without
        // the workaround. It passes on Android N with driver 145 without the workaround.
        // skbug.com/11834
        if (ctxInfo.driverVersion() < GR_GL_DRIVER_VER(145, 0, 0)) {
            fDisallowTexSubImageForUnormConfigTexturesEverBoundToFBO = true;
        }
    }

    if (fDriverBugWorkarounds.gl_clear_broken) {
        fPerformColorClearsAsDraws = true;
        fPerformStencilClearsAsDraws = true;
    }

    if (ctxInfo.vendor() == GrGLVendor::kQualcomm) {
        // It appears that all the Adreno GPUs have less than optimal performance when
        // drawing w/ large index buffers.
        fAvoidLargeIndexBufferDraws = true;
    }

    if (ctxInfo.renderer() == GrGLRenderer::kMali4xx ||
        (ctxInfo.renderer() == GrGLRenderer::kWebGL &&
         ctxInfo.webglRenderer() == GrGLRenderer::kMali4xx)) {
        // Perspective SDF text runs significantly slower on Mali-4xx hardware
        fDisablePerspectiveSDFText = true;
    }

    // This was reproduced on the following configurations:
    // - A Galaxy J5 (Adreno 306) running Android 6 with driver 140.0
    // - A Nexus 7 2013 (Adreno 320) running Android 5 with driver 104.0
    // - A Nexus 7 2013 (Adreno 320) running Android 6 with driver 127.0
    // - A Nexus 5 (Adreno 330) running Android 6 with driver 127.0
    // and not produced on:
    // - A Nexus 7 2013 (Adreno 320) running Android 4 with driver 53.0
    // The particular lines that get dropped from test images varies across different devices.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno3xx &&
        ctxInfo.driverVersion() > GR_GL_DRIVER_VER(53, 0, 0)) {
        fRequiresCullFaceEnableDisableWhenDrawingLinesAfterNonLines = true;
    }

    // TODO: Don't apply this on iOS?
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVRRogue &&
        driverVersion <  GR_GL_DRIVER_VER(23, 2, 0)) {
        // Our Chromebook with GrGLRenderer::kPowerVRRogue crashes on large instanced draws. The
        // current minimum number of instances observed to crash is somewhere between 2^14 and 2^15.
        // Keep the number of instances below 1000, just to be safe.
        fMaxInstancesPerDrawWithoutCrashing = 999;
    } else if (fDriverBugWorkarounds.disallow_large_instanced_draw) {
        fMaxInstancesPerDrawWithoutCrashing = 0x4000000;
    }

#ifndef SK_BUILD_FOR_IOS
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVRRogue) {
        // We saw this bug on a TecnoSpark 3 Pro with a PowerVR GE8300.
        // GL_VERSION: "OpenGL ES 3.2 build 1.10@51309121"
        // Possibly this could be more limited by driver version or HW generation.
        // When using samplers, we are seeing a bug where the gpu is sometimes not sampling the
        // correct mip level data. A workaround to this issue is that when binding a texture we also
        // set some texture state, and it seems like any inividual state works (e.g. min/mag filter,
        // base level, max level, etc.). Currently we just set the min filter level every time we
        // bind a texture as the workaround.
        fMustSetAnyTexParameterToEnableMipmapping = true;
        // ColorTypeBackendAllocationTest failed for kAlpha_8 and kGray_8 when using
        // GL_UNPACK_ROW_LENGTH. Perhaps this could be a more limited workaround by applying
        // only to single channel 8 bit unorm formats but we only have a monolithic query for this
        // support at present.
        fWritePixelsRowBytesSupport = false;
        // TransferPixelsToTextureTest fails for all color types on
        // TecnoSpark 3 Pro with a PowerVR GE8300, GL_VERSION: "OpenGL ES 3.2 build 1.10@51309121"
        // if GL_UNPACK_ROW_LENGTH is used.
        fTransferPixelsToRowBytesSupport = false;
    }
#endif

    // Texture uploads sometimes seem to be ignored to textures bound to FBOS on Tegra3.
    if (ctxInfo.renderer() == GrGLRenderer::kTegra_PreK1) {
        fDisallowTexSubImageForUnormConfigTexturesEverBoundToFBO = true;
        fUseDrawInsteadOfAllRenderTargetWrites = true;
    }

#ifdef SK_BUILD_FOR_MAC
    static constexpr bool isMAC = true;
#else
    static constexpr bool isMAC = false;
#endif

#ifdef SK_BUILD_FOR_ANDROID
    // Older versions of Android have problems with setting GL_TEXTURE_BASE_LEVEL or
    // GL_TEXTURE_MAX_LEVEL on GL_TEXTURE_EXTERTNAL_OES textures. We just leave them as is and hope
    // the client never changes them either.
    fDontSetBaseOrMaxLevelForExternalTextures = true;
    // PowerVR can crash setting the levels on Android up to Q for any texture?
    // https://crbug.com/1123874
    if (ctxInfo.vendor() == GrGLVendor::kImagination) {
        fMipmapLevelControlSupport =  false;
    }
#endif

    // We support manual mip-map generation (via iterative downsampling draw calls). This fixes
    // bugs on some cards/drivers that produce incorrect mip-maps for sRGB textures when using
    // glGenerateMipmap. Our implementation requires mip-level sampling control. Additionally,
    // it can be much slower (especially on mobile GPUs), so we opt-in only when necessary:
    if (fMipmapLevelControlSupport &&
        !ctxInfo.isOverCommandBuffer() &&  // http://crbug.com/1224110
        (contextOptions.fDoManualMipmapping                 ||
         ctxInfo.vendor()  == GrGLVendor::kIntel            ||
         (ctxInfo.driver() == GrGLDriver::kNVIDIA && isMAC) ||
         ctxInfo.vendor()  == GrGLVendor::kATI)) {
        fDoManualMipmapping = true;
    }

    // See http://crbug.com/710443
#ifdef SK_BUILD_FOR_MAC
    if (ctxInfo.renderer() == GrGLRenderer::kIntelBroadwell) {
        fClearToBoundaryValuesIsBroken = true;
    }
#endif
    if (ctxInfo.vendor() == GrGLVendor::kQualcomm) {
        fDrawArraysBaseVertexIsBroken = true;
    }

    // b/40043081, b/40045491: indirect draws in ANGLE + D3D are very slow
    if (ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D9 ||
        ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D11) {
        fBaseVertexBaseInstanceSupport = false;
        fNativeDrawIndirectSupport = false;
        fMultiDrawType = MultiDrawType::kNone;
    }

    // https://b.corp.google.com/issues/188410972
    if (ctxInfo.isRunningOverVirgl()) {
        fDrawInstancedSupport = false;
    }

    // http://anglebug.com/4538
    if (fBaseVertexBaseInstanceSupport && !fDrawInstancedSupport) {
        fBaseVertexBaseInstanceSupport = false;
        fNativeDrawIndirectSupport = false;
        fMultiDrawType = MultiDrawType::kNone;
    }

    // Currently the extension is advertised but fb fetch is broken on 500 series Adrenos like the
    // Galaxy S7.
    // TODO: Once this is fixed we can update the check here to look at a driver version number too.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno530 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno5xx_other) {
        shaderCaps->fFBFetchSupport = false;
    }

    if (ctxInfo.renderer() == GrGLRenderer::kTegra_PreK1) {
        // The Tegra3 compiler will sometimes never return if we have min(abs(x), 1.0),
        // so we must do the abs first in a separate expression.
        shaderCaps->fCanUseMinAndAbsTogether = false;

        // Tegra3 fract() seems to trigger undefined behavior for negative values, so we
        // must avoid this condition.
        shaderCaps->fCanUseFractForNegativeValues = false;
    }

    // On Intel GPU there is an issue where it reads the second argument to atan "- %s.x" as an int
    // thus must us -1.0 * %s.x to work correctly
    if (ctxInfo.vendor() == GrGLVendor::kIntel) {
        shaderCaps->fMustForceNegatedAtanParamToFloat = true;
    }

#if defined(SK_BUILD_FOR_MAC)
    if (ctxInfo.vendor() == GrGLVendor::kATI) {
        // The Radeon GLSL compiler on Mac gets confused by ldexp(..., -x).
        // Convert to ldexp(..., x * -1).
        // http://skbug.com/12076
        shaderCaps->fMustForceNegatedLdexpParamToMultiply = true;
    }
#endif

    // On some Intel GPUs there is an issue where the driver outputs bogus values in the shader
    // when floor and abs are called on the same line. Thus we must execute an Op between them to
    // make sure the compiler doesn't re-inline them even if we break the calls apart.
    if (ctxInfo.vendor() == GrGLVendor::kIntel) {
        shaderCaps->fMustDoOpBetweenFloorAndAbs = true;
    }

    // On Adreno devices with framebuffer fetch support, there is a bug where they always return
    // the original dst color when reading the outColor even after being written to. By using a
    // local outColor we can work around this bug.
    if (shaderCaps->fFBFetchSupport && ctxInfo.vendor() == GrGLVendor::kQualcomm) {
        shaderCaps->fRequiresLocalOutputColorForFBFetch = true;
    }

    // Newer Mali GPUs do incorrect static analysis in specific situations: If there is uniform
    // color, and that uniform contains an opaque color, and the output of the shader is only based
    // on that uniform plus soemthing un-trackable (like a texture read), the compiler will deduce
    // that the shader always outputs opaque values. In that case, it appears to remove the shader
    // based blending code it normally injects, turning SrcOver into Src. To fix this, we always
    // insert an extra bit of math on the uniform that confuses the compiler just enough...
    if (ctxInfo.renderer() == GrGLRenderer::kMaliT) {
        shaderCaps->fMustObfuscateUniformColor = true;
    }

#if defined(SK_BUILD_FOR_ANDROID)
    // On the following GPUs, the Perlin noise code needs to aggressively snap to multiples
    // of 1/255 to avoid artifacts in the double table lookup:
    //    Tegra3, PowerVRGE8320 (Wembley), MaliG76, and Adreno308
    // Given the range of vendors we're just blanket enabling it on Android for OpenGL.
    fShaderCaps->fPerlinNoiseRoundingFix = true;
#endif

    // On Mali 400 there is a bug using dFd* in the x direction. So we avoid using it when possible.
    if (ctxInfo.renderer() == GrGLRenderer::kMali4xx) {
        fShaderCaps->fAvoidDfDxForGradientsWhenPossible = true;
    }

#ifdef SK_BUILD_FOR_WIN
    // Check for ANGLE on Windows, so we can workaround a bug in D3D itself (anglebug.com/2098).
    //
    // Basically, if a shader has a construct like:
    //
    // float x = someCondition ? someValue : 0;
    // float2 result = (0 == x) ? float2(x, x)
    //                          : float2(2 * x / x, 0);
    //
    // ... the compiler will produce an error 'NaN and infinity literals not allowed', even though
    // we've explicitly guarded the division with a check against zero. This manifests in much
    // more complex ways in some of our shaders, so we use this caps bit to add an epsilon value
    // to the denominator of divisions, even when we've added checks that the denominator isn't 0.
    if (angle_backend_is_d3d(ctxInfo.angleBackend()) || ctxInfo.isOverCommandBuffer()) {
        shaderCaps->fMustGuardDivisionEvenAfterExplicitZeroCheck = true;
    }
#endif

    // The Adreno 5xx and 6xx produce incorrect results when comparing a pair of matrices.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno530 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno5xx_other ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno615 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno620 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno630 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno640 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno6xx_other) {
        shaderCaps->fRewriteMatrixComparisons = true;
    }

    // We've seen Adreno 3xx devices produce incorrect (flipped) values for gl_FragCoord, in some
    // (rare) situations. It's sporadic, and mostly on older drivers. Additionally, old Adreno
    // compilers (see crbug.com/skia/4078) crash when accessing .zw of gl_FragCoord, so just bypass
    // using gl_FragCoord at all to get around it.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno3xx) {
        shaderCaps->fCanUseFragCoord = false;
    }

    // gl_FragCoord has an incorrect subpixel offset on legacy Tegra hardware.
    if (ctxInfo.renderer() == GrGLRenderer::kTegra_PreK1) {
        shaderCaps->fCanUseFragCoord = false;
    }

    if (fDriverBugWorkarounds.add_and_true_to_loop_condition) {
        shaderCaps->fAddAndTrueToLoopCondition = true;
    }

    if (fDriverBugWorkarounds.unfold_short_circuit_as_ternary_operation) {
        shaderCaps->fUnfoldShortCircuitAsTernary = true;
    }

    if (fDriverBugWorkarounds.emulate_abs_int_function) {
        shaderCaps->fEmulateAbsIntFunction = true;
    }

    if (fDriverBugWorkarounds.rewrite_do_while_loops) {
        shaderCaps->fRewriteDoWhileLoops = true;
    }

    if (fDriverBugWorkarounds.remove_pow_with_constant_exponent) {
        shaderCaps->fRemovePowWithConstantExponent = true;
    }

    if (fDriverBugWorkarounds.disable_dual_source_blending_support) {
        shaderCaps->fDualSourceBlendingSupport = false;
    }

    if (ctxInfo.renderer() == GrGLRenderer::kAdreno3xx ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno4xx_other) {
        shaderCaps->fMustWriteToFragColor = true;
    }

    // Disabling advanced blend on various platforms with major known issues. We also block Chrome
    // command buffer for now until its own denylists can be updated.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno430       ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno4xx_other ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno530       ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno5xx_other ||
        ctxInfo.driver()   == GrGLDriver::kIntel             ||
        ctxInfo.angleVendor() == GrGLVendor::kIntel          ||
        ctxInfo.isOverCommandBuffer()                        ||
        ctxInfo.vendor()   == GrGLVendor::kARM /* http://skbug.com/11906 */) {
        fBlendEquationSupport = kBasic_BlendEquationSupport;
        shaderCaps->fAdvBlendEqInteraction = GrShaderCaps::kNotSupported_AdvBlendEqInteraction;
    }

    // Non-coherent advanced blend has an issue on NVIDIA pre 337.00.
    if (((ctxInfo.driver() == GrGLDriver::kNVIDIA &&
          ctxInfo.driverVersion() < GR_GL_DRIVER_VER(337, 00, 0)) ||
         (ctxInfo.angleBackend() == GrGLANGLEBackend::kOpenGL &&
          ctxInfo.angleDriver() == GrGLDriver::kNVIDIA &&
          ctxInfo.angleDriverVersion() < GR_GL_DRIVER_VER(337, 00, 0))) &&
        kAdvanced_BlendEquationSupport == fBlendEquationSupport) {
        fBlendEquationSupport = kBasic_BlendEquationSupport;
        shaderCaps->fAdvBlendEqInteraction = GrShaderCaps::kNotSupported_AdvBlendEqInteraction;
    }

    if (fDriverBugWorkarounds.disable_blend_equation_advanced) {
        fBlendEquationSupport = kBasic_BlendEquationSupport;
        shaderCaps->fAdvBlendEqInteraction = GrShaderCaps::kNotSupported_AdvBlendEqInteraction;
    }

    if (this->advancedBlendEquationSupport()) {
        if ((ctxInfo.driver() == GrGLDriver::kNVIDIA &&
             ctxInfo.driverVersion() < GR_GL_DRIVER_VER(355, 00, 0)) ||
            (ctxInfo.angleBackend() == GrGLANGLEBackend::kOpenGL &&
             ctxInfo.angleDriver() == GrGLDriver::kNVIDIA &&
             ctxInfo.angleDriverVersion() < GR_GL_DRIVER_VER(355, 00, 0))) {
            // Disable color-dodge and color-burn on pre-355.00 NVIDIA.
            fAdvBlendEqDisableFlags |= (1 << static_cast<int>(skgpu::BlendEquation::kColorDodge)) |
                                       (1 << static_cast<int>(skgpu::BlendEquation::kColorBurn));
        }
        if (ctxInfo.vendor() == GrGLVendor::kARM) {
            // Disable color-burn on ARM until the fix is released.
            fAdvBlendEqDisableFlags |= (1 << static_cast<int>(skgpu::BlendEquation::kColorBurn));
        }
    }

    // On Adreno 5xx devices, there is a bug where we first draw using dual source blending. Thus
    // the dst blend func references the dst. Then the next draw we disable blending. However, on
    // the second draw the driver has a bug where it tries to access the second color output again.
    // This is fixed by reseting the blend function to anything that does not reference src2 when we
    // disable blending.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno530 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno5xx_other ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno620 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno640) {
        fMustResetBlendFuncBetweenDualSourceAndDisable = true;
    }

    // Many ES3 drivers only advertise the ES2 image_external extension, but support the _essl3
    // extension, and require that it be enabled to work with ESSL3. Other devices require the ES2
    // extension to be enabled, even when using ESSL3. Enabling both extensions fixes both cases.
    // skbug.com/7713
    if (ctxInfo.hasExtension("GL_OES_EGL_image_external") &&
        ctxInfo.glslGeneration() >= SkSL::GLSLGeneration::k330 &&
        !shaderCaps->fExternalTextureSupport) { // i.e. Missing the _essl3 extension
        shaderCaps->fExternalTextureSupport = true;
        shaderCaps->fExternalTextureExtensionString = "GL_OES_EGL_image_external";
        shaderCaps->fSecondExternalTextureExtensionString = "GL_OES_EGL_image_external_essl3";
    }

#ifdef SK_BUILD_FOR_IOS
    // iOS drivers appear to implement TexSubImage by creating a staging buffer, and copying
    // UNPACK_ROW_LENGTH * height bytes. That's unsafe in several scenarios, and the simplest fix
    // is to just disable the feature.
    // https://github.com/flutter/flutter/issues/16718
    // https://bugreport.apple.com/web/?problemID=39948888
    fWritePixelsRowBytesSupport = false;
    // This affects all iOS devices for transfering from a PBO as well (presumably the issue is in
    // the GL->Metal layer).
    fTransferPixelsToRowBytesSupport = false;
#endif

    if (ctxInfo.vendor()   == GrGLVendor::kIntel       ||  // IntelIris640 drops draws completely.
        ctxInfo.webglVendor() == GrGLVendor::kIntel    ||  // Disable if the webgl vendor is Intel
        ctxInfo.renderer() == GrGLRenderer::kMaliT     ||  // Some curves appear flat on GalaxyS6.
        ctxInfo.renderer() == GrGLRenderer::kAdreno3xx ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno430 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno4xx_other ||  // We get garbage on Adreno405.
        ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D9) {  // D3D9 conic strokes fail.
        fDisableTessellationPathRenderer = true;
    }
    // We found that on Wembley devices (PowerVR GE8320) that using tessellation path renderer would
    // cause lots of rendering errors where it seemed like vertices were in the wrong place. This
    // led to lots of GMs drawing nothing (e.g. dashing4) or lots of garbage. The Wembley devices
    // were running Android 12 with a driver version of 1.13. We previously had TecnoSpark devices
    // with the same GPU running on Android P (driver 1.10) which did not have this issue. We don't
    // know when the bug appeared in the driver so for now we disable tessellation path renderer for
    // all matching gpus regardless of driver version.
    //
    // 2022-10-28 Update: Testing via Flutter found this is not a problem on driver version 1.15.
    // See https://github.com/flutter/flutter/issues/113596
    // GL_VERSION : OpenGL ES 3.1 build 1.15@6133109
    // GL_RENDERER: PowerVR Rogue AXE-1-16M
    // GL_VENDOR  : Imagination Technologies
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVRRogue &&
        driverVersion < GR_GL_DRIVER_VER(1, 15, 0)) {
        fDisableTessellationPathRenderer = true;
    }

    // The Wembley device draws the mesh_update GM incorrectly when using transfer buffers. Buffer
    // to buffer transfers affect draws earlier in the GL command sequence.
    // Android API: 31
    // GL_VERSION : OpenGL ES 3.2 build 1.13@5720833
    // GL_RENDERER: PowerVR Rogue GE8300
    // GL_VENDOR  : Imagination Technologies
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVRRogue) {
        fTransferFromBufferToBufferSupport = false;
    }

    // The Wembley device fails shader compilations with no error message when there is a const
    // parameter. Given that we've already passed through SkSL compilation and enforced that the
    // parameter is never written, it is harmless to strip the const off when writing GLSL.
    // Android API: 31
    // GL_VERSION : OpenGL ES 3.2 build 1.13@5720833
    // GL_RENDERER: PowerVR Rogue GE8300
    // GL_VENDOR  : Imagination Technologies
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVRRogue) {
        fShaderCaps->fRemoveConstFromFunctionParameters = true;
    }
#ifdef SK_BUILD_FOR_WIN
    // glDrawElementsIndirect fails GrMeshTest on every Win10 Intel bot.
    if (ctxInfo.driver() == GrGLDriver::kIntel ||
        (ctxInfo.angleVendor()  == GrGLVendor::kIntel &&
         ctxInfo.angleBackend() == GrGLANGLEBackend::kOpenGL)) {
        fNativeDrawIndexedIndirectIsBroken = true;
        fUseClientSideIndirectBuffers = true;
    }
#endif

    // PowerVRGX6250 drops every pixel if we modify the sample mask while color writes are disabled.
    if (ctxInfo.renderer() == GrGLRenderer::kPowerVRRogue) {
        fNeverDisableColorWrites = true;
        shaderCaps->fMustWriteToFragColor = true;
    }

    // It appears that Qualcomm drivers don't actually support
    // GL_NV_shader_noperspective_interpolation in ES 3.00 or 3.10 shaders, only 3.20.
    // https://crbug.com/986581
    if (ctxInfo.vendor() == GrGLVendor::kQualcomm &&
        SkSL::GLSLGeneration::k320es != ctxInfo.glslGeneration()) {
        shaderCaps->fNoPerspectiveInterpolationSupport = false;
    }

    // We disable srgb write control for Adreno4xx devices.
    // see: https://bug.skia.org/5329
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno430 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno4xx_other) {
        fSRGBWriteControl = false;
    }

    // MacPro devices with AMD cards fail to create MSAA sRGB render buffers.
#if defined(SK_BUILD_FOR_MAC)
    if (ctxInfo.vendor() == GrGLVendor::kATI) {
        formatWorkarounds->fDisableSRGBRenderWithMSAAForMacAMD = true;
    }
#endif

    // Command buffer fails glTexSubImage2D with type == GL_HALF_FLOAT_OES if a GL_RGBA16F texture
    // is created with glTexStorage2D. See crbug.com/1008003.
    formatWorkarounds->fDisableRGBA16FTexStorageForCrBug1008003 =
            ctxInfo.isOverCommandBuffer() && ctxInfo.version() < GR_GL_VER(3, 0);

#if defined(SK_BUILD_FOR_WIN)
    // On Intel Windows ES contexts it seems that using texture storage with BGRA causes
    // problems with cross-context SkImages.
    formatWorkarounds->fDisableBGRATextureStorageForIntelWindowsES =
            ctxInfo.driver() == GrGLDriver::kIntel && GR_IS_GR_GL_ES(ctxInfo.standard());
#endif

    // On the Intel Iris 6100, interacting with LUM16F seems to confuse the driver. After
    // writing to/reading from a LUM16F texture reads from/writes to other formats behave
    // erratically.
    // All Adrenos claim to support LUM16F but don't appear to actually do so.
    // The failing devices/gpus were: Nexus5/Adreno330, Nexus5x/Adreno418, Pixel/Adreno530,
    // Pixel2XL/Adreno540 and Pixel3/Adreno630
    formatWorkarounds->fDisableLuminance16F =
            (ctxInfo.renderer() == GrGLRenderer::kIntelBroadwell ||
             ctxInfo.vendor() == GrGLVendor::kQualcomm) &&
            ctxInfo.angleBackend() == GrGLANGLEBackend::kUnknown;

#ifdef SK_BUILD_FOR_MAC
    // On a MacBookPro 11.5 running MacOS 10.13 with a Radeon M370X the TransferPixelsFrom test
    // fails when transferring out from a GL_RG8 texture using GL_RG/GL_UNSIGNED_BYTE.
    // The same error also occurs in MacOS 10.15 with a Radeon Pro 5300M.
    formatWorkarounds->fDisallowDirectRG8ReadPixels =
            ctxInfo.renderer() == GrGLRenderer::kAMDRadeonR9M3xx  ||
            ctxInfo.renderer() == GrGLRenderer::kAMDRadeonPro5xxx ||
            ctxInfo.renderer() == GrGLRenderer::kAMDRadeonProVegaxx;
#endif

#ifdef SK_BUILD_FOR_ANDROID
    // crbug.com/945506. Telemetry reported a memory usage regression for Android Go Chrome/WebView
    // when using glTexStorage2D. This appears to affect OOP-R (so not just over command buffer).
    // Update 10/2023, it looks like this may just effect chrome Android GO devices which are
    // running on Mali-T720. It does not seem to impact Qualcomm devices. We have no tests to verify
    // if newer ARM devices are impacted, so for now we keep this disabled on all ARM by default.
    //
    // We allow the client to pass in a GrContextOption flag to say they prefer having tex storage
    // support regadless of memory usage impacts. This is important for supporting Protected
    // textures as they require tex storage support.
    if (ctxInfo.vendor() == GrGLVendor::kARM &&
        !contextOptions.fAlwaysUseTexStorageWhenAvailable &&
        !fSupportsProtectedContent) {
        formatWorkarounds->fDisableTexStorage = true;
    }
#endif

    // https://github.com/flutter/flutter/issues/38700
    if (ctxInfo.driver() == GrGLDriver::kAndroidEmulator) {
        shaderCaps->fNoDefaultPrecisionForExternalSamplers = true;
    }

    // http://skbug.com/9491: Nexus5 produces rendering artifacts when we use QCOM_tiled_rendering.
    if (ctxInfo.renderer() == GrGLRenderer::kAdreno3xx) {
        fTiledRenderingSupport = false;
    }
    // https://github.com/flutter/flutter/issues/47164
    // https://github.com/flutter/flutter/issues/47804
    if (fTiledRenderingSupport && (!glInterface->fFunctions.fStartTiling ||
                                   !glInterface->fFunctions.fEndTiling)) {
        // Some devices expose the QCOM tiled memory extension string but don't actually provide the
        // start and end tiling functions (see above flutter bugs). To work around this, the funcs
        // are marked optional in the interface generator, but we turn off the tiled rendering cap
        // if they aren't provided. This disabling is in driver workarounds so that SKQP will still
        // fail on devices that advertise the extension w/o the functions.
        fTiledRenderingSupport = false;
    }

    if (ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D9) {
        formatWorkarounds->fDisallowBGRA8ReadPixels = true;
    }

    // We disable MSAA for older Intel GPUs. Before Gen9, performance was very bad. Even with Gen9,
    // we've seen driver crashes in the wild.
    // (crbug.com/527565, crbug.com/983926)
    if (ctxInfo.vendor() == GrGLVendor::kIntel || ctxInfo.angleVendor() == GrGLVendor::kIntel) {
        // Gen11 seems mostly ok, except we avoid drawing lines with MSAA. (anglebug.com/7796)
        if (ctxInfo.renderer() >= GrGLRenderer::kIntelIceLake &&
            contextOptions.fAllowMSAAOnNewIntel) {
            if (fMSFBOType != kNone_MSFBOType) {
                fAvoidLineDraws = true;
            }
        } else {
            fMSFBOType = kNone_MSFBOType;
        }
    }

    // ANGLE's D3D9 backend + AMD GPUs are flaky with program binary caching (skbug.com/10395)
    if (ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D9 &&
        ctxInfo.angleVendor()  == GrGLVendor::kATI) {
        fProgramBinarySupport = false;
    }

    // skbug.com/11204. Avoid recursion issue in SurfaceContext::writePixels.
    if (fDisallowTexSubImageForUnormConfigTexturesEverBoundToFBO) {
        fReuseScratchTextures = false;
    }

    // skbug.com/11935. Don't reorder on these GPUs in GL on old drivers.
    if ((ctxInfo.renderer() == GrGLRenderer::kAdreno620 ||
        ctxInfo.renderer() == GrGLRenderer::kAdreno640) &&
        ctxInfo.driverVersion() < GR_GL_DRIVER_VER(571, 0, 0)) {
        fAvoidReorderingRenderTasks = true;
    }

    // http://crbug.com/1197152
    // http://b/187364475
    // We could limit this < 1.13 on ChromeOS but we don't really have a good way to detect
    // ChromeOS from here.
    if (ctxInfo.renderer()      == GrGLRenderer::kPowerVRRogue &&
        ctxInfo.driver()        == GrGLDriver::kImagination &&
        driverVersion <  GR_GL_DRIVER_VER(1, 16, 0)) {
        fShaderCaps->fShaderDerivativeSupport = false;
    }

    if (ctxInfo.driver() == GrGLDriver::kFreedreno) {
        formatWorkarounds->fDisallowUnorm16Transfers = true;
    }

    // If we keep rebind the same texture to an FBO's color attachment but changing between MSAA and
    // non-MSAA we get corruption in the texture contents. Binding texture 0 and then rebinding the
    // original texture avoids this.
    // This was found on Nexus 5, Android 6.0.1, build M4B30Z
    // GL_VENDOR  : "Qualcomm"
    // GL_RENDERER: "Adreno (TM) 330"
    // GL_VERSION : "OpenGL ES 3.0 V@127.0 AU@  (GIT@I96aee987eb)"
    //
    // We also so alpha blending issues on these GMs skbug_9819, p3_ovals, p3 on Mali-Gxx devices
    // The GM issues were observed on a Galaxy S9 running Android 10:
    // GL_VERSION : "OpenGL ES 3.2 v1.r19p0-01rel0.###other-sha0123456789ABCDEF0###"
    // GL_RENDERER: "Mali-G72"
    // GL_VENDOR  : "ARM"
    // and a P30 running Android 9:
    // GL_VERSION : "OpenGL ES 3.2 v1.r16p0-01rel0.4aee637066427cbcd25297324dba15f5"
    // GL_RENDERER: "Mali-G76"
    // GL_VENDOR  : "ARM"
    // but *not* a Galaxy S20 running Android 10:
    // GL_VERSION : "OpenGL ES 3.2 v1.r20p0-01rel0.###other-sha0123456789ABCDEF0###"
    // GL_RENDERER: "Mali-G77"
    // GL_VENDOR  : "ARM"
    // It's unclear if the difference is driver version or Bifrost vs Valhall. The workaround is
    // fairly trivial so just applying to all Bifrost and Valhall.
    if ((ctxInfo.renderer() == GrGLRenderer::kAdreno3xx &&
         ctxInfo.driver()   == GrGLDriver::kQualcomm) ||
        (ctxInfo.renderer() == GrGLRenderer::kMaliG)) {
        fBindTexture0WhenChangingTextureFBOMultisampleCount = true;
    }

    // skbug.com/12640
    // We found that on the Galaxy S7 the TransferPixelsTo test would fail after adding
    // glCheckFramebufferStatus() checks when making new FBOs. Note that the returned status was
    // GL_FRAMEBUFFER_COMPLETE. Switching the color binding to ID 0 and back to the original
    // afterwards works around the issue.
    // GL_VENDOR  : "ARM"
    // GL_RENDERER: "Mali-T880"
    // GL_VERSION : "OpenGL ES 3.2 v1.r22p0-01rel0.f294e54ceb2cb2d81039204fa4b0402e"
    //
    // This *didn't* reproduce on a Kevin ChromeOS device:
    // GL_VENDOR  : "ARM"
    // GL_RENDERER: "Mali-T860"
    // GL_VERSION : "OpenGL ES 3.2 v1.r26p0-01rel0.217d2597f6bd19b169343737782e56e3"
    if (ctxInfo.renderer()      == GrGLRenderer::kMaliT &&
        ctxInfo.driver()        == GrGLDriver::kARM     &&
        ctxInfo.driverVersion()  < GR_GL_DRIVER_VER(1, 26, 0)) {
        fRebindColorAttachmentAfterCheckFramebufferStatus = true;
    }

#ifdef SK_BUILD_FOR_MAC
    // skbug.com/398631003
    if (ctxInfo.vendor() == GrGLVendor::kApple &&
        // Even the GL ANGLE backend doesn't have this issue, so the workaround is only necessary
        // if we're not rendering with ANGLE.
        ctxInfo.angleBackend() == GrGLANGLEBackend::kUnknown) {
        fBindDefaultFramebufferOnPresent = true;
    }
#endif

    // skbug.com/13286
    // We found that the P30 produces a GL error when setting GL_TEXTURE_MAX_ANISOTROPY as a sampler
    // parameter but not as a texture parameter. We are disabling anisotropy on drivers that may
    // be affected.
    //
    // FAIL on P30
    // GL_VENDOR  : ARM
    // GL_RENDERER: Mali-G76
    // GL_VERSION : OpenGL ES 3.2 v1.r16p0-01rel0.4aee637066427cbcd25297324dba15f5
    //
    // PASS on Pixel6
    // GL_VENDOR  : ARM
    // GL_RENDERER: Mali-G78
    // GL_VERSION : OpenGL ES 3.2 v1.r32p1-00pxl0.b7e5868a59a273f4a9f58d1657ef99de
    //
    // PASS on Galaxy S30:
    // GL_VENDOR  : ARM
    // GL_RENDERER: Mali-G77
    // GL_VERSION : OpenGL ES 3.2 v1.r20p0-01rel0.###other-sha0123456789ABCDEF0###
    //
    // PASS on Galaxy S9:
    // GL_VENDOR  : ARM
    // GL_RENDERER: Mali-G72
    // GL_VENDOR  : OpenGL ES 3.2 v1.r19p0-01rel0.###other-sha0123456789ABCDEF0###
    if (ctxInfo.renderer()      == GrGLRenderer::kMaliG &&
        ctxInfo.driver()        == GrGLDriver::kARM     &&
        ctxInfo.driverVersion()  < GR_GL_DRIVER_VER(1, 19, 0)) {
        fAnisoSupport = false;
    }

    // b/229626353
    // On certain classes of Adreno running WebGL, glTexSubImage2D() occasionally fails to upload
    // texels on time for sampling. The solution is to call glFlush() before glTexSubImage2D().
    // Seen on:
    // * Nexus 5x (Adreno 418)
    // * Nexus 6 (Adreno 420)
    // * Pixel 3 (Adreno 630)
    if (ctxInfo.renderer()       == GrGLRenderer::kWebGL &&
        (ctxInfo.webglRenderer() == GrGLRenderer::kAdreno4xx_other ||
         ctxInfo.webglRenderer() == GrGLRenderer::kAdreno630)) {
        fFlushBeforeWritePixels = true;
    }
    // crbug.com/1395777
    // There appears to be a driver bug in GLSL program linking on Mali 400 and 450 devices with
    // driver version 2.1.199xx that causes the copy-as-draw programs in GrGLGpu to fail. The crash
    // rate increased when scaling copy support was added, so disallow scaling copy-as-draws on
    // these devices.
    if (ctxInfo.renderer() == GrGLRenderer::kMali4xx &&
        ctxInfo.driverVersion() >= GR_GL_DRIVER_VER(2, 1, 19900)) {
        fDisableScalingCopyAsDraws = true;
    }
    // skbug.com/14194
    // Setting the max level is technically unnecessary, but on Intel drivers it makes it
    // clear that a rendering feedback loop is not occurring, and avoids hitting a slow path.
    // When running on ANGLE, however, this triggers the validator because we can only use
    // levels between BASE_LEVEL and MAX_LEVEL for a framebuffer, and we're trying to use
    // MAX_LEVEL+1. So instead we set up sync points between each mipmap level render.
    if (ctxInfo.vendor() == GrGLVendor::kIntel  &&
        ctxInfo.angleBackend() == GrGLANGLEBackend::kUnknown) {
        fRegenerateMipmapType = RegenerateMipmapType::kBasePlusMaxLevel;
    } else if (ctxInfo.angleVendor() == GrGLVendor::kIntel) {
        fRegenerateMipmapType = RegenerateMipmapType::kBasePlusSync;
    }
#ifdef SK_BUILD_FOR_MAC
    // On Apple Silicon, RG88 requires 2-byte alignment for transfer buffer readback
    if (ctxInfo.vendor() == GrGLVendor::kApple) {
        fPadRG88TransferAlignment = true;
    }
#endif
}