[Android] HC++ external view embedder and JNI plumbing. (#162493)

Part of HC++ work. still not wired up end to end, but only requires a
few more swapchain changes and a runtime flag...
This commit is contained in:
Jonah Williams 2025-02-04 17:09:13 -08:00 committed by GitHub
parent b68321a45d
commit 8c11026d3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 870 additions and 36 deletions

View File

@ -41611,6 +41611,8 @@ ORIGIN: ../../../flutter/shell/platform/android/context/android_context.cc + ../
ORIGIN: ../../../flutter/shell/platform/android/context/android_context.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder_2.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder_2.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/flutter_main.cc + ../../../flutter/LICENSE
@ -44563,6 +44565,8 @@ 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_2.cc
FILE: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder_2.h
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/flutter_main.cc

View File

@ -115,11 +115,12 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceVulkanImpeller::AcquireFrame(
}
SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height);
return impeller::RenderToTarget(aiks_context->GetContentContext(), //
render_target, //
display_list, //
sk_cull_rect, //
/*reset_host_buffer=*/true //
return impeller::RenderToTarget(
aiks_context->GetContentContext(), //
render_target, //
display_list, //
sk_cull_rect, //
/*reset_host_buffer=*/surface_frame.submit_info().frame_boundary //
);
};

View File

@ -91,6 +91,25 @@ class MockPlatformViewAndroidJNI : public PlatformViewAndroidJNI {
(),
(override));
MOCK_METHOD(void, FlutterViewDestroyOverlaySurfaces, (), (override));
MOCK_METHOD(ASurfaceTransaction*, createTransaction, (), (override));
MOCK_METHOD(void, swapTransaction, (), (override));
MOCK_METHOD(void, applyTransaction, (), (override));
MOCK_METHOD(void, destroyOverlaySurface2, (), (override));
MOCK_METHOD(std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>,
createOverlaySurface2,
(),
(override));
MOCK_METHOD(void,
onDisplayPlatformView2,
(int32_t view_id,
int32_t x,
int32_t y,
int32_t width,
int32_t height,
int32_t viewWidth,
int32_t viewHeight,
MutatorsStack mutators_stack),
(override));
MOCK_METHOD(std::unique_ptr<std::vector<std::string>>,
FlutterViewComputePlatformResolvedLocale,
(std::vector<std::string> supported_locales_data),

View File

@ -9,6 +9,8 @@ source_set("external_view_embedder") {
sources = [
"external_view_embedder.cc",
"external_view_embedder.h",
"external_view_embedder_2.cc",
"external_view_embedder_2.h",
"surface_pool.cc",
"surface_pool.h",
]

View File

@ -19,7 +19,8 @@ AndroidExternalViewEmbedder::AndroidExternalViewEmbedder(
android_context_(android_context),
jni_facade_(std::move(jni_facade)),
surface_factory_(std::move(surface_factory)),
surface_pool_(std::make_unique<SurfacePool>()),
surface_pool_(
std::make_unique<SurfacePool>(/*use_new_surface_methods=*/false)),
task_runners_(task_runners) {}
// |ExternalViewEmbedder|

View File

@ -74,8 +74,10 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
const fml::RefPtr<fml::RasterThreadMerger>&
raster_thread_merger) override;
// |ExternalViewEmbedder|
bool SupportsDynamicThreadMerging() override;
// |ExternalViewEmbedder|
void Teardown() override;
// Gets the rect based on the device pixel ratio of a platform view displayed

View File

@ -0,0 +1,254 @@
// 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/external_view_embedder_2.h"
#include "flow/view_slicer.h"
#include "flutter/common/constants.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/trace_event.h"
#include "fml/make_copyable.h"
namespace flutter {
AndroidExternalViewEmbedder2::AndroidExternalViewEmbedder2(
const AndroidContext& android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
std::shared_ptr<AndroidSurfaceFactory> surface_factory,
const TaskRunners& task_runners)
: ExternalViewEmbedder(),
android_context_(android_context),
jni_facade_(std::move(jni_facade)),
surface_factory_(std::move(surface_factory)),
surface_pool_(
std::make_unique<SurfacePool>(/*use_new_surface_methods=*/true)),
task_runners_(task_runners) {}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::PrerollCompositeEmbeddedView(
int64_t view_id,
std::unique_ptr<EmbeddedViewParams> params) {
TRACE_EVENT0("flutter",
"AndroidExternalViewEmbedder2::PrerollCompositeEmbeddedView");
SkRect view_bounds = SkRect::Make(frame_size_);
std::unique_ptr<EmbedderViewSlice> view;
view = std::make_unique<DisplayListEmbedderViewSlice>(view_bounds);
slices_.insert_or_assign(view_id, std::move(view));
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|
DlCanvas* AndroidExternalViewEmbedder2::CompositeEmbeddedView(int64_t view_id) {
if (slices_.count(view_id) == 1) {
return slices_.at(view_id)->canvas();
}
return nullptr;
}
SkRect AndroidExternalViewEmbedder2::GetViewRect(
int64_t view_id,
const std::unordered_map<int64_t, EmbeddedViewParams>& view_params) {
const EmbeddedViewParams& params = view_params.at(view_id);
// https://github.com/flutter/flutter/issues/59821
return SkRect::MakeXYWH(params.finalBoundingRect().x(), //
params.finalBoundingRect().y(), //
params.finalBoundingRect().width(), //
params.finalBoundingRect().height() //
);
}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::SubmitFlutterView(
int64_t flutter_view_id,
GrDirectContext* context,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
std::unique_ptr<SurfaceFrame> frame) {
TRACE_EVENT0("flutter", "AndroidExternalViewEmbedder2::SubmitFlutterView");
if (!FrameHasPlatformLayers()) {
frame->Submit();
jni_facade_->applyTransaction();
return;
}
std::unordered_map<int64_t, SkRect> view_rects;
for (auto platform_id : composition_order_) {
view_rects[platform_id] = GetViewRect(platform_id, view_params_);
}
std::unordered_map<int64_t, SkRect> overlay_layers =
SliceViews(frame->Canvas(), //
composition_order_, //
slices_, //
view_rects //
);
// Create Overlay frame.
surface_pool_->TrimLayers();
std::unique_ptr<SurfaceFrame> overlay_frame;
if (surface_pool_->HasLayers()) {
for (int64_t view_id : composition_order_) {
std::unordered_map<int64_t, SkRect>::const_iterator overlay =
overlay_layers.find(view_id);
if (overlay == overlay_layers.end()) {
continue;
}
if (overlay_frame == nullptr) {
std::shared_ptr<OverlayLayer> layer = surface_pool_->GetLayer(
context, android_context_, jni_facade_, surface_factory_);
overlay_frame = layer->surface->AcquireFrame(frame_size_);
}
DlCanvas* overlay_canvas = overlay_frame->Canvas();
int restore_count = overlay_canvas->GetSaveCount();
overlay_canvas->Save();
overlay_canvas->ClipRect(overlay->second);
overlay_canvas->Clear(DlColor::kTransparent());
slices_[view_id]->render_into(overlay_canvas);
overlay_canvas->RestoreToCount(restore_count);
}
}
if (overlay_frame != nullptr) {
overlay_frame->set_submit_info({.frame_boundary = false});
overlay_frame->Submit();
}
frame->Submit();
task_runners_.GetPlatformTaskRunner()->PostTask(fml::MakeCopyable(
[&, composition_order = composition_order_, view_params = view_params_,
jni_facade = jni_facade_, device_pixel_ratio = device_pixel_ratio_,
slices = std::move(slices_)]() -> void {
jni_facade->swapTransaction();
for (int64_t view_id : composition_order) {
SkRect view_rect = GetViewRect(view_id, view_params);
const EmbeddedViewParams& params = view_params.at(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(), //
params.sizePoints().width() * device_pixel_ratio,
params.sizePoints().height() * device_pixel_ratio,
params.mutatorsStack() //
);
}
if (!surface_pool_->HasLayers()) {
surface_pool_->GetLayer(context, android_context_, jni_facade_,
surface_factory_);
}
jni_facade->FlutterViewEndFrame();
}));
}
// |ExternalViewEmbedder|
std::unique_ptr<SurfaceFrame>
AndroidExternalViewEmbedder2::CreateSurfaceIfNeeded(GrDirectContext* context,
int64_t view_id,
EmbedderViewSlice* slice,
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_);
DlCanvas* overlay_canvas = frame->Canvas();
overlay_canvas->Clear(DlColor::kTransparent());
// Offset the picture since its absolute position on the scene is determined
// by the position of the overlay view.
slice->render_into(overlay_canvas);
return frame;
}
// |ExternalViewEmbedder|
PostPrerollResult AndroidExternalViewEmbedder2::PostPrerollAction(
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
return PostPrerollResult::kSuccess;
}
bool AndroidExternalViewEmbedder2::FrameHasPlatformLayers() {
return !composition_order_.empty();
}
// |ExternalViewEmbedder|
DlCanvas* AndroidExternalViewEmbedder2::GetRootCanvas() {
// On Android, the root surface is created from the on-screen render target.
return nullptr;
}
void AndroidExternalViewEmbedder2::Reset() {
previous_frame_view_count_ = composition_order_.size();
composition_order_.clear();
slices_.clear();
}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::BeginFrame(
GrDirectContext* context,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::PrepareFlutterView(
SkISize frame_size,
double device_pixel_ratio) {
Reset();
// The surface size changed. Therefore, destroy existing surfaces as
// the existing surfaces in the pool can't be recycled.
if (frame_size_ != frame_size) {
DestroySurfaces();
}
surface_pool_->SetFrameSize(frame_size);
frame_size_ = frame_size;
device_pixel_ratio_ = device_pixel_ratio;
}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::CancelFrame() {
Reset();
}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::EndFrame(
bool should_resubmit_frame,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {}
// |ExternalViewEmbedder|
bool AndroidExternalViewEmbedder2::SupportsDynamicThreadMerging() {
return false;
}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::Teardown() {
DestroySurfaces();
}
// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder2::DestroySurfaces() {
if (!surface_pool_->HasLayers()) {
return;
}
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetPlatformTaskRunner(),
[&]() {
surface_pool_->DestroyLayers(jni_facade_);
latch.Signal();
});
latch.Wait();
}
} // namespace flutter

View File

@ -0,0 +1,159 @@
// 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_EXTERNAL_VIEW_EMBEDDER_2_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_VIEW_EMBEDDER_EXTERNAL_VIEW_EMBEDDER_2_H_
#include <unordered_map>
#include "flutter/common/task_runners.h"
#include "flutter/flow/embedded_views.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 "flutter/shell/platform/android/surface/android_surface.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|.
///
/// This implementation of the external view embedder is designed only to use
/// HC++ mode. Mixing old HC modes is not supported, but either of the texture
/// composition based platform views can be used with either mode.
class AndroidExternalViewEmbedder2 final : public ExternalViewEmbedder {
public:
AndroidExternalViewEmbedder2(
const AndroidContext& android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
std::shared_ptr<AndroidSurfaceFactory> surface_factory,
const TaskRunners& task_runners);
// |ExternalViewEmbedder|
void PrerollCompositeEmbeddedView(
int64_t view_id,
std::unique_ptr<flutter::EmbeddedViewParams> params) override;
// |ExternalViewEmbedder|
DlCanvas* CompositeEmbeddedView(int64_t view_id) override;
// |ExternalViewEmbedder|
void SubmitFlutterView(
int64_t flutter_view_id,
GrDirectContext* context,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
std::unique_ptr<SurfaceFrame> frame) override;
// |ExternalViewEmbedder|
PostPrerollResult PostPrerollAction(
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger)
override;
// |ExternalViewEmbedder|
DlCanvas* GetRootCanvas() override;
// |ExternalViewEmbedder|
void BeginFrame(GrDirectContext* context,
const fml::RefPtr<fml::RasterThreadMerger>&
raster_thread_merger) override;
// |ExternalViewEmbedder|
void PrepareFlutterView(SkISize frame_size,
double device_pixel_ratio) override;
// |ExternalViewEmbedder|
void CancelFrame() override;
// |ExternalViewEmbedder|
void EndFrame(bool should_resubmit_frame,
const fml::RefPtr<fml::RasterThreadMerger>&
raster_thread_merger) override;
// |ExternalViewEmbedder|
bool SupportsDynamicThreadMerging() override;
// |ExternalViewEmbedder|
void Teardown() override;
// Gets the rect based on the device pixel ratio of a platform view displayed
// on the screen.
static SkRect GetViewRect(
int64_t view_id,
const std::unordered_map<int64_t, EmbeddedViewParams>& view_params);
private:
// The number of frames the rasterizer task runner will continue
// to run on the platform thread after no platform view is rendered.
//
// Note: this is an arbitrary number that attempts to account for cases
// where the platform view might be momentarily off the screen.
static const int kDefaultMergedLeaseDuration = 10;
// Provides metadata to the Android surfaces.
const AndroidContext& android_context_;
// Allows to call methods in Java.
const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
// Allows to create surfaces.
const std::shared_ptr<AndroidSurfaceFactory> surface_factory_;
// Holds surfaces. Allows to recycle surfaces or allocate new ones.
const std::unique_ptr<SurfacePool> surface_pool_;
// The task runners.
const TaskRunners task_runners_;
// 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_;
// The |EmbedderViewSlice| implementation keyed off the platform view id,
// which contains any subsequent operations until the next platform view or
// the end of the last leaf node in the layer tree.
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices_;
// The params for a platform view, which contains the size, position and
// mutation stack.
std::unordered_map<int64_t, EmbeddedViewParams> view_params_;
// The number of platform views in the previous frame.
int64_t previous_frame_view_count_;
// Destroys the surfaces created from the surface factory.
// This method schedules a task on the platform thread, and waits for
// the task until it completes.
void DestroySurfaces();
// Resets the state.
void Reset();
// Whether the layer tree in the current frame has platform layers.
bool FrameHasPlatformLayers();
// Creates a Surface when needed or recycles an existing one.
// Finally, draws the picture on the frame's canvas.
std::unique_ptr<SurfaceFrame> CreateSurfaceIfNeeded(GrDirectContext* context,
int64_t view_id,
EmbedderViewSlice* slice,
const SkRect& rect);
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_VIEW_EMBEDDER_EXTERNAL_VIEW_EMBEDDER_2_H_

View File

@ -17,7 +17,8 @@ OverlayLayer::OverlayLayer(int id,
OverlayLayer::~OverlayLayer() = default;
SurfacePool::SurfacePool() = default;
SurfacePool::SurfacePool(bool use_new_surface_methods)
: use_new_surface_methods_(use_new_surface_methods) {}
SurfacePool::~SurfacePool() = default;
@ -42,7 +43,9 @@ std::shared_ptr<OverlayLayer> SurfacePool::GetLayer(
"rendering.";
std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata> java_metadata =
jni_facade->FlutterViewCreateOverlaySurface();
use_new_surface_methods_
? jni_facade->createOverlaySurface2()
: jni_facade->FlutterViewCreateOverlaySurface();
FML_CHECK(java_metadata->window);
android_surface->SetNativeWindow(java_metadata->window, jni_facade);
@ -96,7 +99,11 @@ void SurfacePool::DestroyLayersLocked(
if (layers_.empty()) {
return;
}
jni_facade->FlutterViewDestroyOverlaySurfaces();
if (use_new_surface_methods_) {
jni_facade->destroyOverlaySurface2();
} else {
jni_facade->FlutterViewDestroyOverlaySurfaces();
}
layers_.clear();
available_layer_index_ = 0;
}
@ -115,4 +122,9 @@ void SurfacePool::SetFrameSize(SkISize frame_size) {
requested_frame_size_ = frame_size;
}
void SurfacePool::TrimLayers() {
std::lock_guard lock(mutex_);
layers_.erase(layers_.begin() + available_layer_index_, layers_.end());
available_layer_index_ = 0;
}
} // namespace flutter

View File

@ -45,7 +45,7 @@ struct OverlayLayer {
class SurfacePool {
public:
SurfacePool();
explicit SurfacePool(bool use_new_surface_methods);
~SurfacePool();
@ -76,6 +76,8 @@ class SurfacePool {
// Returns true if the current pool has layers in use.
bool HasLayers();
void TrimLayers();
private:
// The index of the entry in the layers_ vector that determines the beginning
// of the unused layers. For example, consider the following vector:
@ -102,6 +104,7 @@ class SurfacePool {
// Used to guard public methods.
std::mutex mutex_;
bool use_new_surface_methods_ = false;
void DestroyLayersLocked(
const std::shared_ptr<PlatformViewAndroidJNI>& jni_facade);

View File

@ -38,7 +38,7 @@ class TestAndroidSurfaceFactory : public AndroidSurfaceFactory {
};
TEST(SurfacePool, GetLayerAllocateOneLayer) {
auto pool = std::make_unique<SurfacePool>();
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/false);
auto gr_context = GrDirectContext::MakeMock(nullptr);
auto android_context =
@ -69,7 +69,7 @@ TEST(SurfacePool, GetLayerAllocateOneLayer) {
}
TEST(SurfacePool, GetUnusedLayers) {
auto pool = std::make_unique<SurfacePool>();
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/false);
auto gr_context = GrDirectContext::MakeMock(nullptr);
auto android_context =
@ -102,7 +102,7 @@ TEST(SurfacePool, GetUnusedLayers) {
}
TEST(SurfacePool, GetLayerRecycle) {
auto pool = std::make_unique<SurfacePool>();
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/false);
auto gr_context_1 = GrDirectContext::MakeMock(nullptr);
auto jni_mock = std::make_shared<JNIMock>();
@ -147,7 +147,7 @@ TEST(SurfacePool, GetLayerRecycle) {
}
TEST(SurfacePool, GetLayerAllocateTwoLayers) {
auto pool = std::make_unique<SurfacePool>();
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/false);
auto gr_context = GrDirectContext::MakeMock(nullptr);
auto android_context =
@ -185,8 +185,45 @@ TEST(SurfacePool, GetLayerAllocateTwoLayers) {
ASSERT_EQ(1, layer_2->id);
}
TEST(SurfacePool, DestroyLayersNew) {
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/true);
auto jni_mock = std::make_shared<JNIMock>();
EXPECT_CALL(*jni_mock, destroyOverlaySurface2()).Times(0);
pool->DestroyLayers(jni_mock);
auto gr_context = GrDirectContext::MakeMock(nullptr);
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
EXPECT_CALL(*jni_mock, createOverlaySurface2())
.Times(1)
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));
auto surface_factory =
std::make_shared<TestAndroidSurfaceFactory>([gr_context, window]() {
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;
});
pool->GetLayer(gr_context.get(), *android_context, jni_mock, surface_factory);
EXPECT_CALL(*jni_mock, destroyOverlaySurface2());
ASSERT_TRUE(pool->HasLayers());
pool->DestroyLayers(jni_mock);
ASSERT_FALSE(pool->HasLayers());
ASSERT_TRUE(pool->GetUnusedLayers().empty());
}
TEST(SurfacePool, DestroyLayers) {
auto pool = std::make_unique<SurfacePool>();
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/false);
auto jni_mock = std::make_shared<JNIMock>();
EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(0);
@ -223,7 +260,7 @@ TEST(SurfacePool, DestroyLayers) {
}
TEST(SurfacePool, DestroyLayersFrameSizeChanged) {
auto pool = std::make_unique<SurfacePool>();
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/false);
auto jni_mock = std::make_shared<JNIMock>();
auto gr_context = GrDirectContext::MakeMock(nullptr);
@ -267,5 +304,50 @@ TEST(SurfacePool, DestroyLayersFrameSizeChanged) {
ASSERT_TRUE(pool->HasLayers());
}
TEST(SurfacePool, DestroyLayersFrameSizeChangedNew) {
auto pool = std::make_unique<SurfacePool>(/*use_new_surface_methods=*/true);
auto jni_mock = std::make_shared<JNIMock>();
auto gr_context = GrDirectContext::MakeMock(nullptr);
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
auto surface_factory =
std::make_shared<TestAndroidSurfaceFactory>([gr_context, window]() {
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;
});
pool->SetFrameSize(SkISize::Make(10, 10));
EXPECT_CALL(*jni_mock, destroyOverlaySurface2()).Times(0);
EXPECT_CALL(*jni_mock, createOverlaySurface2())
.Times(1)
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
0, window))));
ASSERT_FALSE(pool->HasLayers());
pool->GetLayer(gr_context.get(), *android_context, jni_mock, surface_factory);
ASSERT_TRUE(pool->HasLayers());
pool->SetFrameSize(SkISize::Make(20, 20));
EXPECT_CALL(*jni_mock, destroyOverlaySurface2()).Times(1);
EXPECT_CALL(*jni_mock, createOverlaySurface2())
.Times(1)
.WillOnce(Return(
ByMove(std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
1, window))));
pool->GetLayer(gr_context.get(), *android_context, jni_mock, surface_factory);
ASSERT_TRUE(pool->GetUnusedLayers().empty());
ASSERT_TRUE(pool->HasLayers());
}
} // namespace testing
} // namespace flutter

View File

@ -321,7 +321,6 @@ public class FlutterJNI {
asyncWaitForVsyncDelegate = delegate;
}
// TODO(mattcarroll): add javadocs
// Called by native.
private static void asyncWaitForVsync(final long cookie) {
if (asyncWaitForVsyncDelegate != null) {
@ -596,7 +595,6 @@ public class FlutterJNI {
}
}
// TODO(mattcarroll): get native to call this when rendering stops.
@VisibleForTesting
@UiThread
void onRenderingStopped() {
@ -810,8 +808,6 @@ public class FlutterJNI {
if (accessibilityDelegate != null) {
accessibilityDelegate.updateSemantics(buffer, strings, stringAttributeArgs);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
/**
@ -831,8 +827,6 @@ public class FlutterJNI {
if (accessibilityDelegate != null) {
accessibilityDelegate.updateCustomAccessibilityActions(buffer, strings);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
/** Sends a semantics action to Flutter's engine, without any additional arguments. */
@ -896,8 +890,6 @@ public class FlutterJNI {
private native void nativeSetSemanticsEnabled(long nativeShellHolderId, boolean enabled);
// TODO(mattcarroll): figure out what flags are supported and add javadoc about when/why/where to
// use this.
@UiThread
public void setAccessibilityFeatures(int flags) {
ensureRunningOnMainThread();
@ -1073,7 +1065,6 @@ public class FlutterJNI {
}
// Called by native on any thread.
// TODO(mattcarroll): determine if message is nonull or nullable
@SuppressWarnings("unused")
@VisibleForTesting
public void handlePlatformMessage(
@ -1086,19 +1077,14 @@ public class FlutterJNI {
} else {
nativeCleanupMessageData(messageData);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
// Called by native to respond to a platform message that we sent.
// TODO(mattcarroll): determine if reply is nonull or nullable
@SuppressWarnings("unused")
private void handlePlatformMessageResponse(int replyId, ByteBuffer reply) {
if (platformMessageHandler != null) {
platformMessageHandler.handlePlatformMessageResponse(replyId, reply);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
/**
@ -1149,7 +1135,6 @@ public class FlutterJNI {
int position,
int responseId);
// TODO(mattcarroll): differentiate between channel responses and platform responses.
public void invokePlatformMessageEmptyResponseCallback(int responseId) {
// Called on any thread.
shellHolderLock.readLock().lock();
@ -1171,7 +1156,6 @@ public class FlutterJNI {
private native void nativeInvokePlatformMessageEmptyResponseCallback(
long nativeShellHolderId, int responseId);
// TODO(mattcarroll): differentiate between channel responses and platform responses.
public void invokePlatformMessageResponseCallback(
int responseId, @NonNull ByteBuffer message, int position) {
// Called on any thread.
@ -1557,7 +1541,6 @@ public class FlutterJNI {
viewId, x, y, width, height, viewWidth, viewHeight, mutatorsStack);
}
// TODO(mattcarroll): determine if this is nonull or nullable
@UiThread
public Bitmap getBitmap() {
ensureRunningOnMainThread();
@ -1565,7 +1548,6 @@ public class FlutterJNI {
return nativeGetBitmap(nativeShellHolderId);
}
// TODO(mattcarroll): determine if this is nonull or nullable
private native Bitmap nativeGetBitmap(long nativeShellHolderId);
/**

View File

@ -114,6 +114,31 @@ class JNIMock final : public PlatformViewAndroidJNI {
MOCK_METHOD(void, FlutterViewDestroyOverlaySurfaces, (), (override));
MOCK_METHOD(ASurfaceTransaction*, createTransaction, (), (override));
MOCK_METHOD(void, swapTransaction, (), (override));
MOCK_METHOD(void, applyTransaction, (), (override));
MOCK_METHOD(void, destroyOverlaySurface2, (), (override));
MOCK_METHOD(std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>,
createOverlaySurface2,
(),
(override));
MOCK_METHOD(void,
onDisplayPlatformView2,
(int32_t view_id,
int32_t x,
int32_t y,
int32_t width,
int32_t height,
int32_t viewWidth,
int32_t viewHeight,
MutatorsStack mutators_stack),
(override));
MOCK_METHOD(std::unique_ptr<std::vector<std::string>>,
FlutterViewComputePlatformResolvedLocale,
(std::vector<std::string> supported_locales_data),

View File

@ -19,6 +19,8 @@
#include "flutter/fml/platform/android/scoped_java_ref.h"
#endif
struct ASurfaceTransaction;
namespace flutter {
#if FML_OS_ANDROID
@ -214,6 +216,27 @@ class PlatformViewAndroidJNI {
///
virtual void FlutterViewDestroyOverlaySurfaces() = 0;
// New Platform View Support.
virtual ASurfaceTransaction* createTransaction() = 0;
virtual void swapTransaction() = 0;
virtual void applyTransaction() = 0;
virtual std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>
createOverlaySurface2() = 0;
virtual void destroyOverlaySurface2() = 0;
virtual void onDisplayPlatformView2(int32_t view_id,
int32_t x,
int32_t y,
int32_t width,
int32_t height,
int32_t viewWidth,
int32_t viewHeight,
MutatorsStack mutators_stack) = 0;
//----------------------------------------------------------------------------
/// @brief Computes the locale Android would select.
///

View File

@ -30,6 +30,7 @@
#endif
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder_2.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/surface/android_surface.h"
@ -366,6 +367,10 @@ std::unique_ptr<Surface> PlatformViewAndroid::CreateRenderingSurface() {
// |PlatformView|
std::shared_ptr<ExternalViewEmbedder>
PlatformViewAndroid::CreateExternalViewEmbedder() {
if (android_use_new_platform_view_) {
return std::make_shared<AndroidExternalViewEmbedder2>(
*android_context_, jni_facade_, surface_factory_, task_runners_);
}
return std::make_shared<AndroidExternalViewEmbedder>(
*android_context_, jni_facade_, surface_factory_, task_runners_);
}

View File

@ -128,6 +128,7 @@ class PlatformViewAndroid final : public PlatformView {
std::unique_ptr<AndroidSurface> android_surface_;
std::shared_ptr<PlatformMessageHandlerAndroid> platform_message_handler_;
bool android_use_new_platform_view_ = false;
// |PlatformView|
void UpdateSemantics(

View File

@ -33,6 +33,7 @@
#include "flutter/shell/platform/android/image_external_texture_gl.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/platform_view_android.h"
#include "impeller/toolkit/android/proc_table.h"
#define ANDROID_SHELL_HOLDER \
(reinterpret_cast<AndroidShellHolder*>(shell_holder))
@ -132,8 +133,6 @@ static jmethodID g_request_dart_deferred_library_method = nullptr;
// Called By Java
static jmethodID g_on_display_platform_view_method = nullptr;
// static jmethodID g_on_composite_platform_view_method = nullptr;
static jmethodID g_on_display_overlay_surface_method = nullptr;
static jmethodID g_overlay_surface_id_method = nullptr;
@ -146,6 +145,19 @@ static jmethodID g_bitmap_copy_pixels_from_buffer_method = nullptr;
static jmethodID g_bitmap_config_value_of = nullptr;
// New platform Views
static jmethodID g_create_transaction_method = nullptr;
static jmethodID g_swap_transaction_method = nullptr;
static jmethodID g_apply_transaction_method = nullptr;
static jmethodID g_create_overlay_surface2_method = nullptr;
static jmethodID g_destroy_overlay_surface2_method = nullptr;
static jmethodID g_on_display_platform_view2_method = nullptr;
// Mutators
static fml::jni::ScopedJavaGlobalRef<jclass>* g_mutators_stack_class = nullptr;
static jmethodID g_mutators_stack_init_method = nullptr;
@ -968,6 +980,60 @@ bool RegisterApi(JNIEnv* env) {
return false;
}
// new platform views
g_create_transaction_method =
env->GetMethodID(g_flutter_jni_class->obj(), "createTransaction",
"()Landroid/view/SurfaceControl$Transaction;");
if (g_create_transaction_method == nullptr) {
FML_LOG(ERROR) << "Could not locate createTransaction method";
return false;
}
g_swap_transaction_method =
env->GetMethodID(g_flutter_jni_class->obj(), "swapTransactions", "()V");
if (g_swap_transaction_method == nullptr) {
FML_LOG(ERROR) << "Could not locate swapTransactions method";
return false;
}
g_apply_transaction_method =
env->GetMethodID(g_flutter_jni_class->obj(), "applyTransactions", "()V");
if (g_apply_transaction_method == nullptr) {
FML_LOG(ERROR) << "Could not locate applyTransactions method";
return false;
}
g_create_overlay_surface2_method =
env->GetMethodID(g_flutter_jni_class->obj(), "createOverlaySurface2",
"()Lio/flutter/embedding/engine/FlutterOverlaySurface;");
if (g_create_overlay_surface2_method == nullptr) {
FML_LOG(ERROR) << "Could not locate createOverlaySurface2 method";
return false;
}
g_destroy_overlay_surface2_method = env->GetMethodID(
g_flutter_jni_class->obj(), "destroyOverlaySurface2", "()V");
if (g_destroy_overlay_surface2_method == nullptr) {
FML_LOG(ERROR) << "Could not locate destroyOverlaySurface2 method";
return false;
}
g_on_display_platform_view2_method =
env->GetMethodID(g_flutter_jni_class->obj(), "onDisplayPlatformView2",
"(IIIIIIILio/flutter/embedding/engine/mutatorsstack/"
"FlutterMutatorsStack;)V");
if (g_on_display_platform_view2_method == nullptr) {
FML_LOG(ERROR) << "Could not locate onDisplayPlatformView2 method";
return false;
}
//
fml::jni::ScopedJavaLocalRef<jclass> overlay_surface_class(
env, env->FindClass("io/flutter/embedding/engine/FlutterOverlaySurface"));
if (overlay_surface_class.is_null()) {
@ -1913,4 +1979,176 @@ bool PlatformViewAndroidJNIImpl::RequestDartDeferredLibrary(
return true;
}
// New Platform View Support.
ASurfaceTransaction* PlatformViewAndroidJNIImpl::createTransaction() {
JNIEnv* env = fml::jni::AttachCurrentThread();
auto java_object = java_object_.get(env);
fml::jni::ScopedJavaLocalRef<jobject> transaction(
env,
env->CallObjectMethod(java_object.obj(), g_create_transaction_method));
if (transaction.is_null()) {
return nullptr;
}
FML_CHECK(fml::jni::CheckException(env));
return impeller::android::GetProcTable().ASurfaceTransaction_fromJava(
env, transaction.obj());
}
void PlatformViewAndroidJNIImpl::swapTransaction() {
JNIEnv* env = fml::jni::AttachCurrentThread();
auto java_object = java_object_.get(env);
if (java_object.is_null()) {
return;
}
env->CallVoidMethod(java_object.obj(), g_swap_transaction_method);
FML_CHECK(fml::jni::CheckException(env));
}
void PlatformViewAndroidJNIImpl::applyTransaction() {
JNIEnv* env = fml::jni::AttachCurrentThread();
auto java_object = java_object_.get(env);
if (java_object.is_null()) {
return;
}
env->CallVoidMethod(java_object.obj(), g_apply_transaction_method);
FML_CHECK(fml::jni::CheckException(env));
}
std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>
PlatformViewAndroidJNIImpl::createOverlaySurface2() {
JNIEnv* env = fml::jni::AttachCurrentThread();
auto java_object = java_object_.get(env);
if (java_object.is_null()) {
return nullptr;
}
fml::jni::ScopedJavaLocalRef<jobject> overlay(
env, env->CallObjectMethod(java_object.obj(),
g_create_overlay_surface2_method));
FML_CHECK(fml::jni::CheckException(env));
if (overlay.is_null()) {
return std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(0,
nullptr);
}
jint overlay_id =
env->CallIntMethod(overlay.obj(), g_overlay_surface_id_method);
jobject overlay_surface =
env->CallObjectMethod(overlay.obj(), g_overlay_surface_surface_method);
auto overlay_window = fml::MakeRefCounted<AndroidNativeWindow>(
ANativeWindow_fromSurface(env, overlay_surface));
return std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
overlay_id, std::move(overlay_window));
}
void PlatformViewAndroidJNIImpl::destroyOverlaySurface2() {
JNIEnv* env = fml::jni::AttachCurrentThread();
auto java_object = java_object_.get(env);
if (java_object.is_null()) {
return;
}
env->CallVoidMethod(java_object.obj(), g_destroy_overlay_surface2_method);
FML_CHECK(fml::jni::CheckException(env));
}
void PlatformViewAndroidJNIImpl::onDisplayPlatformView2(
int32_t view_id,
int32_t x,
int32_t y,
int32_t width,
int32_t height,
int32_t viewWidth,
int32_t viewHeight,
MutatorsStack mutators_stack) {
JNIEnv* env = fml::jni::AttachCurrentThread();
auto java_object = java_object_.get(env);
if (java_object.is_null()) {
return;
}
jobject mutatorsStack = env->NewObject(g_mutators_stack_class->obj(),
g_mutators_stack_init_method);
std::vector<std::shared_ptr<Mutator>>::const_iterator iter =
mutators_stack.Begin();
while (iter != mutators_stack.End()) {
switch ((*iter)->GetType()) {
case kTransform: {
const SkMatrix& matrix = (*iter)->GetMatrix();
SkScalar matrix_array[9];
matrix.get9(matrix_array);
fml::jni::ScopedJavaLocalRef<jfloatArray> transformMatrix(
env, env->NewFloatArray(9));
env->SetFloatArrayRegion(transformMatrix.obj(), 0, 9, matrix_array);
env->CallVoidMethod(mutatorsStack,
g_mutators_stack_push_transform_method,
transformMatrix.obj());
break;
}
case kClipRect: {
const SkRect& rect = (*iter)->GetRect();
env->CallVoidMethod(
mutatorsStack, g_mutators_stack_push_cliprect_method,
static_cast<int>(rect.left()), static_cast<int>(rect.top()),
static_cast<int>(rect.right()), static_cast<int>(rect.bottom()));
break;
}
case kClipRRect: {
const SkRRect& rrect = (*iter)->GetRRect();
const SkRect& rect = rrect.rect();
const SkVector& upper_left = rrect.radii(SkRRect::kUpperLeft_Corner);
const SkVector& upper_right = rrect.radii(SkRRect::kUpperRight_Corner);
const SkVector& lower_right = rrect.radii(SkRRect::kLowerRight_Corner);
const SkVector& lower_left = rrect.radii(SkRRect::kLowerLeft_Corner);
SkScalar radiis[8] = {
upper_left.x(), upper_left.y(), upper_right.x(), upper_right.y(),
lower_right.x(), lower_right.y(), lower_left.x(), lower_left.y(),
};
fml::jni::ScopedJavaLocalRef<jfloatArray> radiisArray(
env, env->NewFloatArray(8));
env->SetFloatArrayRegion(radiisArray.obj(), 0, 8, radiis);
env->CallVoidMethod(
mutatorsStack, g_mutators_stack_push_cliprrect_method,
static_cast<int>(rect.left()), static_cast<int>(rect.top()),
static_cast<int>(rect.right()), static_cast<int>(rect.bottom()),
radiisArray.obj());
break;
}
// TODO(cyanglaz): Implement other mutators.
// https://github.com/flutter/flutter/issues/58426
case kClipPath:
case kOpacity:
case kBackdropFilter:
break;
}
++iter;
}
env->CallVoidMethod(java_object.obj(), g_on_display_platform_view2_method,
view_id, x, y, width, height, viewWidth, viewHeight,
mutatorsStack);
FML_CHECK(fml::jni::CheckException(env));
}
} // namespace flutter

View File

@ -103,6 +103,27 @@ class PlatformViewAndroidJNIImpl final : public PlatformViewAndroidJNI {
double FlutterViewGetScaledFontSize(double unscaled_font_size,
int configuration_id) const override;
// New Platform View Support.
ASurfaceTransaction* createTransaction() override;
void swapTransaction() override;
void applyTransaction() override;
std::unique_ptr<PlatformViewAndroidJNI::OverlayMetadata>
createOverlaySurface2() override;
void destroyOverlaySurface2() override;
void onDisplayPlatformView2(int32_t view_id,
int32_t x,
int32_t y,
int32_t width,
int32_t height,
int32_t viewWidth,
int32_t viewHeight,
MutatorsStack mutators_stack) override;
private:
// Reference to FlutterJNI object.
const fml::jni::JavaObjectWeakGlobalRef java_object_;