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;