diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter old mode 100755 new mode 100644 index 31c79959f78d8..a95048e53fe68 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -897,6 +897,7 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_codec_serializer.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/texture_registrar.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_result_functions_unittests.cc @@ -905,6 +906,8 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_codec.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/texture_registrar_impl.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/texture_registrar_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/engine_switches.cc FILE: ../../../flutter/shell/platform/common/cpp/engine_switches.h FILE: ../../../flutter/shell/platform/common/cpp/engine_switches_unittests.cc @@ -1432,6 +1435,8 @@ FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plu FILE: ../../../flutter/shell/platform/windows/client_wrapper/plugin_registrar_windows_unittests.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.h +FILE: ../../../flutter/shell/platform/windows/external_texture_gl.cc +FILE: ../../../flutter/shell/platform/windows/external_texture_gl.h FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.cc FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.h FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle_unittests.cc @@ -1439,6 +1444,9 @@ FILE: ../../../flutter/shell/platform/windows/flutter_windows.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.h FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine_unittests.cc +FILE: ../../../flutter/shell/platform/windows/flutter_windows_texture_registrar.cc +FILE: ../../../flutter/shell/platform/windows/flutter_windows_texture_registrar.h +FILE: ../../../flutter/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_view.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_view.h FILE: ../../../flutter/shell/platform/windows/flutter_windows_win32.cc diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index 719029b63257b..96a1c34177066 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -51,6 +51,7 @@ executable("client_wrapper_unittests") { "standard_method_codec_unittests.cc", "testing/test_codec_extensions.cc", "testing/test_codec_extensions.h", + "texture_registrar_unittests.cc", ] deps = [ diff --git a/shell/platform/common/cpp/client_wrapper/core_implementations.cc b/shell/platform/common/cpp/client_wrapper/core_implementations.cc index 21136fdb40bb5..1412db7c939f2 100644 --- a/shell/platform/common/cpp/client_wrapper/core_implementations.cc +++ b/shell/platform/common/cpp/client_wrapper/core_implementations.cc @@ -15,9 +15,11 @@ #include #include +#include #include "binary_messenger_impl.h" #include "include/flutter/engine_method_result.h" +#include "texture_registrar_impl.h" namespace flutter { @@ -146,4 +148,44 @@ void ReplyManager::SendResponseData(const std::vector* data) { } // namespace internal +// ========== texture_registrar_impl.h ========== + +TextureRegistrarImpl::TextureRegistrarImpl( + FlutterDesktopTextureRegistrarRef texture_registrar_ref) + : texture_registrar_ref_(texture_registrar_ref) {} + +TextureRegistrarImpl::~TextureRegistrarImpl() = default; + +int64_t TextureRegistrarImpl::RegisterTexture(TextureVariant* texture) { + if (auto pixel_buffer_texture = std::get_if(texture)) { + FlutterDesktopTextureInfo info = {}; + info.type = kFlutterDesktopPixelBufferTexture; + info.pixel_buffer_config.user_data = pixel_buffer_texture; + info.pixel_buffer_config.callback = + [](size_t width, size_t height, + void* user_data) -> const FlutterDesktopPixelBuffer* { + auto texture = static_cast(user_data); + auto buffer = texture->CopyPixelBuffer(width, height); + return buffer; + }; + + int64_t texture_id = FlutterDesktopTextureRegistrarRegisterExternalTexture( + texture_registrar_ref_, &info); + return texture_id; + } + + std::cerr << "Attempting to register unknown texture variant." << std::endl; + return -1; +} // namespace flutter + +bool TextureRegistrarImpl::MarkTextureFrameAvailable(int64_t texture_id) { + return FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable( + texture_registrar_ref_, texture_id); +} + +bool TextureRegistrarImpl::UnregisterTexture(int64_t texture_id) { + return FlutterDesktopTextureRegistrarUnregisterExternalTexture( + texture_registrar_ref_, texture_id); +} + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni index 264bf774e6111..c2ee524e0f117 100644 --- a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni +++ b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni @@ -24,6 +24,7 @@ core_cpp_client_wrapper_includes = "include/flutter/standard_codec_serializer.h", "include/flutter/standard_message_codec.h", "include/flutter/standard_method_codec.h", + "include/flutter/texture_registrar.h", ], "abspath") @@ -34,6 +35,7 @@ core_cpp_client_wrapper_internal_headers = get_path_info([ "binary_messenger_impl.h", "byte_buffer_streams.h", + "texture_registrar_impl.h", ], "abspath") diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h index a01b75e8c9136..3c4afa0b121ab 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h @@ -13,6 +13,7 @@ #include #include "binary_messenger.h" +#include "texture_registrar.h" namespace flutter { @@ -41,6 +42,10 @@ class PluginRegistrar { // This pointer will remain valid for the lifetime of this instance. BinaryMessenger* messenger() { return messenger_.get(); } + // Returns the texture registrar to use for the plugin to render a pixel + // buffer. + TextureRegistrar* texture_registrar() { return texture_registrar_.get(); } + // Takes ownership of |plugin|. // // Plugins are not required to call this method if they have other lifetime @@ -62,6 +67,8 @@ class PluginRegistrar { std::unique_ptr messenger_; + std::unique_ptr texture_registrar_; + // Plugins registered for ownership. std::set> plugins_; }; diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/texture_registrar.h b/shell/platform/common/cpp/client_wrapper/include/flutter/texture_registrar.h new file mode 100644 index 0000000000000..77b1e167b6bcc --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/texture_registrar.h @@ -0,0 +1,75 @@ +// 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_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TEXTURE_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TEXTURE_REGISTRAR_H_ + +#include + +#include +#include +#include +#include + +namespace flutter { + +// A pixel buffer texture. +class PixelBufferTexture { + public: + // A callback used for retrieving pixel buffers. + typedef std::function + CopyBufferCallback; + + // Creates a pixel buffer texture that uses the provided |copy_buffer_cb| to + // retrieve the buffer. + // As the callback is usually invoked from the render thread, the callee must + // take care of proper synchronization. It also needs to be ensured that the + // returned buffer isn't released prior to unregistering this texture. + PixelBufferTexture(CopyBufferCallback copy_buffer_callback) + : copy_buffer_callback_(copy_buffer_callback) {} + + // Returns the callback-provided FlutterDesktopPixelBuffer that contains the + // actual pixel data. The intended surface size is specified by |width| and + // |height|. + const FlutterDesktopPixelBuffer* CopyPixelBuffer(size_t width, + size_t height) const { + return copy_buffer_callback_(width, height); + } + + private: + const CopyBufferCallback copy_buffer_callback_; +}; + +// The available texture variants. +// Only PixelBufferTexture is currently implemented. +// Other variants are expected to be added in the future. +typedef std::variant TextureVariant; + +// An object keeping track of external textures. +// +// Thread safety: +// It's safe to call the member methods from any thread. +class TextureRegistrar { + public: + virtual ~TextureRegistrar() = default; + + // Registers a |texture| object and returns the ID for that texture. + virtual int64_t RegisterTexture(TextureVariant* texture) = 0; + + // Notifies the flutter engine that the texture object corresponding + // to |texure_id| needs to render a new frame. + // + // For PixelBufferTextures, this will effectively make the engine invoke + // the callback that was provided upon creating the texture. + virtual bool MarkTextureFrameAvailable(int64_t texture_id) = 0; + + // Unregisters an existing Texture object. + // Textures must not be unregistered while they're in use. + virtual bool UnregisterTexture(int64_t texture_id) = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TEXTURE_REGISTRAR_H_ diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc index b5ab707c80aec..65fef5cccfa28 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc @@ -10,6 +10,7 @@ #include "binary_messenger_impl.h" #include "include/flutter/engine_method_result.h" #include "include/flutter/method_channel.h" +#include "texture_registrar_impl.h" namespace flutter { @@ -19,6 +20,11 @@ PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar) : registrar_(registrar) { auto core_messenger = FlutterDesktopPluginRegistrarGetMessenger(registrar_); messenger_ = std::make_unique(core_messenger); + + auto texture_registrar = + FlutterDesktopRegistrarGetTextureRegistrar(registrar_); + texture_registrar_ = + std::make_unique(texture_registrar); } PluginRegistrar::~PluginRegistrar() { diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc index b3a92aa67d714..c30e07a26183f 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc @@ -214,4 +214,15 @@ TEST(PluginRegistrarTest, ManagerRemovesOnDestruction) { nullptr); } +// Tests that the texture registrar getter returns a non-null TextureRegistrar +TEST(PluginRegistrarTest, TextureRegistrarNotNull) { + auto dummy_registrar_handle = + reinterpret_cast(1); + PluginRegistrar registrar(dummy_registrar_handle); + + TextureRegistrar* texture_registrar = registrar.texture_registrar(); + + ASSERT_NE(texture_registrar, nullptr); +} + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc index 749f99c02443c..265f85407edfc 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc @@ -92,3 +92,41 @@ void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger, s_stub_implementation->MessengerSetCallback(channel, callback, user_data); } } + +FlutterDesktopTextureRegistrarRef FlutterDesktopRegistrarGetTextureRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + return reinterpret_cast(1); +} + +int64_t FlutterDesktopTextureRegistrarRegisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + const FlutterDesktopTextureInfo* info) { + uint64_t result = -1; + if (s_stub_implementation) { + result = + s_stub_implementation->TextureRegistrarRegisterExternalTexture(info); + } + return result; +} + +bool FlutterDesktopTextureRegistrarUnregisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id) { + bool result = false; + if (s_stub_implementation) { + result = s_stub_implementation->TextureRegistrarUnregisterExternalTexture( + texture_id); + } + return result; +} + +bool FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id) { + bool result = false; + if (s_stub_implementation) { + result = s_stub_implementation->TextureRegistrarMarkTextureFrameAvailable( + texture_id); + } + return result; +} diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h index d7b966740fded..9549aa2aa0867 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h @@ -64,6 +64,22 @@ class StubFlutterApi { virtual void MessengerSetCallback(const char* channel, FlutterDesktopMessageCallback callback, void* user_data) {} + + // Called for FlutterDesktopRegisterExternalTexture. + virtual int64_t TextureRegistrarRegisterExternalTexture( + const FlutterDesktopTextureInfo* info) { + return -1; + } + + // Called for FlutterDesktopUnregisterExternalTexture. + virtual bool TextureRegistrarUnregisterExternalTexture(int64_t texture_id) { + return false; + } + + // Called for FlutterDesktopMarkExternalTextureFrameAvailable. + virtual bool TextureRegistrarMarkTextureFrameAvailable(int64_t texture_id) { + return false; + } }; // A test helper that owns a stub implementation, making it the test stub for diff --git a/shell/platform/common/cpp/client_wrapper/texture_registrar_impl.h b/shell/platform/common/cpp/client_wrapper/texture_registrar_impl.h new file mode 100644 index 0000000000000..128ed843d259e --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/texture_registrar_impl.h @@ -0,0 +1,40 @@ +// 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_COMMON_CPP_CLIENT_WRAPPER_TEXTURE_REGISTRAR_IMPL_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TEXTURE_REGISTRAR_IMPL_H_ + +#include "include/flutter/texture_registrar.h" + +namespace flutter { + +// Wrapper around a FlutterDesktopTextureRegistrarRef that implements the +// TextureRegistrar API. +class TextureRegistrarImpl : public TextureRegistrar { + public: + explicit TextureRegistrarImpl( + FlutterDesktopTextureRegistrarRef texture_registrar_ref); + virtual ~TextureRegistrarImpl(); + + // Prevent copying. + TextureRegistrarImpl(TextureRegistrarImpl const&) = delete; + TextureRegistrarImpl& operator=(TextureRegistrarImpl const&) = delete; + + // |flutter::TextureRegistrar| + int64_t RegisterTexture(TextureVariant* texture) override; + + // |flutter::TextureRegistrar| + bool MarkTextureFrameAvailable(int64_t texture_id) override; + + // |flutter::TextureRegistrar| + bool UnregisterTexture(int64_t texture_id) override; + + private: + // Handle for interacting with the C API. + FlutterDesktopTextureRegistrarRef texture_registrar_ref_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TEXTURE_REGISTRAR_IMPL_H_ diff --git a/shell/platform/common/cpp/client_wrapper/texture_registrar_unittests.cc b/shell/platform/common/cpp/client_wrapper/texture_registrar_unittests.cc new file mode 100644 index 0000000000000..6a0718462e5c9 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/texture_registrar_unittests.cc @@ -0,0 +1,146 @@ +// 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/common/cpp/client_wrapper/include/flutter/texture_registrar.h" + +#include +#include +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h" +#include "gtest/gtest.h" + +namespace flutter { + +namespace { + +// Stub implementation to validate calls to the API. +class TestApi : public testing::StubFlutterApi { + public: + struct FakePixelBufferTexture { + int64_t texture_id; + int32_t mark_count; + FlutterDesktopPixelBufferTextureCallback texture_callback; + void* user_data; + }; + + public: + int64_t TextureRegistrarRegisterExternalTexture( + const FlutterDesktopTextureInfo* info) override { + last_texture_id_++; + + auto texture = std::make_unique(); + texture->texture_callback = info->pixel_buffer_config.callback; + texture->user_data = info->pixel_buffer_config.user_data; + texture->mark_count = 0; + texture->texture_id = last_texture_id_; + + textures_[last_texture_id_] = std::move(texture); + return last_texture_id_; + } + + bool TextureRegistrarUnregisterExternalTexture(int64_t texture_id) override { + auto it = textures_.find(texture_id); + if (it != textures_.end()) { + textures_.erase(it); + return true; + } + return false; + } + + bool TextureRegistrarMarkTextureFrameAvailable(int64_t texture_id) override { + auto it = textures_.find(texture_id); + if (it != textures_.end()) { + it->second->mark_count++; + return true; + } + return false; + } + + FakePixelBufferTexture* GetFakeTexture(int64_t texture_id) { + auto it = textures_.find(texture_id); + if (it != textures_.end()) { + return it->second.get(); + } + return nullptr; + } + + int64_t last_texture_id() { return last_texture_id_; } + + size_t textures_size() { return textures_.size(); } + + private: + int64_t last_texture_id_ = -1; + std::map> textures_; +}; + +} // namespace + +// Tests thats textures can be registered and unregistered. +TEST(TextureRegistrarTest, RegisterUnregisterTexture) { + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + + auto dummy_registrar_handle = + reinterpret_cast(1); + PluginRegistrar registrar(dummy_registrar_handle); + TextureRegistrar* textures = registrar.texture_registrar(); + ASSERT_NE(textures, nullptr); + + EXPECT_EQ(test_api->last_texture_id(), -1); + auto texture = test_api->GetFakeTexture(0); + EXPECT_EQ(texture, nullptr); + + auto pixel_buffer_texture = std::make_unique( + PixelBufferTexture([](size_t width, size_t height) { return nullptr; })); + int64_t texture_id = textures->RegisterTexture(pixel_buffer_texture.get()); + EXPECT_EQ(test_api->last_texture_id(), texture_id); + EXPECT_EQ(test_api->textures_size(), static_cast(1)); + + texture = test_api->GetFakeTexture(texture_id); + EXPECT_EQ(texture->texture_id, texture_id); + EXPECT_EQ(texture->user_data, + std::get_if(pixel_buffer_texture.get())); + + textures->MarkTextureFrameAvailable(texture_id); + textures->MarkTextureFrameAvailable(texture_id); + bool success = textures->MarkTextureFrameAvailable(texture_id); + EXPECT_TRUE(success); + EXPECT_EQ(texture->mark_count, 3); + + success = textures->UnregisterTexture(texture_id); + EXPECT_TRUE(success); + + texture = test_api->GetFakeTexture(texture_id); + EXPECT_EQ(texture, nullptr); + EXPECT_EQ(test_api->textures_size(), static_cast(0)); +} + +// Tests that unregistering a texture with an unknown id returns false. +TEST(TextureRegistrarTest, UnregisterInvalidTexture) { + auto dummy_registrar_handle = + reinterpret_cast(1); + PluginRegistrar registrar(dummy_registrar_handle); + + TextureRegistrar* textures = registrar.texture_registrar(); + + bool success = textures->UnregisterTexture(42); + EXPECT_FALSE(success); +} + +// Tests that claiming a new frame being available for an unknown texture +// returns false. +TEST(TextureRegistrarTest, MarkFrameAvailableInvalidTexture) { + auto dummy_registrar_handle = + reinterpret_cast(1); + PluginRegistrar registrar(dummy_registrar_handle); + + TextureRegistrar* textures = registrar.texture_registrar(); + + bool success = textures->MarkTextureFrameAvailable(42); + EXPECT_FALSE(success); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/public/flutter_plugin_registrar.h b/shell/platform/common/cpp/public/flutter_plugin_registrar.h index 5287a00ec5c69..820b134177afd 100644 --- a/shell/platform/common/cpp/public/flutter_plugin_registrar.h +++ b/shell/platform/common/cpp/public/flutter_plugin_registrar.h @@ -10,6 +10,7 @@ #include "flutter_export.h" #include "flutter_messenger.h" +#include "flutter_texture_registrar.h" #if defined(__cplusplus) extern "C" { @@ -27,6 +28,11 @@ FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopPluginRegistrarGetMessenger( FlutterDesktopPluginRegistrarRef registrar); +// Returns the texture registrar associated with this registrar. +FLUTTER_EXPORT FlutterDesktopTextureRegistrarRef +FlutterDesktopRegistrarGetTextureRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + // Registers a callback to be called when the plugin registrar is destroyed. FLUTTER_EXPORT void FlutterDesktopPluginRegistrarSetDestructionHandler( FlutterDesktopPluginRegistrarRef registrar, diff --git a/shell/platform/common/cpp/public/flutter_texture_registrar.h b/shell/platform/common/cpp/public/flutter_texture_registrar.h index e7217c7d7b3fa..81219d64e662c 100644 --- a/shell/platform/common/cpp/public/flutter_texture_registrar.h +++ b/shell/platform/common/cpp/public/flutter_texture_registrar.h @@ -1,3 +1,94 @@ // 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_COMMON_CPP_PUBLIC_FLUTTER_TEXTURE_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_TEXTURE_REGISTRAR_H_ + +#include +#include + +#include "flutter_export.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +struct FlutterDesktopTextureRegistrar; +// Opaque reference to a texture registrar. +typedef struct FlutterDesktopTextureRegistrar* + FlutterDesktopTextureRegistrarRef; + +// Possible values for the type specified in FlutterDesktopTextureInfo. +// Additional types may be added in the future. +typedef enum { + // A Pixel buffer-based texture. + kFlutterDesktopPixelBufferTexture +} FlutterDesktopTextureType; + +// An image buffer object. +typedef struct { + // The pixel data buffer. + const uint8_t* buffer; + // Width of the pixel buffer. + size_t width; + // Height of the pixel buffer. + size_t height; +} FlutterDesktopPixelBuffer; + +// The pixel buffer copy callback definition provided to +// the Flutter engine to copy the texture. +// It is invoked with the intended surface size specified by |width| and +// |height| and the |user_data| held by FlutterDesktopPixelBufferTextureConfig. +// +// As this is usually called from the render thread, the callee must take +// care of proper synchronization. It also needs to be ensured that the +// returned FlutterDesktopPixelBuffer isn't released prior to unregistering +// the corresponding texture. +typedef const FlutterDesktopPixelBuffer* ( + *FlutterDesktopPixelBufferTextureCallback)(size_t width, + size_t height, + void* user_data); + +// An object used to configure pixel buffer textures. +typedef struct { + // The callback used by the engine to copy the pixel buffer object. + FlutterDesktopPixelBufferTextureCallback callback; + // Opaque data that will get passed to the provided |callback|. + void* user_data; +} FlutterDesktopPixelBufferTextureConfig; + +typedef struct { + FlutterDesktopTextureType type; + union { + FlutterDesktopPixelBufferTextureConfig pixel_buffer_config; + }; +} FlutterDesktopTextureInfo; + +// Registers a new texture with the Flutter engine and returns the texture ID. +// This function can be called from any thread. +FLUTTER_EXPORT int64_t FlutterDesktopTextureRegistrarRegisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + const FlutterDesktopTextureInfo* info); + +// Unregisters an existing texture from the Flutter engine for a |texture_id|. +// Returns true on success or false if the specified texture doesn't exist. +// This function can be called from any thread. +// However, textures must not be unregistered while they're in use. +FLUTTER_EXPORT bool FlutterDesktopTextureRegistrarUnregisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id); + +// Marks that a new texture frame is available for a given |texture_id|. +// Returns true on success or false if the specified texture doesn't exist. +// This function can be called from any thread. +FLUTTER_EXPORT bool +FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_TEXTURE_REGISTRAR_H_ diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index 423e5a80b6f31..c926e86ca549c 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -1072,3 +1072,30 @@ void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger, messenger->engine->message_dispatcher->SetMessageCallback(channel, callback, user_data); } + +FlutterDesktopTextureRegistrarRef FlutterDesktopRegistrarGetTextureRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + std::cerr << "GLFW Texture support is not implemented yet." << std::endl; + return nullptr; +} + +int64_t FlutterDesktopTextureRegistrarRegisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + const FlutterDesktopTextureInfo* texture_info) { + std::cerr << "GLFW Texture support is not implemented yet." << std::endl; + return -1; +} + +bool FlutterDesktopTextureRegistrarUnregisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id) { + std::cerr << "GLFW Texture support is not implemented yet." << std::endl; + return false; +} + +bool FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id) { + std::cerr << "GLFW Texture support is not implemented yet." << std::endl; + return false; +} diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 873125ad4ebef..6baf2996ce237 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -48,11 +48,15 @@ source_set("flutter_windows_source") { "angle_surface_manager.h", "cursor_handler.cc", "cursor_handler.h", + "external_texture_gl.cc", + "external_texture_gl.h", "flutter_project_bundle.cc", "flutter_project_bundle.h", "flutter_windows.cc", "flutter_windows_engine.cc", "flutter_windows_engine.h", + "flutter_windows_texture_registrar.cc", + "flutter_windows_texture_registrar.h", "flutter_windows_view.cc", "flutter_windows_view.h", "key_event_handler.cc", @@ -160,6 +164,7 @@ if (target_os == "winuwp") { sources = [ "flutter_project_bundle_unittests.cc", "flutter_windows_engine_unittests.cc", + "flutter_windows_texture_registrar_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", "testing/engine_embedder_api_modifier.h", diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index afe35b8f88911..1a292c04a41f1 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -127,6 +127,12 @@ FlutterDesktopMessengerRef FlutterDesktopEngineGetMessenger( return reinterpret_cast(2); } +FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar( + FlutterDesktopEngineRef engine) { + // The stub ignores this, so just return an arbitrary non-zero value. + return reinterpret_cast(3); +} + HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef controller) { if (s_stub_implementation) { return s_stub_implementation->ViewGetHWND(); diff --git a/shell/platform/windows/external_texture_gl.cc b/shell/platform/windows/external_texture_gl.cc new file mode 100644 index 0000000000000..50b9979c7b12e --- /dev/null +++ b/shell/platform/windows/external_texture_gl.cc @@ -0,0 +1,131 @@ +// 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/windows/external_texture_gl.h" + +#include +#include +#include + +namespace { + +typedef void (*glGenTexturesProc)(GLsizei n, GLuint* textures); +typedef void (*glDeleteTexturesProc)(GLsizei n, const GLuint* textures); +typedef void (*glBindTextureProc)(GLenum target, GLuint texture); +typedef void (*glTexParameteriProc)(GLenum target, GLenum pname, GLint param); +typedef void (*glTexImage2DProc)(GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void* data); + +// A struct containing pointers to resolved gl* functions. +struct GlProcs { + glGenTexturesProc glGenTextures; + glDeleteTexturesProc glDeleteTextures; + glBindTextureProc glBindTexture; + glTexParameteriProc glTexParameteri; + glTexImage2DProc glTexImage2D; + bool valid; +}; + +static const GlProcs& GlProcs() { + static struct GlProcs procs = {}; + static bool initialized = false; + if (!initialized) { + procs.glGenTextures = + reinterpret_cast(eglGetProcAddress("glGenTextures")); + procs.glDeleteTextures = reinterpret_cast( + eglGetProcAddress("glDeleteTextures")); + procs.glBindTexture = + reinterpret_cast(eglGetProcAddress("glBindTexture")); + procs.glTexParameteri = reinterpret_cast( + eglGetProcAddress("glTexParameteri")); + procs.glTexImage2D = + reinterpret_cast(eglGetProcAddress("glTexImage2D")); + + procs.valid = procs.glGenTextures && procs.glDeleteTextures && + procs.glBindTexture && procs.glTexParameteri && + procs.glTexImage2D; + initialized = true; + } + return procs; +} + +} // namespace + +namespace flutter { + +struct ExternalTextureGLState { + GLuint gl_texture = 0; +}; + +ExternalTextureGL::ExternalTextureGL( + FlutterDesktopPixelBufferTextureCallback texture_callback, + void* user_data) + : state_(std::make_unique()), + texture_callback_(texture_callback), + user_data_(user_data) {} + +ExternalTextureGL::~ExternalTextureGL() { + const auto& gl = GlProcs(); + if (gl.valid && state_->gl_texture != 0) { + gl.glDeleteTextures(1, &state_->gl_texture); + } +} + +bool ExternalTextureGL::PopulateTexture(size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture) { + if (!CopyPixelBuffer(width, height)) { + return false; + } + + // Populate the texture object used by the engine. + opengl_texture->target = GL_TEXTURE_2D; + opengl_texture->name = state_->gl_texture; + 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; +} + +bool ExternalTextureGL::CopyPixelBuffer(size_t& width, size_t& height) { + const FlutterDesktopPixelBuffer* pixel_buffer = + texture_callback_(width, height, user_data_); + const auto& gl = GlProcs(); + if (!gl.valid || !pixel_buffer || !pixel_buffer->buffer) { + return false; + } + width = pixel_buffer->width; + height = pixel_buffer->height; + + if (state_->gl_texture == 0) { + gl.glGenTextures(1, &state_->gl_texture); + + gl.glBindTexture(GL_TEXTURE_2D, state_->gl_texture); + + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + } else { + gl.glBindTexture(GL_TEXTURE_2D, state_->gl_texture); + } + gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pixel_buffer->width, + pixel_buffer->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, + pixel_buffer->buffer); + return true; +} + +} // namespace flutter diff --git a/shell/platform/windows/external_texture_gl.h b/shell/platform/windows/external_texture_gl.h new file mode 100644 index 0000000000000..a2e61f5e33ac4 --- /dev/null +++ b/shell/platform/windows/external_texture_gl.h @@ -0,0 +1,56 @@ +// 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_WINDOWS_EXTERNAL_TEXTURE_GL_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_GL_H_ + +#include + +#include + +#include "flutter/shell/platform/common/cpp/public/flutter_texture_registrar.h" +#include "flutter/shell/platform/embedder/embedder.h" + +namespace flutter { + +typedef struct ExternalTextureGLState ExternalTextureGLState; + +// An abstraction of an OpenGL texture. +class ExternalTextureGL { + public: + ExternalTextureGL(FlutterDesktopPixelBufferTextureCallback texture_callback, + void* user_data); + + virtual ~ExternalTextureGL(); + + // Returns the unique id of this texture. + int64_t texture_id() { return reinterpret_cast(this); } + + void MarkFrameAvailable(); + + // Attempts to populate the specified |opengl_texture| with texture details + // such as the name, width, height and the pixel format upon successfully + // copying the buffer provided by |texture_callback_|. See |CopyPixelBuffer|. + // Returns true on success or false if the pixel buffer could not be copied. + bool PopulateTexture(size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture); + + private: + // Attempts to copy the pixel buffer returned by |texture_callback_| to + // OpenGL. + // The |width| and |height| will be set to the actual bounds of the copied + // pixel buffer. + // Returns true on success or false if the pixel buffer returned + // by |texture_callback_| was invalid. + bool CopyPixelBuffer(size_t& width, size_t& height); + + std::unique_ptr state_; + FlutterDesktopPixelBufferTextureCallback texture_callback_ = nullptr; + void* user_data_ = nullptr; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_GL_H_ diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 177d103f7bc30..1c56965d88a5a 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -49,6 +49,18 @@ static FlutterDesktopViewRef HandleForView(flutter::FlutterWindowsView* view) { return reinterpret_cast(view); } +// Returns the texture registrar corresponding to the given opaque API handle. +static flutter::FlutterWindowsTextureRegistrar* TextureRegistrarFromHandle( + FlutterDesktopTextureRegistrarRef ref) { + return reinterpret_cast(ref); +} + +// Returns the opaque API handle for the given texture registrar instance. +static FlutterDesktopTextureRegistrarRef HandleForTextureRegistrar( + flutter::FlutterWindowsTextureRegistrar* registrar) { + return reinterpret_cast(registrar); +} + void FlutterDesktopViewControllerDestroy( FlutterDesktopViewControllerRef controller) { delete controller; @@ -105,6 +117,12 @@ FlutterDesktopMessengerRef FlutterDesktopEngineGetMessenger( return EngineFromHandle(engine)->messenger(); } +FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar( + FlutterDesktopEngineRef engine) { + return HandleForTextureRegistrar( + EngineFromHandle(engine)->texture_registrar()); +} + HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view) { return std::get(*ViewFromHandle(view)->GetRenderTarget()); } @@ -171,3 +189,29 @@ void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger, messenger->engine->message_dispatcher()->SetMessageCallback(channel, callback, user_data); } + +FlutterDesktopTextureRegistrarRef FlutterDesktopRegistrarGetTextureRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + return HandleForTextureRegistrar(registrar->engine->texture_registrar()); +} + +int64_t FlutterDesktopTextureRegistrarRegisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + const FlutterDesktopTextureInfo* texture_info) { + return TextureRegistrarFromHandle(texture_registrar) + ->RegisterTexture(texture_info); +} + +bool FlutterDesktopTextureRegistrarUnregisterExternalTexture( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id) { + return TextureRegistrarFromHandle(texture_registrar) + ->UnregisterTexture(texture_id); +} + +bool FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable( + FlutterDesktopTextureRegistrarRef texture_registrar, + int64_t texture_id) { + return TextureRegistrarFromHandle(texture_registrar) + ->MarkTextureFrameAvailable(texture_id); +} diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index a284baa6f9da9..8fcedb2a7ecdd 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -62,6 +62,16 @@ FlutterRendererConfig GetRendererConfig() { } return host->view()->MakeResourceCurrent(); }; + config.open_gl.gl_external_texture_frame_callback = + [](void* user_data, int64_t texture_id, size_t width, size_t height, + FlutterOpenGLTexture* texture) -> bool { + auto host = static_cast(user_data); + if (!host->texture_registrar()) { + return false; + } + return host->texture_registrar()->PopulateTexture(texture_id, width, height, + texture); + }; return config; } @@ -122,6 +132,7 @@ FlutterWindowsEngine::FlutterWindowsEngine(const FlutterProjectBundle& project) messenger_wrapper_ = std::make_unique(messenger_.get()); message_dispatcher_ = std::make_unique(messenger_.get()); + texture_registrar_ = std::make_unique(this); #ifndef WINUWP window_proc_delegate_manager_ = std::make_unique(); @@ -183,7 +194,8 @@ bool FlutterWindowsEngine::RunWithEntrypoint(const char* entrypoint) { platform_task_runner.post_task_callback = [](FlutterTask task, uint64_t target_time_nanos, void* user_data) -> void { - static_cast(user_data)->PostTask(task, target_time_nanos); + static_cast(user_data)->PostFlutterTask(task, + target_time_nanos); }; FlutterCustomTaskRunners custom_task_runners = {}; custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners); @@ -356,4 +368,20 @@ void FlutterWindowsEngine::SendSystemSettings() { settings_channel_->Send(settings); } +bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) { + return (embedder_api_.RegisterExternalTexture(engine_, texture_id) == + kSuccess); +} + +bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) { + return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) == + kSuccess); +} + +bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable( + int64_t texture_id) { + return (embedder_api_.MarkExternalTextureFrameAvailable( + engine_, texture_id) == kSuccess); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 86fe2c9a20978..5e2cac869c704 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -15,6 +15,7 @@ #include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/flutter_project_bundle.h" +#include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h" #include "flutter/shell/platform/windows/public/flutter_windows.h" #include "flutter/shell/platform/windows/task_runner.h" #include "flutter/shell/platform/windows/window_state.h" @@ -80,6 +81,10 @@ class FlutterWindowsEngine { TaskRunner* task_runner() { return task_runner_.get(); } + FlutterWindowsTextureRegistrar* texture_registrar() { + return texture_registrar_.get(); + } + #ifndef WINUWP Win32WindowProcDelegateManager* window_proc_delegate_manager() { return window_proc_delegate_manager_.get(); @@ -113,6 +118,16 @@ class FlutterWindowsEngine { // Informs the engine that the system font list has changed. void ReloadSystemFonts(); + // Attempts to register the texture with the given |texture_id|. + bool RegisterExternalTexture(int64_t texture_id); + + // Attempts to unregister the texture with the given |texture_id|. + bool UnregisterExternalTexture(int64_t texture_id); + + // Notifies the engine about a new frame being available for the + // given |texture_id|. + bool MarkExternalTextureFrameAvailable(int64_t texture_id); + private: // Allows swapping out embedder_api_ calls in tests. friend class EngineEmbedderApiModifier; @@ -151,6 +166,9 @@ class FlutterWindowsEngine { // The plugin registrar handle given to API clients. std::unique_ptr plugin_registrar_; + // The texture registrar. + std::unique_ptr texture_registrar_; + // The MethodChannel used for communication with the Flutter engine. std::unique_ptr> settings_channel_; diff --git a/shell/platform/windows/flutter_windows_texture_registrar.cc b/shell/platform/windows/flutter_windows_texture_registrar.cc new file mode 100644 index 0000000000000..f6c10db14b713 --- /dev/null +++ b/shell/platform/windows/flutter_windows_texture_registrar.cc @@ -0,0 +1,89 @@ +// 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/windows/flutter_windows_texture_registrar.h" + +#include "flutter/shell/platform/windows/flutter_windows_engine.h" + +#include +#include + +namespace flutter { + +FlutterWindowsTextureRegistrar::FlutterWindowsTextureRegistrar( + FlutterWindowsEngine* engine) + : engine_(engine) {} + +int64_t FlutterWindowsTextureRegistrar::RegisterTexture( + const FlutterDesktopTextureInfo* texture_info) { + if (texture_info->type != kFlutterDesktopPixelBufferTexture) { + std::cerr << "Attempted to register texture of unsupport type." + << std::endl; + return -1; + } + + if (!texture_info->pixel_buffer_config.callback) { + std::cerr << "Invalid pixel buffer texture callback." << std::endl; + return -1; + } + + auto texture_gl = std::make_unique( + texture_info->pixel_buffer_config.callback, + texture_info->pixel_buffer_config.user_data); + int64_t texture_id = texture_gl->texture_id(); + + { + std::lock_guard lock(map_mutex_); + textures_[texture_id] = std::move(texture_gl); + } + + engine_->task_runner()->RunNowOrPostTask([engine = engine_, texture_id]() { + engine->RegisterExternalTexture(texture_id); + }); + + return texture_id; +} + +bool FlutterWindowsTextureRegistrar::UnregisterTexture(int64_t texture_id) { + { + std::lock_guard lock(map_mutex_); + auto it = textures_.find(texture_id); + if (it == textures_.end()) { + return false; + } + textures_.erase(it); + } + + engine_->task_runner()->RunNowOrPostTask([engine = engine_, texture_id]() { + engine->UnregisterExternalTexture(texture_id); + }); + return true; +} + +bool FlutterWindowsTextureRegistrar::MarkTextureFrameAvailable( + int64_t texture_id) { + engine_->task_runner()->RunNowOrPostTask([engine = engine_, texture_id]() { + engine->MarkExternalTextureFrameAvailable(texture_id); + }); + return true; +} + +bool FlutterWindowsTextureRegistrar::PopulateTexture( + int64_t texture_id, + size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture) { + flutter::ExternalTextureGL* texture; + { + std::lock_guard lock(map_mutex_); + auto it = textures_.find(texture_id); + if (it == textures_.end()) { + return false; + } + texture = it->second.get(); + } + return texture->PopulateTexture(width, height, opengl_texture); +} + +}; // namespace flutter diff --git a/shell/platform/windows/flutter_windows_texture_registrar.h b/shell/platform/windows/flutter_windows_texture_registrar.h new file mode 100644 index 0000000000000..75d724f799b61 --- /dev/null +++ b/shell/platform/windows/flutter_windows_texture_registrar.h @@ -0,0 +1,55 @@ +// 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_WINDOWS_FLUTTER_WINDOWS_TEXTURE_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOWS_TEXTURE_REGISTRAR_H_ + +#include +#include +#include + +#include "flutter/shell/platform/windows/external_texture_gl.h" + +namespace flutter { + +class FlutterWindowsEngine; + +// An object managing the registration of an external texture. +// Thread safety: All member methods are thread safe. +class FlutterWindowsTextureRegistrar { + public: + explicit FlutterWindowsTextureRegistrar(FlutterWindowsEngine* engine); + + // Registers a texture described by the given |texture_info| object. + // Returns the non-zero, positive texture id or -1 on error. + int64_t RegisterTexture(const FlutterDesktopTextureInfo* texture_info); + + // Attempts to unregister the texture identified by |texture_id|. + // Returns true if the texture was successfully unregistered. + bool UnregisterTexture(int64_t texture_id); + + // Notifies the engine about a new frame being available. + // Returns true on success. + bool MarkTextureFrameAvailable(int64_t texture_id); + + // Attempts to populate the given |texture| by copying the + // contents of the texture identified by |texture_id|. + // Returns true on success. + bool PopulateTexture(int64_t texture_id, + size_t width, + size_t height, + FlutterOpenGLTexture* texture); + + private: + FlutterWindowsEngine* engine_ = nullptr; + + // All registered textures, keyed by their IDs. + std::unordered_map> + textures_; + std::mutex map_mutex_; +}; + +}; // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOWS_TEXTURE_REGISTRAR_H_ diff --git a/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc b/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc new file mode 100644 index 0000000000000..2183093bc9df5 --- /dev/null +++ b/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc @@ -0,0 +1,118 @@ +// 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 + +#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" +#include "flutter/shell/platform/windows/flutter_windows_engine.h" +#include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h" +#include "flutter/shell/platform/windows/testing/engine_embedder_api_modifier.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +namespace { +// Returns an engine instance configured with dummy project path values. +std::unique_ptr GetTestEngine() { + FlutterDesktopEngineProperties properties = {}; + properties.assets_path = L"C:\\foo\\flutter_assets"; + properties.icu_data_path = L"C:\\foo\\icudtl.dat"; + properties.aot_library_path = L"C:\\foo\\aot.so"; + FlutterProjectBundle project(properties); + return std::make_unique(project); +} +} // namespace + +TEST(FlutterWindowsTextureRegistrarTest, CreateDestroy) { + std::unique_ptr engine = GetTestEngine(); + + FlutterWindowsTextureRegistrar registrar(engine.get()); + + EXPECT_TRUE(true); +} + +TEST(FlutterWindowsTextureRegistrarTest, RegisterUnregisterTexture) { + std::unique_ptr engine = GetTestEngine(); + EngineEmbedderApiModifier modifier(engine.get()); + + FlutterWindowsTextureRegistrar registrar(engine.get()); + + FlutterDesktopTextureInfo texture_info = {}; + texture_info.type = kFlutterDesktopPixelBufferTexture; + texture_info.pixel_buffer_config.callback = + [](size_t width, size_t height, + void* user_data) -> const FlutterDesktopPixelBuffer* { + return nullptr; + }; + + int64_t registered_texture_id = 0; + bool register_called = false; + modifier.embedder_api().RegisterExternalTexture = MOCK_ENGINE_PROC( + RegisterExternalTexture, ([®ister_called, ®istered_texture_id]( + auto engine, auto texture_id) { + register_called = true; + registered_texture_id = texture_id; + return kSuccess; + })); + + bool unregister_called = false; + modifier.embedder_api().UnregisterExternalTexture = MOCK_ENGINE_PROC( + UnregisterExternalTexture, ([&unregister_called, ®istered_texture_id]( + auto engine, auto texture_id) { + unregister_called = true; + EXPECT_EQ(registered_texture_id, texture_id); + return kSuccess; + })); + + bool mark_frame_available_called = false; + modifier.embedder_api().MarkExternalTextureFrameAvailable = + MOCK_ENGINE_PROC(MarkExternalTextureFrameAvailable, + ([&mark_frame_available_called, ®istered_texture_id]( + auto engine, auto texture_id) { + mark_frame_available_called = true; + EXPECT_EQ(registered_texture_id, texture_id); + return kSuccess; + })); + + auto texture_id = registrar.RegisterTexture(&texture_info); + EXPECT_TRUE(register_called); + EXPECT_NE(texture_id, -1); + EXPECT_EQ(texture_id, registered_texture_id); + + EXPECT_TRUE(registrar.MarkTextureFrameAvailable(texture_id)); + EXPECT_TRUE(mark_frame_available_called); + + EXPECT_TRUE(registrar.UnregisterTexture(texture_id)); + EXPECT_TRUE(unregister_called); +} + +TEST(FlutterWindowsTextureRegistrarTest, RegisterUnknownTextureType) { + std::unique_ptr engine = GetTestEngine(); + EngineEmbedderApiModifier modifier(engine.get()); + + FlutterWindowsTextureRegistrar registrar(engine.get()); + + FlutterDesktopTextureInfo texture_info = {}; + texture_info.type = static_cast(1234); + + auto texture_id = registrar.RegisterTexture(&texture_info); + + EXPECT_EQ(texture_id, -1); +} + +TEST(FlutterWindowsTextureRegistrarTest, PopulateInvalidTexture) { + std::unique_ptr engine = GetTestEngine(); + + FlutterWindowsTextureRegistrar registrar(engine.get()); + + auto result = registrar.PopulateTexture(1, 640, 480, nullptr); + EXPECT_FALSE(result); +} + +// TODO Add additional tests for PopulateTexture() once we've mocked gl* +// functions used by ExternalTextureGL + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h index dbcaf8df17035..a79c357bdbeed 100644 --- a/shell/platform/windows/public/flutter_windows.h +++ b/shell/platform/windows/public/flutter_windows.h @@ -159,6 +159,11 @@ FlutterDesktopEngineGetPluginRegistrar(FlutterDesktopEngineRef engine, FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine); +// Returns the texture registrar associated with the engine. +FLUTTER_EXPORT FlutterDesktopTextureRegistrarRef +FlutterDesktopEngineGetTextureRegistrar( + FlutterDesktopTextureRegistrarRef texture_registrar); + // ========== View ========== // Return backing HWND for manipulation in host application. diff --git a/shell/platform/windows/task_runner.h b/shell/platform/windows/task_runner.h index 9d2c22b9043c0..0e3d44450281e 100644 --- a/shell/platform/windows/task_runner.h +++ b/shell/platform/windows/task_runner.h @@ -19,16 +19,31 @@ typedef uint64_t (*CurrentTimeProc)(); // Abstract custom task runner for scheduling custom tasks. class TaskRunner { public: + using TaskTimePoint = std::chrono::steady_clock::time_point; using TaskExpiredCallback = std::function; + using TaskClosure = std::function; virtual ~TaskRunner() = default; // Returns if the current thread is the UI thread. virtual bool RunsTasksOnCurrentThread() const = 0; - // Post a Flutter engine tasks to the event loop for delayed execution. - virtual void PostTask(FlutterTask flutter_task, - uint64_t flutter_target_time_nanos) = 0; + // Post a Flutter engine task to the event loop for delayed execution. + virtual void PostFlutterTask(FlutterTask flutter_task, + uint64_t flutter_target_time_nanos) = 0; + + // Post a task to the event loop. + virtual void PostTask(TaskClosure task) = 0; + + // Post a task to the event loop or run it immediately if this is being called + // from the main thread. + void RunNowOrPostTask(TaskClosure task) { + if (RunsTasksOnCurrentThread()) { + task(); + } else { + PostTask(std::move(task)); + } + } // Creates a new task runner with the given main thread ID, current time // provider, and callback for tasks that are ready to be run. diff --git a/shell/platform/windows/task_runner_win32.cc b/shell/platform/windows/task_runner_win32.cc index bb1fe147707de..d51a52879de1e 100644 --- a/shell/platform/windows/task_runner_win32.cc +++ b/shell/platform/windows/task_runner_win32.cc @@ -35,7 +35,7 @@ bool TaskRunnerWin32::RunsTasksOnCurrentThread() const { std::chrono::nanoseconds TaskRunnerWin32::ProcessTasks() { const TaskTimePoint now = TaskTimePoint::clock::now(); - std::vector expired_tasks; + std::vector expired_tasks; // Process expired tasks. { @@ -52,7 +52,7 @@ std::chrono::nanoseconds TaskRunnerWin32::ProcessTasks() { // because we are still holding onto the task queue mutex. We don't want // other threads to block on posting tasks onto this thread till we are // done processing expired tasks. - expired_tasks.push_back(task_queue_.top().task); + expired_tasks.push_back(task_queue_.top()); // Remove the tasks from the delayed tasks queue. task_queue_.pop(); @@ -63,7 +63,10 @@ std::chrono::nanoseconds TaskRunnerWin32::ProcessTasks() { { // Flushing tasks here without holing onto the task queue mutex. for (const auto& task : expired_tasks) { - on_task_expired_(&task); + if (auto flutter_task = std::get_if(&task.variant)) { + on_task_expired_(flutter_task); + } else if (auto closure = std::get_if(&task.variant)) + (*closure)(); } } @@ -84,15 +87,25 @@ TaskRunnerWin32::TaskTimePoint TaskRunnerWin32::TimePointFromFlutterTime( return now + std::chrono::nanoseconds(flutter_duration); } -void TaskRunnerWin32::PostTask(FlutterTask flutter_task, - uint64_t flutter_target_time_nanos) { - static std::atomic_uint64_t sGlobalTaskOrder(0); - +void TaskRunnerWin32::PostFlutterTask(FlutterTask flutter_task, + uint64_t flutter_target_time_nanos) { Task task; - task.order = ++sGlobalTaskOrder; task.fire_time = TimePointFromFlutterTime(flutter_target_time_nanos); - task.task = flutter_task; + task.variant = flutter_task; + EnqueueTask(std::move(task)); +} + +void TaskRunnerWin32::PostTask(TaskClosure closure) { + Task task; + task.fire_time = TaskTimePoint::clock::now(); + task.variant = std::move(closure); + EnqueueTask(std::move(task)); +} +void TaskRunnerWin32::EnqueueTask(Task task) { + static std::atomic_uint64_t sGlobalTaskOrder(0); + + task.order = ++sGlobalTaskOrder; { std::lock_guard lock(task_queue_mutex_); task_queue_.push(task); diff --git a/shell/platform/windows/task_runner_win32.h b/shell/platform/windows/task_runner_win32.h index 1ac2f64b861c5..cd7e3dd877620 100644 --- a/shell/platform/windows/task_runner_win32.h +++ b/shell/platform/windows/task_runner_win32.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/task_runner.h" @@ -36,17 +37,21 @@ class TaskRunnerWin32 : public TaskRunner { bool RunsTasksOnCurrentThread() const override; // |TaskRunner| - void PostTask(FlutterTask flutter_task, - uint64_t flutter_target_time_nanos) override; + void PostFlutterTask(FlutterTask flutter_task, + uint64_t flutter_target_time_nanos) override; + + // |TaskRunner| + void PostTask(TaskClosure task) override; std::chrono::nanoseconds ProcessTasks(); private: - using TaskTimePoint = std::chrono::steady_clock::time_point; + typedef std::variant TaskVariant; + struct Task { uint64_t order; TaskTimePoint fire_time; - FlutterTask task; + TaskVariant variant; struct Comparer { bool operator()(const Task& a, const Task& b) { @@ -58,6 +63,9 @@ class TaskRunnerWin32 : public TaskRunner { }; }; + // Enqueues the given task. + void EnqueueTask(Task task); + // Returns a TaskTimePoint computed from the given target time from Flutter. TaskTimePoint TimePointFromFlutterTime( uint64_t flutter_target_time_nanos) const; diff --git a/shell/platform/windows/task_runner_winuwp.cc b/shell/platform/windows/task_runner_winuwp.cc index a2fbe7040c412..8bfc4c9cbd2cc 100644 --- a/shell/platform/windows/task_runner_winuwp.cc +++ b/shell/platform/windows/task_runner_winuwp.cc @@ -31,8 +31,8 @@ bool TaskRunnerWinUwp::RunsTasksOnCurrentThread() const { return GetCurrentThreadId() == main_thread_id_; } -void TaskRunnerWinUwp::PostTask(FlutterTask flutter_task, - uint64_t flutter_target_time_nanos) { +void TaskRunnerWinUwp::PostFlutterTask(FlutterTask flutter_task, + uint64_t flutter_target_time_nanos) { // TODO: Handle the target time. See // https://github.com/flutter/flutter/issues/70890. @@ -41,4 +41,11 @@ void TaskRunnerWinUwp::PostTask(FlutterTask flutter_task, [this, flutter_task]() { on_task_expired_(&flutter_task); }); } +void TaskRunnerWinUwp::PostTask(TaskClosure task) { + // TODO: Handle the target time. See PostFlutterTask() + + dispatcher_.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, + [task]() { task() }); +} + } // namespace flutter diff --git a/shell/platform/windows/task_runner_winuwp.h b/shell/platform/windows/task_runner_winuwp.h index 49bdb4da587ce..654dd0e54dc97 100644 --- a/shell/platform/windows/task_runner_winuwp.h +++ b/shell/platform/windows/task_runner_winuwp.h @@ -34,8 +34,11 @@ class TaskRunnerWinUwp : public TaskRunner { bool RunsTasksOnCurrentThread() const override; // |TaskRunner| - void PostTask(FlutterTask flutter_task, - uint64_t flutter_target_time_nanos) override; + void PostFlutterTask(FlutterTask flutter_task, + uint64_t flutter_target_time_nanos) override; + + // |TaskRunner| + void PostTask(TaskClosure task) override; private: DWORD main_thread_id_;