blob: 5306b195e74effaa585b5de4153c47e299519b0f [file] [log] [blame]
// 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.
#define FML_USED_ON_EMBEDDER
#include "flutter/shell/common/rasterizer.h"
#include <memory>
#include <optional>
#include "flutter/flow/frame_timings.h"
#include "flutter/fml/synchronization/count_down_latch.h"
#include "flutter/fml/time/time_point.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/testing/testing.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrTypes.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#include "gmock/gmock.h"
using testing::_;
using testing::ByMove;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
namespace flutter {
namespace {
constexpr float kDevicePixelRatio = 2.0f;
constexpr int64_t kImplicitViewId = 0;
std::vector<std::unique_ptr<LayerTreeTask>> SingleLayerTreeList(
int64_t view_id,
std::unique_ptr<LayerTree> layer_tree,
float pixel_ratio) {
std::vector<std::unique_ptr<LayerTreeTask>> tasks;
tasks.push_back(std::make_unique<LayerTreeTask>(
view_id, std::move(layer_tree), pixel_ratio));
return tasks;
}
class MockDelegate : public Rasterizer::Delegate {
public:
MOCK_METHOD(void,
OnFrameRasterized,
(const FrameTiming& frame_timing),
(override));
MOCK_METHOD(fml::Milliseconds, GetFrameBudget, (), (override));
MOCK_METHOD(fml::TimePoint, GetLatestFrameTargetTime, (), (const, override));
MOCK_METHOD(const TaskRunners&, GetTaskRunners, (), (const, override));
MOCK_METHOD(const fml::RefPtr<fml::RasterThreadMerger>,
GetParentRasterThreadMerger,
(),
(const, override));
MOCK_METHOD(std::shared_ptr<const fml::SyncSwitch>,
GetIsGpuDisabledSyncSwitch,
(),
(const, override));
MOCK_METHOD(const Settings&, GetSettings, (), (const, override));
MOCK_METHOD(bool,
ShouldDiscardLayerTree,
(int64_t, const flutter::LayerTree&),
(override));
};
class MockSurface : 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(GrDirectContext*, GetContext, (), (override));
MOCK_METHOD(std::unique_ptr<GLContextResult>,
MakeRenderContextCurrent,
(),
(override));
MOCK_METHOD(bool, ClearRenderContext, (), (override));
MOCK_METHOD(bool, AllowsDrawingWhenGpuDisabled, (), (const, override));
};
class MockExternalViewEmbedder : public ExternalViewEmbedder {
public:
MOCK_METHOD(DlCanvas*, GetRootCanvas, (), (override));
MOCK_METHOD(void, CancelFrame, (), (override));
MOCK_METHOD(
void,
BeginFrame,
(GrDirectContext * context,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger),
(override));
MOCK_METHOD(void,
PrepareFlutterView,
(SkISize frame_size, double device_pixel_ratio),
(override));
MOCK_METHOD(void,
PrerollCompositeEmbeddedView,
(int64_t view_id, std::unique_ptr<EmbeddedViewParams> params),
(override));
MOCK_METHOD(
PostPrerollResult,
PostPrerollAction,
(const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger),
(override));
MOCK_METHOD(DlCanvas*, CompositeEmbeddedView, (int64_t view_id), (override));
MOCK_METHOD(void,
SubmitFlutterView,
(int64_t flutter_view_id,
GrDirectContext* context,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
std::unique_ptr<SurfaceFrame> frame),
(override));
MOCK_METHOD(
void,
EndFrame,
(bool should_resubmit_frame,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger),
(override));
MOCK_METHOD(bool, SupportsDynamicThreadMerging, (), (override));
};
} // namespace
TEST(RasterizerTest, create) {
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
EXPECT_TRUE(rasterizer != nullptr);
}
static std::unique_ptr<FrameTimingsRecorder> CreateFinishedBuildRecorder(
fml::TimePoint timestamp) {
std::unique_ptr<FrameTimingsRecorder> recorder =
std::make_unique<FrameTimingsRecorder>();
recorder->RecordVsync(timestamp, timestamp);
recorder->RecordBuildStart(timestamp);
recorder->RecordBuildEnd(timestamp);
return recorder;
}
static std::unique_ptr<FrameTimingsRecorder> CreateFinishedBuildRecorder() {
return CreateFinishedBuildRecorder(fml::TimePoint::Now());
}
TEST(RasterizerTest, drawEmptyPipeline) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
latch.Signal();
});
latch.Wait();
}
TEST(RasterizerTest,
drawWithExternalViewEmbedderExternalViewEmbedderSubmitFrameCalled) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true));
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
.WillOnce(Return(ByMove(std::move(surface_frame))));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
EXPECT_CALL(*external_view_embedder,
BeginFrame(/*context=*/nullptr,
/*raster_thread_merger=*/
fml::RefPtr<fml::RasterThreadMerger>(nullptr)))
.Times(1);
EXPECT_CALL(*external_view_embedder, PrepareFlutterView(
/*frame_size=*/SkISize(),
/*device_pixel_ratio=*/2.0))
.Times(1);
EXPECT_CALL(*external_view_embedder,
SubmitFlutterView(/*flutter_view_id=*/kImplicitViewId, _, _, _))
.Times(1);
EXPECT_CALL(
*external_view_embedder,
EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/fml::RefPtr<fml::RasterThreadMerger>(
nullptr)))
.Times(1);
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(/*root_layer=*/nullptr,
/*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
latch.Signal();
});
latch.Wait();
}
TEST(
RasterizerTest,
drawWithExternalViewEmbedderAndThreadMergerNotMergedExternalViewEmbedderSubmitFrameNotCalled) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging)
.WillRepeatedly(Return(true));
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true));
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
.WillOnce(Return(ByMove(std::move(surface_frame))));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
EXPECT_CALL(*external_view_embedder, BeginFrame(/*context=*/nullptr,
/*raster_thread_merger=*/_))
.Times(1);
EXPECT_CALL(*external_view_embedder, PrepareFlutterView(
/*frame_size=*/SkISize(),
/*device_pixel_ratio=*/2.0))
.Times(1);
EXPECT_CALL(*external_view_embedder,
SubmitFlutterView(/*flutter_view_id=*/kImplicitViewId, _, _, _))
.Times(0);
EXPECT_CALL(*external_view_embedder, EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/_))
.Times(1);
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
latch.Signal();
});
latch.Wait();
}
TEST(
RasterizerTest,
drawWithExternalViewEmbedderAndThreadsMergedExternalViewEmbedderSubmitFrameCalled) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
fml::MessageLoop::EnsureInitializedForCurrentThread();
TaskRunners task_runners("test",
fml::MessageLoop::GetCurrent().GetTaskRunner(),
fml::MessageLoop::GetCurrent().GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true));
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
.WillOnce(Return(ByMove(std::move(surface_frame))));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging)
.WillRepeatedly(Return(true));
EXPECT_CALL(*external_view_embedder, BeginFrame(/*context=*/nullptr,
/*raster_thread_merger=*/_))
.Times(1);
EXPECT_CALL(*external_view_embedder, PrepareFlutterView(
/*frame_size=*/SkISize(),
/*device_pixel_ratio=*/2.0))
.Times(1);
EXPECT_CALL(*external_view_embedder,
SubmitFlutterView(/*flutter_view_id=*/kImplicitViewId, _, _, _))
.Times(1);
EXPECT_CALL(*external_view_embedder, EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/_))
.Times(1);
rasterizer->Setup(std::move(surface));
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(/*root_layer=*/nullptr,
/*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
}
TEST(RasterizerTest,
drawLastLayerTreeWithThreadsMergedExternalViewEmbedderAndEndFrameCalled) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
fml::MessageLoop::EnsureInitializedForCurrentThread();
TaskRunners task_runners("test",
fml::MessageLoop::GetCurrent().GetTaskRunner(),
fml::MessageLoop::GetCurrent().GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame1 = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
auto surface_frame2 = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled())
.WillRepeatedly(Return(true));
// Prepare two frames for Draw() and DrawLastLayerTrees().
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
.WillOnce(Return(ByMove(std::move(surface_frame1))))
.WillOnce(Return(ByMove(std::move(surface_frame2))));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging)
.WillRepeatedly(Return(true));
EXPECT_CALL(*external_view_embedder, BeginFrame(/*context=*/nullptr,
/*raster_thread_merger=*/_))
.Times(2);
EXPECT_CALL(*external_view_embedder, PrepareFlutterView(
/*frame_size=*/SkISize(),
/*device_pixel_ratio=*/2.0))
.Times(2);
EXPECT_CALL(*external_view_embedder,
SubmitFlutterView(/*flutter_view_id=*/kImplicitViewId, _, _, _))
.Times(2);
EXPECT_CALL(*external_view_embedder, EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/_))
.Times(2);
rasterizer->Setup(std::move(surface));
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(/*root_layer=*/nullptr,
/*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
// The Draw() will respectively call BeginFrame(), SubmitFlutterView() and
// EndFrame() one time.
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
// The DrawLastLayerTrees() will respectively call BeginFrame(),
// SubmitFlutterView() and EndFrame() one more time, totally 2 times.
rasterizer->DrawLastLayerTrees(CreateFinishedBuildRecorder());
}
TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
EXPECT_CALL(
*external_view_embedder,
EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/fml::RefPtr<fml::RasterThreadMerger>(
nullptr)))
.Times(0);
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
latch.Signal();
});
latch.Wait();
}
TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
auto is_gpu_disabled_sync_switch =
std::make_shared<const fml::SyncSwitch>(false);
ON_CALL(delegate, GetIsGpuDisabledSyncSwitch())
.WillByDefault(Return(is_gpu_disabled_sync_switch));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
rasterizer->Setup(std::move(surface));
EXPECT_CALL(*external_view_embedder, BeginFrame(/*context=*/nullptr,
/*raster_thread_merger=*/_))
.Times(0);
EXPECT_CALL(*external_view_embedder, PrepareFlutterView(
/*frame_size=*/SkISize(),
/*device_pixel_ratio=*/2.0))
.Times(0);
EXPECT_CALL(
*external_view_embedder,
EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/fml::RefPtr<fml::RasterThreadMerger>(
nullptr)))
.Times(0);
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
// Always discard the layer tree.
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(true));
DrawStatus status = rasterizer->Draw(pipeline);
EXPECT_EQ(status, DrawStatus::kDone);
EXPECT_EQ(rasterizer->GetLastDrawStatus(kImplicitViewId),
DrawSurfaceStatus::kDiscarded);
latch.Signal();
});
latch.Wait();
}
TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
rasterizer->Setup(std::move(surface));
EXPECT_CALL(
*external_view_embedder,
EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/fml::RefPtr<fml::RasterThreadMerger>(
nullptr)))
.Times(0);
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
DrawStatus status = rasterizer->Draw(pipeline);
EXPECT_EQ(status, DrawStatus::kPipelineEmpty);
latch.Signal();
});
latch.Wait();
}
TEST(RasterizerTest, drawMultipleViewsWithExternalViewEmbedder) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
std::shared_ptr<NiceMock<MockExternalViewEmbedder>> external_view_embedder =
std::make_shared<NiceMock<MockExternalViewEmbedder>>();
rasterizer->SetExternalViewEmbedder(external_view_embedder);
EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging)
.WillRepeatedly(Return(false));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true));
EXPECT_CALL(*surface, AcquireFrame(SkISize())).Times(2);
ON_CALL(*surface, AcquireFrame).WillByDefault([](const SkISize& size) {
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
return std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
});
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
EXPECT_CALL(*external_view_embedder, BeginFrame(/*context=*/nullptr,
/*raster_thread_merger=*/_))
.Times(1);
EXPECT_CALL(*external_view_embedder,
PrepareFlutterView(/*frame_size=*/SkISize(),
/*device_pixel_ratio=*/1.5))
.Times(1);
EXPECT_CALL(*external_view_embedder,
SubmitFlutterView(/*flutter_view_id=*/0, _, _, _))
.Times(1);
EXPECT_CALL(*external_view_embedder,
PrepareFlutterView(/*frame_size=*/SkISize(),
/*device_pixel_ratio=*/2.0))
.Times(1);
EXPECT_CALL(*external_view_embedder,
SubmitFlutterView(/*flutter_view_id=*/1, _, _, _))
.Times(1);
EXPECT_CALL(*external_view_embedder, EndFrame(/*should_resubmit_frame=*/false,
/*raster_thread_merger=*/_))
.Times(1);
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
std::vector<std::unique_ptr<LayerTreeTask>> tasks;
tasks.push_back(std::make_unique<LayerTreeTask>(
0, std::make_unique<LayerTree>(nullptr, SkISize()), 1.5));
tasks.push_back(std::make_unique<LayerTreeTask>(
1, std::make_unique<LayerTree>(nullptr, SkISize()), 2.0));
auto layer_tree_item = std::make_unique<FrameItem>(
std::move(tasks), CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
latch.Signal();
});
latch.Wait();
}
TEST(RasterizerTest,
drawWithGpuEnabledAndSurfaceAllowsDrawingWhenGpuDisabledDoesAcquireFrame) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
auto is_gpu_disabled_sync_switch =
std::make_shared<const fml::SyncSwitch>(false);
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, /*framebuffer_info=*/framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true));
ON_CALL(delegate, GetIsGpuDisabledSyncSwitch())
.WillByDefault(Return(is_gpu_disabled_sync_switch));
EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0);
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
.WillOnce(Return(ByMove(std::move(surface_frame))));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
latch.Signal();
});
latch.Wait();
}
TEST(
RasterizerTest,
drawWithGpuDisabledAndSurfaceAllowsDrawingWhenGpuDisabledDoesAcquireFrame) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
auto is_gpu_disabled_sync_switch =
std::make_shared<const fml::SyncSwitch>(true);
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, /*framebuffer_info=*/framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true));
ON_CALL(delegate, GetIsGpuDisabledSyncSwitch())
.WillByDefault(Return(is_gpu_disabled_sync_switch));
EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0);
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
.WillOnce(Return(ByMove(std::move(surface_frame))));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
DrawStatus status = rasterizer->Draw(pipeline);
EXPECT_EQ(status, DrawStatus::kDone);
latch.Signal();
});
latch.Wait();
}
TEST(
RasterizerTest,
drawWithGpuEnabledAndSurfaceDisallowsDrawingWhenGpuDisabledDoesAcquireFrame) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
auto is_gpu_disabled_sync_switch =
std::make_shared<const fml::SyncSwitch>(false);
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, /*framebuffer_info=*/framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false));
EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch())
.WillOnce(Return(is_gpu_disabled_sync_switch));
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
.WillOnce(Return(ByMove(std::move(surface_frame))));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
DrawStatus status = rasterizer->Draw(pipeline);
EXPECT_EQ(status, DrawStatus::kDone);
latch.Signal();
});
latch.Wait();
}
TEST(
RasterizerTest,
drawWithGpuDisabledAndSurfaceDisallowsDrawingWhenGpuDisabledDoesntAcquireFrame) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_)).Times(0);
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
auto is_gpu_disabled_sync_switch =
std::make_shared<const fml::SyncSwitch>(true);
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
auto surface_frame = std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, /*framebuffer_info=*/framebuffer_info,
/*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false));
EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch())
.WillOnce(Return(is_gpu_disabled_sync_switch));
EXPECT_CALL(*surface, AcquireFrame(SkISize())).Times(0);
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
DrawStatus status = rasterizer->Draw(pipeline);
EXPECT_EQ(status, DrawStatus::kGpuUnavailable);
latch.Signal();
});
latch.Wait();
}
TEST(
RasterizerTest,
FrameTimingRecorderShouldStartRecordingRasterTimeBeforeSurfaceAcquireFrame) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
EXPECT_CALL(delegate, OnFrameRasterized(_))
.WillOnce([&](const FrameTiming& frame_timing) {
fml::TimePoint now = fml::TimePoint::Now();
fml::TimePoint raster_start =
frame_timing.Get(FrameTiming::kRasterStart);
EXPECT_TRUE(now - raster_start < fml::TimeDelta::FromSecondsF(1));
});
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
auto is_gpu_disabled_sync_switch =
std::make_shared<const fml::SyncSwitch>(false);
ON_CALL(delegate, GetIsGpuDisabledSyncSwitch())
.WillByDefault(Return(is_gpu_disabled_sync_switch));
ON_CALL(*surface, AcquireFrame(SkISize()))
.WillByDefault(::testing::Invoke([] { return nullptr; }));
EXPECT_CALL(*surface, AcquireFrame(SkISize()));
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillOnce(Return(ByMove(std::make_unique<GLContextDefaultResult>(true))));
rasterizer->Setup(std::move(surface));
fml::AutoResetWaitableEvent latch;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder());
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
DrawStatus status = rasterizer->Draw(pipeline);
EXPECT_EQ(status, DrawStatus::kDone);
EXPECT_EQ(rasterizer->GetLastDrawStatus(kImplicitViewId),
DrawSurfaceStatus::kFailed);
latch.Signal();
});
latch.Wait();
}
TEST(RasterizerTest,
drawLayerTreeWithCorrectFrameTimingWhenPipelineIsMoreAvailable) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners));
fml::AutoResetWaitableEvent latch;
std::unique_ptr<Rasterizer> rasterizer;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer = std::make_unique<Rasterizer>(delegate);
latch.Signal();
});
latch.Wait();
auto surface = std::make_unique<NiceMock<MockSurface>>();
EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled())
.WillRepeatedly(Return(true));
ON_CALL(*surface, AcquireFrame(SkISize()))
.WillByDefault(::testing::Invoke([] {
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
return std::make_unique<SurfaceFrame>(
/*surface=*/
nullptr, framebuffer_info,
/*submit_callback=*/
[](const SurfaceFrame& frame, DlCanvas*) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
}));
ON_CALL(*surface, MakeRenderContextCurrent())
.WillByDefault(::testing::Invoke(
[] { return std::make_unique<GLContextDefaultResult>(true); }));
fml::CountDownLatch count_down_latch(2);
auto first_timestamp = fml::TimePoint::Now();
auto second_timestamp = first_timestamp + fml::TimeDelta::FromMilliseconds(8);
std::vector<fml::TimePoint> timestamps = {first_timestamp, second_timestamp};
int frame_rasterized_count = 0;
EXPECT_CALL(delegate, OnFrameRasterized(_))
.Times(2)
.WillRepeatedly([&](const FrameTiming& frame_timing) {
EXPECT_EQ(timestamps[frame_rasterized_count],
frame_timing.Get(FrameTiming::kVsyncStart));
EXPECT_EQ(timestamps[frame_rasterized_count],
frame_timing.Get(FrameTiming::kBuildStart));
EXPECT_EQ(timestamps[frame_rasterized_count],
frame_timing.Get(FrameTiming::kBuildFinish));
frame_rasterized_count++;
count_down_latch.CountDown();
});
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer->Setup(std::move(surface));
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
for (int i = 0; i < 2; i++) {
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder(timestamps[i]));
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
EXPECT_EQ(result.is_first_item, i == 0);
}
// Although we only call 'Rasterizer::Draw' once, it will be called twice
// finally because there are two items in the pipeline.
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
});
count_down_latch.Wait();
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer.reset();
latch.Signal();
});
latch.Wait();
}
TEST(RasterizerTest, TeardownFreesResourceCache) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
auto surface = std::make_unique<NiceMock<MockSurface>>();
auto context = GrDirectContext::MakeMock(nullptr);
context->setResourceCacheLimit(0);
EXPECT_CALL(*surface, MakeRenderContextCurrent())
.WillRepeatedly([]() -> std::unique_ptr<GLContextResult> {
return std::make_unique<GLContextDefaultResult>(true);
});
EXPECT_CALL(*surface, GetContext()).WillRepeatedly(Return(context.get()));
rasterizer->Setup(std::move(surface));
EXPECT_EQ(context->getResourceCacheLimit(), 0ul);
rasterizer->SetResourceCacheMaxBytes(10000000, false);
EXPECT_EQ(context->getResourceCacheLimit(), 10000000ul);
EXPECT_EQ(context->getResourceCachePurgeableBytes(), 0ul);
int count = 0;
size_t bytes = 0;
context->getResourceCacheUsage(&count, &bytes);
EXPECT_EQ(bytes, 0ul);
auto image_info =
SkImageInfo::MakeN32Premul(500, 500, SkColorSpace::MakeSRGB());
auto sk_surface = SkSurfaces::RenderTarget(context.get(),
skgpu::Budgeted::kYes, image_info);
EXPECT_TRUE(sk_surface);
SkPaint paint;
sk_surface->getCanvas()->drawPaint(paint);
context->flushAndSubmit(GrSyncCpu::kYes);
EXPECT_EQ(context->getResourceCachePurgeableBytes(), 0ul);
sk_surface.reset();
context->getResourceCacheUsage(&count, &bytes);
EXPECT_GT(bytes, 0ul);
EXPECT_GT(context->getResourceCachePurgeableBytes(), 0ul);
rasterizer->Teardown();
EXPECT_EQ(context->getResourceCachePurgeableBytes(), 0ul);
}
TEST(RasterizerTest, TeardownNoSurface) {
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform |
ThreadHost::Type::kRaster | ThreadHost::Type::kIo |
ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
EXPECT_CALL(delegate, GetTaskRunners())
.WillRepeatedly(ReturnRef(task_runners));
auto rasterizer = std::make_unique<Rasterizer>(delegate);
EXPECT_TRUE(rasterizer);
rasterizer->Teardown();
}
TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) {
GTEST_SKIP() << "eglPresentationTime is disabled due to "
"https://github.com/flutter/flutter/issues/112503";
#if false
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
ThreadHost::Type::kIo | ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners));
fml::AutoResetWaitableEvent latch;
std::unique_ptr<Rasterizer> rasterizer;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer = std::make_unique<Rasterizer>(delegate);
latch.Signal();
});
latch.Wait();
const auto millis_16 = fml::TimeDelta::FromMilliseconds(16);
const auto first_timestamp = fml::TimePoint::Now() + millis_16;
auto second_timestamp = first_timestamp + millis_16;
std::vector<fml::TimePoint> timestamps = {first_timestamp, second_timestamp};
int frames_submitted = 0;
fml::CountDownLatch submit_latch(2);
auto surface = std::make_unique<MockSurface>();
ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true));
ON_CALL(*surface, AcquireFrame(SkISize()))
.WillByDefault(::testing::Invoke([&] {
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
return std::make_unique<SurfaceFrame>(
/*surface=*/nullptr, framebuffer_info,
/*submit_callback=*/
[&](const SurfaceFrame& frame, DlCanvas*) {
const auto pres_time = *frame.submit_info().presentation_time;
const auto diff = pres_time - first_timestamp;
int num_frames_submitted = frames_submitted++;
EXPECT_EQ(diff.ToMilliseconds(),
num_frames_submitted * millis_16.ToMilliseconds());
submit_latch.CountDown();
return true;
},
/*frame_size=*/SkISize::Make(800, 600));
}));
ON_CALL(*surface, MakeRenderContextCurrent())
.WillByDefault(::testing::Invoke(
[] { return std::make_unique<GLContextDefaultResult>(true); }));
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer->Setup(std::move(surface));
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
for (int i = 0; i < 2; i++) {
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder(timestamps[i]));
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
EXPECT_EQ(result.is_first_item, i == 0);
}
// Although we only call 'Rasterizer::Draw' once, it will be called twice
// finally because there are two items in the pipeline.
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
});
submit_latch.Wait();
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer.reset();
latch.Signal();
});
latch.Wait();
#endif // false
}
TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) {
GTEST_SKIP() << "eglPresentationTime is disabled due to "
"https://github.com/flutter/flutter/issues/112503";
#if false
std::string test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
ThreadHost thread_host("io.flutter.test." + test_name + ".",
ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
ThreadHost::Type::kIo | ThreadHost::Type::kUi);
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
NiceMock<MockDelegate> delegate;
Settings settings;
ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings));
ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners));
fml::AutoResetWaitableEvent latch;
std::unique_ptr<Rasterizer> rasterizer;
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer = std::make_unique<Rasterizer>(delegate);
latch.Signal();
});
latch.Wait();
const auto millis_16 = fml::TimeDelta::FromMilliseconds(16);
const auto first_timestamp = fml::TimePoint::Now() - millis_16;
fml::CountDownLatch submit_latch(1);
auto surface = std::make_unique<MockSurface>();
ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true));
ON_CALL(*surface, AcquireFrame(SkISize()))
.WillByDefault(::testing::Invoke([&] {
SurfaceFrame::FramebufferInfo framebuffer_info;
framebuffer_info.supports_readback = true;
return std::make_unique<SurfaceFrame>(
/*surface=*/nullptr, framebuffer_info,
/*submit_callback=*/
[&](const SurfaceFrame& frame, DlCanvas*) {
const std::optional<fml::TimePoint> pres_time =
frame.submit_info().presentation_time;
EXPECT_EQ(pres_time, std::nullopt);
submit_latch.CountDown();
return true;
},
/*frame_size=*/SkISize::Make(800, 600));
}));
ON_CALL(*surface, MakeRenderContextCurrent())
.WillByDefault(::testing::Invoke(
[] { return std::make_unique<GLContextDefaultResult>(true); }));
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer->Setup(std::move(surface));
auto pipeline = std::make_shared<FramePipeline>(/*depth=*/10);
auto layer_tree = std::make_unique<LayerTree>(
/*root_layer=*/nullptr, /*frame_size=*/SkISize());
auto layer_tree_item = std::make_unique<FrameItem>(
SingleLayerTreeList(kImplicitViewId, std::move(layer_tree),
kDevicePixelRatio),
CreateFinishedBuildRecorder(first_timestamp));
PipelineProduceResult result =
pipeline->Produce().Complete(std::move(layer_tree_item));
EXPECT_TRUE(result.success);
EXPECT_EQ(result.is_first_item, true);
ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false));
rasterizer->Draw(pipeline);
});
submit_latch.Wait();
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
rasterizer.reset();
latch.Signal();
});
latch.Wait();
#endif // false
}
} // namespace flutter