diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 215297bdfd324..317b455fbb5fb 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1571,6 +1571,9 @@ FILE: ../../../flutter/shell/platform/linux/fl_method_response.cc FILE: ../../../flutter/shell/platform/linux/fl_method_response_test.cc FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_plugin.h +FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture.cc +FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h +FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_test.cc FILE: ../../../flutter/shell/platform/linux/fl_platform_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_platform_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar.cc @@ -1595,6 +1598,14 @@ FILE: ../../../flutter/shell/platform/linux/fl_task_runner.cc FILE: ../../../flutter/shell/platform/linux/fl_task_runner.h FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.h +FILE: ../../../flutter/shell/platform/linux/fl_texture.cc +FILE: ../../../flutter/shell/platform/linux/fl_texture_gl.cc +FILE: ../../../flutter/shell/platform/linux/fl_texture_gl_private.h +FILE: ../../../flutter/shell/platform/linux/fl_texture_gl_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_texture_private.h +FILE: ../../../flutter/shell/platform/linux/fl_texture_registrar.cc +FILE: ../../../flutter/shell/platform/linux/fl_texture_registrar_private.h +FILE: ../../../flutter/shell/platform/linux/fl_texture_registrar_test.cc FILE: ../../../flutter/shell/platform/linux/fl_value.cc FILE: ../../../flutter/shell/platform/linux/fl_value_test.cc FILE: ../../../flutter/shell/platform/linux/fl_view.cc @@ -1616,11 +1627,15 @@ FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_call. FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_texture.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_texture_gl.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_value.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/flutter_linux.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 94be849bcd17c..20adc1e31d068 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -55,11 +55,15 @@ _public_headers = [ "public/flutter_linux/fl_method_channel.h", "public/flutter_linux/fl_method_codec.h", "public/flutter_linux/fl_method_response.h", + "public/flutter_linux/fl_pixel_buffer_texture.h", "public/flutter_linux/fl_plugin_registrar.h", "public/flutter_linux/fl_plugin_registry.h", "public/flutter_linux/fl_standard_message_codec.h", "public/flutter_linux/fl_standard_method_codec.h", "public/flutter_linux/fl_string_codec.h", + "public/flutter_linux/fl_texture.h", + "public/flutter_linux/fl_texture_gl.h", + "public/flutter_linux/fl_texture_registrar.h", "public/flutter_linux/fl_value.h", "public/flutter_linux/fl_view.h", "public/flutter_linux/flutter_linux.h", @@ -114,6 +118,7 @@ source_set("flutter_linux_sources") { "fl_method_codec.cc", "fl_method_response.cc", "fl_mouse_cursor_plugin.cc", + "fl_pixel_buffer_texture.cc", "fl_platform_plugin.cc", "fl_plugin_registrar.cc", "fl_plugin_registry.cc", @@ -127,6 +132,9 @@ source_set("flutter_linux_sources") { "fl_task_runner.cc", "fl_task_runner.h", "fl_text_input_plugin.cc", + "fl_texture.cc", + "fl_texture_gl.cc", + "fl_texture_registrar.cc", "fl_value.cc", "fl_view.cc", "fl_view_accessible.cc", @@ -184,9 +192,12 @@ executable("flutter_linux_unittests") { "fl_method_channel_test.cc", "fl_method_codec_test.cc", "fl_method_response_test.cc", + "fl_pixel_buffer_texture_test.cc", "fl_standard_message_codec_test.cc", "fl_standard_method_codec_test.cc", "fl_string_codec_test.cc", + "fl_texture_gl_test.cc", + "fl_texture_registrar_test.cc", "fl_value_test.cc", "testing/fl_test.cc", "testing/mock_engine.cc", diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 116fd3f5c7e18..05e3d084e3e9f 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -8,6 +8,7 @@ #include +#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/fl_dart_project_private.h" #include "flutter/shell/platform/linux/fl_engine_private.h" @@ -15,6 +16,7 @@ #include "flutter/shell/platform/linux/fl_renderer.h" #include "flutter/shell/platform/linux/fl_renderer_headless.h" #include "flutter/shell/platform/linux/fl_settings_plugin.h" +#include "flutter/shell/platform/linux/fl_texture_registrar_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h" // Unique number associated with platform tasks. @@ -30,6 +32,7 @@ struct _FlEngine { FlRenderer* renderer; FlBinaryMessenger* binary_messenger; FlSettingsPlugin* settings_plugin; + FlTextureRegistrar* texture_registrar; FlTaskRunner* task_runner; FlutterEngineAOTData aot_data; FLUTTER_API_SYMBOL(FlutterEngine) engine; @@ -214,6 +217,26 @@ static bool fl_engine_gl_make_resource_current(void* user_data) { return result; } +// Called by the engine to retrieve an external texture. +static bool fl_engine_gl_external_texture_frame_callback( + void* user_data, + int64_t texture_id, + size_t width, + size_t height, + FlutterOpenGLTexture* texture) { + FlEngine* self = static_cast(user_data); + if (!self->texture_registrar) { + return false; + } + g_autoptr(GError) error = nullptr; + gboolean result = fl_texture_registrar_populate_gl_external_texture( + self->texture_registrar, texture_id, width, height, texture, &error); + if (!result) { + g_warning("%s", error->message); + } + return result; +} + // Called by the engine to determine if it is on the GTK thread. static bool fl_engine_runs_task_on_current_thread(void* user_data) { FlEngine* self = static_cast(user_data); @@ -276,7 +299,8 @@ static FlPluginRegistrar* fl_engine_get_registrar_for_plugin( const gchar* name) { FlEngine* self = FL_ENGINE(registry); - return fl_plugin_registrar_new(nullptr, self->binary_messenger); + return fl_plugin_registrar_new(nullptr, self->binary_messenger, + self->texture_registrar); } static void fl_engine_plugin_registry_iface_init( @@ -299,6 +323,7 @@ static void fl_engine_dispose(GObject* object) { g_clear_object(&self->project); g_clear_object(&self->renderer); + g_clear_object(&self->texture_registrar); g_clear_object(&self->binary_messenger); g_clear_object(&self->settings_plugin); g_clear_object(&self->task_runner); @@ -330,6 +355,7 @@ static void fl_engine_init(FlEngine* self) { self->embedder_api.struct_size = sizeof(FlutterEngineProcTable); FlutterEngineGetProcAddresses(&self->embedder_api); + self->texture_registrar = fl_texture_registrar_new(self); self->binary_messenger = fl_binary_messenger_new(self); } @@ -362,6 +388,8 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { config.open_gl.fbo_callback = fl_engine_gl_get_fbo; config.open_gl.present = fl_engine_gl_present; config.open_gl.make_resource_current = fl_engine_gl_make_resource_current; + config.open_gl.gl_external_texture_frame_callback = + fl_engine_gl_external_texture_frame_callback; FlutterTaskRunnerDescription platform_task_runner = {}; platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription); @@ -673,6 +701,27 @@ void fl_engine_dispatch_semantics_action(FlEngine* self, action_data, action_data_length); } +gboolean fl_engine_mark_texture_frame_available(FlEngine* self, + int64_t texture_id) { + g_return_val_if_fail(FL_IS_ENGINE(self), FALSE); + return self->embedder_api.MarkExternalTextureFrameAvailable( + self->engine, texture_id) == kSuccess; +} + +gboolean fl_engine_register_external_texture(FlEngine* self, + int64_t texture_id) { + g_return_val_if_fail(FL_IS_ENGINE(self), FALSE); + return self->embedder_api.RegisterExternalTexture(self->engine, texture_id) == + kSuccess; +} + +gboolean fl_engine_unregister_external_texture(FlEngine* self, + int64_t texture_id) { + g_return_val_if_fail(FL_IS_ENGINE(self), FALSE); + return self->embedder_api.UnregisterExternalTexture(self->engine, + texture_id) == kSuccess; +} + G_MODULE_EXPORT FlBinaryMessenger* fl_engine_get_binary_messenger( FlEngine* self) { g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); @@ -688,3 +737,9 @@ void fl_engine_execute_task(FlEngine* self, FlutterTask* task) { g_return_if_fail(FL_IS_ENGINE(self)); self->embedder_api.RunTask(self->engine, task); } + +G_MODULE_EXPORT FlTextureRegistrar* fl_engine_get_texture_registrar( + FlEngine* self) { + g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); + return self->texture_registrar; +} diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h index b5e0081eb0fbb..fdc35ac54b5c4 100644 --- a/shell/platform/linux/fl_engine_private.h +++ b/shell/platform/linux/fl_engine_private.h @@ -253,6 +253,44 @@ FlTaskRunner* fl_engine_get_task_runner(FlEngine* engine); */ void fl_engine_execute_task(FlEngine* engine, FlutterTask* task); +/** + * fl_engine_mark_texture_frame_available: + * @engine: an #FlEngine. + * @texture_id: the identifier of the texture whose frame has been updated. + * + * Tells the Flutter engine that a new texture frame is available for the given + * texture. + * + * Returns: %TRUE on success. + */ +gboolean fl_engine_mark_texture_frame_available(FlEngine* engine, + int64_t texture_id); + +/** + * fl_engine_register_external_texture: + * @engine: an #FlEngine. + * @texture_id: the identifier of the texture that is available. + * + * Tells the Flutter engine that a new external texture is available. + * + * Returns: %TRUE on success. + */ +gboolean fl_engine_register_external_texture(FlEngine* engine, + int64_t texture_id); + +/** + * fl_engine_unregister_external_texture: + * @engine: an #FlEngine. + * @texture_id: the identifier of the texture that is not available anymore. + * + * Tells the Flutter engine that an existing external texture is not available + * anymore. + * + * Returns: %TRUE on success. + */ +gboolean fl_engine_unregister_external_texture(FlEngine* engine, + int64_t texture_id); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_ diff --git a/shell/platform/linux/fl_pixel_buffer_texture.cc b/shell/platform/linux/fl_pixel_buffer_texture.cc new file mode 100644 index 0000000000000..f0f75bef1146f --- /dev/null +++ b/shell/platform/linux/fl_pixel_buffer_texture.cc @@ -0,0 +1,103 @@ +// 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 "flutter/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h" + +#include +#include + +#include "flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h" + +typedef struct { + GLuint texture_id; +} FlPixelBufferTexturePrivate; + +// Added here to stop the compiler from optimising this function away. +G_MODULE_EXPORT GType fl_pixel_buffer_texture_get_type(); + +static void fl_pixel_buffer_texture_iface_init(FlTextureInterface* iface) {} + +G_DEFINE_TYPE_WITH_CODE( + FlPixelBufferTexture, + fl_pixel_buffer_texture, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(fl_texture_get_type(), + fl_pixel_buffer_texture_iface_init); + G_ADD_PRIVATE(FlPixelBufferTexture)) + +static void fl_pixel_buffer_texture_dispose(GObject* object) { + FlPixelBufferTexture* self = FL_PIXEL_BUFFER_TEXTURE(object); + FlPixelBufferTexturePrivate* priv = + reinterpret_cast( + fl_pixel_buffer_texture_get_instance_private(self)); + + if (priv->texture_id) { + glDeleteTextures(1, &priv->texture_id); + priv->texture_id = 0; + } + + G_OBJECT_CLASS(fl_pixel_buffer_texture_parent_class)->dispose(object); +} + +static void check_gl_error(int line) { + GLenum err = glGetError(); + if (err) { + g_warning("glGetError %x (%s:%d)\n", err, __FILE__, line); + } +} + +gboolean fl_pixel_buffer_texture_populate(FlPixelBufferTexture* texture, + uint32_t width, + uint32_t height, + FlutterOpenGLTexture* opengl_texture, + GError** error) { + FlPixelBufferTexture* self = FL_PIXEL_BUFFER_TEXTURE(texture); + FlPixelBufferTexturePrivate* priv = + reinterpret_cast( + fl_pixel_buffer_texture_get_instance_private(self)); + + const uint8_t* buffer = nullptr; + if (!FL_PIXEL_BUFFER_TEXTURE_GET_CLASS(self)->copy_pixels( + self, &buffer, &width, &height, error)) { + return FALSE; + } + + if (priv->texture_id == 0) { + glGenTextures(1, &priv->texture_id); + check_gl_error(__LINE__); + glBindTexture(GL_TEXTURE_2D, priv->texture_id); + check_gl_error(__LINE__); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + check_gl_error(__LINE__); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + check_gl_error(__LINE__); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + check_gl_error(__LINE__); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + check_gl_error(__LINE__); + } else { + glBindTexture(GL_TEXTURE_2D, priv->texture_id); + check_gl_error(__LINE__); + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, buffer); + check_gl_error(__LINE__); + + opengl_texture->target = GL_TEXTURE_2D; + opengl_texture->name = priv->texture_id; + opengl_texture->format = GL_RGBA8; + opengl_texture->destruction_callback = nullptr; + opengl_texture->user_data = nullptr; + opengl_texture->width = width; + opengl_texture->height = height; + + return TRUE; +} + +static void fl_pixel_buffer_texture_class_init( + FlPixelBufferTextureClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_pixel_buffer_texture_dispose; +} + +static void fl_pixel_buffer_texture_init(FlPixelBufferTexture* self) {} diff --git a/shell/platform/linux/fl_pixel_buffer_texture_private.h b/shell/platform/linux/fl_pixel_buffer_texture_private.h new file mode 100644 index 0000000000000..bea9d60472962 --- /dev/null +++ b/shell/platform/linux/fl_pixel_buffer_texture_private.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_PIXEL_BUFFER_TEXTURE_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_PIXEL_BUFFER_TEXTURE_PRIVATE_H_ + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" + +G_BEGIN_DECLS + +/** + * fl_pixel_buffer_texture_populate: + * @texture: an #FlPixelBufferTexture. + * @width: width of the texture. + * @height: height of the texture. + * @opengl_texture: (out): return an #FlutterOpenGLTexture. + * @error: (allow-none): #GError location to store the error occurring, or + * %NULL to ignore. + * + * Attempts to populate the specified @opengl_texture with texture details + * such as the name, width, height and the pixel format. + * + * Returns: %TRUE on success. + */ +gboolean fl_pixel_buffer_texture_populate(FlPixelBufferTexture* texture, + uint32_t width, + uint32_t height, + FlutterOpenGLTexture* opengl_texture, + GError** error); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PIXEL_BUFFER_TEXTURE_PRIVATE_H_ diff --git a/shell/platform/linux/fl_pixel_buffer_texture_test.cc b/shell/platform/linux/fl_pixel_buffer_texture_test.cc new file mode 100644 index 0000000000000..ff844f65a233e --- /dev/null +++ b/shell/platform/linux/fl_pixel_buffer_texture_test.cc @@ -0,0 +1,88 @@ +// 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 "flutter/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h" +#include "flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h" +#include "flutter/shell/platform/linux/fl_texture_private.h" +#include "flutter/shell/platform/linux/fl_texture_registrar_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "gtest/gtest.h" + +#include + +static constexpr uint32_t BUFFER_WIDTH = 4u; +static constexpr uint32_t BUFFER_HEIGHT = 4u; +static constexpr uint32_t REAL_BUFFER_WIDTH = 2u; +static constexpr uint32_t REAL_BUFFER_HEIGHT = 2u; + +G_DECLARE_FINAL_TYPE(FlTestPixelBufferTexture, + fl_test_pixel_buffer_texture, + FL, + TEST_PIXEL_BUFFER_TEXTURE, + FlPixelBufferTexture) + +/// A simple texture with fixed contents. +struct _FlTestPixelBufferTexture { + FlPixelBufferTexture parent_instance; +}; + +G_DEFINE_TYPE(FlTestPixelBufferTexture, + fl_test_pixel_buffer_texture, + fl_pixel_buffer_texture_get_type()) + +static gboolean fl_test_pixel_buffer_texture_copy_pixels( + FlPixelBufferTexture* texture, + const uint8_t** out_buffer, + uint32_t* width, + uint32_t* height, + GError** error) { + EXPECT_TRUE(FL_IS_TEST_PIXEL_BUFFER_TEXTURE(texture)); + + // RGBA + static const uint8_t buffer[] = {0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a, + 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, + 0xca, 0xda, 0xea, 0xfa}; + EXPECT_EQ(*width, BUFFER_WIDTH); + EXPECT_EQ(*height, BUFFER_HEIGHT); + *out_buffer = buffer; + *width = REAL_BUFFER_WIDTH; + *height = REAL_BUFFER_HEIGHT; + + return TRUE; +} + +static void fl_test_pixel_buffer_texture_class_init( + FlTestPixelBufferTextureClass* klass) { + FL_PIXEL_BUFFER_TEXTURE_CLASS(klass)->copy_pixels = + fl_test_pixel_buffer_texture_copy_pixels; +} + +static void fl_test_pixel_buffer_texture_init(FlTestPixelBufferTexture* self) {} + +static FlTestPixelBufferTexture* fl_test_pixel_buffer_texture_new() { + return FL_TEST_PIXEL_BUFFER_TEXTURE( + g_object_new(fl_test_pixel_buffer_texture_get_type(), nullptr)); +} + +// Test that getting the texture ID works. +TEST(FlPixelBufferTextureTest, TextureID) { + // Texture ID is not assigned until the pixel buffer is copied once. + g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_pixel_buffer_texture_new()); + EXPECT_EQ(fl_texture_get_texture_id(texture), + reinterpret_cast(texture)); +} + +// Test that populating an OpenGL texture works. +TEST(FlPixelBufferTextureTest, PopulateTexture) { + g_autoptr(FlPixelBufferTexture) texture = + FL_PIXEL_BUFFER_TEXTURE(fl_test_pixel_buffer_texture_new()); + FlutterOpenGLTexture opengl_texture = {0}; + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_pixel_buffer_texture_populate( + texture, BUFFER_WIDTH, BUFFER_HEIGHT, &opengl_texture, &error)); + EXPECT_EQ(error, nullptr); + EXPECT_EQ(opengl_texture.width, REAL_BUFFER_WIDTH); + EXPECT_EQ(opengl_texture.height, REAL_BUFFER_HEIGHT); +} diff --git a/shell/platform/linux/fl_plugin_registrar.cc b/shell/platform/linux/fl_plugin_registrar.cc index 5207497b2fb30..e9fe0318a65b9 100644 --- a/shell/platform/linux/fl_plugin_registrar.cc +++ b/shell/platform/linux/fl_plugin_registrar.cc @@ -15,6 +15,9 @@ struct _FlPluginRegistrar { // Messenger to communicate on. FlBinaryMessenger* messenger; + + // Texture registrar in use. + FlTextureRegistrar* texture_registrar; }; // Added here to stop the compiler from optimizing this function away. @@ -31,6 +34,7 @@ static void fl_plugin_registrar_dispose(GObject* object) { self->view = nullptr; } g_clear_object(&self->messenger); + g_clear_object(&self->texture_registrar); G_OBJECT_CLASS(fl_plugin_registrar_parent_class)->dispose(object); } @@ -41,10 +45,13 @@ static void fl_plugin_registrar_class_init(FlPluginRegistrarClass* klass) { static void fl_plugin_registrar_init(FlPluginRegistrar* self) {} -FlPluginRegistrar* fl_plugin_registrar_new(FlView* view, - FlBinaryMessenger* messenger) { +FlPluginRegistrar* fl_plugin_registrar_new( + FlView* view, + FlBinaryMessenger* messenger, + FlTextureRegistrar* texture_registrar) { g_return_val_if_fail(view == nullptr || FL_IS_VIEW(view), nullptr); g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); + g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(texture_registrar), nullptr); FlPluginRegistrar* self = FL_PLUGIN_REGISTRAR( g_object_new(fl_plugin_registrar_get_type(), nullptr)); @@ -55,6 +62,8 @@ FlPluginRegistrar* fl_plugin_registrar_new(FlView* view, reinterpret_cast(&(self->view))); } self->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger)); + self->texture_registrar = + FL_TEXTURE_REGISTRAR(g_object_ref(texture_registrar)); return self; } @@ -66,6 +75,13 @@ G_MODULE_EXPORT FlBinaryMessenger* fl_plugin_registrar_get_messenger( return self->messenger; } +G_MODULE_EXPORT FlTextureRegistrar* fl_plugin_registrar_get_texture_registrar( + FlPluginRegistrar* self) { + g_return_val_if_fail(FL_IS_PLUGIN_REGISTRAR(self), nullptr); + + return self->texture_registrar; +} + G_MODULE_EXPORT FlView* fl_plugin_registrar_get_view(FlPluginRegistrar* self) { g_return_val_if_fail(FL_IS_PLUGIN_REGISTRAR(self), nullptr); diff --git a/shell/platform/linux/fl_plugin_registrar_private.h b/shell/platform/linux/fl_plugin_registrar_private.h index 712f32a3d98c3..52058c3ce930f 100644 --- a/shell/platform/linux/fl_plugin_registrar_private.h +++ b/shell/platform/linux/fl_plugin_registrar_private.h @@ -15,13 +15,16 @@ G_BEGIN_DECLS * @view: (allow-none): the #FlView that is being plugged into or %NULL for * headless mode. * @messenger: the #FlBinaryMessenger to communicate with. + * @texture_registrar: the #FlTextureRegistrar to communicate with. * * Creates a new #FlPluginRegistrar. * * Returns: a new #FlPluginRegistrar. */ -FlPluginRegistrar* fl_plugin_registrar_new(FlView* view, - FlBinaryMessenger* messenger); +FlPluginRegistrar* fl_plugin_registrar_new( + FlView* view, + FlBinaryMessenger* messenger, + FlTextureRegistrar* texture_registrar); G_END_DECLS diff --git a/shell/platform/linux/fl_texture.cc b/shell/platform/linux/fl_texture.cc new file mode 100644 index 0000000000000..ff26adc31bda3 --- /dev/null +++ b/shell/platform/linux/fl_texture.cc @@ -0,0 +1,21 @@ +// 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 "flutter/shell/platform/linux/public/flutter_linux/fl_texture.h" +#include "flutter/shell/platform/linux/fl_texture_private.h" + +#include +#include + +// Added here to stop the compiler from optimising this function away. +G_MODULE_EXPORT GType fl_texture_get_type(); + +G_DEFINE_INTERFACE(FlTexture, fl_texture, G_TYPE_OBJECT) + +static void fl_texture_default_init(FlTextureInterface* self) {} + +int64_t fl_texture_get_texture_id(FlTexture* self) { + g_return_val_if_fail(FL_IS_TEXTURE(self), -1); + return reinterpret_cast(self); +} diff --git a/shell/platform/linux/fl_texture_gl.cc b/shell/platform/linux/fl_texture_gl.cc new file mode 100644 index 0000000000000..c447ccc7eb9f1 --- /dev/null +++ b/shell/platform/linux/fl_texture_gl.cc @@ -0,0 +1,47 @@ +// 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 "flutter/shell/platform/linux/public/flutter_linux/fl_texture_gl.h" +#include "flutter/shell/platform/linux/fl_texture_gl_private.h" + +#include +#include +#include + +// Added here to stop the compiler from optimising this function away. +G_MODULE_EXPORT GType fl_texture_gl_get_type(); + +static void fl_texture_gl_texture_iface_init(FlTextureInterface* iface) {} + +G_DEFINE_TYPE_WITH_CODE(FlTextureGL, + fl_texture_gl, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(fl_texture_get_type(), + fl_texture_gl_texture_iface_init)) + +static void fl_texture_gl_class_init(FlTextureGLClass* klass) {} + +static void fl_texture_gl_init(FlTextureGL* self) {} + +gboolean fl_texture_gl_populate(FlTextureGL* self, + uint32_t width, + uint32_t height, + FlutterOpenGLTexture* opengl_texture, + GError** error) { + uint32_t target = 0, name = 0; + if (!FL_TEXTURE_GL_GET_CLASS(self)->populate(self, &target, &name, &width, + &height, error)) { + return FALSE; + } + + opengl_texture->target = target; + opengl_texture->name = name; + opengl_texture->format = GL_RGBA8; + opengl_texture->destruction_callback = nullptr; + opengl_texture->user_data = nullptr; + opengl_texture->width = width; + opengl_texture->height = height; + + return TRUE; +} diff --git a/shell/platform/linux/fl_texture_gl_private.h b/shell/platform/linux/fl_texture_gl_private.h new file mode 100644 index 0000000000000..42ac7fcda91a2 --- /dev/null +++ b/shell/platform/linux/fl_texture_gl_private.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_GL_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_GL_PRIVATE_H_ + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_gl.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" + +G_BEGIN_DECLS + +/** + * fl_texture_gl_populate: + * @texture: an #FlTextureGL. + * @width: width of the texture. + * @height: height of the texture. + * @opengl_texture: (out): return an #FlutterOpenGLTexture. + * @error: (allow-none): #GError location to store the error occurring, or + * %NULL to ignore. + * + * Attempts to populate the specified @opengl_texture with texture details + * such as the name, width, height and the pixel format. + * + * Returns: %TRUE on success. + */ +gboolean fl_texture_gl_populate(FlTextureGL* texture, + uint32_t width, + uint32_t height, + FlutterOpenGLTexture* opengl_texture, + GError** error); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_GL_PRIVATE_H_ diff --git a/shell/platform/linux/fl_texture_gl_test.cc b/shell/platform/linux/fl_texture_gl_test.cc new file mode 100644 index 0000000000000..a1edc88b0ad63 --- /dev/null +++ b/shell/platform/linux/fl_texture_gl_test.cc @@ -0,0 +1,79 @@ +// 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 "flutter/shell/platform/linux/fl_texture_gl_private.h" +#include "flutter/shell/platform/linux/fl_texture_private.h" +#include "flutter/shell/platform/linux/fl_texture_registrar_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "gtest/gtest.h" + +#include + +static constexpr uint32_t BUFFER_WIDTH = 4u; +static constexpr uint32_t BUFFER_HEIGHT = 4u; +static constexpr uint32_t REAL_BUFFER_WIDTH = 2u; +static constexpr uint32_t REAL_BUFFER_HEIGHT = 2u; + +G_DECLARE_FINAL_TYPE(FlTestTexture, + fl_test_texture, + FL, + TEST_TEXTURE, + FlTextureGL) + +/// A simple texture. +struct _FlTestTexture { + FlTextureGL parent_instance; +}; + +G_DEFINE_TYPE(FlTestTexture, fl_test_texture, fl_texture_gl_get_type()) + +static gboolean fl_test_texture_populate(FlTextureGL* texture, + uint32_t* target, + uint32_t* name, + uint32_t* width, + uint32_t* height, + GError** error) { + EXPECT_TRUE(FL_IS_TEST_TEXTURE(texture)); + + EXPECT_EQ(*width, BUFFER_WIDTH); + EXPECT_EQ(*height, BUFFER_HEIGHT); + *target = GL_TEXTURE_2D; + *name = 1; + *width = REAL_BUFFER_WIDTH; + *height = REAL_BUFFER_HEIGHT; + + return TRUE; +} + +static void fl_test_texture_class_init(FlTestTextureClass* klass) { + FL_TEXTURE_GL_CLASS(klass)->populate = fl_test_texture_populate; +} + +static void fl_test_texture_init(FlTestTexture* self) {} + +static FlTestTexture* fl_test_texture_new() { + return FL_TEST_TEXTURE(g_object_new(fl_test_texture_get_type(), nullptr)); +} + +// Test that getting the texture ID works. +TEST(FlTextureTest, TextureID) { + // Texture ID is not assigned until the testure is populated. + g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_texture_new()); + EXPECT_EQ(fl_texture_get_texture_id(texture), + reinterpret_cast(texture)); +} + +// Test that populating an OpenGL texture works. +TEST(FlTextureTest, PopulateTexture) { + g_autoptr(FlTextureGL) texture = FL_TEXTURE_GL(fl_test_texture_new()); + FlutterOpenGLTexture opengl_texture = {0}; + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_texture_gl_populate(texture, BUFFER_WIDTH, BUFFER_HEIGHT, + &opengl_texture, &error)); + EXPECT_EQ(error, nullptr); + EXPECT_EQ(opengl_texture.width, REAL_BUFFER_WIDTH); + EXPECT_EQ(opengl_texture.height, REAL_BUFFER_HEIGHT); +} diff --git a/shell/platform/linux/fl_texture_private.h b/shell/platform/linux/fl_texture_private.h new file mode 100644 index 0000000000000..0bf304eb524d7 --- /dev/null +++ b/shell/platform/linux/fl_texture_private.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_PRIVATE_H_ + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture.h" + +G_BEGIN_DECLS + +/** + * fl_texture_get_texture_id: + * @texture: an #FlTexture. + * + * Retrieves the unique id of this texture. + * + * Returns: the unique id of this texture. + */ +int64_t fl_texture_get_texture_id(FlTexture* texture); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_PRIVATE_H_ diff --git a/shell/platform/linux/fl_texture_registrar.cc b/shell/platform/linux/fl_texture_registrar.cc new file mode 100644 index 0000000000000..70c6e59cbd198 --- /dev/null +++ b/shell/platform/linux/fl_texture_registrar.cc @@ -0,0 +1,168 @@ +// 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 "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_pixel_buffer_texture_private.h" +#include "flutter/shell/platform/linux/fl_texture_gl_private.h" +#include "flutter/shell/platform/linux/fl_texture_private.h" +#include "flutter/shell/platform/linux/fl_texture_registrar_private.h" + +struct _FlTextureRegistrar { + GObject parent_instance; + + // Weak reference to the engine this texture registrar is created for. + FlEngine* engine; + + // Internal record for registered textures. + // + // It is a map from Flutter texture ID to #FlTexture instance created by + // plugins. The keys are directly stored int64s. The values are stored + // pointer to #FlTexture. This table is freed by the responder. + GHashTable* textures; +}; + +G_DEFINE_TYPE(FlTextureRegistrar, fl_texture_registrar, G_TYPE_OBJECT) + +static void engine_weak_notify_cb(gpointer user_data, + GObject* where_the_object_was) { + FlTextureRegistrar* self = FL_TEXTURE_REGISTRAR(user_data); + self->engine = nullptr; + + // Unregister any textures. + g_autoptr(GHashTable) textures = self->textures; + self->textures = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, + g_object_unref); + g_hash_table_remove_all(textures); +} + +static void fl_texture_registrar_dispose(GObject* object) { + FlTextureRegistrar* self = FL_TEXTURE_REGISTRAR(object); + + g_clear_pointer(&self->textures, g_hash_table_unref); + + if (self->engine != nullptr) { + g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self); + self->engine = nullptr; + } + + G_OBJECT_CLASS(fl_texture_registrar_parent_class)->dispose(object); +} + +static void fl_texture_registrar_class_init(FlTextureRegistrarClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_texture_registrar_dispose; +} + +static void fl_texture_registrar_init(FlTextureRegistrar* self) { + self->textures = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, + g_object_unref); +} + +G_MODULE_EXPORT gboolean +fl_texture_registrar_register_texture(FlTextureRegistrar* self, + FlTexture* texture) { + g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(self), FALSE); + g_return_val_if_fail(FL_IS_TEXTURE(texture), FALSE); + + if (FL_IS_TEXTURE_GL(texture) || FL_IS_PIXEL_BUFFER_TEXTURE(texture)) { + g_hash_table_insert(self->textures, + GINT_TO_POINTER(fl_texture_get_texture_id(texture)), + g_object_ref(texture)); + + if (self->engine == nullptr) { + return FALSE; + } + + return fl_engine_register_external_texture( + self->engine, fl_texture_get_texture_id(texture)); + } else { + // We currently only support #FlTextureGL and #FlPixelBufferTexture. + return FALSE; + } +} + +G_MODULE_EXPORT gboolean +fl_texture_registrar_mark_texture_frame_available(FlTextureRegistrar* self, + FlTexture* texture) { + g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(self), FALSE); + + if (self->engine == nullptr) { + return FALSE; + } + + if (fl_texture_registrar_get_texture( + self, fl_texture_get_texture_id(texture)) == nullptr) { + g_warning("Unregistered texture %p", texture); + return FALSE; + } + + return fl_engine_mark_texture_frame_available( + self->engine, fl_texture_get_texture_id(texture)); +} + +gboolean fl_texture_registrar_populate_gl_external_texture( + FlTextureRegistrar* self, + int64_t texture_id, + uint32_t width, + uint32_t height, + FlutterOpenGLTexture* opengl_texture, + GError** error) { + FlTexture* texture = fl_texture_registrar_get_texture(self, texture_id); + if (texture == nullptr) { + g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED, + "Unable to find texture %" G_GINT64_FORMAT, texture_id); + return FALSE; + } + if (FL_IS_TEXTURE_GL(texture)) { + return fl_texture_gl_populate(FL_TEXTURE_GL(texture), width, height, + opengl_texture, error); + } else if (FL_IS_PIXEL_BUFFER_TEXTURE(texture)) { + return fl_pixel_buffer_texture_populate( + FL_PIXEL_BUFFER_TEXTURE(texture), width, height, opengl_texture, error); + } else { + g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED, + "Unsupported texture type %" G_GINT64_FORMAT, texture_id); + return FALSE; + } +} + +G_MODULE_EXPORT gboolean +fl_texture_registrar_unregister_texture(FlTextureRegistrar* self, + FlTexture* texture) { + g_return_val_if_fail(FL_IS_TEXTURE_REGISTRAR(self), FALSE); + + if (!g_hash_table_remove( + self->textures, + GINT_TO_POINTER(fl_texture_get_texture_id(texture)))) { + g_warning("Unregistering a non-existent texture %p", texture); + return FALSE; + } + + if (self->engine == nullptr) { + return FALSE; + } + + return fl_engine_unregister_external_texture( + self->engine, fl_texture_get_texture_id(texture)); +} + +FlTexture* fl_texture_registrar_get_texture(FlTextureRegistrar* registrar, + int64_t texture_id) { + return reinterpret_cast( + g_hash_table_lookup(registrar->textures, GINT_TO_POINTER(texture_id))); +} + +FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine) { + FlTextureRegistrar* self = FL_TEXTURE_REGISTRAR( + g_object_new(fl_texture_registrar_get_type(), nullptr)); + + self->engine = engine; + g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self); + + return self; +} diff --git a/shell/platform/linux/fl_texture_registrar_private.h b/shell/platform/linux/fl_texture_registrar_private.h new file mode 100644 index 0000000000000..b4ea9b2119219 --- /dev/null +++ b/shell/platform/linux/fl_texture_registrar_private.h @@ -0,0 +1,60 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_PRIVATE_H_ + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" + +G_BEGIN_DECLS + +/** + * fl_texture_registrar_new: + * @engine: an #FlEngine. + * + * Creates a new #FlTextureRegistrar. + * + * Returns: a new #FlTextureRegistrar. + */ +FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine); + +/** + * fl_texture_registrar_populate_gl_external_texture: + * @registrar: an #FlTextureRegistrar. + * @texture_id: ID of texture. + * @width: width of the texture. + * @height: height of the texture. + * @opengl_texture: (out): return an #FlutterOpenGLTexture. + * @error: (allow-none): #GError location to store the error occurring, or + * %NULL to ignore. + * + * Attempts to populate the given @texture_id. + * + * Returns: %TRUE on success. + */ +gboolean fl_texture_registrar_populate_gl_external_texture( + FlTextureRegistrar* registrar, + int64_t texture_id, + uint32_t width, + uint32_t height, + FlutterOpenGLTexture* opengl_texture, + GError** error); + +/** + * fl_texture_registrar_get_texture: + * @registrar: an #FlTextureRegistrar. + * @texture_id: ID of texture. + * + * Gets a registered texture by @texture_id. + * + * Returns: an #FlTexture, or %NULL if not found. + */ +FlTexture* fl_texture_registrar_get_texture(FlTextureRegistrar* registrar, + int64_t texture_id); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_PRIVATE_H_ 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..6d9845ee59602 --- /dev/null +++ b/shell/platform/linux/fl_texture_registrar_test.cc @@ -0,0 +1,105 @@ +// 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 "flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h" +#include "flutter/shell/platform/linux/fl_texture_private.h" +#include "flutter/shell/platform/linux/fl_texture_registrar_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_texture_gl.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "gtest/gtest.h" + +#include + +#include + +static constexpr uint32_t BUFFER_WIDTH = 4u; +static constexpr uint32_t BUFFER_HEIGHT = 4u; +static constexpr uint32_t REAL_BUFFER_WIDTH = 2u; +static constexpr uint32_t REAL_BUFFER_HEIGHT = 2u; + +G_DECLARE_FINAL_TYPE(FlTestRegistrarTexture, + fl_test_registrar_texture, + FL, + TEST_REGISTRAR_TEXTURE, + FlTextureGL) + +/// A simple texture. +struct _FlTestRegistrarTexture { + FlTextureGL parent_instance; +}; + +G_DEFINE_TYPE(FlTestRegistrarTexture, + fl_test_registrar_texture, + fl_texture_gl_get_type()) + +static gboolean fl_test_registrar_texture_populate(FlTextureGL* texture, + uint32_t* target, + uint32_t* format, + uint32_t* width, + uint32_t* height, + GError** error) { + EXPECT_TRUE(FL_IS_TEST_REGISTRAR_TEXTURE(texture)); + + EXPECT_EQ(*width, BUFFER_WIDTH); + EXPECT_EQ(*height, BUFFER_HEIGHT); + *target = GL_TEXTURE_2D; + *format = GL_R8; + *width = REAL_BUFFER_WIDTH; + *height = REAL_BUFFER_HEIGHT; + + return TRUE; +} + +static void fl_test_registrar_texture_class_init( + FlTestRegistrarTextureClass* klass) { + FL_TEXTURE_GL_CLASS(klass)->populate = fl_test_registrar_texture_populate; +} + +static void fl_test_registrar_texture_init(FlTestRegistrarTexture* self) {} + +static FlTestRegistrarTexture* fl_test_registrar_texture_new() { + return FL_TEST_REGISTRAR_TEXTURE( + g_object_new(fl_test_registrar_texture_get_type(), nullptr)); +} + +// Test that registering a texture works. +TEST(FlTextureRegistrarTest, RegisterTexture) { + g_autoptr(FlEngine) engine = make_mock_engine(); + g_autoptr(FlTextureRegistrar) registrar = fl_texture_registrar_new(engine); + g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_registrar_texture_new()); + + EXPECT_FALSE(fl_texture_registrar_unregister_texture(registrar, texture)); + EXPECT_TRUE(fl_texture_registrar_register_texture(registrar, texture)); + EXPECT_TRUE(fl_texture_registrar_unregister_texture(registrar, texture)); +} + +// Test that marking a texture frame available works. +TEST(FlTextureRegistrarTest, MarkTextureFrameAvailable) { + g_autoptr(FlEngine) engine = make_mock_engine(); + g_autoptr(FlTextureRegistrar) registrar = fl_texture_registrar_new(engine); + g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_registrar_texture_new()); + + EXPECT_FALSE( + fl_texture_registrar_mark_texture_frame_available(registrar, texture)); + EXPECT_TRUE(fl_texture_registrar_register_texture(registrar, texture)); + EXPECT_TRUE( + fl_texture_registrar_mark_texture_frame_available(registrar, texture)); +} + +// Test that populating an OpenGL texture works. +TEST(FlTextureRegistrarTest, PopulateTexture) { + g_autoptr(FlEngine) engine = make_mock_engine(); + g_autoptr(FlTextureRegistrar) registrar = fl_texture_registrar_new(engine); + g_autoptr(FlTexture) texture = FL_TEXTURE(fl_test_registrar_texture_new()); + EXPECT_TRUE(fl_texture_registrar_register_texture(registrar, texture)); + FlutterOpenGLTexture opengl_texture; + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_texture_registrar_populate_gl_external_texture( + registrar, fl_texture_get_texture_id(texture), BUFFER_WIDTH, + BUFFER_HEIGHT, &opengl_texture, &error)); + EXPECT_EQ(error, nullptr); + EXPECT_EQ(opengl_texture.width, REAL_BUFFER_WIDTH); + EXPECT_EQ(opengl_texture.height, REAL_BUFFER_HEIGHT); +} diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index d17a9ee6d6a9d..01d25ea0cf764 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -153,7 +153,8 @@ static FlPluginRegistrar* fl_view_get_registrar_for_plugin( FlView* self = FL_VIEW(registry); return fl_plugin_registrar_new(self, - fl_engine_get_binary_messenger(self->engine)); + fl_engine_get_binary_messenger(self->engine), + fl_engine_get_texture_registrar(self->engine)); } static void fl_view_plugin_registry_iface_init( diff --git a/shell/platform/linux/public/flutter_linux/fl_engine.h b/shell/platform/linux/public/flutter_linux/fl_engine.h index 201c38f4a06d2..6740c0bd8cd81 100644 --- a/shell/platform/linux/public/flutter_linux/fl_engine.h +++ b/shell/platform/linux/public/flutter_linux/fl_engine.h @@ -13,6 +13,7 @@ #include "fl_binary_messenger.h" #include "fl_dart_project.h" +#include "fl_texture_registrar.h" G_BEGIN_DECLS @@ -44,6 +45,16 @@ FlEngine* fl_engine_new_headless(FlDartProject* project); */ FlBinaryMessenger* fl_engine_get_binary_messenger(FlEngine* engine); +/** + * fl_engine_get_texture_registrar: + * @engine: an #FlEngine. + * + * Gets the texture registrar for registering textures. + * + * Returns: an #FlTextureRegistrar. + */ +FlTextureRegistrar* fl_engine_get_texture_registrar(FlEngine* engine); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h b/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h new file mode 100644 index 0000000000000..adddd7728c5d4 --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_pixel_buffer_texture.h @@ -0,0 +1,107 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_PIXEL_BUFFER_TEXTURE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_PIXEL_BUFFER_TEXTURE_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include "fl_texture.h" + +G_BEGIN_DECLS + +G_DECLARE_DERIVABLE_TYPE(FlPixelBufferTexture, + fl_pixel_buffer_texture, + FL, + PIXEL_BUFFER_TEXTURE, + GObject) + +/** + * FlPixelBufferTexture: + * + * #FlPixelBufferTexture represents an OpenGL texture generated from a pixel + * buffer. + * + * The following example shows how to implement an #FlPixelBufferTexture. + * ![ + * // Type definition, constructor, init, destructor and class_init are + * // omitted. + * struct _VideoPixelBufferTexture { // extends FlPixelBufferTexture + * FlPixelBufferTexture parent_instance; + * + * uint8_t *buffer; // your pixel buffer. + * } + * + * G_DEFINE_TYPE(VideoTexture, + * video_texture, + * fl_pixel_buffer_texture_get_type ()) + * + * static gboolean + * video_texture_copy_pixels (FlPixelBufferTexture* texture, + * const uint8_t** out_buffer, + * uint32_t* width, + * uint32_t* height, + * GError** error) { + * // This method is called on Render Thread. Be careful with your + * // cross-thread operation. + * + * // @width and @height are initially stored the canvas size in Flutter. + * + * // You must prepare your pixel buffer in RGBA format. + * // So you may do some format conversion first if your original pixel + * // buffer is not in RGBA format. + * manage_your_pixel_buffer_here (); + * + * if (your_operations_are_successfully_finished) { + * // Directly return pointer to your pixel buffer here. + * // Flutter takes content of your pixel buffer after this function + * // is finished. So you must make the buffer live long enough until + * // next tick of Render Thread. + * // If it is hard to manage lifetime of your pixel buffer, you should + * // take look into #FlTextureGL. + * + * *out_buffer = buffer; + * *width = real_width_of_buffer; + * *height = real_height_of_buffer; + * return TRUE; + * } else { + * // set @error to report failure. + * return FALSE; + * } + * } + * ]| + */ + +struct _FlPixelBufferTextureClass { + GObjectClass parent_class; + + /** + * FlPixelBufferTexture::copy_pixels: + * @texture: an #FlPixelBufferTexture. + * @buffer: (out): pixel data. + * @width: (inout): width of the texture in pixels. + * @height: (inout): height of the texture in pixels. + * @error: (allow-none): #GError location to store the error occurring, or + * %NULL to ignore. + * + * Retrieve pixel buffer in RGBA format. + * + * As this method is usually invoked from the render thread, you must + * take care of proper synchronization. It also needs to be ensured that + * the returned buffer is not released prior to unregistering this texture. + * + * Returns: %TRUE on success. + */ + gboolean (*copy_pixels)(FlPixelBufferTexture* texture, + const uint8_t** buffer, + uint32_t* width, + uint32_t* height, + GError** error); +}; + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PIXEL_BUFFER_TEXTURE_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h index 5ac073c1844ae..5ccd74752fa14 100644 --- a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h +++ b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h @@ -12,6 +12,7 @@ #include #include "fl_binary_messenger.h" +#include "fl_texture_registrar.h" #include "fl_view.h" G_BEGIN_DECLS @@ -39,6 +40,17 @@ G_DECLARE_FINAL_TYPE(FlPluginRegistrar, FlBinaryMessenger* fl_plugin_registrar_get_messenger( FlPluginRegistrar* registrar); +/** + * fl_plugin_registrar_get_texture_registrar: + * @registrar: an #FlPluginRegistrar. + * + * Gets the texture registrar this plugin can communicate with. + * + * Returns: an #FlTextureRegistrar. + */ +FlTextureRegistrar* fl_plugin_registrar_get_texture_registrar( + FlPluginRegistrar* registrar); + /** * fl_plugin_registrar_get_view: * @registrar: an #FlPluginRegistrar. diff --git a/shell/platform/linux/public/flutter_linux/fl_texture.h b/shell/platform/linux/public/flutter_linux/fl_texture.h new file mode 100644 index 0000000000000..45ceb514785ba --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_texture.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +G_DECLARE_INTERFACE(FlTexture, fl_texture, FL, TEXTURE, GObject) + +/** + * FlTexture: + * + * #FlTexture represents a texture. + * + * You can derive #FlTextureGL for populating hardware-accelerated textures or + * instantiate #FlPixelBufferTexture for populating pixel buffers. Do NOT + * directly implement this interface. + */ + +struct _FlTextureInterface { + GTypeInterface g_iface; +}; + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_texture_gl.h b/shell/platform/linux/public/flutter_linux/fl_texture_gl.h new file mode 100644 index 0000000000000..949f4afba5ac1 --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_texture_gl.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_GL_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_GL_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include "fl_texture.h" + +G_BEGIN_DECLS + +G_DECLARE_DERIVABLE_TYPE(FlTextureGL, fl_texture_gl, FL, TEXTURE_GL, GObject) + +/** + * FlTextureGL: + * + * #FlTextureGL is an abstract class that represents an OpenGL texture. + * + * If you want to render textures in other OpenGL context, create and use the + * #GdkGLContext by calling gdk_window_create_gl_context () with the #GdkWindow + * of #FlView. The context will be shared with the one used by Flutter. + * + * The following example shows how to implement an #FlTextureGL. + * ![ + * #include + * + * // Type definition, constructor, init, destructor, and class_init are + * // omitted. + * struct _VideoTextureGL { // extends FlTextureGL + * FlTextureGL parent_instance; + * + * GLuint texture_id; + * }; + * + * G_DEFINE_TYPE(VideoTexture, + * video_texture, + * fl_texture_gl_get_type ()) + * + * static gboolean + * video_texture_populate (FlTextureGL *texture, + * uint32_t *target, + * uint32_t *name, + * uint32_t *width, + * uint32_t *height, + * GError **error) { + * VideoTextureGL *self = VIDEO_TEXTURE_GL (texture); + * if (self->texture_id == 0) { + * glGenTextures (1, &self->texture_id); + * glBindTexture (GL_TEXTURE_2D, self->texture_id); + * // further configuration here. + * } else { + * glBindTexture (GL_TEXTURE_2D, self->texture_id); + * } + * + * // For example, we render pixel buffer here. + * // Note that Flutter only accepts textures in GL_RGBA8 format. + * static char buffer[] = { 0x1f, 0x2f, 0x3f, 0x4f }; // 1x1 pixel. + * glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, + * GL_UNSIGNED_BYTE, buffer); + * + * *target = GL_TEXTURE_2D; + * *name = self->texture_id; + * *width = 1; + * *height = 1; + * + * return TRUE; + * } + * ]| + */ + +struct _FlTextureGLClass { + GObjectClass parent_class; + + /** + * Virtual method called when Flutter populates this texture. The OpenGL + * context used by Flutter has been already set. + * @texture: an #FlTexture. + * @target: texture target (example GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE). + * @name: (out): name of texture. + * @width: (inout): width of the texture in pixels. + * @height: (inout): height of the texture in pixels. + * @error: (allow-none): #GError location to store the error occurring, or + * %NULL to ignore. + * + * Returns: %TRUE on success. + */ + gboolean (*populate)(FlTextureGL* texture, + uint32_t* target, + uint32_t* name, + uint32_t* width, + uint32_t* height, + GError** error); +}; + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h b/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h new file mode 100644 index 0000000000000..b978559a80b38 --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h @@ -0,0 +1,76 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +#include "fl_texture.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlTextureRegistrar, + fl_texture_registrar, + FL, + TEXTURE_REGISTRAR, + GObject) + +/** + * FlTextureRegistrar: + * + * #FlTextureRegistrar is used when registering textures. + * + * Flutter Framework accesses your texture by the related unique texture ID. To + * draw your texture in Dart, you should add Texture widget in your widget tree + * with the same texture ID. Use platform channels to send this unique texture + * ID to the Dart side. + */ + +/** + * fl_texture_registrar_register_texture: + * @registrar: an #FlTextureRegistrar. + * @texture: an #FlTexture for registration. + * + * Registers a texture. + * + * Returns: %TRUE on success. + */ +gboolean fl_texture_registrar_register_texture(FlTextureRegistrar* registrar, + FlTexture* texture); + +/** + * fl_texture_registrar_mark_texture_frame_available: + * @registrar: an #FlTextureRegistrar. + * @texture: the texture that has a frame available. + * + * Notifies the flutter engine that the texture object has updated and needs to + * be rerendered. + * + * Returns: %TRUE on success. + */ +gboolean fl_texture_registrar_mark_texture_frame_available( + FlTextureRegistrar* registrar, + FlTexture* texture); + +/** + * fl_texture_registrar_unregister_texture: + * @registrar: an #FlTextureRegistrar. + * @texture: the texture being unregistered. + * + * Unregisters an existing texture object. + * + * Returns: %TRUE on success. + */ +gboolean fl_texture_registrar_unregister_texture(FlTextureRegistrar* registrar, + FlTexture* texture); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_H_ diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h index f80bef47d730b..2d9317ddf5342 100644 --- a/shell/platform/linux/public/flutter_linux/flutter_linux.h +++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h @@ -20,11 +20,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include #include #include diff --git a/shell/platform/linux/testing/mock_engine.cc b/shell/platform/linux/testing/mock_engine.cc index fc748b69c128b..ccb1cb1c0c448 100644 --- a/shell/platform/linux/testing/mock_engine.cc +++ b/shell/platform/linux/testing/mock_engine.cc @@ -10,6 +10,7 @@ // Over time existing tests should be migrated and this file should be removed. #include +#include #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/fl_method_codec_private.h" @@ -20,11 +21,16 @@ const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1; +struct _FlutterEngineTexture { + bool has_new_frame; +}; + 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, @@ -469,6 +475,33 @@ FlutterEngineResult FlutterEngineDispatchSemanticsAction( return kSuccess; } +FlutterEngineResult FlutterEngineRegisterExternalTexture( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + int64_t texture_identifier) { + _FlutterEngineTexture texture; + texture.has_new_frame = 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.has_new_frame = true; + return kSuccess; +} + +FlutterEngineResult FlutterEngineUnregisterExternalTexture( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + int64_t texture_identifier) { + engine->textures.erase(texture_identifier); + return kSuccess; +} + } // namespace FlutterEngineResult FlutterEngineGetProcAddresses( @@ -502,6 +535,9 @@ FlutterEngineResult FlutterEngineGetProcAddresses( table->UpdateSemanticsEnabled = &FlutterEngineUpdateSemanticsEnabled; table->DispatchSemanticsAction = &FlutterEngineDispatchSemanticsAction; table->RunsAOTCompiledDartCode = &FlutterEngineRunsAOTCompiledDartCode; - + table->RegisterExternalTexture = &FlutterEngineRegisterExternalTexture; + table->MarkExternalTextureFrameAvailable = + &FlutterEngineMarkExternalTextureFrameAvailable; + table->UnregisterExternalTexture = &FlutterEngineUnregisterExternalTexture; return kSuccess; } diff --git a/shell/platform/linux/testing/mock_epoxy.cc b/shell/platform/linux/testing/mock_epoxy.cc index 329b598f39284..91a3cca60b5db 100644 --- a/shell/platform/linux/testing/mock_epoxy.cc +++ b/shell/platform/linux/testing/mock_epoxy.cc @@ -374,6 +374,10 @@ static void _glTexImage2D(GLenum target, GLenum type, const void* pixels) {} +static GLenum _glGetError() { + return GL_NO_ERROR; +} + bool epoxy_has_gl_extension(const char* extension) { return false; } @@ -458,6 +462,7 @@ void (*epoxy_glTexImage2D)(GLenum target, GLenum format, GLenum type, const void* pixels); +GLenum (*epoxy_glGetError)(); static void library_init() { epoxy_eglBindAPI = _eglBindAPI; @@ -483,4 +488,5 @@ static void library_init() { epoxy_glTexParameterf = _glTexParameterf; epoxy_glTexParameteri = _glTexParameteri; epoxy_glTexImage2D = _glTexImage2D; + epoxy_glGetError = _glGetError; }