GL: Add recreateFboUponFlush workaround.

For the moment, apply it to all Qualcomm GPUs. The workaround will be
restricted further in follow-on commits.

Pass gl::Context to GLImplFactory::createSync on all backends in order
to provide it to the GL backend for the workaround.

Fixed lurking problem with escape sequences in angle_format.py.

Incorporated new unit tests, including some which force-enable the
workaround; should be a no-op on other platforms.

Co-authored with jetski-cli.

Bug: chromium:493747593
Change-Id: I027bfb484611a89615620001f160d0c909a7b22f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7815775
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Kenneth Russell <kbr@chromium.org>
diff --git a/include/platform/autogen/FeaturesGL_autogen.h b/include/platform/autogen/FeaturesGL_autogen.h
index d5e9061..d30f958 100644
--- a/include/platform/autogen/FeaturesGL_autogen.h
+++ b/include/platform/autogen/FeaturesGL_autogen.h
@@ -692,6 +692,12 @@
         &members,
     };
 
+    FeatureInfo recreateFboUponFlush = {
+        "recreateFboUponFlush",
+        FeatureCategory::OpenGLWorkarounds,
+        &members,
+    };
+
 };
 
 inline FeaturesGL::FeaturesGL()  = default;
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
index 6c1a696..7232893 100644
--- a/include/platform/gl_features.json
+++ b/include/platform/gl_features.json
@@ -901,6 +901,14 @@
                 "Some drivers have internal failures when attempting to allocate and initialize buffers larger than 1gb."
             ],
             "issue": "http://crbug.com/475877320"
+        },
+        {
+            "name": "recreate_fbo_upon_flush",
+            "category": "Workarounds",
+            "description": [
+                "Recreate FBO upon flush/finish/fencesync under certain conditions to work around driver bugs."
+            ],
+            "issue": "http://crbug.com/493747593"
         }
     ]
 }
diff --git a/scripts/code_generation_hashes/ANGLE_format.json b/scripts/code_generation_hashes/ANGLE_format.json
index 5061608..3591ef2 100644
--- a/scripts/code_generation_hashes/ANGLE_format.json
+++ b/scripts/code_generation_hashes/ANGLE_format.json
@@ -4,7 +4,7 @@
   "src/libANGLE/renderer/Format_table_autogen.cpp":
     "21e0b0aa9ce772d528e7063ee94cede7",
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/angle_format_data.json":
     "f82d30f65cebd8c787ac0d29fa4c0221",
   "src/libANGLE/renderer/angle_format_map.json":
diff --git a/scripts/code_generation_hashes/ANGLE_load_functions_table.json b/scripts/code_generation_hashes/ANGLE_load_functions_table.json
index ec85f68..ec8f763 100644
--- a/scripts/code_generation_hashes/ANGLE_load_functions_table.json
+++ b/scripts/code_generation_hashes/ANGLE_load_functions_table.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/gen_load_functions_table.py":
     "73e96775a0bd1a424f3f8585b1c5175f",
   "src/libANGLE/renderer/load_functions_data.json":
diff --git a/scripts/code_generation_hashes/D3D11_format.json b/scripts/code_generation_hashes/D3D11_format.json
index 90fadc6..8cd8a9e 100644
--- a/scripts/code_generation_hashes/D3D11_format.json
+++ b/scripts/code_generation_hashes/D3D11_format.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py":
     "70dcd2607cd481f450233c7c2b5c7ac0",
   "src/libANGLE/renderer/d3d/d3d11/texture_format_data.json":
diff --git a/scripts/code_generation_hashes/DXGI_format.json b/scripts/code_generation_hashes/DXGI_format.json
index 648d43d..214b818 100644
--- a/scripts/code_generation_hashes/DXGI_format.json
+++ b/scripts/code_generation_hashes/DXGI_format.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/angle_format_map.json":
     "abad08e462a0839d1600d83f83bcad8b",
   "src/libANGLE/renderer/dxgi_format_data.json":
diff --git a/scripts/code_generation_hashes/GL_copy_conversion_table.json b/scripts/code_generation_hashes/GL_copy_conversion_table.json
index 341b8b8..af3566a 100644
--- a/scripts/code_generation_hashes/GL_copy_conversion_table.json
+++ b/scripts/code_generation_hashes/GL_copy_conversion_table.json
@@ -6,5 +6,5 @@
   "src/libANGLE/gen_copy_conversion_table.py":
     "3b1f0182a41af78b44930fb415ff0fb6",
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30"
+    "49c55a74e763b1cfd0d96c4b49b4e402"
 }
diff --git a/scripts/code_generation_hashes/GL_format_map.json b/scripts/code_generation_hashes/GL_format_map.json
index eda4f5c..73563bb 100644
--- a/scripts/code_generation_hashes/GL_format_map.json
+++ b/scripts/code_generation_hashes/GL_format_map.json
@@ -8,5 +8,5 @@
   "src/libANGLE/gen_format_map.py":
     "32b43aedcbad2aa5eaa47052cc8573ac",
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30"
+    "49c55a74e763b1cfd0d96c4b49b4e402"
 }
diff --git a/scripts/code_generation_hashes/Metal_default_shaders.json b/scripts/code_generation_hashes/Metal_default_shaders.json
index 79e6c2a..68a8512 100644
--- a/scripts/code_generation_hashes/Metal_default_shaders.json
+++ b/scripts/code_generation_hashes/Metal_default_shaders.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/angle_format_map.json":
     "abad08e462a0839d1600d83f83bcad8b",
   "src/libANGLE/renderer/gen_angle_format_table.py":
diff --git a/scripts/code_generation_hashes/Metal_format_table.json b/scripts/code_generation_hashes/Metal_format_table.json
index 1279b30..663e0d2 100644
--- a/scripts/code_generation_hashes/Metal_format_table.json
+++ b/scripts/code_generation_hashes/Metal_format_table.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/angle_format_map.json":
     "abad08e462a0839d1600d83f83bcad8b",
   "src/libANGLE/renderer/metal/gen_mtl_format_table.py":
diff --git a/scripts/code_generation_hashes/OpenGL_dispatch_table.json b/scripts/code_generation_hashes/OpenGL_dispatch_table.json
index 17f66d2..dea39df 100644
--- a/scripts/code_generation_hashes/OpenGL_dispatch_table.json
+++ b/scripts/code_generation_hashes/OpenGL_dispatch_table.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/gl/DispatchTableGL_autogen.cpp":
     "31c088a9f24d58dc3b440412ff1410b1",
   "src/libANGLE/renderer/gl/DispatchTableGL_autogen.h":
diff --git a/scripts/code_generation_hashes/Vulkan_format.json b/scripts/code_generation_hashes/Vulkan_format.json
index b81d75c..ba5152f 100644
--- a/scripts/code_generation_hashes/Vulkan_format.json
+++ b/scripts/code_generation_hashes/Vulkan_format.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/angle_format_map.json":
     "abad08e462a0839d1600d83f83bcad8b",
   "src/libANGLE/renderer/vulkan/gen_vk_format_table.py":
diff --git a/scripts/code_generation_hashes/Vulkan_mandatory_format_support_table.json b/scripts/code_generation_hashes/Vulkan_mandatory_format_support_table.json
index 02679eb..1616f34 100644
--- a/scripts/code_generation_hashes/Vulkan_mandatory_format_support_table.json
+++ b/scripts/code_generation_hashes/Vulkan_mandatory_format_support_table.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/vulkan/gen_vk_mandatory_format_support_table.py":
     "a9fd6f3ff2b584aff382364489146c76",
   "src/libANGLE/renderer/vulkan/vk_format_map.json":
diff --git a/scripts/code_generation_hashes/WebGPU_format.json b/scripts/code_generation_hashes/WebGPU_format.json
index 68bae1f..5380ce4 100644
--- a/scripts/code_generation_hashes/WebGPU_format.json
+++ b/scripts/code_generation_hashes/WebGPU_format.json
@@ -1,6 +1,6 @@
 {
   "src/libANGLE/renderer/angle_format.py":
-    "45ffbde9a8edc7cec1c6c3afc5517b30",
+    "49c55a74e763b1cfd0d96c4b49b4e402",
   "src/libANGLE/renderer/angle_format_map.json":
     "abad08e462a0839d1600d83f83bcad8b",
   "src/libANGLE/renderer/wgpu/gen_wgpu_format_table.py":
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 742a100..57f4a73 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -8146,7 +8146,7 @@
 GLsync Context::fenceSync(GLenum condition, GLbitfield flags)
 {
     SyncID syncHandle;
-    if (!mState.mSyncManager->createSync(mImplementation.get(), &syncHandle))
+    if (!mState.mSyncManager->createSync(mImplementation.get(), this, &syncHandle))
     {
         handleExhaustionError(angle::EntryPoint::GLFenceSync);
         return nullptr;
diff --git a/src/libANGLE/Fence.cpp b/src/libANGLE/Fence.cpp
index 07115c9..35c6ae4 100644
--- a/src/libANGLE/Fence.cpp
+++ b/src/libANGLE/Fence.cpp
@@ -64,9 +64,9 @@
     return angle::Result::Continue;
 }
 
-Sync::Sync(rx::GLImplFactory *factory, SyncID id)
+Sync::Sync(rx::GLImplFactory *factory, SyncID id, const Context *context)
     : RefCountObject(factory->generateSerial(), id),
-      mFence(factory->createSync()),
+      mFence(factory->createSync(context)),
       mLabel(),
       mCondition(GL_SYNC_GPU_COMMANDS_COMPLETE),
       mFlags(0)
diff --git a/src/libANGLE/Fence.h b/src/libANGLE/Fence.h
index ed0ae84..f36153e 100644
--- a/src/libANGLE/Fence.h
+++ b/src/libANGLE/Fence.h
@@ -53,7 +53,7 @@
 class Sync final : public RefCountObject<SyncID>, public LabeledObject
 {
   public:
-    Sync(rx::GLImplFactory *factory, SyncID id);
+    Sync(rx::GLImplFactory *factory, SyncID id, const Context *context);
     ~Sync() override;
 
     void onDestroy(const Context *context) override;
diff --git a/src/libANGLE/Fence_unittest.cpp b/src/libANGLE/Fence_unittest.cpp
index 251d217..85df992 100644
--- a/src/libANGLE/Fence_unittest.cpp
+++ b/src/libANGLE/Fence_unittest.cpp
@@ -96,9 +96,9 @@
     void SetUp() override
     {
         mImpl = new MockSyncImpl;
-        EXPECT_CALL(factory, createSync()).WillOnce(Return(mImpl));
+        EXPECT_CALL(factory, createSync(_)).WillOnce(Return(mImpl));
 
-        mFence = new gl::Sync(&factory, {1});
+        mFence = new gl::Sync(&factory, {1}, nullptr);
         EXPECT_CALL(*mImpl, destroy());
         mFence->addRef();
     }
diff --git a/src/libANGLE/ResourceManager.cpp b/src/libANGLE/ResourceManager.cpp
index 42e5502..d1f5e8f 100644
--- a/src/libANGLE/ResourceManager.cpp
+++ b/src/libANGLE/ResourceManager.cpp
@@ -364,13 +364,13 @@
     sync->release(context);
 }
 
-bool SyncManager::createSync(rx::GLImplFactory *factory, SyncID *outSync)
+bool SyncManager::createSync(rx::GLImplFactory *factory, const Context *context, SyncID *outSync)
 {
     if (!mHandleAllocator.allocate(&outSync->value))
     {
         return false;
     }
-    Sync *sync = new Sync(factory, *outSync);
+    Sync *sync = new Sync(factory, *outSync, context);
     sync->addRef();
     mObjectMap.assign(*outSync, sync);
     return true;
diff --git a/src/libANGLE/ResourceManager.h b/src/libANGLE/ResourceManager.h
index cf23fc4..1959aec 100644
--- a/src/libANGLE/ResourceManager.h
+++ b/src/libANGLE/ResourceManager.h
@@ -286,7 +286,7 @@
 class SyncManager : public TypedResourceManager<Sync, SyncManager, SyncID>
 {
   public:
-    bool createSync(rx::GLImplFactory *factory, SyncID *outSync);
+    bool createSync(rx::GLImplFactory *factory, const Context *context, SyncID *outSync);
     Sync *getSync(SyncID handle) const;
 
     static void DeleteObject(const Context *context, Sync *sync);
diff --git a/src/libANGLE/renderer/GLImplFactory.h b/src/libANGLE/renderer/GLImplFactory.h
index ba7985f..1eee80c 100644
--- a/src/libANGLE/renderer/GLImplFactory.h
+++ b/src/libANGLE/renderer/GLImplFactory.h
@@ -28,6 +28,7 @@
 namespace gl
 {
 class State;
+class Context;
 }  // namespace gl
 
 namespace rx
@@ -86,7 +87,7 @@
     // Query and Fence creation
     virtual QueryImpl *createQuery(gl::QueryType type) = 0;
     virtual FenceNVImpl *createFenceNV()               = 0;
-    virtual SyncImpl *createSync()                     = 0;
+    virtual SyncImpl *createSync(const gl::Context *context) = 0;
 
     // Transform Feedback creation
     virtual TransformFeedbackImpl *createTransformFeedback(
diff --git a/src/libANGLE/renderer/angle_format.py b/src/libANGLE/renderer/angle_format.py
index 8dbf48a..71f1aab 100644
--- a/src/libANGLE/renderer/angle_format.py
+++ b/src/libANGLE/renderer/angle_format.py
@@ -102,7 +102,7 @@
 def get_channel_tokens(format_id):
     if 'EXTERNAL' in format_id:
         return ['R8', 'G8', 'B8', 'A8']
-    r = re.compile(r'([' + kChannels + '][\d]+)')
+    r = re.compile(r'([' + kChannels + r'][\d]+)')
     return list(filter(r.match, r.split(format_id)))
 
 
@@ -158,7 +158,7 @@
     if internal_format.find('INT_10_10_10_2_OES') == 0:
         return 'rgba'
 
-    channels_pattern = re.compile('GL_(COMPRESSED_)?(SIGNED_)?(ETC\d_)?([A-Z]+)')
+    channels_pattern = re.compile(r'GL_(COMPRESSED_)?(SIGNED_)?(ETC\d_)?([A-Z]+)')
     match = re.search(channels_pattern, internal_format)
     channels_string = match.group(4)
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
index 780737a..7bb463d 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
@@ -228,7 +228,7 @@
     return new FenceNV11(mRenderer);
 }
 
-SyncImpl *Context11::createSync()
+SyncImpl *Context11::createSync(const gl::Context *)
 {
     return new Sync11(mRenderer);
 }
diff --git a/src/libANGLE/renderer/d3d/d3d11/Context11.h b/src/libANGLE/renderer/d3d/d3d11/Context11.h
index 93fc111..6859029 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Context11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Context11.h
@@ -55,7 +55,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override;
     FenceNVImpl *createFenceNV() override;
-    SyncImpl *createSync() override;
+    SyncImpl *createSync(const gl::Context *context) override;
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(
diff --git a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
index 99c1e17..6724f11 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
@@ -121,7 +121,7 @@
     return new FenceNV9(mRenderer);
 }
 
-SyncImpl *Context9::createSync()
+SyncImpl *Context9::createSync(const gl::Context *)
 {
     // D3D9 doesn't support ES 3.0 and its sync objects.
     UNREACHABLE();
diff --git a/src/libANGLE/renderer/d3d/d3d9/Context9.h b/src/libANGLE/renderer/d3d/d3d9/Context9.h
index 8cf106f..f87bbe9 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Context9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/Context9.h
@@ -53,7 +53,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override;
     FenceNVImpl *createFenceNV() override;
-    SyncImpl *createSync() override;
+    SyncImpl *createSync(const gl::Context *context) override;
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(
diff --git a/src/libANGLE/renderer/gl/ContextGL.cpp b/src/libANGLE/renderer/gl/ContextGL.cpp
index fdfa3b1..1eca4c4 100644
--- a/src/libANGLE/renderer/gl/ContextGL.cpp
+++ b/src/libANGLE/renderer/gl/ContextGL.cpp
@@ -193,9 +193,14 @@
     }
 }
 
-SyncImpl *ContextGL::createSync()
+SyncImpl *ContextGL::createSync(const gl::Context *context)
 {
-    return new SyncGL(getFunctions());
+    SyncImpl *sync = new SyncGL(getFunctions());
+    if (context)
+    {
+        static_cast<void>(applyRecreateFboWorkaroundIfNeeded(context));
+    }
+    return sync;
 }
 
 TransformFeedbackImpl *ContextGL::createTransformFeedback(const gl::TransformFeedbackState &state)
@@ -239,14 +244,81 @@
     return new OverlayImpl(state);
 }
 
+angle::Result ContextGL::applyRecreateFboWorkaroundIfNeeded(const gl::Context *context)
+{
+    if (!getFeaturesGL().recreateFboUponFlush.enabled)
+    {
+        return angle::Result::Continue;
+    }
+
+    gl::Framebuffer *drawFbo = context->getState().getDrawFramebuffer();
+    if (!drawFbo || drawFbo->isDefault())
+    {
+        return angle::Result::Continue;
+    }
+
+    FramebufferGL *drawFboGL = GetImplAs<FramebufferGL>(drawFbo);
+
+    bool needWorkaround = false;
+
+    // The workaround is needed when the draw framebuffer has a depth or stencil
+    // attachment with either of the following criteria:
+    //
+    // 1) The attachment is a renderbuffer.
+    //
+    // 2) The attachment is an immutable texture, and the attached level of the
+    // texture was never initialized via glTexSubImage2D.
+
+    const gl::FramebufferAttachment *depthAtt   = drawFbo->getDepthAttachment();
+    const gl::FramebufferAttachment *stencilAtt = drawFbo->getStencilAttachment();
+
+    const gl::FramebufferAttachment *attachments[] = {depthAtt, stencilAtt};
+    for (const gl::FramebufferAttachment *att : attachments)
+    {
+        if (!att)
+        {
+            continue;
+        }
+
+        if (att->type() == GL_RENDERBUFFER)
+        {
+            needWorkaround = true;
+            break;
+        }
+        else if (att->type() == GL_TEXTURE)
+        {
+            const gl::Texture *texture = att->getTexture();
+            if (texture->getImmutableFormat())
+            {
+                const gl::ImageIndex &index = att->getTextureImageIndex();
+                const gl::ImageDesc &desc   = texture->getState().getImageDesc(index);
+                if (desc.initState == gl::InitState::MayNeedInit)
+                {
+                    needWorkaround = true;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (needWorkaround)
+    {
+        ANGLE_TRY(drawFboGL->recreateFbo(context));
+    }
+
+    return angle::Result::Continue;
+}
+
 angle::Result ContextGL::flush(const gl::Context *context)
 {
-    return mRenderer->flush();
+    ANGLE_TRY(mRenderer->flush());
+    return applyRecreateFboWorkaroundIfNeeded(context);
 }
 
 angle::Result ContextGL::finish(const gl::Context *context)
 {
-    return mRenderer->finish();
+    ANGLE_TRY(mRenderer->finish());
+    return applyRecreateFboWorkaroundIfNeeded(context);
 }
 
 ANGLE_INLINE angle::Result ContextGL::setDrawArraysState(const gl::Context *context,
diff --git a/src/libANGLE/renderer/gl/ContextGL.h b/src/libANGLE/renderer/gl/ContextGL.h
index e441800..a5be68e 100644
--- a/src/libANGLE/renderer/gl/ContextGL.h
+++ b/src/libANGLE/renderer/gl/ContextGL.h
@@ -74,7 +74,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override;
     FenceNVImpl *createFenceNV() override;
-    SyncImpl *createSync() override;
+    SyncImpl *createSync(const gl::Context *context) override;
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(
@@ -316,6 +316,8 @@
     gl::AttributesMask updateAttributesForBaseInstance(GLuint baseInstance);
     void resetUpdatedAttributes(gl::AttributesMask attribMask);
 
+    angle::Result applyRecreateFboWorkaroundIfNeeded(const gl::Context *context);
+
   protected:
     std::shared_ptr<RendererGL> mRenderer;
 
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.cpp b/src/libANGLE/renderer/gl/FramebufferGL.cpp
index 6889e4c..55d1527 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.cpp
+++ b/src/libANGLE/renderer/gl/FramebufferGL.cpp
@@ -1339,6 +1339,80 @@
     return blitter->clearFramebuffer(context, colorAttachments, depth, stencil, this);
 }
 
+angle::Result FramebufferGL::recreateFbo(const gl::Context *context)
+{
+    const FunctionsGL *functions = GetFunctionsGL(context);
+    StateManagerGL *stateManager = GetStateManagerGL(context);
+
+    stateManager->deleteFramebuffer(mFramebufferID);
+    mFramebufferID = 0;
+    functions->genFramebuffers(1, &mFramebufferID);
+    stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
+
+    const gl::FramebufferState &state = getState();
+    size_t numColorAttachments        = state.getColorAttachments().size();
+    gl::Framebuffer::DirtyBits dirtyBits;
+
+    // Setting dirty bits that require entry points from later context
+    // versions will crash, as syncState dereferences null function
+    // pointers.
+    for (size_t i = 0; i < numColorAttachments; ++i)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + i);
+    }
+    if (state.getDepthAttachment())
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
+    }
+    if (state.getStencilAttachment())
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT);
+    }
+    const DrawBuffersVector<GLenum> &drawBufferStates = state.getDrawBufferStates();
+    if (drawBufferStates[0] != GL_COLOR_ATTACHMENT0 ||
+        std::find_if_not(drawBufferStates.begin() + 1, drawBufferStates.end(),
+                         [](const GLenum &current) { return current == GL_NONE; }) !=
+            drawBufferStates.end())
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_DRAW_BUFFERS);
+    }
+    if (state.getReadBufferState() != GL_COLOR_ATTACHMENT0)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_READ_BUFFER);
+    }
+    if (state.getDefaultWidth() != 0)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_DEFAULT_WIDTH);
+    }
+    if (state.getDefaultHeight() != 0)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT);
+    }
+    if (state.getDefaultSamples() != 0)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES);
+    }
+    if (state.getDefaultFixedSampleLocations() != 0)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS);
+    }
+    if (state.getDefaultLayers() != 0)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_DEFAULT_LAYERS);
+    }
+    if (state.getWriteControlMode() != SrgbWriteControlMode::Default)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE);
+    }
+    if (state.getFlipY() != GL_FALSE)
+    {
+        dirtyBits.set(Framebuffer::DIRTY_BIT_FLIP_Y);
+    }
+
+    // Leave other bits like DIRTY_BIT_FOVEATION alone.
+    return syncState(context, GL_FRAMEBUFFER, dirtyBits, gl::Command::Other);
+}
+
 angle::Result FramebufferGL::syncState(const gl::Context *context,
                                        GLenum binding,
                                        const gl::Framebuffer::DirtyBits &dirtyBits,
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.h b/src/libANGLE/renderer/gl/FramebufferGL.h
index d2f7c17..9163555 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.h
+++ b/src/libANGLE/renderer/gl/FramebufferGL.h
@@ -91,6 +91,8 @@
                             const gl::Framebuffer::DirtyBits &dirtyBits,
                             gl::Command command) override;
 
+    angle::Result recreateFbo(const gl::Context *context);
+
     void updateDefaultFramebufferID(GLuint framebufferID);
     bool isDefault() const { return mState.isDefault(); }
 
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 4f7239e..5594a61 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -2683,6 +2683,10 @@
     // exposed.
     ANGLE_FEATURE_CONDITION(features, bgraTexImageFormatsBroken, !isMesa && isQualcomm);
 
+    // Recreate FBO upon flush/finish/fencesync under certain conditions to work around Qualcomm
+    // driver bugs.
+    ANGLE_FEATURE_CONDITION(features, recreateFboUponFlush, isQualcomm);
+
     // glGenerateMipmap may silently fail on mesa, leaving mips that are expected to be recreated to
     // match the base level in their original shape, hidden from ANGLE and its validation.
     ANGLE_FEATURE_CONDITION(features, recreateMipmapLevelsBeforeGenerate, isMesa);
diff --git a/src/libANGLE/renderer/metal/ContextMtl.h b/src/libANGLE/renderer/metal/ContextMtl.h
index 35be494..0fca4f8 100644
--- a/src/libANGLE/renderer/metal/ContextMtl.h
+++ b/src/libANGLE/renderer/metal/ContextMtl.h
@@ -243,7 +243,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override;
     FenceNVImpl *createFenceNV() override;
-    SyncImpl *createSync() override;
+    SyncImpl *createSync(const gl::Context *context) override;
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(
diff --git a/src/libANGLE/renderer/metal/ContextMtl.mm b/src/libANGLE/renderer/metal/ContextMtl.mm
index 35bc62f..21f9753 100644
--- a/src/libANGLE/renderer/metal/ContextMtl.mm
+++ b/src/libANGLE/renderer/metal/ContextMtl.mm
@@ -1521,7 +1521,7 @@
 {
     return new FenceNVMtl();
 }
-SyncImpl *ContextMtl::createSync()
+SyncImpl *ContextMtl::createSync(const gl::Context *)
 {
     return new SyncMtl();
 }
diff --git a/src/libANGLE/renderer/null/ContextNULL.cpp b/src/libANGLE/renderer/null/ContextNULL.cpp
index c15a1d8..df0ab3e 100644
--- a/src/libANGLE/renderer/null/ContextNULL.cpp
+++ b/src/libANGLE/renderer/null/ContextNULL.cpp
@@ -482,7 +482,7 @@
     return new FenceNVNULL();
 }
 
-SyncImpl *ContextNULL::createSync()
+SyncImpl *ContextNULL::createSync(const gl::Context *)
 {
     return new SyncNULL();
 }
diff --git a/src/libANGLE/renderer/null/ContextNULL.h b/src/libANGLE/renderer/null/ContextNULL.h
index bca0122..a2e49d8 100644
--- a/src/libANGLE/renderer/null/ContextNULL.h
+++ b/src/libANGLE/renderer/null/ContextNULL.h
@@ -231,7 +231,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override;
     FenceNVImpl *createFenceNV() override;
-    SyncImpl *createSync() override;
+    SyncImpl *createSync(const gl::Context *context) override;
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index d2a2177..78fd17a 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -6287,7 +6287,7 @@
     return new FenceNVVk();
 }
 
-SyncImpl *ContextVk::createSync()
+SyncImpl *ContextVk::createSync(const gl::Context *)
 {
     return new SyncVk();
 }
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.h b/src/libANGLE/renderer/vulkan/ContextVk.h
index 163cd93..a344834 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.h
+++ b/src/libANGLE/renderer/vulkan/ContextVk.h
@@ -360,7 +360,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override;
     FenceNVImpl *createFenceNV() override;
-    SyncImpl *createSync() override;
+    SyncImpl *createSync(const gl::Context *context) override;
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(
diff --git a/src/libANGLE/renderer/wgpu/ContextWgpu.cpp b/src/libANGLE/renderer/wgpu/ContextWgpu.cpp
index c894e88..1bbfdda 100644
--- a/src/libANGLE/renderer/wgpu/ContextWgpu.cpp
+++ b/src/libANGLE/renderer/wgpu/ContextWgpu.cpp
@@ -1112,7 +1112,7 @@
     return new FenceNVWgpu();
 }
 
-SyncImpl *ContextWgpu::createSync()
+SyncImpl *ContextWgpu::createSync(const gl::Context *)
 {
     return new SyncWgpu();
 }
diff --git a/src/libANGLE/renderer/wgpu/ContextWgpu.h b/src/libANGLE/renderer/wgpu/ContextWgpu.h
index 3fb933f..ef996ce 100644
--- a/src/libANGLE/renderer/wgpu/ContextWgpu.h
+++ b/src/libANGLE/renderer/wgpu/ContextWgpu.h
@@ -260,7 +260,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override;
     FenceNVImpl *createFenceNV() override;
-    SyncImpl *createSync() override;
+    SyncImpl *createSync(const gl::Context *context) override;
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(
diff --git a/src/tests/angle_unittests_utils.h b/src/tests/angle_unittests_utils.h
index 8400776..529677b 100644
--- a/src/tests/angle_unittests_utils.h
+++ b/src/tests/angle_unittests_utils.h
@@ -60,7 +60,7 @@
     // Query and Fence creation
     QueryImpl *createQuery(gl::QueryType type) override { return nullptr; }
     FenceNVImpl *createFenceNV() override { return nullptr; }
-    SyncImpl *createSync() override { return nullptr; }
+    SyncImpl *createSync(const gl::Context *context) override { return nullptr; }
 
     // Transform Feedback creation
     TransformFeedbackImpl *createTransformFeedback(const gl::TransformFeedbackState &state) override
@@ -101,7 +101,7 @@
                  VertexArrayImpl *(const gl::VertexArrayState &, const gl::VertexArrayBuffers &));
     MOCK_METHOD1(createQuery, QueryImpl *(gl::QueryType type));
     MOCK_METHOD0(createFenceNV, FenceNVImpl *());
-    MOCK_METHOD0(createSync, SyncImpl *());
+    MOCK_METHOD1(createSync, SyncImpl *(const gl::Context *));
     MOCK_METHOD1(createTransformFeedback,
                  TransformFeedbackImpl *(const gl::TransformFeedbackState &));
     MOCK_METHOD1(createSampler, SamplerImpl *(const gl::SamplerState &));
diff --git a/src/tests/gl_tests/FramebufferTest.cpp b/src/tests/gl_tests/FramebufferTest.cpp
index a029996..1f01ecb 100644
--- a/src/tests/gl_tests/FramebufferTest.cpp
+++ b/src/tests/gl_tests/FramebufferTest.cpp
@@ -9421,3 +9421,293 @@
                                ES3_VULKAN().disable(Feature::PreferDynamicRendering));
 ANGLE_INSTANTIATE_TEST_ES3_AND(DefaultFramebufferTest,
                                ES3_VULKAN().disable(Feature::PreferDynamicRendering));
+
+class FramebufferTest_ES3FBOWorkaround : public ANGLETest<>
+{
+  protected:
+    FramebufferTest_ES3FBOWorkaround()
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+        setConfigDepthBits(24);
+        setConfigStencilBits(8);
+    }
+
+    void testSetUp() override
+    {
+        mColorProgram =
+            CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
+        ASSERT_NE(0u, mColorProgram);
+        mColorUniformLocation = glGetUniformLocation(mColorProgram, essl1_shaders::ColorUniform());
+        ASSERT_NE(-1, mColorUniformLocation);
+    }
+
+    void testTearDown() override
+    {
+        if (mColorProgram != 0)
+        {
+            glDeleteProgram(mColorProgram);
+        }
+    }
+
+    void drawRedQuad()
+    {
+        glUseProgram(mColorProgram);
+        glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
+        drawQuad(mColorProgram, essl1_shaders::PositionAttrib(), 0.5f);
+    }
+
+    void drawGreenQuad()
+    {
+        glUseProgram(mColorProgram);
+        glUniform4f(mColorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
+        drawQuad(mColorProgram, essl1_shaders::PositionAttrib(), 0.5f);
+    }
+
+    GLuint mColorProgram        = 0;
+    GLint mColorUniformLocation = -1;
+};
+
+// Sub-Test 1: Renderbuffer depth/stencil attachment Case
+TEST_P(FramebufferTest_ES3FBOWorkaround, RenderbufferWorkaround)
+{
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLRenderbuffer rbo;
+    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 128, 128);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
+
+    GLTexture texture;
+    glBindTexture(GL_TEXTURE_2D, texture);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 128, 128);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+
+    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+    ASSERT_GL_NO_ERROR();
+
+    // Draw a red quad to color buffer
+    drawRedQuad();
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
+
+    // Trigger flush -> FBO recreation!
+    glFlush();
+    ASSERT_GL_NO_ERROR();
+
+    // Draw a green quad to color buffer
+    drawGreenQuad();
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    ASSERT_GL_NO_ERROR();
+}
+
+// Sub-Test 2: Uninitialized Texture Case
+TEST_P(FramebufferTest_ES3FBOWorkaround, UninitializedTextureWorkaround)
+{
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLTexture depthTex;
+    glBindTexture(GL_TEXTURE_2D, depthTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, 128, 128);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0);
+
+    GLTexture colorTex;
+    glBindTexture(GL_TEXTURE_2D, colorTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 128, 128);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
+
+    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+    ASSERT_GL_NO_ERROR();
+
+    // Draw red
+    drawRedQuad();
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
+
+    // Trigger flush -> FBO recreation!
+    glFlush();
+    ASSERT_GL_NO_ERROR();
+
+    // Draw green
+    drawGreenQuad();
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    ASSERT_GL_NO_ERROR();
+}
+
+// Sub-Test 3: Initialized Texture Case (Workaround does NOT trigger)
+TEST_P(FramebufferTest_ES3FBOWorkaround, InitializedTextureNoWorkaround)
+{
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLTexture depthTex;
+    glBindTexture(GL_TEXTURE_2D, depthTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, 128, 128);
+
+    // Initialize/clear the depth texture level using glTexSubImage2D
+    std::vector<GLushort> depthData(128 * 128, 0);
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 128, 128, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,
+                    depthData.data());
+    ASSERT_GL_NO_ERROR();
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0);
+
+    GLTexture colorTex;
+    glBindTexture(GL_TEXTURE_2D, colorTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 128, 128);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
+
+    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+    ASSERT_GL_NO_ERROR();
+
+    // Draw red
+    drawRedQuad();
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
+
+    // Trigger flush -> Workaround should NOT trigger, but we test that everything still works
+    glFlush();
+    ASSERT_GL_NO_ERROR();
+
+    // Draw green
+    drawGreenQuad();
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    ASSERT_GL_NO_ERROR();
+}
+
+// Sub-Test 4: FBO combined with FenceSync
+TEST_P(FramebufferTest_ES3FBOWorkaround, FBOAndFenceSync)
+{
+    GLuint i;
+    GLuint num_buffers;
+    GLuint buffer1;
+    GLuint buffer2;
+    GLuint framebuffer;
+    GLuint textures[2];
+    GLuint renderbuffers[2];
+    GLuint framebuffers[2];
+    GLuint textures2[2];
+    GLuint *buffers;
+    void *pixels;
+    char *g_prepared_buffer;
+    char data[16];
+
+    g_prepared_buffer                   = (char *)calloc(1, 0x400);
+    *(int *)(g_prepared_buffer + 0x00)  = 0x40082000;
+    *(int *)(g_prepared_buffer + 0x04)  = 0x40282000;
+    *(int *)(g_prepared_buffer + 0x08)  = 0x40282000;
+    *(int *)(g_prepared_buffer + 0x0c)  = 0x40282000;
+    *(int *)(g_prepared_buffer + 0x28)  = 0x33800000;
+    *(int *)(g_prepared_buffer + 0x110) = 0x40282000;
+    *(int *)(g_prepared_buffer + 0x114) = 0x40282000;
+    *(int *)(g_prepared_buffer + 0x118) = 0x40282000;
+    *(int *)(g_prepared_buffer + 0x11c) = 0x40282000;
+
+    pixels      = calloc(1, 0x4000);
+    num_buffers = 10;
+    buffers     = (GLuint *)calloc(1, 4 * num_buffers);
+
+    glGenFramebuffers(2, framebuffers);
+    glGenRenderbuffers(2, renderbuffers);
+    glGenTextures(2, textures);
+    glBindTexture(GL_TEXTURE_2D, textures[0]);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 40, 40);
+    glBindTexture(GL_TEXTURE_2D, textures[1]);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 43, 43);
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[0]);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    glDeleteFramebuffers(1, framebuffers);
+    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 40, 40);
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[1]);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
+                              renderbuffers[0]);
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glGenFramebuffers(1, framebuffers);
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[0]);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
+                              renderbuffers[0]);
+    glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[1]);
+    glReadPixels(0, 0, 40, 40, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[1]);
+    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glGenFramebuffers(1, &framebuffer);
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0);
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    glDeleteFramebuffers(1, &framebuffer);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 40, 40);
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[1]);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
+                              renderbuffers[0]);
+    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glPixelStorei(GL_PACK_ROW_LENGTH, 43);
+    glGenBuffers(1, &buffer1);
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer1);
+    glBufferData(GL_PIXEL_UNPACK_BUFFER, 0x400, g_prepared_buffer, GL_DYNAMIC_DRAW);
+    glGenTextures(2, textures2);
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, textures2[0]);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 0x25, 1, 0, GL_RGBA, GL_FLOAT, 0);
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, textures2[1]);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 0x27, 1, 0, GL_RGBA, GL_FLOAT, 0);
+    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+    glPixelStorei(GL_PACK_ROW_LENGTH, 43);
+    glGenBuffers(num_buffers, buffers);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glGenBuffers(1, &buffer2);
+    glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer2);
+    glBufferData(GL_PIXEL_PACK_BUFFER, 0x4000, pixels, GL_STATIC_COPY);
+    glFinish();
+    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+    for (i = 0; i < num_buffers; i++)
+    {
+        glBindBuffer(GL_ARRAY_BUFFER, buffers[i]);
+        glBufferData(GL_ARRAY_BUFFER, 16, &data, GL_DYNAMIC_DRAW);
+    }
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    for (i = 0; i < num_buffers; i++)
+    {
+        glDeleteBuffers(1, &buffers[i]);
+        glActiveTexture(GL_TEXTURE0);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 0x25, 1, 0, GL_RGBA, GL_FLOAT, 0);
+        glActiveTexture(GL_TEXTURE1);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 0x27, 1, 0, GL_RGBA, GL_FLOAT, 0);
+    }
+    glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); /* crash / recreateFbo workaround point! */
+    ASSERT_GL_NO_ERROR();
+
+    // Cleanup
+    glDeleteFramebuffers(1, framebuffers);
+    glDeleteFramebuffers(1, &framebuffers[1]);
+    glDeleteRenderbuffers(2, renderbuffers);
+    glDeleteTextures(2, textures);
+    glDeleteTextures(2, textures2);
+    glDeleteBuffers(1, &buffer1);
+    glDeleteBuffers(1, &buffer2);
+    free(g_prepared_buffer);
+    free(pixels);
+    free(buffers);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FramebufferTest_ES3FBOWorkaround);
+ANGLE_INSTANTIATE_TEST_ES3_AND(FramebufferTest_ES3FBOWorkaround,
+                               ES3_OPENGL().enable(Feature::RecreateFboUponFlush),
+                               ES3_OPENGLES().enable(Feature::RecreateFboUponFlush));
diff --git a/util/autogen/angle_features_autogen.cpp b/util/autogen/angle_features_autogen.cpp
index 55389d0..59306b6 100644
--- a/util/autogen/angle_features_autogen.cpp
+++ b/util/autogen/angle_features_autogen.cpp
@@ -287,6 +287,7 @@
     {Feature::QueryCounterBitsGeneratesErrors, "queryCounterBitsGeneratesErrors"},
     {Feature::ReadPixelsUsingImplementationColorReadFormatForNorm16, "readPixelsUsingImplementationColorReadFormatForNorm16"},
     {Feature::ReapplyUBOBindingsAfterUsingBinaryProgram, "reapplyUBOBindingsAfterUsingBinaryProgram"},
+    {Feature::RecreateFboUponFlush, "recreateFboUponFlush"},
     {Feature::RecreateMipmapLevelsBeforeGenerate, "recreateMipmapLevelsBeforeGenerate"},
     {Feature::RecycleVkEvent, "recycleVkEvent"},
     {Feature::RegenerateStructNames, "regenerateStructNames"},
diff --git a/util/autogen/angle_features_autogen.h b/util/autogen/angle_features_autogen.h
index d5016ce..4c46125 100644
--- a/util/autogen/angle_features_autogen.h
+++ b/util/autogen/angle_features_autogen.h
@@ -287,6 +287,7 @@
     QueryCounterBitsGeneratesErrors,
     ReadPixelsUsingImplementationColorReadFormatForNorm16,
     ReapplyUBOBindingsAfterUsingBinaryProgram,
+    RecreateFboUponFlush,
     RecreateMipmapLevelsBeforeGenerate,
     RecycleVkEvent,
     RegenerateStructNames,