Implement external view embedder on Android (flutter/engine#19033)

The external view embedder allows to embed Android views in a Flutter app.
This commit is contained in:
Emmanuel Garcia 2020-06-20 12:51:55 -07:00 committed by GitHub
parent 0e8db3a1de
commit 202c1af268
51 changed files with 1446 additions and 238 deletions

View File

@ -74,7 +74,6 @@ group("flutter") {
"//flutter/lib/ui:ui_unittests",
"//flutter/runtime:runtime_unittests",
"//flutter/shell/common:shell_unittests",
"//flutter/shell/platform/android/external_view_embedder:android_external_view_embedder_unittests",
"//flutter/shell/platform/embedder:embedder_unittests",
"//flutter/testing:testing_unittests",
"//flutter/third_party/txt:txt_unittests",
@ -85,6 +84,8 @@ group("flutter") {
"//flutter/fml:fml_benchmarks",
"//flutter/lib/ui:ui_benchmarks",
"//flutter/shell/common:shell_benchmarks",
"//flutter/shell/platform/android/external_view_embedder:android_external_view_embedder_unittests",
"//flutter/shell/platform/android/jni:jni_unittests",
"//flutter/third_party/txt:txt_benchmarks",
]
}

View File

@ -645,8 +645,6 @@ FILE: ../../../flutter/shell/gpu/gpu_surface_vulkan.h
FILE: ../../../flutter/shell/gpu/gpu_surface_vulkan_delegate.cc
FILE: ../../../flutter/shell/gpu/gpu_surface_vulkan_delegate.h
FILE: ../../../flutter/shell/platform/android/AndroidManifest.xml
FILE: ../../../flutter/shell/platform/android/android_context.cc
FILE: ../../../flutter/shell/platform/android/android_context.h
FILE: ../../../flutter/shell/platform/android/android_context_gl.cc
FILE: ../../../flutter/shell/platform/android/android_context_gl.h
FILE: ../../../flutter/shell/platform/android/android_environment_gl.cc
@ -654,12 +652,8 @@ FILE: ../../../flutter/shell/platform/android/android_environment_gl.h
FILE: ../../../flutter/shell/platform/android/android_exports.lst
FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.cc
FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.h
FILE: ../../../flutter/shell/platform/android/android_native_window.cc
FILE: ../../../flutter/shell/platform/android/android_native_window.h
FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc
FILE: ../../../flutter/shell/platform/android/android_shell_holder.h
FILE: ../../../flutter/shell/platform/android/android_surface.cc
FILE: ../../../flutter/shell/platform/android/android_surface.h
FILE: ../../../flutter/shell/platform/android/android_surface_gl.cc
FILE: ../../../flutter/shell/platform/android/android_surface_gl.h
FILE: ../../../flutter/shell/platform/android/android_surface_software.cc
@ -668,9 +662,14 @@ FILE: ../../../flutter/shell/platform/android/android_surface_vulkan.cc
FILE: ../../../flutter/shell/platform/android/android_surface_vulkan.h
FILE: ../../../flutter/shell/platform/android/apk_asset_provider.cc
FILE: ../../../flutter/shell/platform/android/apk_asset_provider.h
FILE: ../../../flutter/shell/platform/android/context/android_context.cc
FILE: ../../../flutter/shell/platform/android/context/android_context.h
FILE: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder.cc
FILE: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder.h
FILE: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc
FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool.cc
FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool.h
FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool_unittests.cc
FILE: ../../../flutter/shell/platform/android/flutter_main.cc
FILE: ../../../flutter/shell/platform/android/flutter_main.h
FILE: ../../../flutter/shell/platform/android/io/flutter/Log.java
@ -786,6 +785,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArgument
FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterView.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/TextureRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/VsyncWaiter.java
FILE: ../../../flutter/shell/platform/android/jni/jni_mock.h
FILE: ../../../flutter/shell/platform/android/jni/jni_mock_unittest.cc
FILE: ../../../flutter/shell/platform/android/jni/platform_view_android_jni.cc
FILE: ../../../flutter/shell/platform/android/jni/platform_view_android_jni.h
FILE: ../../../flutter/shell/platform/android/library_loader.cc
@ -796,6 +797,12 @@ FILE: ../../../flutter/shell/platform/android/platform_view_android.h
FILE: ../../../flutter/shell/platform/android/platform_view_android_jni_impl.cc
FILE: ../../../flutter/shell/platform/android/platform_view_android_jni_impl.h
FILE: ../../../flutter/shell/platform/android/robolectric.properties
FILE: ../../../flutter/shell/platform/android/surface/android_native_window.cc
FILE: ../../../flutter/shell/platform/android/surface/android_native_window.h
FILE: ../../../flutter/shell/platform/android/surface/android_surface.cc
FILE: ../../../flutter/shell/platform/android/surface/android_surface.h
FILE: ../../../flutter/shell/platform/android/surface/android_surface_mock.cc
FILE: ../../../flutter/shell/platform/android/surface/android_surface_mock.h
FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.cc
FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/basic_message_channel_unittests.cc

View File

@ -227,9 +227,11 @@ class ExternalViewEmbedder {
// sets the stage for the next pre-roll.
virtual void CancelFrame() = 0;
virtual void BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio) = 0;
virtual void BeginFrame(
SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) = 0;
virtual void PrerollCompositeEmbeddedView(
int view_id,

View File

@ -28,12 +28,15 @@ void RasterThreadMerger::MergeWithLease(size_t lease_term) {
}
}
bool RasterThreadMerger::IsOnRasterizingThread() {
const auto current_queue_id = MessageLoop::GetCurrentTaskQueueId();
bool RasterThreadMerger::IsOnPlatformThread() const {
return MessageLoop::GetCurrentTaskQueueId() == platform_queue_id_;
}
bool RasterThreadMerger::IsOnRasterizingThread() const {
if (is_merged_) {
return current_queue_id == platform_queue_id_;
return IsOnPlatformThread();
} else {
return current_queue_id == gpu_queue_id_;
return !IsOnPlatformThread();
}
}

View File

@ -44,7 +44,10 @@ class RasterThreadMerger
// Returns true if the current thread owns rasterizing.
// When the threads are merged, platform thread owns rasterizing.
// When un-merged, raster thread owns rasterizing.
bool IsOnRasterizingThread();
bool IsOnRasterizingThread() const;
// Returns true if the current thread is the platform thread.
bool IsOnPlatformThread() const;
private:
static const int kLeaseNotSet;

View File

@ -92,12 +92,14 @@ TEST(RasterThreadMerger, IsNotOnRasterizingThread) {
loop1->GetTaskRunner()->PostTask([&]() {
ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread());
ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
pre_merge.CountDown();
});
loop2->GetTaskRunner()->PostTask([&]() {
ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
pre_merge.CountDown();
});
@ -108,6 +110,7 @@ TEST(RasterThreadMerger, IsNotOnRasterizingThread) {
loop1->GetTaskRunner()->PostTask([&]() {
ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
post_merge.CountDown();
});
@ -116,6 +119,7 @@ TEST(RasterThreadMerger, IsNotOnRasterizingThread) {
// this will be false since this is going to be run
// on loop1 really.
ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
post_merge.CountDown();
});
@ -126,12 +130,14 @@ TEST(RasterThreadMerger, IsNotOnRasterizingThread) {
loop1->GetTaskRunner()->PostTask([&]() {
ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread());
ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
post_unmerge.CountDown();
});
loop2->GetTaskRunner()->PostTask([&]() {
ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
post_unmerge.CountDown();
});

View File

@ -391,9 +391,9 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) {
SkCanvas* embedder_root_canvas = nullptr;
if (external_view_embedder != nullptr) {
external_view_embedder->BeginFrame(layer_tree.frame_size(),
surface_->GetContext(),
layer_tree.device_pixel_ratio());
external_view_embedder->BeginFrame(
layer_tree.frame_size(), surface_->GetContext(),
layer_tree.device_pixel_ratio(), raster_thread_merger_);
embedder_root_canvas = external_view_embedder->GetRootCanvas();
}

View File

@ -6,9 +6,11 @@ namespace flutter {
void ShellTestExternalViewEmbedder::CancelFrame() {}
// |ExternalViewEmbedder|
void ShellTestExternalViewEmbedder::BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio) {}
void ShellTestExternalViewEmbedder::BeginFrame(
SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {}
// |ExternalViewEmbedder|
void ShellTestExternalViewEmbedder::PrerollCompositeEmbeddedView(

View File

@ -29,9 +29,11 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder {
void CancelFrame() override;
// |ExternalViewEmbedder|
void BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio) override;
void BeginFrame(
SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
// |ExternalViewEmbedder|
void PrerollCompositeEmbeddedView(

View File

@ -23,20 +23,14 @@ shared_library("flutter_shell_native") {
sources = [
"$root_build_dir/flutter_icu/icudtl.o",
"android_context.cc",
"android_context.h",
"android_context_gl.cc",
"android_context_gl.h",
"android_environment_gl.cc",
"android_environment_gl.h",
"android_external_texture_gl.cc",
"android_external_texture_gl.h",
"android_native_window.cc",
"android_native_window.h",
"android_shell_holder.cc",
"android_shell_holder.h",
"android_surface.cc",
"android_surface.h",
"android_surface_gl.cc",
"android_surface_gl.h",
"android_surface_software.cc",
@ -67,8 +61,11 @@ shared_library("flutter_shell_native") {
"//flutter/runtime",
"//flutter/runtime:libdart",
"//flutter/shell/common",
"//flutter/shell/platform/android/context",
"//flutter/shell/platform/android/external_view_embedder",
"//flutter/shell/platform/android/jni",
"//flutter/shell/platform/android/surface",
"//flutter/shell/platform/android/surface:native_window",
"//third_party/skia",
]

View File

@ -1,37 +0,0 @@
// 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/android/android_context.h"
#include "flutter/shell/platform/android/android_context_gl.h"
#include "flutter/shell/platform/android/android_environment_gl.h"
namespace flutter {
std::shared_ptr<AndroidContext> CreateContextGL() {
auto context_gl = std::make_shared<AndroidContextGL>(
AndroidRenderingAPI::kOpenGLES,
fml::MakeRefCounted<AndroidEnvironmentGL>());
FML_CHECK(context_gl->IsValid()) << "Could not create an Android context GL.";
return context_gl;
}
AndroidContext::AndroidContext(AndroidRenderingAPI rendering_api)
: rendering_api_(rendering_api) {}
AndroidContext::~AndroidContext() = default;
AndroidRenderingAPI AndroidContext::RenderingApi() const {
return rendering_api_;
}
std::shared_ptr<AndroidContext> AndroidContext::Create(
AndroidRenderingAPI rendering_api) {
if (rendering_api == AndroidRenderingAPI::kOpenGLES) {
return CreateContextGL();
}
return std::make_shared<AndroidContext>(rendering_api);
}
} // namespace flutter

View File

@ -9,9 +9,9 @@
#include "flutter/fml/memory/ref_counted.h"
#include "flutter/fml/memory/ref_ptr.h"
#include "flutter/shell/common/platform_view.h"
#include "flutter/shell/platform/android/android_context.h"
#include "flutter/shell/platform/android/android_environment_gl.h"
#include "flutter/shell/platform/android/android_native_window.h"
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/surface/android_native_window.h"
#include "third_party/skia/include/core/SkSize.h"
namespace flutter {

View File

@ -16,6 +16,7 @@
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/platform/android/jni_util.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/platform/android/platform_view_android.h"

View File

@ -1,41 +0,0 @@
// 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/android/android_surface.h"
#include <utility>
#include "flutter/shell/platform/android/android_surface_gl.h"
#include "flutter/shell/platform/android/android_surface_software.h"
#if SHELL_ENABLE_VULKAN
#include "flutter/shell/platform/android/android_surface_vulkan.h"
#endif // SHELL_ENABLE_VULKAN
namespace flutter {
std::unique_ptr<AndroidSurface> AndroidSurface::Create(
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
std::unique_ptr<AndroidSurface> surface;
switch (android_context->RenderingApi()) {
case AndroidRenderingAPI::kSoftware:
surface = std::make_unique<AndroidSurfaceSoftware>(jni_facade);
break;
case AndroidRenderingAPI::kOpenGLES:
surface = std::make_unique<AndroidSurfaceGL>(android_context, jni_facade);
break;
case AndroidRenderingAPI::kVulkan:
#if SHELL_ENABLE_VULKAN
surface = std::make_unique<AndroidSurfaceVulkan>(jni_facade);
#endif // SHELL_ENABLE_VULKAN
break;
}
FML_CHECK(surface);
return surface->IsValid() ? std::move(surface) : nullptr;
;
}
AndroidSurface::~AndroidSurface() = default;
} // namespace flutter

View File

@ -13,19 +13,22 @@ namespace flutter {
AndroidSurfaceGL::AndroidSurfaceGL(
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
: native_window_(nullptr),
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const AndroidSurface::Factory& surface_factory)
: external_view_embedder_(
std::make_unique<AndroidExternalViewEmbedder>(android_context,
jni_facade,
surface_factory)),
android_context_(
std::static_pointer_cast<AndroidContextGL>(android_context)),
native_window_(nullptr),
onscreen_surface_(nullptr),
offscreen_surface_(nullptr) {
android_context_ =
std::static_pointer_cast<AndroidContextGL>(android_context);
// Acquire the offscreen surface.
offscreen_surface_ = android_context_->CreateOffscreenSurface();
if (!offscreen_surface_->IsValid()) {
offscreen_surface_ = nullptr;
}
external_view_embedder_ =
std::make_unique<AndroidExternalViewEmbedder>(jni_facade);
}
AndroidSurfaceGL::~AndroidSurfaceGL() = default;

View File

@ -12,9 +12,9 @@
#include "flutter/shell/gpu/gpu_surface_gl.h"
#include "flutter/shell/platform/android/android_context_gl.h"
#include "flutter/shell/platform/android/android_environment_gl.h"
#include "flutter/shell/platform/android/android_surface.h"
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/surface/android_surface.h"
namespace flutter {
@ -22,7 +22,8 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate,
public AndroidSurface {
public:
AndroidSurfaceGL(std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const AndroidSurface::Factory& surface_factory);
~AndroidSurfaceGL() override;
@ -63,9 +64,10 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate,
ExternalViewEmbedder* GetExternalViewEmbedder() override;
private:
const std::unique_ptr<AndroidExternalViewEmbedder> external_view_embedder_;
const std::shared_ptr<AndroidContextGL> android_context_;
fml::RefPtr<AndroidNativeWindow> native_window_;
std::unique_ptr<AndroidExternalViewEmbedder> external_view_embedder_;
std::shared_ptr<AndroidContextGL> android_context_;
std::unique_ptr<AndroidEGLSurface> onscreen_surface_;
std::unique_ptr<AndroidEGLSurface> offscreen_surface_;

View File

@ -37,11 +37,15 @@ bool GetSkColorType(int32_t buffer_format,
} // anonymous namespace
AndroidSurfaceSoftware::AndroidSurfaceSoftware(
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
AndroidSurface::Factory surface_factory)
: external_view_embedder_(
std::make_unique<AndroidExternalViewEmbedder>(android_context,
jni_facade,
surface_factory)) {
GetSkColorType(WINDOW_FORMAT_RGBA_8888, &target_color_type_,
&target_alpha_type_);
external_view_embedder_ =
std::make_unique<AndroidExternalViewEmbedder>(jni_facade);
}
AndroidSurfaceSoftware::~AndroidSurfaceSoftware() = default;

View File

@ -9,16 +9,18 @@
#include "flutter/fml/platform/android/jni_weak_ref.h"
#include "flutter/fml/platform/android/scoped_java_ref.h"
#include "flutter/shell/gpu/gpu_surface_software.h"
#include "flutter/shell/platform/android/android_surface.h"
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/surface/android_surface.h"
namespace flutter {
class AndroidSurfaceSoftware final : public AndroidSurface,
public GPUSurfaceSoftwareDelegate {
public:
AndroidSurfaceSoftware(std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
AndroidSurfaceSoftware(std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
AndroidSurface::Factory surface_factory);
~AndroidSurfaceSoftware() override;
@ -53,11 +55,12 @@ class AndroidSurfaceSoftware final : public AndroidSurface,
ExternalViewEmbedder* GetExternalViewEmbedder() override;
private:
const std::unique_ptr<AndroidExternalViewEmbedder> external_view_embedder_;
sk_sp<SkSurface> sk_surface_;
fml::RefPtr<AndroidNativeWindow> native_window_;
SkColorType target_color_type_;
SkAlphaType target_alpha_type_;
std::unique_ptr<AndroidExternalViewEmbedder> external_view_embedder_;
FML_DISALLOW_COPY_AND_ASSIGN(AndroidSurfaceSoftware);
};

View File

@ -13,11 +13,14 @@
namespace flutter {
AndroidSurfaceVulkan::AndroidSurfaceVulkan(
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
: proc_table_(fml::MakeRefCounted<vulkan::VulkanProcTable>()) {
external_view_embedder_ =
std::make_unique<AndroidExternalViewEmbedder>(jni_facade);
}
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
AndroidSurface::Factory surface_factory)
: external_view_embedder_(
std::make_unique<AndroidExternalViewEmbedder>(android_context,
jni_facade,
surface_factory)),
proc_table_(fml::MakeRefCounted<vulkan::VulkanProcTable>()) {}
AndroidSurfaceVulkan::~AndroidSurfaceVulkan() = default;

View File

@ -9,10 +9,9 @@
#include <memory>
#include "flutter/fml/macros.h"
#include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h"
#include "flutter/shell/platform/android/android_native_window.h"
#include "flutter/shell/platform/android/android_surface.h"
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/surface/android_surface.h"
#include "flutter/vulkan/vulkan_window.h"
namespace flutter {
@ -20,7 +19,9 @@ namespace flutter {
class AndroidSurfaceVulkan : public AndroidSurface,
public GPUSurfaceVulkanDelegate {
public:
AndroidSurfaceVulkan(std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
AndroidSurfaceVulkan(std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
AndroidSurface::Factory surface_factory);
~AndroidSurfaceVulkan() override;
@ -52,9 +53,10 @@ class AndroidSurfaceVulkan : public AndroidSurface,
fml::RefPtr<vulkan::VulkanProcTable> vk() override;
private:
const std::unique_ptr<AndroidExternalViewEmbedder> external_view_embedder_;
fml::RefPtr<vulkan::VulkanProcTable> proc_table_;
fml::RefPtr<AndroidNativeWindow> native_window_;
std::unique_ptr<AndroidExternalViewEmbedder> external_view_embedder_;
FML_DISALLOW_COPY_AND_ASSIGN(AndroidSurfaceVulkan);
};

View File

@ -0,0 +1,18 @@
# 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.
import("//flutter/common/config.gni")
source_set("context") {
sources = [
"android_context.cc",
"android_context.h",
]
public_configs = [ "//flutter:config" ]
deps = [
"//flutter/fml",
]
}

View File

@ -0,0 +1,22 @@
// 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/android/context/android_context.h"
namespace flutter {
AndroidContext::AndroidContext(AndroidRenderingAPI rendering_api)
: rendering_api_(rendering_api) {}
AndroidContext::~AndroidContext() = default;
AndroidRenderingAPI AndroidContext::RenderingApi() const {
return rendering_api_;
}
bool AndroidContext::IsValid() const {
return true;
}
} // namespace flutter

View File

@ -6,9 +6,6 @@
#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_CONTEXT_H_
#include "flutter/fml/macros.h"
#include "flutter/fml/memory/ref_counted.h"
#include "flutter/fml/memory/ref_ptr.h"
#include "flutter/shell/common/platform_view.h"
namespace flutter {
@ -27,11 +24,10 @@ class AndroidContext {
~AndroidContext();
static std::shared_ptr<AndroidContext> Create(
AndroidRenderingAPI rendering_api);
AndroidRenderingAPI RenderingApi() const;
bool IsValid() const;
private:
const AndroidRenderingAPI rendering_api_;

View File

@ -2,18 +2,15 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/android/config.gni")
import("//flutter/common/config.gni")
import("//flutter/shell/config.gni")
import("//flutter/shell/gpu/gpu.gni")
import("//flutter/shell/version/version.gni")
import("//flutter/testing/testing.gni")
source_set("external_view_embedder") {
sources = [
"external_view_embedder.cc",
"external_view_embedder.h",
"surface_pool.cc",
"surface_pool.h",
]
public_configs = [ "//flutter:config" ]
@ -22,7 +19,9 @@ source_set("external_view_embedder") {
"//flutter/common",
"//flutter/flow",
"//flutter/fml",
"//flutter/shell/platform/android/context",
"//flutter/shell/platform/android/jni",
"//flutter/shell/platform/android/surface",
"//third_party/skia",
]
}
@ -36,11 +35,15 @@ executable("android_external_view_embedder_unittests") {
sources = [
"external_view_embedder_unittests.cc",
"surface_pool_unittests.cc",
]
deps = [
":external_view_embedder",
":external_view_embedder_fixtures",
"//flutter/shell/gpu:gpu_surface_gl",
"//flutter/shell/platform/android/jni:jni_mock",
"//flutter/shell/platform/android/surface:surface_mock",
"//flutter/testing",
"//flutter/testing:dart",
"//flutter/testing:skia",

View File

@ -9,57 +9,191 @@
namespace flutter {
AndroidExternalViewEmbedder::AndroidExternalViewEmbedder(
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
: ExternalViewEmbedder(), jni_facade_(jni_facade) {}
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const AndroidSurface::Factory& surface_factory)
: ExternalViewEmbedder(),
android_context_(android_context),
jni_facade_(jni_facade),
surface_factory_(surface_factory),
surface_pool_(std::make_unique<SurfacePool>()) {}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView(
int view_id,
std::unique_ptr<EmbeddedViewParams> params) {
// TODO(egarciad): Implement hybrid composition.
// https://github.com/flutter/flutter/issues/55270
TRACE_EVENT0("flutter",
"AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView");
picture_recorders_[view_id] = std::make_unique<SkPictureRecorder>();
picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_));
auto rtree_factory = RTreeFactory();
view_rtrees_.insert({view_id, rtree_factory.getInstance()});
auto picture_recorder = std::make_unique<SkPictureRecorder>();
picture_recorder->beginRecording(SkRect::Make(frame_size_), &rtree_factory);
picture_recorders_.insert({view_id, std::move(picture_recorder)});
composition_order_.push_back(view_id);
// Update params only if they changed.
if (view_params_.count(view_id) == 1 &&
view_params_.at(view_id) == *params.get()) {
return;
}
view_params_.insert_or_assign(view_id, EmbeddedViewParams(*params.get()));
}
// |ExternalViewEmbedder|
SkCanvas* AndroidExternalViewEmbedder::CompositeEmbeddedView(int view_id) {
return picture_recorders_[view_id]->getRecordingCanvas();
if (picture_recorders_.count(view_id) == 1) {
return picture_recorders_.at(view_id)->getRecordingCanvas();
}
return nullptr;
}
// |ExternalViewEmbedder|
std::vector<SkCanvas*> AndroidExternalViewEmbedder::GetCurrentCanvases() {
// TODO(egarciad): Implement hybrid composition.
// https://github.com/flutter/flutter/issues/55270
std::vector<SkCanvas*> canvases;
for (size_t i = 0; i < composition_order_.size(); i++) {
int64_t view_id = composition_order_[i];
canvases.push_back(picture_recorders_[view_id]->getRecordingCanvas());
canvases.push_back(picture_recorders_.at(view_id)->getRecordingCanvas());
}
return canvases;
}
SkRect AndroidExternalViewEmbedder::GetViewRect(int view_id) const {
const EmbeddedViewParams& params = view_params_.at(view_id);
// TODO(egarciad): The rect should be computed from the mutator stack.
// https://github.com/flutter/flutter/issues/59821
return SkRect::MakeXYWH(params.offsetPixels.x(), //
params.offsetPixels.y(), //
params.sizePoints.width() * device_pixel_ratio_, //
params.sizePoints.height() * device_pixel_ratio_ //
);
}
// |ExternalViewEmbedder|
bool AndroidExternalViewEmbedder::SubmitFrame(
GrContext* context,
std::unique_ptr<SurfaceFrame> frame) {
// TODO(egarciad): Implement hybrid composition.
// https://github.com/flutter/flutter/issues/55270
TRACE_EVENT0("flutter", "AndroidExternalViewEmbedder::SubmitFrame");
if (should_run_rasterizer_on_platform_thread_) {
// Don't submit the current frame if the frame will be resubmitted.
return true;
}
std::unordered_map<int64_t, std::list<SkRect>> overlay_layers;
std::unordered_map<int64_t, sk_sp<SkPicture>> pictures;
SkCanvas* background_canvas = frame->SkiaCanvas();
// Restore the clip context after exiting this method since it's changed
// below.
SkAutoCanvasRestore save(background_canvas, /*doSave=*/true);
for (size_t i = 0; i < composition_order_.size(); i++) {
int64_t view_id = composition_order_[i];
frame->SkiaCanvas()->drawPicture(
picture_recorders_[view_id]->finishRecordingAsPicture());
SkRect view_rect = GetViewRect(view_id);
// Display the platform view. If it's already displayed, then it's
// just positioned and sized.
jni_facade_->FlutterViewOnDisplayPlatformView(view_id, //
view_rect.x(), //
view_rect.y(), //
view_rect.width(), //
view_rect.height() //
);
sk_sp<SkPicture> picture =
picture_recorders_.at(view_id)->finishRecordingAsPicture();
FML_CHECK(picture);
pictures.insert({view_id, picture});
sk_sp<RTree> rtree = view_rtrees_.at(view_id);
// Determinate if Flutter UI intersects with any of the previous
// platform views stacked by z position.
//
// This is done by querying the r-tree that holds the records for the
// picture recorder corresponding to the flow layers added after a platform
// view layer.
for (ssize_t j = i; j >= 0; j--) {
int64_t current_view_id = composition_order_[j];
SkRect current_view_rect = GetViewRect(current_view_id);
// Each rect corresponds to a native view that renders Flutter UI.
std::list<SkRect> intersection_rects =
rtree->searchNonOverlappingDrawnRects(current_view_rect);
auto allocation_size = intersection_rects.size();
// Limit the number of native views, so it doesn't grow forever.
//
// In this case, the rects are merged into a single one that is the union
// of all the rects.
if (allocation_size > kMaxLayerAllocations) {
SkRect joined_rect;
for (const SkRect& rect : intersection_rects) {
joined_rect.join(rect);
}
intersection_rects.clear();
intersection_rects.push_back(joined_rect);
}
for (SkRect& intersection_rect : intersection_rects) {
// Get the intersection rect between the current rect
// and the platform view rect.
// joined_rect.intersect(platform_view_rect);
// Subpixels in the platform may not align with the canvas subpixels.
//
// To workaround it, round the floating point bounds and make the rect
// slighly larger. For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4,
// 5}.
intersection_rect.set(intersection_rect.roundOut());
// Clip the background canvas, so it doesn't contain any of the pixels
// drawn on the overlay layer.
background_canvas->clipRect(intersection_rect, SkClipOp::kDifference);
}
overlay_layers.insert({current_view_id, intersection_rects});
}
background_canvas->drawPicture(pictures.at(view_id));
}
return frame->Submit();
// Submit the background canvas frame before switching the GL context to
// the surfaces above.
frame->Submit();
for (int64_t view_id : composition_order_) {
for (const SkRect& overlay_rect : overlay_layers.at(view_id)) {
CreateSurfaceIfNeeded(context, //
view_id, //
pictures.at(view_id), //
overlay_rect //
)
->Submit();
}
}
return true;
}
// |ExternalViewEmbedder|
std::unique_ptr<SurfaceFrame>
AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrContext* context,
int64_t view_id,
sk_sp<SkPicture> picture,
const SkRect& rect) {
std::shared_ptr<OverlayLayer> layer = surface_pool_->GetLayer(
context, android_context_, jni_facade_, surface_factory_);
std::unique_ptr<SurfaceFrame> frame =
layer->surface->AcquireFrame(frame_size_);
// Display the overlay surface. If it's already displayed, then it's
// just positioned and sized.
jni_facade_->FlutterViewDisplayOverlaySurface(layer->id, //
rect.x(), //
rect.y(), //
rect.width(), //
rect.height() //
);
SkCanvas* overlay_canvas = frame->SkiaCanvas();
overlay_canvas->clear(SK_ColorTRANSPARENT);
// Offset the picture since its absolute position on the scene is determined
// by the position of the overlay view.
overlay_canvas->translate(-rect.x(), -rect.y());
overlay_canvas->drawPicture(picture);
return frame;
}
// |ExternalViewEmbedder|
@ -100,11 +234,18 @@ void AndroidExternalViewEmbedder::Reset() {
}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder::BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio) {
void AndroidExternalViewEmbedder::BeginFrame(
SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
Reset();
frame_size_ = frame_size;
device_pixel_ratio_ = device_pixel_ratio;
// JNI method must be called on the platform thread.
if (raster_thread_merger->IsOnPlatformThread()) {
jni_facade_->FlutterViewBeginFrame();
}
}
// |ExternalViewEmbedder|
@ -119,6 +260,11 @@ void AndroidExternalViewEmbedder::EndFrame(
raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration);
should_run_rasterizer_on_platform_thread_ = false;
}
surface_pool_->RecycleLayers();
// JNI method must be called on the platform thread.
if (raster_thread_merger->IsOnPlatformThread()) {
jni_facade_->FlutterViewEndFrame();
}
}
} // namespace flutter

View File

@ -6,16 +6,30 @@
#define FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_VIEW_EMBEDDER_H_
#include "flutter/flow/embedded_views.h"
#include "flutter/flow/rtree.h"
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/external_view_embedder/surface_pool.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
namespace flutter {
//------------------------------------------------------------------------------
/// Allows to embed Android views into a Flutter application.
///
/// This class calls Java methods via |PlatformViewAndroidJNI| to manage the
/// lifecycle of the Android view corresponding to |flutter::PlatformViewLayer|.
///
/// It also orchestrates overlay surfaces. These are Android views
/// that render above (by Z order) the Android view corresponding to
/// |flutter::PlatformViewLayer|.
///
class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
public:
AndroidExternalViewEmbedder(
std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const AndroidSurface::Factory& surface_factory);
// |ExternalViewEmbedder|
void PrerollCompositeEmbeddedView(
@ -40,9 +54,11 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
SkCanvas* GetRootCanvas() override;
// |ExternalViewEmbedder|
void BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio) override;
void BeginFrame(
SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
// |ExternalViewEmbedder|
void CancelFrame() override;
@ -51,9 +67,12 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
void EndFrame(
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
// Gets the rect based on the device pixel ratio of a platform view displayed
// on the screen.
SkRect GetViewRect(int view_id) const;
private:
// Allows to call methods in Java.
const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
static const int kMaxLayerAllocations = 2;
// The number of frames the rasterizer task runner will continue
// to run on the platform thread after no platform view is rendered.
@ -62,13 +81,29 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
// where the platform view might be momentarily off the screen.
static const int kDefaultMergedLeaseDuration = 10;
// Provides metadata to the Android surfaces.
const std::shared_ptr<AndroidContext> android_context_;
// Allows to call methods in Java.
const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
// Allows to create surfaces.
const AndroidSurface::Factory surface_factory_;
// Holds surfaces. Allows to recycle surfaces or allocate new ones.
const std::unique_ptr<SurfacePool> surface_pool_;
// Whether the rasterizer task runner should run on the platform thread.
// When this is true, the current frame is cancelled and resubmitted.
bool should_run_rasterizer_on_platform_thread_ = false;
// The size of the background canvas.
// The size of the root canvas.
SkISize frame_size_;
// The pixel ratio used to determinate the size of a platform view layer
// relative to the device layout system.
double device_pixel_ratio_;
// The order of composition. Each entry contains a unique id for the platform
// view.
std::vector<int64_t> composition_order_;
@ -76,10 +111,25 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
// The platform view's picture recorder keyed off the platform view id, which
// contains any subsequent operation until the next platform view or the end
// of the last leaf node in the layer tree.
std::map<int64_t, std::unique_ptr<SkPictureRecorder>> picture_recorders_;
std::unordered_map<int64_t, std::unique_ptr<SkPictureRecorder>>
picture_recorders_;
/// Resets the state.
// The params for a platform view, which contains the size, position and
// mutation stack.
std::unordered_map<int64_t, EmbeddedViewParams> view_params_;
// The r-tree that captures the operations for the picture recorders.
std::unordered_map<int64_t, sk_sp<RTree>> view_rtrees_;
// Resets the state.
void Reset();
// Creates a Surface when needed or recycles an existing one.
// Finally, draws the picture on the frame's canvas.
std::unique_ptr<SurfaceFrame> CreateSurfaceIfNeeded(GrContext* context,
int64_t view_id,
sk_sp<SkPicture> picture,
const SkRect& rect);
};
} // namespace flutter

View File

@ -2,18 +2,81 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/surface.h"
#include "flutter/fml/raster_thread_merger.h"
#include "flutter/fml/thread.h"
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"
#include "flutter/shell/platform/android/jni/jni_mock.h"
#include "flutter/shell/platform/android/surface/android_surface_mock.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "third_party/skia/include/gpu/GrContext.h"
namespace flutter {
namespace testing {
TEST(AndroidExternalViewEmbedder, GetCurrentCanvases) {
auto embedder = new AndroidExternalViewEmbedder(nullptr);
using ::testing::ByMove;
using ::testing::Return;
embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0);
class SurfaceMock : public Surface {
public:
MOCK_METHOD(bool, IsValid, (), (override));
MOCK_METHOD(std::unique_ptr<SurfaceFrame>,
AcquireFrame,
(const SkISize& size),
(override));
MOCK_METHOD(SkMatrix, GetRootTransformation, (), (const, override));
MOCK_METHOD(GrContext*, GetContext, (), (override));
MOCK_METHOD(flutter::ExternalViewEmbedder*,
GetExternalViewEmbedder,
(),
(override));
MOCK_METHOD(std::unique_ptr<GLContextResult>,
MakeRenderContextCurrent,
(),
(override));
};
fml::RefPtr<fml::RasterThreadMerger> GetThreadMergerFromPlatformThread() {
auto rasterizer_thread = new fml::Thread("rasterizer");
auto rasterizer_queue_id =
rasterizer_thread->GetTaskRunner()->GetTaskQueueId();
// Assume the current thread is the platform thread.
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto platform_queue_id = fml::MessageLoop::GetCurrentTaskQueueId();
return fml::MakeRefCounted<fml::RasterThreadMerger>(platform_queue_id,
rasterizer_queue_id);
}
fml::RefPtr<fml::RasterThreadMerger> GetThreadMergerFromRasterThread() {
auto platform_thread = new fml::Thread("rasterizer");
auto platform_queue_id = platform_thread->GetTaskRunner()->GetTaskQueueId();
// Assume the current thread is the raster thread.
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto rasterizer_queue_id = fml::MessageLoop::GetCurrentTaskQueueId();
return fml::MakeRefCounted<fml::RasterThreadMerger>(platform_queue_id,
rasterizer_queue_id);
}
TEST(AndroidExternalViewEmbedder, GetCurrentCanvases) {
auto jni_mock = std::make_shared<JNIMock>();
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0,
raster_thread_merger);
embedder->PrerollCompositeEmbeddedView(
0, std::make_unique<EmbeddedViewParams>());
@ -26,20 +89,46 @@ TEST(AndroidExternalViewEmbedder, GetCurrentCanvases) {
ASSERT_EQ(SkISize::Make(10, 20), canvases[1]->getBaseLayerSize());
}
TEST(AndroidExternalViewEmbedder, CompositeEmbeddedView) {
auto embedder = new AndroidExternalViewEmbedder(nullptr);
TEST(AndroidExternalViewEmbedder, GetCurrentCanvases__CompositeOrder) {
auto jni_mock = std::make_shared<JNIMock>();
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0,
raster_thread_merger);
embedder->PrerollCompositeEmbeddedView(
0, std::make_unique<EmbeddedViewParams>());
ASSERT_TRUE(embedder->CompositeEmbeddedView(0) != nullptr);
embedder->PrerollCompositeEmbeddedView(
1, std::make_unique<EmbeddedViewParams>());
ASSERT_TRUE(embedder->CompositeEmbeddedView(1) != nullptr);
auto canvases = embedder->GetCurrentCanvases();
ASSERT_EQ(2UL, canvases.size());
ASSERT_EQ(embedder->CompositeEmbeddedView(0), canvases[0]);
ASSERT_EQ(embedder->CompositeEmbeddedView(1), canvases[1]);
}
TEST(AndroidExternalViewEmbedder, CompositeEmbeddedView) {
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, nullptr, nullptr);
ASSERT_EQ(nullptr, embedder->CompositeEmbeddedView(0));
embedder->PrerollCompositeEmbeddedView(
0, std::make_unique<EmbeddedViewParams>());
ASSERT_NE(nullptr, embedder->CompositeEmbeddedView(0));
ASSERT_EQ(nullptr, embedder->CompositeEmbeddedView(1));
embedder->PrerollCompositeEmbeddedView(
1, std::make_unique<EmbeddedViewParams>());
ASSERT_NE(nullptr, embedder->CompositeEmbeddedView(1));
}
TEST(AndroidExternalViewEmbedder, CancelFrame) {
auto embedder = new AndroidExternalViewEmbedder(nullptr);
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, nullptr, nullptr);
embedder->PrerollCompositeEmbeddedView(
0, std::make_unique<EmbeddedViewParams>());
@ -50,18 +139,16 @@ TEST(AndroidExternalViewEmbedder, CancelFrame) {
}
TEST(AndroidExternalViewEmbedder, RasterizerRunsOnPlatformThread) {
auto embedder = new AndroidExternalViewEmbedder(nullptr);
auto platform_thread = new fml::Thread("platform");
auto rasterizer_thread = new fml::Thread("rasterizer");
auto platform_queue_id = platform_thread->GetTaskRunner()->GetTaskQueueId();
auto rasterizer_queue_id =
rasterizer_thread->GetTaskRunner()->GetTaskQueueId();
auto jni_mock = std::make_shared<JNIMock>();
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
auto raster_thread_merger = fml::MakeRefCounted<fml::RasterThreadMerger>(
platform_queue_id, rasterizer_queue_id);
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
ASSERT_FALSE(raster_thread_merger->IsMerged());
embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0);
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0,
raster_thread_merger);
// Push a platform view.
embedder->PrerollCompositeEmbeddedView(
0, std::make_unique<EmbeddedViewParams>());
@ -70,7 +157,9 @@ TEST(AndroidExternalViewEmbedder, RasterizerRunsOnPlatformThread) {
ASSERT_EQ(PostPrerollResult::kResubmitFrame, postpreroll_result);
ASSERT_TRUE(embedder->SubmitFrame(nullptr, nullptr));
EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
embedder->EndFrame(raster_thread_merger);
ASSERT_TRUE(raster_thread_merger->IsMerged());
int pending_frames = 0;
@ -82,23 +171,221 @@ TEST(AndroidExternalViewEmbedder, RasterizerRunsOnPlatformThread) {
}
TEST(AndroidExternalViewEmbedder, RasterizerRunsOnRasterizerThread) {
auto embedder = new AndroidExternalViewEmbedder(nullptr);
auto platform_thread = new fml::Thread("platform");
auto rasterizer_thread = new fml::Thread("rasterizer");
auto platform_queue_id = platform_thread->GetTaskRunner()->GetTaskQueueId();
auto rasterizer_queue_id =
rasterizer_thread->GetTaskRunner()->GetTaskQueueId();
auto jni_mock = std::make_shared<JNIMock>();
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
auto raster_thread_merger = fml::MakeRefCounted<fml::RasterThreadMerger>(
platform_queue_id, rasterizer_queue_id);
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
ASSERT_FALSE(raster_thread_merger->IsMerged());
PostPrerollResult result = embedder->PostPrerollAction(raster_thread_merger);
ASSERT_EQ(PostPrerollResult::kSuccess, result);
EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
embedder->EndFrame(raster_thread_merger);
ASSERT_FALSE(raster_thread_merger->IsMerged());
}
TEST(AndroidExternalViewEmbedder, PlatformViewRect) {
auto jni_mock = std::make_shared<JNIMock>();
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(SkISize::Make(100, 100), nullptr, 1.5,
raster_thread_merger);
auto view_params = std::make_unique<EmbeddedViewParams>();
view_params->offsetPixels = SkPoint::Make(10, 20);
view_params->sizePoints = SkSize::Make(30, 40);
auto view_id = 0;
embedder->PrerollCompositeEmbeddedView(view_id, std::move(view_params));
ASSERT_EQ(SkRect::MakeXYWH(10, 20, 45, 60), embedder->GetViewRect(view_id));
}
TEST(AndroidExternalViewEmbedder, PlatformViewRect__ChangedParams) {
auto jni_mock = std::make_shared<JNIMock>();
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(SkISize::Make(100, 100), nullptr, 1.5,
raster_thread_merger);
auto view_id = 0;
auto view_params_1 = std::make_unique<EmbeddedViewParams>();
view_params_1->offsetPixels = SkPoint::Make(10, 20);
view_params_1->sizePoints = SkSize::Make(30, 40);
embedder->PrerollCompositeEmbeddedView(view_id, std::move(view_params_1));
auto view_params_2 = std::make_unique<EmbeddedViewParams>();
view_params_2->offsetPixels = SkPoint::Make(50, 60);
view_params_2->sizePoints = SkSize::Make(70, 80);
embedder->PrerollCompositeEmbeddedView(view_id, std::move(view_params_2));
ASSERT_EQ(SkRect::MakeXYWH(50, 60, 105, 120), embedder->GetViewRect(view_id));
}
TEST(AndroidExternalViewEmbedder, SubmitFrame__RecycleSurfaces) {
auto jni_mock = std::make_shared<JNIMock>();
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
auto gr_context = GrContext::MakeMock(nullptr);
auto frame_size = SkISize::Make(1000, 1000);
auto surface_factory =
[gr_context, window, frame_size](
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
auto surface_frame_1 = std::make_unique<SurfaceFrame>(
SkSurface::MakeNull(1000, 1000), false,
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
return true;
});
auto surface_frame_2 = std::make_unique<SurfaceFrame>(
SkSurface::MakeNull(1000, 1000), false,
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
return true;
});
auto surface_mock = std::make_unique<SurfaceMock>();
EXPECT_CALL(*surface_mock, AcquireFrame(frame_size))
.Times(2 /* frames */)
.WillOnce(Return(ByMove(std::move(surface_frame_1))))
.WillOnce(Return(ByMove(std::move(surface_frame_2))));
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()))
.WillOnce(Return(ByMove(std::move(surface_mock))));
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
return android_surface_mock;
};
auto embedder = std::make_unique<AndroidExternalViewEmbedder>(
android_context, jni_mock, surface_factory);
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
// ------------------ First frame ------------------ //
{
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger);
// Add an Android view.
auto view_params_1 = std::make_unique<EmbeddedViewParams>();
view_params_1->offsetPixels = SkPoint::Make(100, 100);
// TODO(egarciad): Investigate why Flow applies the device pixel ratio to
// the offsetPixels, but not the sizePoints.
view_params_1->sizePoints = SkSize::Make(200, 200);
embedder->PrerollCompositeEmbeddedView(0, std::move(view_params_1));
// This is the recording canvas flow writes to.
auto canvas_1 = embedder->CompositeEmbeddedView(0);
auto rect_paint = SkPaint();
rect_paint.setColor(SkColors::kCyan);
rect_paint.setStyle(SkPaint::Style::kFill_Style);
// This simulates Flutter UI that doesn't intersect with the Android view.
canvas_1->drawRect(SkRect::MakeXYWH(0, 0, 50, 50), rect_paint);
// This simulates Flutter UI that intersects with the Android view.
canvas_1->drawRect(SkRect::MakeXYWH(50, 50, 200, 200), rect_paint);
canvas_1->drawRect(SkRect::MakeXYWH(150, 150, 100, 100), rect_paint);
// Create a new overlay surface.
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));
// The JNI call to display the Android view.
EXPECT_CALL(*jni_mock,
FlutterViewOnDisplayPlatformView(0, 100, 100, 300, 300));
// The JNI call to display the overlay surface.
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
auto surface_frame =
std::make_unique<SurfaceFrame>(SkSurface::MakeNull(1000, 1000), false,
[](const SurfaceFrame& surface_frame,
SkCanvas* canvas) { return true; });
embedder->SubmitFrame(gr_context.get(), std::move(surface_frame));
EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
embedder->EndFrame(raster_thread_merger);
}
// ------------------ Second frame ------------------ //
{
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger);
// Add an Android view.
auto view_params_1 = std::make_unique<EmbeddedViewParams>();
view_params_1->offsetPixels = SkPoint::Make(100, 100);
// TODO(egarciad): Investigate why Flow applies the device pixel ratio to
// the offsetPixels, but not the sizePoints.
view_params_1->sizePoints = SkSize::Make(200, 200);
embedder->PrerollCompositeEmbeddedView(0, std::move(view_params_1));
// This is the recording canvas flow writes to.
auto canvas_1 = embedder->CompositeEmbeddedView(0);
auto rect_paint = SkPaint();
rect_paint.setColor(SkColors::kCyan);
rect_paint.setStyle(SkPaint::Style::kFill_Style);
// This simulates Flutter UI that doesn't intersect with the Android view.
canvas_1->drawRect(SkRect::MakeXYWH(0, 0, 50, 50), rect_paint);
// This simulates Flutter UI that intersects with the Android view.
canvas_1->drawRect(SkRect::MakeXYWH(50, 50, 200, 200), rect_paint);
canvas_1->drawRect(SkRect::MakeXYWH(150, 150, 100, 100), rect_paint);
// Don't create a new overlay surface since it's recycled from the first
// frame.
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface()).Times(0);
// The JNI call to display the Android view.
EXPECT_CALL(*jni_mock,
FlutterViewOnDisplayPlatformView(0, 100, 100, 300, 300));
// The JNI call to display the overlay surface.
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
auto surface_frame =
std::make_unique<SurfaceFrame>(SkSurface::MakeNull(1000, 1000), false,
[](const SurfaceFrame& surface_frame,
SkCanvas* canvas) { return true; });
embedder->SubmitFrame(gr_context.get(), std::move(surface_frame));
EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
embedder->EndFrame(raster_thread_merger);
}
}
TEST(AndroidExternalViewEmbedder, DoesNotCallJNIPlatformThreadOnlyMethods) {
auto jni_mock = std::make_shared<JNIMock>();
auto embedder =
std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
// While on the raster thread, don't make JNI calls as these methods can only
// run on the platform thread.
auto raster_thread_merger = GetThreadMergerFromRasterThread();
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()).Times(0);
embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0,
raster_thread_merger);
EXPECT_CALL(*jni_mock, FlutterViewEndFrame()).Times(0);
embedder->EndFrame(raster_thread_merger);
}
} // namespace testing
} // namespace flutter

View File

@ -0,0 +1,81 @@
// 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/android/external_view_embedder/surface_pool.h"
namespace flutter {
OverlayLayer::OverlayLayer(int id,
std::unique_ptr<AndroidSurface> android_surface,
std::unique_ptr<Surface> surface)
: id(id),
android_surface(std::move(android_surface)),
surface(std::move(surface)){};
OverlayLayer::~OverlayLayer() = default;
SurfacePool::SurfacePool() = default;
SurfacePool::~SurfacePool() = default;
std::shared_ptr<OverlayLayer> SurfacePool::GetLayer(
GrContext* gr_context,
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const AndroidSurface::Factory& surface_factory) {
intptr_t gr_context_key = reinterpret_cast<intptr_t>(gr_context);
// Allocate a new surface if there isn't one available.
if (available_layer_index_ >= layers_.size()) {
std::unique_ptr<AndroidSurface> android_surface =
surface_factory(android_context, jni_facade);
FML_CHECK(android_surface && android_surface->IsValid())
<< "Could not create an OpenGL, Vulkan or Software surface to setup "
"rendering.";
std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata> java_metadata =
jni_facade->FlutterViewCreateOverlaySurface();
FML_CHECK(java_metadata->window);
android_surface->SetNativeWindow(java_metadata->window);
std::unique_ptr<Surface> surface =
android_surface->CreateGPUSurface(gr_context);
std::shared_ptr<OverlayLayer> layer =
std::make_shared<OverlayLayer>(java_metadata->id, //
std::move(android_surface), //
std::move(surface) //
);
layer->gr_context_key = gr_context_key;
layers_.push_back(layer);
}
std::shared_ptr<OverlayLayer> layer = layers_[available_layer_index_];
// Since the surfaces are recycled, it's possible that the GrContext is
// different.
if (gr_context_key != layer->gr_context_key) {
layer->gr_context_key = gr_context_key;
// The overlay already exists, but the GrContext was changed so we need to
// recreate the rendering surface with the new GrContext.
std::unique_ptr<Surface> surface =
layer->android_surface->CreateGPUSurface(gr_context);
layer->surface = std::move(surface);
}
available_layer_index_++;
return layer;
}
void SurfacePool::RecycleLayers() {
available_layer_index_ = 0;
}
std::vector<std::shared_ptr<OverlayLayer>> SurfacePool::GetUnusedLayers() {
std::vector<std::shared_ptr<OverlayLayer>> results;
for (size_t i = available_layer_index_; i < layers_.size(); i++) {
results.push_back(layers_[i]);
}
return results;
}
} // namespace flutter

View File

@ -0,0 +1,86 @@
// 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_ANDROID_EXTERNAL_VIEW_EMBEDDER_SURFACE_POOL_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_VIEW_EMBEDDER_SURFACE_POOL_H_
#include "flutter/flow/surface.h"
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/surface/android_surface.h"
namespace flutter {
//------------------------------------------------------------------------------
/// An Overlay layer represents an `android.view.View` in the C side.
///
/// The `id` is used to uniquely identify the layer and recycle it between
/// frames.
///
struct OverlayLayer {
OverlayLayer(int id,
std::unique_ptr<AndroidSurface> android_surface,
std::unique_ptr<Surface> surface);
~OverlayLayer();
// A unique id to identify the overlay when it gets recycled.
const int id;
// A GPU surface.
const std::unique_ptr<AndroidSurface> android_surface;
// A GPU surface. This may change when the overlay is recycled.
std::unique_ptr<Surface> surface;
// The `GrContext` that is currently used by the overlay surfaces.
// We track this to know when the GrContext for the Flutter app has changed
// so we can update the overlay with the new context.
//
// This may change when the overlay is recycled.
intptr_t gr_context_key;
};
// This class isn't thread safe.
class SurfacePool {
public:
SurfacePool();
~SurfacePool();
// Gets a layer from the pool if available, or allocates a new one.
// Finally, it marks the layer as used. That is, it increments
// `available_layer_index_`.
std::shared_ptr<OverlayLayer> GetLayer(
GrContext* gr_context,
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const AndroidSurface::Factory& surface_factory);
// Gets the layers in the pool that aren't currently used.
// This method doesn't mark the layers as unused.
std::vector<std::shared_ptr<OverlayLayer>> GetUnusedLayers();
// Marks the layers in the pool as available for reuse.
void RecycleLayers();
private:
// The index of the entry in the layers_ vector that determines the beginning
// of the unused layers. For example, consider the following vector:
// _____
// | 0 |
// |---|
// | 1 | <-- `available_layer_index_`
// |---|
// | 2 |
// |---|
//
// This indicates that entries starting from 1 can be reused meanwhile the
// entry at position 0 cannot be reused.
size_t available_layer_index_ = 0;
std::vector<std::shared_ptr<OverlayLayer>> layers_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_VIEW_EMBEDDER_SURFACE_POOL_H_

View File

@ -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/fml/make_copyable.h"
#include "flutter/shell/platform/android/external_view_embedder/surface_pool.h"
#include "flutter/shell/platform/android/jni/jni_mock.h"
#include "flutter/shell/platform/android/surface/android_surface_mock.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "third_party/skia/include/gpu/GrContext.h"
namespace flutter {
namespace testing {
using ::testing::ByMove;
using ::testing::Return;
TEST(SurfacePool, GetLayer__AllocateOneLayer) {
auto pool = std::make_unique<SurfacePool>();
auto gr_context = GrContext::MakeMock(nullptr);
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
auto jni_mock = std::make_shared<JNIMock>();
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));
auto surface_factory =
[gr_context, window](std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()));
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
return android_surface_mock;
};
auto layer = pool->GetLayer(gr_context.get(), android_context, jni_mock,
surface_factory);
ASSERT_NE(nullptr, layer);
ASSERT_EQ(reinterpret_cast<intptr_t>(gr_context.get()),
layer->gr_context_key);
}
TEST(SurfacePool, GetUnusedLayers) {
auto pool = std::make_unique<SurfacePool>();
auto gr_context = GrContext::MakeMock(nullptr);
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
auto jni_mock = std::make_shared<JNIMock>();
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));
auto surface_factory =
[gr_context, window](std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()));
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
return android_surface_mock;
};
auto layer = pool->GetLayer(gr_context.get(), android_context, jni_mock,
surface_factory);
ASSERT_EQ(0UL, pool->GetUnusedLayers().size());
pool->RecycleLayers();
ASSERT_EQ(1UL, pool->GetUnusedLayers().size());
ASSERT_EQ(layer, pool->GetUnusedLayers()[0]);
}
TEST(SurfacePool, GetLayer__Recycle) {
auto pool = std::make_unique<SurfacePool>();
auto gr_context_1 = GrContext::MakeMock(nullptr);
auto jni_mock = std::make_shared<JNIMock>();
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
auto gr_context_2 = GrContext::MakeMock(nullptr);
auto surface_factory =
[gr_context_1, gr_context_2, window](
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
// Allocate two GPU surfaces for each gr context.
EXPECT_CALL(*android_surface_mock,
CreateGPUSurface(gr_context_1.get()));
EXPECT_CALL(*android_surface_mock,
CreateGPUSurface(gr_context_2.get()));
// Set the native window once.
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
return android_surface_mock;
};
auto layer_1 = pool->GetLayer(gr_context_1.get(), android_context, jni_mock,
surface_factory);
pool->RecycleLayers();
auto layer_2 = pool->GetLayer(gr_context_2.get(), android_context, jni_mock,
surface_factory);
ASSERT_NE(nullptr, layer_1);
ASSERT_EQ(layer_1, layer_2);
ASSERT_EQ(reinterpret_cast<intptr_t>(gr_context_2.get()),
layer_1->gr_context_key);
ASSERT_EQ(reinterpret_cast<intptr_t>(gr_context_2.get()),
layer_2->gr_context_key);
}
TEST(SurfacePool, GetLayer__AllocateTwoLayers) {
auto pool = std::make_unique<SurfacePool>();
auto gr_context = GrContext::MakeMock(nullptr);
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
auto jni_mock = std::make_shared<JNIMock>();
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.Times(2)
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))))
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
1, window))));
auto surface_factory =
[gr_context, window](std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
auto android_surface_mock = std::make_unique<AndroidSurfaceMock>();
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()));
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
return android_surface_mock;
};
auto layer_1 = pool->GetLayer(gr_context.get(), android_context, jni_mock,
surface_factory);
auto layer_2 = pool->GetLayer(gr_context.get(), android_context, jni_mock,
surface_factory);
ASSERT_NE(nullptr, layer_1);
ASSERT_NE(nullptr, layer_2);
ASSERT_NE(layer_1, layer_2);
ASSERT_EQ(0, layer_1->id);
ASSERT_EQ(1, layer_2->id);
}
} // namespace testing
} // namespace flutter

View File

@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//flutter/common/config.gni")
import("//flutter/testing/testing.gni")
source_set("jni") {
sources = [
@ -15,6 +16,41 @@ source_set("jni") {
deps = [
"//flutter/fml",
"//flutter/lib/ui",
"//flutter/shell/platform/android/surface:native_window",
"//third_party/skia",
]
}
source_set("jni_mock") {
testonly = true
sources = [
"jni_mock.h",
]
public_configs = [ "//flutter:config" ]
deps = [
":jni",
]
}
test_fixtures("jni_fixtures") {
fixtures = []
}
executable("jni_unittests") {
testonly = true
sources = [
"jni_mock_unittest.cc",
]
deps = [
":jni_fixtures",
":jni_mock",
"//flutter/testing",
"//flutter/testing:dart",
"//flutter/testing:skia",
]
}

View File

@ -0,0 +1,91 @@
// 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_ANDROID_JNI_MOCK_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_JNI_MOCK_H_
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "gmock/gmock.h"
namespace flutter {
//------------------------------------------------------------------------------
/// Mock for |PlatformViewAndroidJNI|. This implementation can be used in unit
/// tests without requiring the Android toolchain.
///
class JNIMock final : public PlatformViewAndroidJNI {
public:
MOCK_METHOD(void,
FlutterViewHandlePlatformMessage,
(fml::RefPtr<flutter::PlatformMessage> message, int responseId),
(override));
MOCK_METHOD(void,
FlutterViewHandlePlatformMessageResponse,
(int responseId, std::unique_ptr<fml::Mapping> data),
(override));
MOCK_METHOD(void,
FlutterViewUpdateSemantics,
(std::vector<uint8_t> buffer, std::vector<std::string> strings),
(override));
MOCK_METHOD(void,
FlutterViewUpdateCustomAccessibilityActions,
(std::vector<uint8_t> actions_buffer,
std::vector<std::string> strings),
(override));
MOCK_METHOD(void, FlutterViewOnFirstFrame, (), (override));
MOCK_METHOD(void, FlutterViewOnPreEngineRestart, (), (override));
MOCK_METHOD(void,
SurfaceTextureAttachToGLContext,
(JavaWeakGlobalRef surface_texture, int textureId),
(override));
MOCK_METHOD(void,
SurfaceTextureUpdateTexImage,
(JavaWeakGlobalRef surface_texture),
(override));
MOCK_METHOD(void,
SurfaceTextureGetTransformMatrix,
(JavaWeakGlobalRef surface_texture, SkMatrix& transform),
(override));
MOCK_METHOD(void,
SurfaceTextureDetachFromGLContext,
(JavaWeakGlobalRef surface_texture),
(override));
MOCK_METHOD(void,
FlutterViewOnDisplayPlatformView,
(int view_id, int x, int y, int width, int height),
(override));
MOCK_METHOD(void,
FlutterViewDisplayOverlaySurface,
(int surface_id, int x, int y, int width, int height),
(override));
MOCK_METHOD(void, FlutterViewBeginFrame, (), (override));
MOCK_METHOD(void, FlutterViewEndFrame, (), (override));
MOCK_METHOD(std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>,
FlutterViewCreateOverlaySurface,
(),
(override));
MOCK_METHOD(std::unique_ptr<std::vector<std::string>>,
FlutterViewComputePlatformResolvedLocale,
(std::vector<std::string> supported_locales_data),
(override));
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_ANDROID_JNI_MOCK_H_

View File

@ -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.
#include "flutter/shell/platform/android/jni/jni_mock.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace flutter {
namespace testing {
TEST(JNIMock, FlutterViewHandlePlatformMessage) {
JNIMock mock;
auto message =
fml::MakeRefCounted<PlatformMessage>("<channel-name>", nullptr);
auto response_id = 1;
EXPECT_CALL(mock, FlutterViewHandlePlatformMessage(message, response_id));
mock.FlutterViewHandlePlatformMessage(message, response_id);
}
} // namespace testing
} // namespace flutter

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_JNI_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_JNI_H_
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_JNI_PLATFORM_VIEW_ANDROID_JNI_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_JNI_PLATFORM_VIEW_ANDROID_JNI_H_
#include "flutter/fml/macros.h"
#include "flutter/fml/mapping.h"
@ -13,6 +13,7 @@
#endif
#include "flutter/lib/ui/window/platform_message.h"
#include "flutter/shell/platform/android/surface/android_native_window.h"
#include "third_party/skia/include/core/SkMatrix.h"
namespace flutter {
@ -147,12 +148,32 @@ class PlatformViewAndroidJNI {
///
virtual void FlutterViewEndFrame() = 0;
//------------------------------------------------------------------------------
/// The metadata returned from Java which is converted into an |OverlayLayer|
/// by |SurfacePool|.
///
struct OverlayMetadata {
OverlayMetadata(int id, fml::RefPtr<AndroidNativeWindow> window)
: id(id), window(window){};
~OverlayMetadata() = default;
// A unique id to identify the overlay when it gets recycled.
const int id;
// Holds a reference to the native window. That is, an `ANativeWindow`,
// which is the C counterpart of the `android.view.Surface` object in Java.
const fml::RefPtr<AndroidNativeWindow> window;
};
//----------------------------------------------------------------------------
/// @brief Instantiates an overlay surface in hybrid composition.
/// @brief Instantiates an overlay surface in hybrid composition and
/// provides the necessary metadata to operate the surface in C.
///
/// @note Must be called from the platform thread.
///
virtual void FlutterViewCreateOverlaySurface() = 0;
virtual std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>
FlutterViewCreateOverlaySurface() = 0;
//----------------------------------------------------------------------------
/// @brief Computes the locale Android would select.
@ -164,4 +185,4 @@ class PlatformViewAndroidJNI {
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_JNI_H_
#endif // FLUTTER_SHELL_PLATFORM_ANDROID_JNI_PLATFORM_VIEW_ANDROID_JNI_H_

View File

@ -10,15 +10,43 @@
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/shell/common/shell_io_manager.h"
#include "flutter/shell/gpu/gpu_surface_gl_delegate.h"
#include "flutter/shell/platform/android/android_context.h"
#include "flutter/shell/platform/android/android_context_gl.h"
#include "flutter/shell/platform/android/android_external_texture_gl.h"
#include "flutter/shell/platform/android/android_surface_gl.h"
#include "flutter/shell/platform/android/android_surface_software.h"
#if SHELL_ENABLE_VULKAN
#include "flutter/shell/platform/android/android_surface_vulkan.h"
#endif // SHELL_ENABLE_VULKAN
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/platform_message_response_android.h"
#include "flutter/shell/platform/android/vsync_waiter_android.h"
namespace flutter {
std::unique_ptr<AndroidSurface> SurfaceFactory(
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade) {
FML_CHECK(SurfaceFactory);
switch (android_context->RenderingApi()) {
case AndroidRenderingAPI::kSoftware:
return std::make_unique<AndroidSurfaceSoftware>(
android_context, jni_facade, SurfaceFactory);
case AndroidRenderingAPI::kOpenGLES:
return std::make_unique<AndroidSurfaceGL>(android_context, jni_facade,
SurfaceFactory);
case AndroidRenderingAPI::kVulkan:
#if SHELL_ENABLE_VULKAN
return std::make_unique<AndroidSurfaceVulkan>(android_context, jni_facade,
SurfaceFactory);
#endif // SHELL_ENABLE_VULKAN
return nullptr;
}
return nullptr;
}
PlatformViewAndroid::PlatformViewAndroid(
PlatformView::Delegate& delegate,
flutter::TaskRunners task_runners,
@ -27,16 +55,23 @@ PlatformViewAndroid::PlatformViewAndroid(
: PlatformView(delegate, std::move(task_runners)), jni_facade_(jni_facade) {
std::shared_ptr<AndroidContext> android_context;
if (use_software_rendering) {
android_context = AndroidContext::Create(AndroidRenderingAPI::kSoftware);
android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
} else {
#if SHELL_ENABLE_VULKAN
android_context = AndroidContext::Create(AndroidRenderingAPI::kVulkan);
android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kVulkan);
#else // SHELL_ENABLE_VULKAN
android_context = AndroidContext::Create(AndroidRenderingAPI::kOpenGLES);
android_context = std::make_shared<AndroidContextGL>(
AndroidRenderingAPI::kOpenGLES,
fml::MakeRefCounted<AndroidEnvironmentGL>());
#endif // SHELL_ENABLE_VULKAN
}
android_surface_ = AndroidSurface::Create(android_context, jni_facade);
FML_CHECK(android_surface_)
FML_CHECK(android_context && android_context->IsValid())
<< "Could not create an Android context.";
android_surface_ = SurfaceFactory(std::move(android_context), jni_facade);
FML_CHECK(android_surface_ && android_surface_->IsValid())
<< "Could not create an OpenGL, Vulkan or Software surface to setup "
"rendering.";
}

View File

@ -15,9 +15,9 @@
#include "flutter/fml/platform/android/scoped_java_ref.h"
#include "flutter/lib/ui/window/platform_message.h"
#include "flutter/shell/common/platform_view.h"
#include "flutter/shell/platform/android/android_native_window.h"
#include "flutter/shell/platform/android/android_surface.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/surface/android_native_window.h"
#include "flutter/shell/platform/android/surface/android_surface.h"
namespace flutter {

View File

@ -1104,17 +1104,21 @@ void PlatformViewAndroidJNIImpl::FlutterViewEndFrame() {
FML_CHECK(CheckException(env));
}
void PlatformViewAndroidJNIImpl::FlutterViewCreateOverlaySurface() {
std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>
PlatformViewAndroidJNIImpl::FlutterViewCreateOverlaySurface() {
JNIEnv* env = fml::jni::AttachCurrentThread();
auto java_object = java_object_.get(env);
if (java_object.is_null()) {
return;
return nullptr;
}
env->CallVoidMethod(java_object.obj(), g_create_overlay_surface_method);
FML_CHECK(CheckException(env));
// TODO(egarciad): Wire this up.
// https://github.com/flutter/flutter/issues/55270
return std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(0, nullptr);
}
std::unique_ptr<std::vector<std::string>>

View File

@ -66,7 +66,8 @@ class PlatformViewAndroidJNIImpl final : public PlatformViewAndroidJNI {
void FlutterViewEndFrame() override;
void FlutterViewCreateOverlaySurface() override;
std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>
FlutterViewCreateOverlaySurface() override;
std::unique_ptr<std::vector<std::string>>
FlutterViewComputePlatformResolvedLocale(

View File

@ -0,0 +1,54 @@
# 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.
source_set("surface") {
sources = [
"android_surface.cc",
"android_surface.h",
]
public_configs = [ "//flutter:config" ]
deps = [
":native_window",
"//flutter/flow",
"//flutter/fml",
"//flutter/shell/platform/android/context",
"//flutter/shell/platform/android/jni",
"//third_party/skia",
]
}
source_set("native_window") {
sources = [
"android_native_window.cc",
"android_native_window.h",
]
public_configs = [ "//flutter:config" ]
deps = [
"//flutter/fml",
"//third_party/skia",
]
}
source_set("surface_mock") {
testonly = true
sources = [
"android_surface_mock.cc",
"android_surface_mock.h",
]
public_configs = [ "//flutter:config" ]
deps = [
":surface",
"//flutter/shell/gpu:gpu_surface_gl",
"//third_party/googletest:gmock",
"//third_party/googletest:gtest",
"//third_party/skia",
]
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/android/android_native_window.h"
#include "flutter/shell/platform/android/surface/android_native_window.h"
namespace flutter {
@ -10,7 +10,9 @@ AndroidNativeWindow::AndroidNativeWindow(Handle window) : window_(window) {}
AndroidNativeWindow::~AndroidNativeWindow() {
if (window_ != nullptr) {
#if OS_ANDROID
ANativeWindow_release(window_);
#endif // OS_ANDROID
window_ = nullptr;
}
}
@ -24,9 +26,13 @@ AndroidNativeWindow::Handle AndroidNativeWindow::handle() const {
}
SkISize AndroidNativeWindow::GetSize() const {
#if OS_ANDROID
return window_ == nullptr ? SkISize::Make(0, 0)
: SkISize::Make(ANativeWindow_getWidth(window_),
ANativeWindow_getHeight(window_));
#else // OS_ANDROID
return SkISize::Make(0, 0);
#endif // OS_ANDROID
}
} // namespace flutter

View File

@ -5,10 +5,14 @@
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_NATIVE_WINDOW_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_NATIVE_WINDOW_H_
#include "flutter/fml/build_config.h"
#if OS_ANDROID
#include <android/native_window.h>
#endif // OS_ANDROID
#include "flutter/fml/macros.h"
#include "flutter/fml/memory/ref_counted.h"
#include "flutter/fml/memory/ref_ptr.h"
#include "third_party/skia/include/core/SkSize.h"
namespace flutter {
@ -16,7 +20,11 @@ namespace flutter {
class AndroidNativeWindow
: public fml::RefCountedThreadSafe<AndroidNativeWindow> {
public:
#if OS_ANDROID
using Handle = ANativeWindow*;
#else // OS_ANDROID
using Handle = std::nullptr_t;
#endif // OS_ANDROID
bool IsValid() const;

View File

@ -0,0 +1,11 @@
// 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/android/surface/android_surface.h"
namespace flutter {
AndroidSurface::~AndroidSurface() = default;
} // namespace flutter

View File

@ -5,25 +5,20 @@
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SURFACE_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SURFACE_H_
#include <memory>
#include "flutter/flow/surface.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/platform/android/jni_util.h"
#include "flutter/fml/platform/android/jni_weak_ref.h"
#include "flutter/shell/common/platform_view.h"
#include "flutter/shell/platform/android/android_context.h"
#include "flutter/shell/platform/android/android_native_window.h"
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/surface/android_native_window.h"
#include "third_party/skia/include/core/SkSize.h"
namespace flutter {
class AndroidSurface {
public:
static std::unique_ptr<AndroidSurface> Create(
using Factory = std::function<std::unique_ptr<AndroidSurface>(
std::shared_ptr<AndroidContext> android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)>;
virtual ~AndroidSurface();

View File

@ -0,0 +1,29 @@
// 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/android/surface/android_surface_mock.h"
namespace flutter {
std::unique_ptr<GLContextResult> AndroidSurfaceMock::GLContextMakeCurrent() {
return std::make_unique<GLContextDefaultResult>(/*static_result=*/true);
}
bool AndroidSurfaceMock::GLContextClearCurrent() {
return true;
}
bool AndroidSurfaceMock::GLContextPresent() {
return true;
}
intptr_t AndroidSurfaceMock::GLContextFBO() const {
return 0;
}
ExternalViewEmbedder* AndroidSurfaceMock::GetExternalViewEmbedder() {
return nullptr;
}
} // namespace flutter

View File

@ -0,0 +1,59 @@
// 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_ANDROID_ANDROID_SURFACE_MOCK_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SURFACE_MOCK_H_
#include "flutter/shell/gpu/gpu_surface_gl.h"
#include "flutter/shell/platform/android/surface/android_surface.h"
#include "gmock/gmock.h"
namespace flutter {
//------------------------------------------------------------------------------
/// Mock for |AndroidSurface|. This implementation can be used in unit
/// tests without requiring the Android toolchain.
///
class AndroidSurfaceMock final : public GPUSurfaceGLDelegate,
public AndroidSurface {
public:
MOCK_METHOD(bool, IsValid, (), (const, override));
MOCK_METHOD(void, TeardownOnScreenContext, (), (override));
MOCK_METHOD(std::unique_ptr<Surface>,
CreateGPUSurface,
(GrContext * gr_context),
(override));
MOCK_METHOD(bool, OnScreenSurfaceResize, (const SkISize& size), (override));
MOCK_METHOD(bool, ResourceContextMakeCurrent, (), (override));
MOCK_METHOD(bool, ResourceContextClearCurrent, (), (override));
MOCK_METHOD(bool,
SetNativeWindow,
(fml::RefPtr<AndroidNativeWindow> window),
(override));
// |GPUSurfaceGLDelegate|
std::unique_ptr<GLContextResult> GLContextMakeCurrent() override;
// |GPUSurfaceGLDelegate|
bool GLContextClearCurrent() override;
// |GPUSurfaceGLDelegate|
bool GLContextPresent() override;
// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO() const override;
// |GPUSurfaceGLDelegate|
ExternalViewEmbedder* GetExternalViewEmbedder() override;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SURFACE_MOCK_H_

View File

@ -62,7 +62,11 @@ class IOSSurface : public ExternalViewEmbedder {
void CancelFrame() override;
// |ExternalViewEmbedder|
void BeginFrame(SkISize frame_size, GrContext* context, double device_pixel_ratio) override;
void BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
// |ExternalViewEmbedder|
void PrerollCompositeEmbeddedView(int view_id,
std::unique_ptr<flutter::EmbeddedViewParams> params) override;

View File

@ -94,7 +94,10 @@ void IOSSurface::CancelFrame() {
}
// |ExternalViewEmbedder|
void IOSSurface::BeginFrame(SkISize frame_size, GrContext* context, double device_pixel_ratio) {
void IOSSurface::BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
TRACE_EVENT0("flutter", "IOSSurface::BeginFrame");
FML_CHECK(platform_views_controller_ != nullptr);
platform_views_controller_->SetFrameSize(frame_size);

View File

@ -47,9 +47,11 @@ void EmbedderExternalViewEmbedder::CancelFrame() {
}
// |ExternalViewEmbedder|
void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio) {
void EmbedderExternalViewEmbedder::BeginFrame(
SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
Reset();
pending_frame_size_ = frame_size;

View File

@ -73,9 +73,11 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder {
void CancelFrame() override;
// |ExternalViewEmbedder|
void BeginFrame(SkISize frame_size,
GrContext* context,
double device_pixel_ratio) override;
void BeginFrame(
SkISize frame_size,
GrContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
// |ExternalViewEmbedder|
void PrerollCompositeEmbeddedView(

View File

@ -18,6 +18,7 @@ source_set("testing_lib") {
public_deps = [
"//flutter/fml",
"//third_party/googletest:gmock",
"//third_party/googletest:gtest",
]
public_configs = [ "//flutter:config" ]

View File

@ -131,16 +131,17 @@ def RunCCTests(build_dir, filter):
RunEngineExecutable(build_dir, 'runtime_unittests', filter, shuffle_flags)
# https://github.com/flutter/flutter/issues/36295
if not IsWindows():
# https://github.com/flutter/flutter/issues/36295
RunEngineExecutable(build_dir, 'shell_unittests', filter, shuffle_flags)
# https://github.com/google/googletest/issues/2490
RunEngineExecutable(build_dir, 'android_external_view_embedder_unittests', filter, shuffle_flags)
RunEngineExecutable(build_dir, 'jni_unittests', filter, shuffle_flags)
RunEngineExecutable(build_dir, 'ui_unittests', filter, shuffle_flags)
RunEngineExecutable(build_dir, 'testing_unittests', filter, shuffle_flags)
RunEngineExecutable(build_dir, 'android_external_view_embedder_unittests', filter, shuffle_flags)
# These unit-tests are Objective-C and can only run on Darwin.
if IsMac():
RunEngineExecutable(build_dir, 'flutter_channels_unittests', filter, shuffle_flags)