Save and restore OpenGL bindings that are changed by fl_renderer_render (#51887)

fl_renderer_render uses the raster thread GL context that is also used by Skia.  Skia expects that its bindings have not been changed elsewhere.
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 491a8aa..5c4a81d 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -41986,6 +41986,7 @@
 ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_gdk.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_headless.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_test.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_scrolling_manager.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_scrolling_manager.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_scrolling_manager_test.cc + ../../../flutter/LICENSE
@@ -44897,6 +44898,7 @@
 FILE: ../../../flutter/shell/platform/linux/fl_renderer_gdk.h
 FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc
 FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.h
+FILE: ../../../flutter/shell/platform/linux/fl_renderer_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_scrolling_manager.cc
 FILE: ../../../flutter/shell/platform/linux/fl_scrolling_manager.h
 FILE: ../../../flutter/shell/platform/linux/fl_scrolling_manager_test.cc
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index b2a8e80..c28c7b5 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -213,6 +213,7 @@
     "fl_pixel_buffer_texture_test.cc",
     "fl_platform_plugin_test.cc",
     "fl_plugin_registrar_test.cc",
+    "fl_renderer_test.cc",
     "fl_scrolling_manager_test.cc",
     "fl_settings_plugin_test.cc",
     "fl_settings_portal_test.cc",
diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc
index c6d20f5..595c315 100644
--- a/shell/platform/linux/fl_renderer.cc
+++ b/shell/platform/linux/fl_renderer.cc
@@ -314,6 +314,16 @@
 
   g_return_if_fail(FL_IS_RENDERER(self));
 
+  // Save bindings that are set by this function.  All bindings must be restored
+  // to their original values because Skia expects that its bindings have not
+  // been altered.
+  GLint saved_texture_binding;
+  glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding);
+  GLint saved_vao_binding;
+  glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao_binding);
+  GLint saved_array_buffer_binding;
+  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding);
+
   glClearColor(0.0, 0.0, 0.0, 1.0);
   glClear(GL_COLOR_BUFFER_BIT);
 
@@ -364,6 +374,10 @@
   }
 
   glFlush();
+
+  glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
+  glBindVertexArray(saved_vao_binding);
+  glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding);
 }
 
 void fl_renderer_cleanup(FlRenderer* self) {
diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc
new file mode 100644
index 0000000..7c01204
--- /dev/null
+++ b/shell/platform/linux/fl_renderer_test.cc
@@ -0,0 +1,53 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gtest/gtest.h"
+
+#include <epoxy/egl.h>
+
+#include "flutter/fml/logging.h"
+#include "flutter/shell/platform/linux/fl_backing_store_provider.h"
+#include "flutter/shell/platform/linux/testing/fl_test_gtk_logs.h"
+#include "flutter/shell/platform/linux/testing/mock_renderer.h"
+
+TEST(FlRendererTest, RestoresGLState) {
+  constexpr int kWidth = 100;
+  constexpr int kHeight = 100;
+
+  flutter::testing::fl_ensure_gtk_init();
+  g_autoptr(FlDartProject) project = fl_dart_project_new();
+  g_autoptr(FlView) view = fl_view_new(project);
+  g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new();
+  g_autoptr(FlBackingStoreProvider) backing_store_provider =
+      fl_backing_store_provider_new(kWidth, kHeight);
+
+  fl_renderer_start(FL_RENDERER(renderer), view);
+  fl_renderer_wait_for_frame(FL_RENDERER(renderer), kWidth, kHeight);
+
+  FlutterBackingStore backing_store;
+  backing_store.type = kFlutterBackingStoreTypeOpenGL;
+  backing_store.open_gl.framebuffer.user_data = backing_store_provider;
+
+  FlutterLayer layer;
+  layer.type = kFlutterLayerContentTypeBackingStore;
+  layer.backing_store = &backing_store;
+  layer.offset = {0, 0};
+  layer.size = {kWidth, kHeight};
+
+  std::array<const FlutterLayer*, 1> layers = {&layer};
+
+  constexpr GLuint kFakeTextureName = 123;
+  glBindTexture(GL_TEXTURE_2D, kFakeTextureName);
+
+  fl_renderer_present_layers(FL_RENDERER(renderer), layers.data(),
+                             layers.size());
+  fl_renderer_render(FL_RENDERER(renderer), kWidth, kHeight);
+
+  GLuint texture_2d_binding;
+  glGetIntegerv(GL_TEXTURE_BINDING_2D,
+                reinterpret_cast<GLint*>(&texture_2d_binding));
+  EXPECT_EQ(texture_2d_binding, kFakeTextureName);
+
+  g_object_ref_sink(view);
+}
diff --git a/shell/platform/linux/testing/mock_epoxy.cc b/shell/platform/linux/testing/mock_epoxy.cc
index e89517d..e82e626 100644
--- a/shell/platform/linux/testing/mock_epoxy.cc
+++ b/shell/platform/linux/testing/mock_epoxy.cc
@@ -325,6 +325,16 @@
 
   return bool_success();
 }
+EGLBoolean _eglQueryContext(EGLDisplay display,
+                            EGLContext context,
+                            EGLint attribute,
+                            EGLint* value) {
+  if (attribute == EGL_CONTEXT_CLIENT_TYPE) {
+    *value = EGL_OPENGL_API;
+    return EGL_TRUE;
+  }
+  return EGL_FALSE;
+}
 
 EGLBoolean _eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
   if (!check_display(dpy) || !check_initialized(dpy)) {
@@ -334,9 +344,15 @@
   return bool_success();
 }
 
+static GLuint bound_texture_2d;
+
 static void _glBindFramebuffer(GLenum target, GLuint framebuffer) {}
 
-static void _glBindTexture(GLenum target, GLuint texture) {}
+static void _glBindTexture(GLenum target, GLuint texture) {
+  if (target == GL_TEXTURE_2D) {
+    bound_texture_2d = texture;
+  }
+}
 
 void _glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {}
 
@@ -360,6 +376,12 @@
   }
 }
 
+static void _glGetIntegerv(GLenum pname, GLint* data) {
+  if (pname == GL_TEXTURE_BINDING_2D) {
+    *data = bound_texture_2d;
+  }
+}
+
 static void _glTexParameterf(GLenum target, GLenum pname, GLfloat param) {}
 
 static void _glTexParameteri(GLenum target, GLenum pname, GLint param) {}
@@ -477,6 +499,7 @@
   epoxy_eglGetProcAddress = _eglGetProcAddress;
   epoxy_eglInitialize = _eglInitialize;
   epoxy_eglMakeCurrent = _eglMakeCurrent;
+  epoxy_eglQueryContext = _eglQueryContext;
   epoxy_eglSwapBuffers = _eglSwapBuffers;
 
   epoxy_glBindFramebuffer = _glBindFramebuffer;
@@ -486,6 +509,7 @@
   epoxy_glFramebufferTexture2D = _glFramebufferTexture2D;
   epoxy_glGenFramebuffers = _glGenFramebuffers;
   epoxy_glGenTextures = _glGenTextures;
+  epoxy_glGetIntegerv = _glGetIntegerv;
   epoxy_glTexParameterf = _glTexParameterf;
   epoxy_glTexParameteri = _glTexParameteri;
   epoxy_glTexImage2D = _glTexImage2D;