external_view_embedder_unittests.cc 16.2 KB
Newer Older
1 2 3 4
// 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.

5
#include "flutter/flow/surface.h"
6 7
#include "flutter/fml/raster_thread_merger.h"
#include "flutter/fml/thread.h"
8
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"
9 10 11
#include "flutter/shell/platform/android/jni/jni_mock.h"
#include "flutter/shell/platform/android/surface/android_surface_mock.h"
#include "gmock/gmock.h"
12
#include "gtest/gtest.h"
13
#include "third_party/skia/include/gpu/GrContext.h"
14 15 16 17

namespace flutter {
namespace testing {

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
using ::testing::ByMove;
using ::testing::Return;

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);
}

70
TEST(AndroidExternalViewEmbedder, GetCurrentCanvases) {
71 72 73 74 75
  auto jni_mock = std::make_shared<JNIMock>();

  auto embedder =
      std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
  auto raster_thread_merger = GetThreadMergerFromPlatformThread();
76

77 78 79
  EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
  embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0,
                       raster_thread_merger);
80 81 82 83 84 85 86 87 88 89 90 91

  embedder->PrerollCompositeEmbeddedView(
      0, std::make_unique<EmbeddedViewParams>());
  embedder->PrerollCompositeEmbeddedView(
      1, std::make_unique<EmbeddedViewParams>());

  auto canvases = embedder->GetCurrentCanvases();
  ASSERT_EQ(2UL, canvases.size());
  ASSERT_EQ(SkISize::Make(10, 20), canvases[0]->getBaseLayerSize());
  ASSERT_EQ(SkISize::Make(10, 20), canvases[1]->getBaseLayerSize());
}

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
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>());
  embedder->PrerollCompositeEmbeddedView(
      1, std::make_unique<EmbeddedViewParams>());

  auto canvases = embedder->GetCurrentCanvases();
  ASSERT_EQ(2UL, canvases.size());
  ASSERT_EQ(embedder->CompositeEmbeddedView(0), canvases[0]);
  ASSERT_EQ(embedder->CompositeEmbeddedView(1), canvases[1]);
}

114
TEST(AndroidExternalViewEmbedder, CompositeEmbeddedView) {
115 116
  auto embedder =
      std::make_unique<AndroidExternalViewEmbedder>(nullptr, nullptr, nullptr);
117

118
  ASSERT_EQ(nullptr, embedder->CompositeEmbeddedView(0));
119 120
  embedder->PrerollCompositeEmbeddedView(
      0, std::make_unique<EmbeddedViewParams>());
121
  ASSERT_NE(nullptr, embedder->CompositeEmbeddedView(0));
122

123
  ASSERT_EQ(nullptr, embedder->CompositeEmbeddedView(1));
124 125
  embedder->PrerollCompositeEmbeddedView(
      1, std::make_unique<EmbeddedViewParams>());
126
  ASSERT_NE(nullptr, embedder->CompositeEmbeddedView(1));
127 128 129
}

TEST(AndroidExternalViewEmbedder, CancelFrame) {
130 131
  auto embedder =
      std::make_unique<AndroidExternalViewEmbedder>(nullptr, nullptr, nullptr);
132 133 134 135 136 137 138 139 140

  embedder->PrerollCompositeEmbeddedView(
      0, std::make_unique<EmbeddedViewParams>());
  embedder->CancelFrame();

  auto canvases = embedder->GetCurrentCanvases();
  ASSERT_EQ(0UL, canvases.size());
}

141
TEST(AndroidExternalViewEmbedder, RasterizerRunsOnPlatformThread) {
142 143 144
  auto jni_mock = std::make_shared<JNIMock>();
  auto embedder =
      std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
145

146
  auto raster_thread_merger = GetThreadMergerFromPlatformThread();
147 148
  ASSERT_FALSE(raster_thread_merger->IsMerged());

149 150 151
  EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
  embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0,
                       raster_thread_merger);
152 153 154 155 156 157 158 159
  // Push a platform view.
  embedder->PrerollCompositeEmbeddedView(
      0, std::make_unique<EmbeddedViewParams>());

  auto postpreroll_result = embedder->PostPrerollAction(raster_thread_merger);
  ASSERT_EQ(PostPrerollResult::kResubmitFrame, postpreroll_result);
  ASSERT_TRUE(embedder->SubmitFrame(nullptr, nullptr));

160
  EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
161
  embedder->EndFrame(/*should_resubmit_frame=*/true, raster_thread_merger);
162

163 164 165 166 167 168 169 170 171 172 173
  ASSERT_TRUE(raster_thread_merger->IsMerged());

  int pending_frames = 0;
  while (raster_thread_merger->IsMerged()) {
    raster_thread_merger->DecrementLease();
    pending_frames++;
  }
  ASSERT_EQ(10, pending_frames);  // kDefaultMergedLeaseDuration
}

TEST(AndroidExternalViewEmbedder, RasterizerRunsOnRasterizerThread) {
174 175 176
  auto jni_mock = std::make_shared<JNIMock>();
  auto embedder =
      std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);
177

178
  auto raster_thread_merger = GetThreadMergerFromPlatformThread();
179 180 181 182 183
  ASSERT_FALSE(raster_thread_merger->IsMerged());

  PostPrerollResult result = embedder->PostPrerollAction(raster_thread_merger);
  ASSERT_EQ(PostPrerollResult::kSuccess, result);

184
  EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
185
  embedder->EndFrame(/*should_resubmit_frame=*/true, raster_thread_merger);
186

187 188 189
  ASSERT_FALSE(raster_thread_merger->IsMerged());
}

190 191 192 193 194 195 196 197 198 199 200
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);

201 202 203 204 205
  MutatorsStack stack;
  SkMatrix matrix = SkMatrix::MakeTrans(10, 20);
  stack.PushTransform(matrix);
  auto view_params =
      std::make_unique<EmbeddedViewParams>(matrix, SkSize::Make(30, 40), stack);
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

  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;
224 225 226 227 228 229 230

  MutatorsStack stack1;
  SkMatrix matrix1 = SkMatrix::MakeTrans(10, 20);
  stack1.PushTransform(matrix1);
  auto view_params_1 = std::make_unique<EmbeddedViewParams>(
      matrix1, SkSize::Make(30, 40), stack1);

231 232
  embedder->PrerollCompositeEmbeddedView(view_id, std::move(view_params_1));

233 234 235 236 237 238
  MutatorsStack stack2;
  SkMatrix matrix2 = SkMatrix::MakeTrans(50, 60);
  stack2.PushTransform(matrix2);
  auto view_params_2 = std::make_unique<EmbeddedViewParams>(
      matrix2, SkSize::Make(70, 80), stack2);

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  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.
293 294 295
    MutatorsStack stack1;
    SkMatrix matrix1 = SkMatrix::MakeTrans(100, 100);
    stack1.PushTransform(matrix1);
296 297
    // TODO(egarciad): Investigate why Flow applies the device pixel ratio to
    // the offsetPixels, but not the sizePoints.
298 299 300
    auto view_params_1 = std::make_unique<EmbeddedViewParams>(
        matrix1, SkSize::Make(200, 200), stack1);

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
    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());
335
    embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger);
336 337 338 339 340 341 342 343
  }

  // ------------------ Second frame ------------------ //
  {
    EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
    embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger);

    // Add an Android view.
344 345 346
    MutatorsStack stack1;
    SkMatrix matrix1 = SkMatrix::MakeTrans(100, 100);
    stack1.PushTransform(matrix1);
347 348
    // TODO(egarciad): Investigate why Flow applies the device pixel ratio to
    // the offsetPixels, but not the sizePoints.
349 350 351
    auto view_params_1 = std::make_unique<EmbeddedViewParams>(
        matrix1, SkSize::Make(200, 200), stack1);

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
    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());
383
    embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger);
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
  }
}

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);
402
  embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger);
403 404
}

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) {
  auto jni_mock = std::make_shared<JNIMock>();
  auto embedder =
      std::make_unique<AndroidExternalViewEmbedder>(nullptr, jni_mock, nullptr);

  auto raster_thread_merger = GetThreadMergerFromPlatformThread();
  ASSERT_FALSE(raster_thread_merger->IsMerged());

  embedder->BeginFrame(SkISize::Make(10, 20), nullptr, 1.0,
                       raster_thread_merger);
  EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces());
  embedder->BeginFrame(SkISize::Make(30, 40), nullptr, 1.0,
                       raster_thread_merger);
}

420 421
}  // namespace testing
}  // namespace flutter