diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index f6e49a65633ee..1e844adf0bf4c 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -150,6 +150,7 @@ executable("flutter_linux_unittests") { "fl_standard_message_codec_test.cc", "fl_standard_method_codec_test.cc", "fl_string_codec_test.cc", + "fl_texture_registrar_test.cc", "fl_value_test.cc", "testing/fl_test.cc", "testing/mock_egl.cc", diff --git a/shell/platform/linux/fl_binary_messenger_test.cc b/shell/platform/linux/fl_binary_messenger_test.cc index 475111e305b25..c443e4d39fa99 100644 --- a/shell/platform/linux/fl_binary_messenger_test.cc +++ b/shell/platform/linux/fl_binary_messenger_test.cc @@ -6,24 +6,8 @@ #include "gtest/gtest.h" #include "flutter/shell/platform/linux/fl_binary_messenger_private.h" -#include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" -#include "flutter/shell/platform/linux/testing/mock_renderer.h" - -// Creates a mock engine that responds to platform messages. -static FlEngine* make_mock_engine() { - g_autoptr(FlDartProject) project = fl_dart_project_new(); - g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); - g_autoptr(GError) renderer_error = nullptr; - EXPECT_TRUE(fl_renderer_setup(FL_RENDERER(renderer), &renderer_error)); - EXPECT_EQ(renderer_error, nullptr); - g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer)); - g_autoptr(GError) engine_error = nullptr; - EXPECT_TRUE(fl_engine_start(engine, &engine_error)); - EXPECT_EQ(engine_error, nullptr); - - return static_cast(g_object_ref(engine)); -} +#include "flutter/shell/platform/linux/testing/fl_test.h" // Checks sending nullptr for a message works. TEST(FlBinaryMessengerTest, SendNullptrMessage) { diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 7141565ef5b61..7fd00b7d86292 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -3,9 +3,9 @@ // found in the LICENSE file. #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" -#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" #include "flutter/shell/platform/linux/fl_renderer.h" @@ -600,13 +600,14 @@ void fl_engine_send_mouse_pointer_event(FlEngine* self, bool fl_engine_mark_texture_frame_available(FlEngine* self, int64_t texture_id) { g_return_val_if_fail(FL_IS_ENGINE(self), false); - return !FlutterEngineMarkExternalTextureFrameAvailable(self->engine, - texture_id); + return FlutterEngineMarkExternalTextureFrameAvailable(self->engine, + texture_id) == kSuccess; } bool fl_engine_register_external_texture(FlEngine* self, int64_t texture_id) { g_return_val_if_fail(FL_IS_ENGINE(self), false); - return !FlutterEngineRegisterExternalTexture(self->engine, texture_id); + return FlutterEngineRegisterExternalTexture(self->engine, texture_id) == + kSuccess; } void fl_engine_unregister_external_texture(FlEngine* self, int64_t texture_id) { diff --git a/shell/platform/linux/fl_external_texture_gl.cc b/shell/platform/linux/fl_external_texture_gl.cc index 688522ae7faf5..0ad818f4bc1df 100644 --- a/shell/platform/linux/fl_external_texture_gl.cc +++ b/shell/platform/linux/fl_external_texture_gl.cc @@ -8,25 +8,26 @@ #include #include +struct { + bool valid; + void (*genTextures)(GLsizei n, GLuint* textures); + void (*bindTexture)(GLenum target, GLuint texture); + void (*texParameteri)(GLenum target, GLenum pname, GLenum param); + void (*texImage2D)(GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void* data); + void (*deleteTextures)(GLsizei n, const GLuint* textures); +} gl; + struct _FlExternalTextureGl { GLuint gl_texture_id; FlTextureCallback callback; - struct { - bool valid; - void (*genTextures)(GLsizei n, GLuint* textures); - void (*bindTexture)(GLenum target, GLuint texture); - void (*texParameteri)(GLenum target, GLenum pname, GLenum param); - void (*texImage2D)(GLenum target, - GLint level, - GLint internalformat, - GLsizei width, - GLsizei height, - GLint border, - GLenum format, - GLenum type, - const void* data); - void (*deleteTextures)(GLsizei n, const GLuint* textures); - } gl; void* user_data; }; @@ -34,8 +35,8 @@ G_DEFINE_TYPE(FlExternalTextureGl, fl_external_texture_gl, G_TYPE_OBJECT) static void fl_external_texture_gl_dispose(GObject* object) { FlExternalTextureGl* self = FL_EXTERNAL_TEXTURE_GL(object); - if (self->gl.valid) { - self->gl.deleteTextures(1, &self->gl_texture_id); + if (gl.valid) { + gl.deleteTextures(1, &self->gl_texture_id); } G_OBJECT_CLASS(fl_external_texture_gl_parent_class)->dispose(object); @@ -66,7 +67,7 @@ bool fl_external_texture_gl_populate_texture( opengl_texture->name = self->gl_texture_id; opengl_texture->format = GL_RGBA8; opengl_texture->destruction_callback = nullptr; - opengl_texture->user_data = static_cast(self); + opengl_texture->user_data = nullptr; opengl_texture->width = real_width; opengl_texture->height = real_height; @@ -74,19 +75,19 @@ bool fl_external_texture_gl_populate_texture( } void fl_external_texture_gl_load_funcs(FlExternalTextureGl* self) { - self->gl.genTextures = reinterpret_cast( + gl.genTextures = reinterpret_cast( eglGetProcAddress("glGenTextures")); - self->gl.bindTexture = reinterpret_cast( + gl.bindTexture = reinterpret_cast( eglGetProcAddress("glBindTexture")); - self->gl.texParameteri = reinterpret_cast( + gl.texParameteri = reinterpret_cast( eglGetProcAddress("glTexParameteri")); - self->gl.texImage2D = + gl.texImage2D = reinterpret_cast( eglGetProcAddress("glTexImage2D")); - self->gl.deleteTextures = reinterpret_cast( + gl.deleteTextures = reinterpret_cast( eglGetProcAddress("glDeleteTextures")); - self->gl.valid = true; + gl.valid = true; } bool fl_external_texture_gl_copy_pixel_buffer(FlExternalTextureGl* self, @@ -100,24 +101,22 @@ bool fl_external_texture_gl_copy_pixel_buffer(FlExternalTextureGl* self, *width = pixel_buffer->width; *height = pixel_buffer->height; - if (!self->gl.valid) { + if (!gl.valid) { fl_external_texture_gl_load_funcs(self); } if (self->gl_texture_id == 0) { - self->gl.genTextures(1, &self->gl_texture_id); - self->gl.bindTexture(GL_TEXTURE_2D, self->gl_texture_id); - self->gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - GL_CLAMP_TO_BORDER); - self->gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - GL_CLAMP_TO_BORDER); - self->gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - self->gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl.genTextures(1, &self->gl_texture_id); + gl.bindTexture(GL_TEXTURE_2D, self->gl_texture_id); + gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { - self->gl.bindTexture(GL_TEXTURE_2D, self->gl_texture_id); + gl.bindTexture(GL_TEXTURE_2D, self->gl_texture_id); } - self->gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pixel_buffer->width, - pixel_buffer->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, - pixel_buffer->buffer); + gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pixel_buffer->width, + pixel_buffer->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, + pixel_buffer->buffer); return true; } diff --git a/shell/platform/linux/fl_external_texture_gl.h b/shell/platform/linux/fl_external_texture_gl.h index cfbb0499f50ff..73453494e49e3 100644 --- a/shell/platform/linux/fl_external_texture_gl.h +++ b/shell/platform/linux/fl_external_texture_gl.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_LINUX_FL_EXTERNAL_TEXURE_GL_H #include + #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" diff --git a/shell/platform/linux/fl_texture_registrar.cc b/shell/platform/linux/fl_texture_registrar.cc index 2c1a8c8c9695a..0c253caff60c0 100644 --- a/shell/platform/linux/fl_texture_registrar.cc +++ b/shell/platform/linux/fl_texture_registrar.cc @@ -3,13 +3,14 @@ // found in the LICENSE file. #include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" + +#include + #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_external_texture_gl.h" #include "flutter/shell/platform/linux/fl_texture_registrar_private.h" -#include - struct _FlTextureRegistrar { GObject parent_instance; diff --git a/shell/platform/linux/fl_texture_registrar_test.cc b/shell/platform/linux/fl_texture_registrar_test.cc new file mode 100644 index 0000000000000..4889a53d8610e --- /dev/null +++ b/shell/platform/linux/fl_texture_registrar_test.cc @@ -0,0 +1,72 @@ +// Copyright 2020 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 "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/fl_external_texture_gl.h" +#include "flutter/shell/platform/linux/fl_texture_registrar_private.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "gtest/gtest.h" + +#include + +// Implements eglGetProcAddress but returns dummy functions. +static void* eglGetProcAddress(const char* name) { + return reinterpret_cast(+[]() {}); +} + +// Test that registering a texture works. +TEST(FlTextureRegistrarTest, RegisterTexture) { + g_autoptr(FlEngine) engine = make_mock_engine(); + FlTextureRegistrar* registrar = fl_texture_registrar_new(engine); + fl_texture_registrar_register_texture(registrar, nullptr, nullptr); +} + +// Test that unregistering a texture works. +TEST(FlTextureRegistrarTest, UnregisterTexture) { + g_autoptr(FlEngine) engine = make_mock_engine(); + FlTextureRegistrar* registrar = fl_texture_registrar_new(engine); + int64_t id = + fl_texture_registrar_register_texture(registrar, nullptr, nullptr); + fl_texture_registrar_unregister_texture(registrar, id); +} + +// Test that marking a texture frame available works. +TEST(FlTextureRegistrarTest, MarkTextureFrameAvailable) { + g_autoptr(FlEngine) engine = make_mock_engine(); + FlTextureRegistrar* registrar = fl_texture_registrar_new(engine); + int64_t id = + fl_texture_registrar_register_texture(registrar, nullptr, nullptr); + fl_texture_registrar_mark_texture_frame_available(registrar, id); +} + +// TODO: Re-enable after figuring out how to stub out eglGetProcAddress. +// Also write tests for #FlExternalTextureGl as well. +// +// Test that populating an OpenGL texture works. +// TEST(FlTextureRegistrarTest, PopulateTexture) { +// g_autoptr(FlEngine) engine = make_mock_engine(); +// FlTextureRegistrar* registrar = fl_texture_registrar_new(engine); +// const uint8_t buffer[] = {0x7a, 0x8a, 0x9a, 0xaa}; +// FlPixelBuffer pixel_buffer; +// pixel_buffer.buffer = buffer; +// pixel_buffer.width = 2; +// pixel_buffer.height = 2; +// FlTextureCallback callback = [](size_t width, size_t height, +// void* user_data) -> const FlPixelBuffer* { +// FlPixelBuffer* pixel_buffer = static_cast(user_data); +// EXPECT_EQ(width, pixel_buffer->width); +// EXPECT_EQ(height, pixel_buffer->height); +// return pixel_buffer; +// }; +// int64_t id = +// fl_texture_registrar_register_texture(registrar, callback, +// &pixel_buffer); +// FlutterOpenGLTexture opengl_texture; +// EXPECT_TRUE(fl_texture_registrar_populate_texture( +// registrar, id, pixel_buffer.width, pixel_buffer.height, +// &opengl_texture)); +// EXPECT_EQ(opengl_texture.width, pixel_buffer.width); +// EXPECT_EQ(opengl_texture.height, pixel_buffer.height); +// } diff --git a/shell/platform/linux/testing/fl_test.cc b/shell/platform/linux/testing/fl_test.cc index bd0864c62d315..1172f30bfd20b 100644 --- a/shell/platform/linux/testing/fl_test.cc +++ b/shell/platform/linux/testing/fl_test.cc @@ -4,6 +4,14 @@ #include "flutter/shell/platform/linux/testing/fl_test.h" +#include + +// Doesn't work if included in the next block. +#include "gtest/gtest.h" + +#include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/testing/mock_renderer.h" + static uint8_t hex_digit_to_int(char value) { if (value >= '0' && value <= '9') return value - '0'; @@ -39,3 +47,17 @@ gchar* bytes_to_hex_string(GBytes* bytes) { g_string_append_printf(hex_string, "%02x", data[i]); return g_string_free(hex_string, FALSE); } + +FlEngine* make_mock_engine() { + g_autoptr(FlDartProject) project = fl_dart_project_new(); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); + g_autoptr(GError) renderer_error = nullptr; + EXPECT_EQ(fl_renderer_setup(FL_RENDERER(renderer), &renderer_error), TRUE); + EXPECT_EQ(renderer_error, nullptr); + g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer)); + g_autoptr(GError) engine_error = nullptr; + EXPECT_EQ(fl_engine_start(engine, &engine_error), TRUE); + EXPECT_EQ(engine_error, nullptr); + + return static_cast(g_object_ref(engine)); +} diff --git a/shell/platform/linux/testing/fl_test.h b/shell/platform/linux/testing/fl_test.h index 9f28da3f3b048..fe897fc71a769 100644 --- a/shell/platform/linux/testing/fl_test.h +++ b/shell/platform/linux/testing/fl_test.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEST_H_ #define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEST_H_ +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" + #include #include @@ -18,6 +20,9 @@ GBytes* hex_string_to_bytes(const gchar* hex_string); // Helper function to convert GBytes into a hexadecimal string (e.g. "01feab") gchar* bytes_to_hex_string(GBytes* bytes); +// Creates a mock engine that responds to platform messages. +FlEngine* make_mock_engine(); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEST_H_ diff --git a/shell/platform/linux/testing/mock_engine.cc b/shell/platform/linux/testing/mock_engine.cc index 31a46ee2b6e32..3b4fbded60458 100644 --- a/shell/platform/linux/testing/mock_engine.cc +++ b/shell/platform/linux/testing/mock_engine.cc @@ -9,12 +9,19 @@ #include "gtest/gtest.h" #include +#include +#include + +struct _FlutterEngineTexture { + bool hasNewFrame; +}; struct _FlutterEngine { bool running; FlutterPlatformMessageCallback platform_message_callback; FlutterTaskRunnerPostTaskCallback platform_post_task_callback; void* user_data; + std::unordered_map textures; _FlutterEngine(FlutterPlatformMessageCallback platform_message_callback, FlutterTaskRunnerPostTaskCallback platform_post_task_callback, @@ -393,17 +400,26 @@ FlutterEngineResult FlutterEngineUpdateLocales(FLUTTER_API_SYMBOL(FlutterEngine) FlutterEngineResult FlutterEngineRegisterExternalTexture( FLUTTER_API_SYMBOL(FlutterEngine) engine, int64_t texture_identifier) { + _FlutterEngineTexture texture; + texture.hasNewFrame = false; + engine->textures[texture_identifier] = texture; return kSuccess; } FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable( FLUTTER_API_SYMBOL(FlutterEngine) engine, int64_t texture_identifier) { + auto val = engine->textures.find(texture_identifier); + if (val == std::end(engine->textures)) { + return kInvalidArguments; + } + val->second.hasNewFrame = true; return kSuccess; } FlutterEngineResult FlutterEngineUnregisterExternalTexture( FLUTTER_API_SYMBOL(FlutterEngine) engine, int64_t texture_identifier) { + engine->textures.erase(texture_identifier); return kSuccess; }