mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Reverts "Reland 3: Multiview pipeline (#49950)" (flutter/engine#50929)
Reverts flutter/engine#49950 Initiated by: dkwingsmt Reason for reverting: Head redness ``` java.lang.RuntimeException: Timeout waiting for firstFrameLatch to signal at dev.flutter.scenarios.ExternalTextureFlutterActivity.waitUntilFlutterRendered(ExternalTextureFlutterActivity.java:98) at dev.flutter.scenariosui.ScreenshotUtil.capture(ScreenshotUtil.java:122) ``` Original PR Author: dkwingsmt Reviewed By: {loic-sharma} This change reverts the following previous change: Original Description: This is the 3rd attempt to land multiview pipeline, following https://github.com/flutter/engine/pull/47239. The pipeline now properly implements the required logic for `scheduleWarmUpFrame` to work in a multi-view setup, following the preparation in https://github.com/flutter/flutter/pull/143290 and https://github.com/flutter/engine/pull/50570. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>
This commit is contained in:
parent
7fd214252c
commit
c388fc8bf0
@ -13,31 +13,6 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* StateToString(FrameTimingsRecorder::State state) {
|
||||
#ifndef NDEBUG
|
||||
switch (state) {
|
||||
case FrameTimingsRecorder::State::kUninitialized:
|
||||
return "kUninitialized";
|
||||
case FrameTimingsRecorder::State::kVsync:
|
||||
return "kVsync";
|
||||
case FrameTimingsRecorder::State::kBuildStart:
|
||||
return "kBuildStart";
|
||||
case FrameTimingsRecorder::State::kBuildEnd:
|
||||
return "kBuildEnd";
|
||||
case FrameTimingsRecorder::State::kRasterStart:
|
||||
return "kRasterStart";
|
||||
case FrameTimingsRecorder::State::kRasterEnd:
|
||||
return "kRasterEnd";
|
||||
};
|
||||
FML_UNREACHABLE();
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::atomic<uint64_t> FrameTimingsRecorder::frame_number_gen_ = {1};
|
||||
|
||||
FrameTimingsRecorder::FrameTimingsRecorder()
|
||||
@ -280,8 +255,7 @@ const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const {
|
||||
}
|
||||
|
||||
void FrameTimingsRecorder::AssertInState(State state) const {
|
||||
FML_DCHECK(state_ == state) << "Expected state " << StateToString(state)
|
||||
<< ", actual state " << StateToString(state_);
|
||||
FML_DCHECK(state_ == state);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -31,7 +31,6 @@ class FrameTimingsRecorder {
|
||||
public:
|
||||
/// Various states that the recorder can be in. When created the recorder is
|
||||
/// in an unitialized state and transtions in sequential order of the states.
|
||||
// After adding an item to this enum, modify StateToString accordingly.
|
||||
enum class State : uint32_t {
|
||||
kUninitialized,
|
||||
kVsync,
|
||||
@ -122,8 +121,6 @@ class FrameTimingsRecorder {
|
||||
///
|
||||
/// Instead of adding a `GetState` method and asserting on the result, this
|
||||
/// method prevents other logic from relying on the state.
|
||||
///
|
||||
/// In release builds, this call is a no-op.
|
||||
void AssertInState(State state) const;
|
||||
|
||||
private:
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#define FML_USED_ON_EMBEDDER
|
||||
|
||||
#include "flutter/common/task_runners.h"
|
||||
#include "flutter/fml/synchronization/count_down_latch.h"
|
||||
#include "flutter/fml/synchronization/waitable_event.h"
|
||||
#include "flutter/lib/ui/painting/canvas.h"
|
||||
#include "flutter/lib/ui/painting/image.h"
|
||||
@ -58,10 +57,6 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) {
|
||||
};
|
||||
|
||||
Settings settings = CreateSettingsForFixture();
|
||||
fml::CountDownLatch frame_latch{2};
|
||||
settings.frame_rasterized_callback = [&frame_latch](const FrameTiming& t) {
|
||||
frame_latch.CountDown();
|
||||
};
|
||||
auto task_runner = CreateNewThread();
|
||||
TaskRunners task_runners("test", // label
|
||||
GetCurrentTaskRunner(), // platform
|
||||
@ -88,15 +83,12 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) {
|
||||
shell->RunEngine(std::move(configuration), [&](auto result) {
|
||||
ASSERT_EQ(result, Engine::RunStatus::Success);
|
||||
});
|
||||
|
||||
message_latch_.Wait();
|
||||
|
||||
ASSERT_TRUE(current_display_list_);
|
||||
ASSERT_TRUE(current_image_);
|
||||
|
||||
// Wait for 2 frames to be rasterized. The 2nd frame releases resources of the
|
||||
// 1st frame.
|
||||
frame_latch.Wait();
|
||||
|
||||
// Force a drain the SkiaUnrefQueue. The engine does this normally as frames
|
||||
// pump, but we force it here to make the test more deterministic.
|
||||
message_latch_.Reset();
|
||||
|
||||
@ -453,9 +453,12 @@ void PlatformConfigurationNativeApi::Render(int64_t view_id,
|
||||
Scene* scene,
|
||||
double width,
|
||||
double height) {
|
||||
// TODO(dkwingsmt): Currently only supports a single window.
|
||||
// See https://github.com/flutter/flutter/issues/135530, item 2.
|
||||
FML_DCHECK(view_id == kFlutterImplicitViewId);
|
||||
UIDartState::ThrowIfUIOperationsProhibited();
|
||||
UIDartState::Current()->platform_configuration()->client()->Render(
|
||||
view_id, scene, width, height);
|
||||
scene, width, height);
|
||||
}
|
||||
|
||||
void PlatformConfigurationNativeApi::SetNeedsReportTimings(bool value) {
|
||||
|
||||
@ -76,10 +76,7 @@ class PlatformConfigurationClient {
|
||||
/// @brief Updates the client's rendering on the GPU with the newly
|
||||
/// provided Scene.
|
||||
///
|
||||
virtual void Render(int64_t view_id,
|
||||
Scene* scene,
|
||||
double width,
|
||||
double height) = 0;
|
||||
virtual void Render(Scene* scene, double width, double height) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// @brief Receives an updated semantics tree from the Framework.
|
||||
|
||||
@ -340,21 +340,21 @@ void RuntimeController::ScheduleFrame() {
|
||||
client_.ScheduleFrame();
|
||||
}
|
||||
|
||||
// |PlatformConfigurationClient|
|
||||
void RuntimeController::EndWarmUpFrame() {
|
||||
client_.EndWarmUpFrame();
|
||||
}
|
||||
|
||||
// |PlatformConfigurationClient|
|
||||
void RuntimeController::Render(int64_t view_id,
|
||||
Scene* scene,
|
||||
double width,
|
||||
double height) {
|
||||
void RuntimeController::Render(Scene* scene, double width, double height) {
|
||||
// TODO(dkwingsmt): Currently only supports a single window.
|
||||
int64_t view_id = kFlutterImplicitViewId;
|
||||
const ViewportMetrics* view_metrics =
|
||||
UIDartState::Current()->platform_configuration()->GetMetrics(view_id);
|
||||
if (view_metrics == nullptr) {
|
||||
return;
|
||||
}
|
||||
client_.Render(view_id, scene->takeLayerTree(width, height),
|
||||
client_.Render(scene->takeLayerTree(width, height),
|
||||
view_metrics->device_pixel_ratio);
|
||||
}
|
||||
|
||||
|
||||
@ -661,10 +661,7 @@ class RuntimeController : public PlatformConfigurationClient {
|
||||
void EndWarmUpFrame() override;
|
||||
|
||||
// |PlatformConfigurationClient|
|
||||
void Render(int64_t view_id,
|
||||
Scene* scene,
|
||||
double width,
|
||||
double height) override;
|
||||
void Render(Scene* scene, double width, double height) override;
|
||||
|
||||
// |PlatformConfigurationClient|
|
||||
void UpdateSemantics(SemanticsUpdate* update) override;
|
||||
|
||||
@ -27,8 +27,7 @@ class RuntimeDelegate {
|
||||
|
||||
virtual void EndWarmUpFrame() = 0;
|
||||
|
||||
virtual void Render(int64_t view_id,
|
||||
std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
virtual void Render(std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
float device_pixel_ratio) = 0;
|
||||
|
||||
virtual void UpdateSemantics(SemanticsNodeUpdates update,
|
||||
|
||||
@ -62,10 +62,6 @@ void Animator::BeginFrame(
|
||||
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
|
||||
TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending",
|
||||
frame_request_number_);
|
||||
// Clear layer trees rendered out of a frame. Only Animator::Render called
|
||||
// within a frame is used.
|
||||
layer_trees_tasks_.clear();
|
||||
|
||||
frame_request_number_++;
|
||||
|
||||
frame_timings_recorder_ = std::move(frame_timings_recorder);
|
||||
@ -116,33 +112,6 @@ void Animator::BeginFrame(
|
||||
dart_frame_deadline_ = frame_target_time.ToEpochDelta();
|
||||
uint64_t frame_number = frame_timings_recorder_->GetFrameNumber();
|
||||
delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
|
||||
}
|
||||
|
||||
void Animator::EndFrame() {
|
||||
FML_DCHECK(frame_timings_recorder_ != nullptr);
|
||||
if (!layer_trees_tasks_.empty()) {
|
||||
// The build is completed in OnAnimatorBeginFrame.
|
||||
frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now());
|
||||
|
||||
delegate_.OnAnimatorUpdateLatestFrameTargetTime(
|
||||
frame_timings_recorder_->GetVsyncTargetTime());
|
||||
|
||||
// Commit the pending continuation.
|
||||
PipelineProduceResult result =
|
||||
producer_continuation_.Complete(std::make_unique<FrameItem>(
|
||||
std::move(layer_trees_tasks_), std::move(frame_timings_recorder_)));
|
||||
|
||||
if (!result.success) {
|
||||
FML_DLOG(INFO) << "Failed to commit to the pipeline";
|
||||
} else if (!result.is_first_item) {
|
||||
// Do nothing. It has been successfully pushed to the pipeline but not as
|
||||
// the first item. Eventually the 'Rasterizer' will consume it, so we
|
||||
// don't need to notify the delegate.
|
||||
} else {
|
||||
delegate_.OnAnimatorDraw(layer_tree_pipeline_);
|
||||
}
|
||||
}
|
||||
frame_timings_recorder_ = nullptr;
|
||||
|
||||
if (!frame_scheduled_ && has_rendered_) {
|
||||
// Wait a tad more than 3 60hz frames before reporting a big idle period.
|
||||
@ -170,18 +139,14 @@ void Animator::EndFrame() {
|
||||
},
|
||||
kNotifyIdleTaskWaitTime);
|
||||
}
|
||||
FML_DCHECK(layer_trees_tasks_.empty());
|
||||
FML_DCHECK(frame_timings_recorder_ == nullptr);
|
||||
}
|
||||
|
||||
void Animator::Render(int64_t view_id,
|
||||
std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
float device_pixel_ratio) {
|
||||
has_rendered_ = true;
|
||||
|
||||
if (!frame_timings_recorder_) {
|
||||
// Framework can directly call render with a built scene. A major reason is
|
||||
// to render warm up frames.
|
||||
// Framework can directly call render with a built scene.
|
||||
frame_timings_recorder_ = std::make_unique<FrameTimingsRecorder>();
|
||||
const fml::TimePoint placeholder_time = fml::TimePoint::Now();
|
||||
frame_timings_recorder_->RecordVsync(placeholder_time, placeholder_time);
|
||||
@ -191,9 +156,35 @@ void Animator::Render(int64_t view_id,
|
||||
TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder_, "flutter",
|
||||
"Animator::Render", /*flow_id_count=*/0,
|
||||
/*flow_ids=*/nullptr);
|
||||
frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now());
|
||||
|
||||
layer_trees_tasks_.push_back(std::make_unique<LayerTreeTask>(
|
||||
delegate_.OnAnimatorUpdateLatestFrameTargetTime(
|
||||
frame_timings_recorder_->GetVsyncTargetTime());
|
||||
|
||||
// TODO(dkwingsmt): Currently only supports a single window.
|
||||
// See https://github.com/flutter/flutter/issues/135530, item 2.
|
||||
int64_t view_id = kFlutterImplicitViewId;
|
||||
std::vector<std::unique_ptr<LayerTreeTask>> layer_trees_tasks;
|
||||
layer_trees_tasks.push_back(std::make_unique<LayerTreeTask>(
|
||||
view_id, std::move(layer_tree), device_pixel_ratio));
|
||||
// Commit the pending continuation.
|
||||
PipelineProduceResult result =
|
||||
producer_continuation_.Complete(std::make_unique<FrameItem>(
|
||||
std::move(layer_trees_tasks), std::move(frame_timings_recorder_)));
|
||||
|
||||
if (!result.success) {
|
||||
FML_DLOG(INFO) << "No pending continuation to commit";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.is_first_item) {
|
||||
// It has been successfully pushed to the pipeline but not as the first
|
||||
// item. Eventually the 'Rasterizer' will consume it, so we don't need to
|
||||
// notify the delegate.
|
||||
return;
|
||||
}
|
||||
|
||||
delegate_.OnAnimatorDraw(layer_tree_pipeline_);
|
||||
}
|
||||
|
||||
const std::weak_ptr<VsyncWaiter> Animator::GetVsyncWaiter() const {
|
||||
@ -265,7 +256,6 @@ void Animator::AwaitVSync() {
|
||||
self->DrawLastLayerTrees(std::move(frame_timings_recorder));
|
||||
} else {
|
||||
self->BeginFrame(std::move(frame_timings_recorder));
|
||||
self->EndFrame();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -275,9 +265,9 @@ void Animator::AwaitVSync() {
|
||||
}
|
||||
|
||||
void Animator::EndWarmUpFrame() {
|
||||
if (!layer_trees_tasks_.empty()) {
|
||||
EndFrame();
|
||||
}
|
||||
// Do nothing. The warm up frame does not need any additional work to end the
|
||||
// frame for now. This will change once the pipeline supports multi-view.
|
||||
// https://github.com/flutter/flutter/issues/142851
|
||||
}
|
||||
|
||||
void Animator::ScheduleSecondaryVsyncCallback(uintptr_t id,
|
||||
|
||||
@ -76,8 +76,7 @@ class Animator final {
|
||||
/// technically, between Animator::BeginFrame and Animator::EndFrame
|
||||
/// (both private methods). Otherwise, this call will be ignored.
|
||||
///
|
||||
void Render(int64_t view_id,
|
||||
std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
void Render(std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
float device_pixel_ratio);
|
||||
|
||||
const std::weak_ptr<VsyncWaiter> GetVsyncWaiter() const;
|
||||
@ -106,13 +105,7 @@ class Animator final {
|
||||
void EnqueueTraceFlowId(uint64_t trace_flow_id);
|
||||
|
||||
private:
|
||||
// Animator's work during a vsync is split into two methods, BeginFrame and
|
||||
// EndFrame. The two methods should be called synchronously back-to-back to
|
||||
// avoid being interrupted by a regular vsync. The reason to split them is to
|
||||
// allow ShellTest::PumpOneFrame to insert a Render in between.
|
||||
|
||||
void BeginFrame(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder);
|
||||
void EndFrame();
|
||||
|
||||
bool CanReuseLastLayerTrees();
|
||||
|
||||
@ -129,7 +122,6 @@ class Animator final {
|
||||
std::shared_ptr<VsyncWaiter> waiter_;
|
||||
|
||||
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder_;
|
||||
std::vector<std::unique_ptr<LayerTreeTask>> layer_trees_tasks_;
|
||||
uint64_t frame_request_number_ = 1;
|
||||
fml::TimeDelta dart_frame_deadline_;
|
||||
std::shared_ptr<FramePipeline> layer_tree_pipeline_;
|
||||
|
||||
@ -23,8 +23,6 @@
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
constexpr int64_t kImplicitViewId = 0;
|
||||
|
||||
class FakeAnimatorDelegate : public Animator::Delegate {
|
||||
public:
|
||||
MOCK_METHOD(void,
|
||||
@ -160,30 +158,20 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyIdleBeforeRender) {
|
||||
latch.Wait();
|
||||
ASSERT_FALSE(delegate.notify_idle_called_);
|
||||
|
||||
fml::AutoResetWaitableEvent render_latch;
|
||||
// Validate it has not notified idle and try to render.
|
||||
task_runners.GetUITaskRunner()->PostDelayedTask(
|
||||
[&] {
|
||||
ASSERT_FALSE(delegate.notify_idle_called_);
|
||||
EXPECT_CALL(delegate, OnAnimatorBeginFrame).WillOnce([&] {
|
||||
auto layer_tree = std::make_unique<LayerTree>(
|
||||
LayerTree::Config(), SkISize::Make(600, 800));
|
||||
animator->Render(kImplicitViewId, std::move(layer_tree), 1.0);
|
||||
render_latch.Signal();
|
||||
});
|
||||
// Request a frame that builds a layer tree and renders a frame.
|
||||
// When the frame is rendered, render_latch will be signaled.
|
||||
animator->RequestFrame(true);
|
||||
auto layer_tree = std::make_unique<LayerTree>(LayerTree::Config(),
|
||||
SkISize::Make(600, 800));
|
||||
animator->Render(std::move(layer_tree), 1.0);
|
||||
task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
|
||||
},
|
||||
// See kNotifyIdleTaskWaitTime in animator.cc.
|
||||
fml::TimeDelta::FromMilliseconds(60));
|
||||
latch.Wait();
|
||||
render_latch.Wait();
|
||||
|
||||
// A frame has been rendered, and the next frame request will notify idle.
|
||||
// But at the moment there isn't another frame request, therefore it still
|
||||
// hasn't notified idle.
|
||||
// Still hasn't notified idle because there has been no frame request.
|
||||
task_runners.GetUITaskRunner()->PostTask([&] {
|
||||
ASSERT_FALSE(delegate.notify_idle_called_);
|
||||
// False to avoid getting cals to BeginFrame that will request more frames
|
||||
@ -236,6 +224,11 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyDelegateIfPipelineIsNotEmpty) {
|
||||
});
|
||||
|
||||
fml::AutoResetWaitableEvent begin_frame_latch;
|
||||
EXPECT_CALL(delegate, OnAnimatorBeginFrame)
|
||||
.WillRepeatedly(
|
||||
[&](fml::TimePoint frame_target_time, uint64_t frame_number) {
|
||||
begin_frame_latch.Signal();
|
||||
});
|
||||
// It must always be called when the method 'Animator::Render' is called,
|
||||
// regardless of whether the pipeline is empty or not.
|
||||
EXPECT_CALL(delegate, OnAnimatorUpdateLatestFrameTargetTime).Times(2);
|
||||
@ -246,16 +239,16 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyDelegateIfPipelineIsNotEmpty) {
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
task_runners.GetUITaskRunner()->PostTask([&] {
|
||||
EXPECT_CALL(delegate, OnAnimatorBeginFrame).WillOnce([&] {
|
||||
auto layer_tree = std::make_unique<LayerTree>(LayerTree::Config(),
|
||||
SkISize::Make(600, 800));
|
||||
animator->Render(kImplicitViewId, std::move(layer_tree), 1.0);
|
||||
begin_frame_latch.Signal();
|
||||
});
|
||||
animator->RequestFrame();
|
||||
task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
|
||||
});
|
||||
begin_frame_latch.Wait();
|
||||
|
||||
PostTaskSync(task_runners.GetUITaskRunner(), [&] {
|
||||
auto layer_tree = std::make_unique<LayerTree>(LayerTree::Config(),
|
||||
SkISize::Make(600, 800));
|
||||
animator->Render(std::move(layer_tree), 1.0);
|
||||
});
|
||||
}
|
||||
|
||||
PostTaskSync(task_runners.GetUITaskRunner(), [&] { animator.reset(); });
|
||||
|
||||
@ -466,8 +466,7 @@ void Engine::EndWarmUpFrame() {
|
||||
animator_->EndWarmUpFrame();
|
||||
}
|
||||
|
||||
void Engine::Render(int64_t view_id,
|
||||
std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
void Engine::Render(std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
float device_pixel_ratio) {
|
||||
if (!layer_tree) {
|
||||
return;
|
||||
@ -478,7 +477,7 @@ void Engine::Render(int64_t view_id,
|
||||
return;
|
||||
}
|
||||
|
||||
animator_->Render(view_id, std::move(layer_tree), device_pixel_ratio);
|
||||
animator_->Render(std::move(layer_tree), device_pixel_ratio);
|
||||
}
|
||||
|
||||
void Engine::UpdateSemantics(SemanticsNodeUpdates update,
|
||||
|
||||
@ -966,8 +966,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
|
||||
std::string DefaultRouteName() override;
|
||||
|
||||
// |RuntimeDelegate|
|
||||
void Render(int64_t view_id,
|
||||
std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
void Render(std::unique_ptr<flutter::LayerTree> layer_tree,
|
||||
float device_pixel_ratio) override;
|
||||
|
||||
// |RuntimeDelegate|
|
||||
|
||||
@ -25,10 +25,8 @@ namespace {
|
||||
using ::testing::Invoke;
|
||||
using ::testing::ReturnRef;
|
||||
|
||||
fml::AutoResetWaitableEvent native_latch;
|
||||
|
||||
void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner,
|
||||
const fml::closure& task) {
|
||||
static void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner,
|
||||
const fml::closure& task) {
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] {
|
||||
task();
|
||||
@ -84,7 +82,7 @@ class MockRuntimeDelegate : public RuntimeDelegate {
|
||||
MOCK_METHOD(void, EndWarmUpFrame, (), (override));
|
||||
MOCK_METHOD(void,
|
||||
Render,
|
||||
(int64_t, std::unique_ptr<flutter::LayerTree>, float),
|
||||
(std::unique_ptr<flutter::LayerTree>, float),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
UpdateSemantics,
|
||||
@ -572,66 +570,6 @@ TEST_F(EngineTest, PassesLoadDartDeferredLibraryErrorToRuntime) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(EngineTest, AnimatorAcceptsMultipleRenders) {
|
||||
MockAnimatorDelegate animator_delegate;
|
||||
std::unique_ptr<EngineContext> engine_context;
|
||||
|
||||
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
|
||||
std::make_shared<MockPlatformMessageHandler>();
|
||||
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
|
||||
.WillOnce(ReturnRef(platform_message_handler));
|
||||
fml::AutoResetWaitableEvent draw_latch;
|
||||
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
|
||||
.WillOnce(
|
||||
Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) {
|
||||
auto status =
|
||||
pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
|
||||
EXPECT_EQ(item->layer_tree_tasks.size(), 2u);
|
||||
EXPECT_EQ(item->layer_tree_tasks[0]->view_id, 1);
|
||||
EXPECT_EQ(item->layer_tree_tasks[1]->view_id, 2);
|
||||
});
|
||||
EXPECT_EQ(status, PipelineConsumeResult::Done);
|
||||
draw_latch.Signal();
|
||||
}));
|
||||
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
|
||||
.WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time,
|
||||
uint64_t frame_number) {
|
||||
engine_context->EngineTaskSync([&](Engine& engine) {
|
||||
engine.BeginFrame(frame_target_time, frame_number);
|
||||
});
|
||||
}));
|
||||
|
||||
native_latch.Reset();
|
||||
AddNativeCallback("NotifyNative", [](auto args) { native_latch.Signal(); });
|
||||
|
||||
std::unique_ptr<Animator> animator;
|
||||
PostSync(task_runners_.GetUITaskRunner(),
|
||||
[&animator, &animator_delegate, &task_runners = task_runners_] {
|
||||
animator = std::make_unique<Animator>(
|
||||
animator_delegate, task_runners,
|
||||
static_cast<std::unique_ptr<VsyncWaiter>>(
|
||||
std::make_unique<testing::ConstantFiringVsyncWaiter>(
|
||||
task_runners)));
|
||||
});
|
||||
|
||||
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
|
||||
std::move(animator));
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings_);
|
||||
configuration.SetEntrypoint("onDrawFrameRenderAllViews");
|
||||
engine_context->Run(std::move(configuration));
|
||||
|
||||
engine_context->EngineTaskSync([](Engine& engine) {
|
||||
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0});
|
||||
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0});
|
||||
});
|
||||
|
||||
native_latch.Wait();
|
||||
|
||||
engine_context->EngineTaskSync(
|
||||
[](Engine& engine) { engine.ScheduleFrame(); });
|
||||
draw_latch.Wait();
|
||||
}
|
||||
|
||||
// The animator should submit to the pipeline the implicit view rendered in a
|
||||
// warm up frame if there's already a continuation (i.e. Animator::BeginFrame
|
||||
// has been called)
|
||||
@ -697,72 +635,4 @@ TEST_F(EngineTest, AnimatorSubmitWarmUpImplicitView) {
|
||||
draw_latch.Wait();
|
||||
}
|
||||
|
||||
// The warm up frame should work if only some of the registered views are
|
||||
// included.
|
||||
//
|
||||
// This test also verifies that the warm up frame can render multiple views.
|
||||
TEST_F(EngineTest, AnimatorSubmitPartialViewsForWarmUp) {
|
||||
MockAnimatorDelegate animator_delegate;
|
||||
std::unique_ptr<EngineContext> engine_context;
|
||||
|
||||
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
|
||||
std::make_shared<MockPlatformMessageHandler>();
|
||||
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
|
||||
.WillOnce(ReturnRef(platform_message_handler));
|
||||
|
||||
fml::AutoResetWaitableEvent continuation_ready_latch;
|
||||
fml::AutoResetWaitableEvent draw_latch;
|
||||
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
|
||||
.WillOnce(
|
||||
Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) {
|
||||
auto status =
|
||||
pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
|
||||
EXPECT_EQ(item->layer_tree_tasks.size(), 2u);
|
||||
EXPECT_EQ(item->layer_tree_tasks[0]->view_id, 1);
|
||||
EXPECT_EQ(item->layer_tree_tasks[1]->view_id, 2);
|
||||
});
|
||||
EXPECT_EQ(status, PipelineConsumeResult::Done);
|
||||
draw_latch.Signal();
|
||||
}));
|
||||
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
|
||||
.WillRepeatedly(
|
||||
Invoke([&engine_context, &continuation_ready_latch](
|
||||
fml::TimePoint frame_target_time, uint64_t frame_number) {
|
||||
continuation_ready_latch.Signal();
|
||||
engine_context->EngineTaskSync([&](Engine& engine) {
|
||||
engine.BeginFrame(frame_target_time, frame_number);
|
||||
});
|
||||
}));
|
||||
|
||||
std::unique_ptr<Animator> animator;
|
||||
PostSync(task_runners_.GetUITaskRunner(),
|
||||
[&animator, &animator_delegate, &task_runners = task_runners_] {
|
||||
animator = std::make_unique<Animator>(
|
||||
animator_delegate, task_runners,
|
||||
static_cast<std::unique_ptr<VsyncWaiter>>(
|
||||
std::make_unique<testing::ConstantFiringVsyncWaiter>(
|
||||
task_runners)));
|
||||
});
|
||||
|
||||
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
|
||||
std::move(animator));
|
||||
|
||||
engine_context->EngineTaskSync([](Engine& engine) {
|
||||
// Schedule a frame to make the animator create a continuation.
|
||||
engine.ScheduleFrame(true);
|
||||
// Add multiple views.
|
||||
engine.AddView(0, ViewportMetrics{1, 10, 10, 22, 0});
|
||||
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0});
|
||||
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0});
|
||||
});
|
||||
|
||||
continuation_ready_latch.Wait();
|
||||
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings_);
|
||||
configuration.SetEntrypoint("renderWarmUpView1and2");
|
||||
engine_context->Run(std::move(configuration));
|
||||
|
||||
draw_latch.Wait();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -532,27 +532,6 @@ void testReportViewWidths() {
|
||||
};
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void onDrawFrameRenderAllViews() {
|
||||
PlatformDispatcher.instance.onDrawFrame = () {
|
||||
for (final FlutterView view in PlatformDispatcher.instance.views) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
final PictureRecorder recorder = PictureRecorder();
|
||||
final Canvas canvas = Canvas(recorder);
|
||||
canvas.drawPaint(Paint()..color = const Color(0xFFABCDEF));
|
||||
final Picture picture = recorder.endRecording();
|
||||
builder.addPicture(Offset.zero, picture);
|
||||
|
||||
final Scene scene = builder.build();
|
||||
view.render(scene);
|
||||
|
||||
scene.dispose();
|
||||
picture.dispose();
|
||||
}
|
||||
};
|
||||
notifyNative();
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void renderWarmUpImplicitView() {
|
||||
bool beginFrameCalled = false;
|
||||
@ -580,35 +559,3 @@ void renderWarmUpImplicitView() {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void renderWarmUpView1and2() {
|
||||
bool beginFrameCalled = false;
|
||||
|
||||
PlatformDispatcher.instance.scheduleWarmUpFrame(
|
||||
beginFrame: () {
|
||||
expect(beginFrameCalled, false);
|
||||
beginFrameCalled = true;
|
||||
},
|
||||
drawFrame: () {
|
||||
expect(beginFrameCalled, true);
|
||||
|
||||
for (final int viewId in <int>[1, 2]) {
|
||||
final FlutterView view = PlatformDispatcher.instance.view(id: viewId)!;
|
||||
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
final PictureRecorder recorder = PictureRecorder();
|
||||
final Canvas canvas = Canvas(recorder);
|
||||
canvas.drawPaint(Paint()..color = const Color(0xFFABCDEF));
|
||||
final Picture picture = recorder.endRecording();
|
||||
builder.addPicture(Offset.zero, picture);
|
||||
|
||||
final Scene scene = builder.build();
|
||||
view.render(scene);
|
||||
|
||||
scene.dispose();
|
||||
picture.dispose();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -127,11 +127,11 @@ static void TestSimulatedInputEvents(
|
||||
ShellTest::DispatchFakePointerData(shell.get());
|
||||
i += 1;
|
||||
}
|
||||
ShellTest::VSyncFlush(shell.get(), &will_draw_new_frame);
|
||||
ShellTest::VSyncFlush(shell.get(), will_draw_new_frame);
|
||||
}
|
||||
// Finally, issue a vsync for the pending event that may be generated duing
|
||||
// the last vsync.
|
||||
ShellTest::VSyncFlush(shell.get(), &will_draw_new_frame);
|
||||
ShellTest::VSyncFlush(shell.get(), will_draw_new_frame);
|
||||
});
|
||||
|
||||
simulation.wait();
|
||||
@ -345,7 +345,8 @@ TEST_F(ShellTest, CanCorrectlyPipePointerPacket) {
|
||||
CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0);
|
||||
packet->SetPointerData(5, data);
|
||||
ShellTest::DispatchPointerData(shell.get(), std::move(packet));
|
||||
ShellTest::VSyncFlush(shell.get());
|
||||
bool will_draw_new_frame;
|
||||
ShellTest::VSyncFlush(shell.get(), will_draw_new_frame);
|
||||
|
||||
reportLatch.Wait();
|
||||
size_t expect_length = 6;
|
||||
@ -406,7 +407,8 @@ TEST_F(ShellTest, CanCorrectlySynthesizePointerPacket) {
|
||||
CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0);
|
||||
packet->SetPointerData(3, data);
|
||||
ShellTest::DispatchPointerData(shell.get(), std::move(packet));
|
||||
ShellTest::VSyncFlush(shell.get());
|
||||
bool will_draw_new_frame;
|
||||
ShellTest::VSyncFlush(shell.get(), will_draw_new_frame);
|
||||
|
||||
reportLatch.Wait();
|
||||
size_t expect_length = 6;
|
||||
|
||||
@ -261,7 +261,6 @@ DrawStatus Rasterizer::Draw(const std::shared_ptr<FramePipeline>& pipeline) {
|
||||
|
||||
bool should_resubmit_frame = ShouldResubmitFrame(draw_result);
|
||||
if (should_resubmit_frame) {
|
||||
FML_CHECK(draw_result.resubmitted_item);
|
||||
auto front_continuation = pipeline->ProduceIfEmpty();
|
||||
PipelineProduceResult pipeline_result =
|
||||
front_continuation.Complete(std::move(draw_result.resubmitted_item));
|
||||
|
||||
@ -22,39 +22,6 @@ namespace testing {
|
||||
|
||||
constexpr int64_t kImplicitViewId = 0;
|
||||
|
||||
FrameContent ViewContent::NoViews() {
|
||||
return std::map<int64_t, ViewContent>();
|
||||
}
|
||||
|
||||
FrameContent ViewContent::DummyView(double width, double height) {
|
||||
FrameContent result;
|
||||
result[kImplicitViewId] = ViewContent{
|
||||
.viewport_metrics = {1.0, width, height, 22, 0},
|
||||
.builder = {},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
FrameContent ViewContent::DummyView(flutter::ViewportMetrics viewport_metrics) {
|
||||
FrameContent result;
|
||||
result[kImplicitViewId] = ViewContent{
|
||||
.viewport_metrics = std::move(viewport_metrics),
|
||||
.builder = {},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
FrameContent ViewContent::ImplicitView(double width,
|
||||
double height,
|
||||
LayerTreeBuilder builder) {
|
||||
FrameContent result;
|
||||
result[kImplicitViewId] = ViewContent{
|
||||
.viewport_metrics = {1.0, width, height, 22, 0},
|
||||
.builder = std::move(builder),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
ShellTest::ShellTest()
|
||||
: thread_host_("io.flutter.test." + GetCurrentTestName() + ".",
|
||||
ThreadHost::Type::kPlatform | ThreadHost::Type::kIo |
|
||||
@ -125,18 +92,16 @@ void ShellTest::RestartEngine(Shell* shell, RunConfiguration configuration) {
|
||||
ASSERT_TRUE(restarted.get_future().get());
|
||||
}
|
||||
|
||||
void ShellTest::VSyncFlush(Shell* shell, bool* will_draw_new_frame) {
|
||||
void ShellTest::VSyncFlush(Shell* shell, bool& will_draw_new_frame) {
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
fml::TaskRunner::RunNowOrPostTask(
|
||||
shell->GetTaskRunners().GetPlatformTaskRunner(),
|
||||
[shell, will_draw_new_frame, &latch] {
|
||||
[shell, &will_draw_new_frame, &latch] {
|
||||
// The following UI task ensures that all previous UI tasks are flushed.
|
||||
fml::AutoResetWaitableEvent ui_latch;
|
||||
shell->GetTaskRunners().GetUITaskRunner()->PostTask(
|
||||
[&ui_latch, will_draw_new_frame]() {
|
||||
if (will_draw_new_frame != nullptr) {
|
||||
*will_draw_new_frame = true;
|
||||
}
|
||||
[&ui_latch, &will_draw_new_frame]() {
|
||||
will_draw_new_frame = true;
|
||||
ui_latch.Signal();
|
||||
});
|
||||
|
||||
@ -189,7 +154,6 @@ void ShellTest::SetViewportMetrics(Shell* shell, double width, double height) {
|
||||
std::make_unique<FrameTimingsRecorder>();
|
||||
recorder->RecordVsync(frame_begin_time, frame_end_time);
|
||||
engine->animator_->BeginFrame(std::move(recorder));
|
||||
engine->animator_->EndFrame();
|
||||
}
|
||||
latch.Signal();
|
||||
});
|
||||
@ -208,22 +172,23 @@ void ShellTest::NotifyIdle(Shell* shell, fml::TimeDelta deadline) {
|
||||
latch.Wait();
|
||||
}
|
||||
|
||||
void ShellTest::PumpOneFrame(Shell* shell) {
|
||||
PumpOneFrame(shell, ViewContent::DummyView());
|
||||
void ShellTest::PumpOneFrame(Shell* shell,
|
||||
double width,
|
||||
double height,
|
||||
LayerTreeBuilder builder) {
|
||||
PumpOneFrame(shell, {1.0, width, height, 22, 0}, std::move(builder));
|
||||
}
|
||||
|
||||
void ShellTest::PumpOneFrame(Shell* shell, FrameContent frame_content) {
|
||||
void ShellTest::PumpOneFrame(Shell* shell,
|
||||
const flutter::ViewportMetrics& viewport_metrics,
|
||||
LayerTreeBuilder builder) {
|
||||
// Set viewport to nonempty, and call Animator::BeginFrame to make the layer
|
||||
// tree pipeline nonempty. Without either of this, the layer tree below
|
||||
// won't be rasterized.
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
fml::WeakPtr<RuntimeDelegate> runtime_delegate = shell->weak_engine_;
|
||||
shell->GetTaskRunners().GetUITaskRunner()->PostTask(
|
||||
[&latch, engine = shell->weak_engine_, &frame_content,
|
||||
runtime_delegate]() {
|
||||
for (auto& [view_id, view_content] : frame_content) {
|
||||
engine->SetViewportMetrics(view_id, view_content.viewport_metrics);
|
||||
}
|
||||
[&latch, engine = shell->weak_engine_, viewport_metrics]() {
|
||||
engine->SetViewportMetrics(kImplicitViewId, viewport_metrics);
|
||||
const auto frame_begin_time = fml::TimePoint::Now();
|
||||
const auto frame_end_time =
|
||||
frame_begin_time + fml::TimeDelta::FromSecondsF(1.0 / 60.0);
|
||||
@ -231,28 +196,28 @@ void ShellTest::PumpOneFrame(Shell* shell, FrameContent frame_content) {
|
||||
std::make_unique<FrameTimingsRecorder>();
|
||||
recorder->RecordVsync(frame_begin_time, frame_end_time);
|
||||
engine->animator_->BeginFrame(std::move(recorder));
|
||||
latch.Signal();
|
||||
});
|
||||
latch.Wait();
|
||||
|
||||
// The BeginFrame phase and the EndFrame phase must be performed in a
|
||||
// single task, otherwise a normal vsync might be inserted in between,
|
||||
// causing flaky assertion errors.
|
||||
|
||||
for (auto& [view_id, view_content] : frame_content) {
|
||||
SkMatrix identity;
|
||||
identity.setIdentity();
|
||||
auto root_layer = std::make_shared<TransformLayer>(identity);
|
||||
auto layer_tree = std::make_unique<LayerTree>(
|
||||
LayerTree::Config{.root_layer = root_layer},
|
||||
SkISize::Make(view_content.viewport_metrics.physical_width,
|
||||
view_content.viewport_metrics.physical_height));
|
||||
float device_pixel_ratio = static_cast<float>(
|
||||
view_content.viewport_metrics.device_pixel_ratio);
|
||||
if (view_content.builder) {
|
||||
view_content.builder(root_layer);
|
||||
}
|
||||
runtime_delegate->Render(view_id, std::move(layer_tree),
|
||||
device_pixel_ratio);
|
||||
latch.Reset();
|
||||
// Call |Render| to rasterize a layer tree and trigger |OnFrameRasterized|
|
||||
fml::WeakPtr<RuntimeDelegate> runtime_delegate = shell->weak_engine_;
|
||||
shell->GetTaskRunners().GetUITaskRunner()->PostTask(
|
||||
[&latch, runtime_delegate, &builder, viewport_metrics]() {
|
||||
SkMatrix identity;
|
||||
identity.setIdentity();
|
||||
auto root_layer = std::make_shared<TransformLayer>(identity);
|
||||
auto layer_tree = std::make_unique<LayerTree>(
|
||||
LayerTree::Config{.root_layer = root_layer},
|
||||
SkISize::Make(viewport_metrics.physical_width,
|
||||
viewport_metrics.physical_height));
|
||||
float device_pixel_ratio =
|
||||
static_cast<float>(viewport_metrics.device_pixel_ratio);
|
||||
if (builder) {
|
||||
builder(root_layer);
|
||||
}
|
||||
engine->animator_->EndFrame();
|
||||
runtime_delegate->Render(std::move(layer_tree), device_pixel_ratio);
|
||||
latch.Signal();
|
||||
});
|
||||
latch.Wait();
|
||||
|
||||
@ -29,38 +29,6 @@
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
// The signature of ViewContent::builder.
|
||||
using LayerTreeBuilder =
|
||||
std::function<void(std::shared_ptr<ContainerLayer> root)>;
|
||||
struct ViewContent;
|
||||
// Defines the content to be rendered to all views of a frame in PumpOneFrame.
|
||||
using FrameContent = std::map<int64_t, ViewContent>;
|
||||
// Defines the content to be rendered to a view in PumpOneFrame.
|
||||
struct ViewContent {
|
||||
flutter::ViewportMetrics viewport_metrics;
|
||||
// Given the root layer, this callback builds the layer tree to be rasterized
|
||||
// in PumpOneFrame.
|
||||
LayerTreeBuilder builder;
|
||||
|
||||
// Build a frame with no views. This is useful when PumpOneFrame is used just
|
||||
// to schedule the frame while the frame content is defined by other means.
|
||||
static FrameContent NoViews();
|
||||
|
||||
// Build a frame with a single implicit view with the specific size and no
|
||||
// content.
|
||||
static FrameContent DummyView(double width = 1, double height = 1);
|
||||
|
||||
// Build a frame with a single implicit view with the specific viewport
|
||||
// metrics and no content.
|
||||
static FrameContent DummyView(flutter::ViewportMetrics viewport_metrics);
|
||||
|
||||
// Build a frame with a single implicit view with the specific size and
|
||||
// content.
|
||||
static FrameContent ImplicitView(double width,
|
||||
double height,
|
||||
LayerTreeBuilder builder);
|
||||
};
|
||||
|
||||
class ShellTest : public FixtureTest {
|
||||
public:
|
||||
struct Config {
|
||||
@ -102,14 +70,24 @@ class ShellTest : public FixtureTest {
|
||||
static void RestartEngine(Shell* shell, RunConfiguration configuration);
|
||||
|
||||
/// Issue as many VSYNC as needed to flush the UI tasks so far, and reset
|
||||
/// the content of `will_draw_new_frame` to true if it's not nullptr.
|
||||
static void VSyncFlush(Shell* shell, bool* will_draw_new_frame = nullptr);
|
||||
/// the `will_draw_new_frame` to true.
|
||||
static void VSyncFlush(Shell* shell, bool& will_draw_new_frame);
|
||||
|
||||
/// Given the root layer, this callback builds the layer tree to be rasterized
|
||||
/// in PumpOneFrame.
|
||||
using LayerTreeBuilder =
|
||||
std::function<void(std::shared_ptr<ContainerLayer> root)>;
|
||||
|
||||
static void SetViewportMetrics(Shell* shell, double width, double height);
|
||||
static void NotifyIdle(Shell* shell, fml::TimeDelta deadline);
|
||||
|
||||
static void PumpOneFrame(Shell* shell);
|
||||
static void PumpOneFrame(Shell* shell, FrameContent frame_content);
|
||||
static void PumpOneFrame(Shell* shell,
|
||||
double width = 1,
|
||||
double height = 1,
|
||||
LayerTreeBuilder = {});
|
||||
static void PumpOneFrame(Shell* shell,
|
||||
const flutter::ViewportMetrics& viewport_metrics,
|
||||
LayerTreeBuilder = {});
|
||||
static void DispatchFakePointerData(Shell* shell);
|
||||
static void DispatchPointerData(Shell* shell,
|
||||
std::unique_ptr<PointerDataPacket> packet);
|
||||
|
||||
@ -42,7 +42,6 @@
|
||||
#include "flutter/shell/common/switches.h"
|
||||
#include "flutter/shell/common/thread_host.h"
|
||||
#include "flutter/shell/common/vsync_waiter_fallback.h"
|
||||
#include "flutter/shell/common/vsync_waiters_test.h"
|
||||
#include "flutter/shell/version/version.h"
|
||||
#include "flutter/testing/mock_canvas.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
@ -876,7 +875,7 @@ TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) {
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
end_frame_latch.Wait();
|
||||
ASSERT_TRUE(end_frame_called);
|
||||
|
||||
@ -950,7 +949,7 @@ TEST_F(ShellTest, PushBackdropFilterToVisitedPlatformViews) {
|
||||
backdrop_filter_layer->Add(platform_view_layer2);
|
||||
};
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
end_frame_latch.Wait();
|
||||
ASSERT_EQ(visited_platform_views, (std::vector<int64_t>{50, 75}));
|
||||
ASSERT_TRUE(stack_75.is_empty());
|
||||
@ -1011,7 +1010,7 @@ TEST_F(ShellTest,
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
end_frame_latch.Wait();
|
||||
|
||||
ASSERT_TRUE(end_frame_called);
|
||||
@ -1057,12 +1056,9 @@ TEST_F(ShellTest, OnPlatformViewDestroyDisablesThreadMerger) {
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
|
||||
auto result = shell->WaitForFirstFrame(fml::TimeDelta::Max());
|
||||
// Wait for the rasterizer to process the frame. WaitForFirstFrame only waits
|
||||
// for the Animator, but end_frame_callback is called by the Rasterizer.
|
||||
PostSync(shell->GetTaskRunners().GetRasterTaskRunner(), [] {});
|
||||
ASSERT_TRUE(result.ok()) << "Result: " << static_cast<int>(result.code())
|
||||
<< ": " << result.message();
|
||||
|
||||
@ -1127,12 +1123,12 @@ TEST_F(ShellTest, OnPlatformViewDestroyAfterMergingThreads) {
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
// Pump one frame to trigger thread merging.
|
||||
end_frame_latch.Wait();
|
||||
// Pump another frame to ensure threads are merged and a regular layer tree is
|
||||
// submitted.
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
// Threads are merged here. PlatformViewNotifyDestroy should be executed
|
||||
// successfully.
|
||||
ASSERT_TRUE(fml::TaskRunnerChecker::RunsOnTheSameThread(
|
||||
@ -1196,7 +1192,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWhenThreadsAreMerging) {
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
// Pump one frame and threads aren't merged
|
||||
end_frame_latch.Wait();
|
||||
ASSERT_FALSE(fml::TaskRunnerChecker::RunsOnTheSameThread(
|
||||
@ -1207,7 +1203,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWhenThreadsAreMerging) {
|
||||
// threads
|
||||
external_view_embedder->UpdatePostPrerollResult(
|
||||
PostPrerollResult::kResubmitFrame);
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
|
||||
// Now destroy the platform view immediately.
|
||||
// Two things can happen here:
|
||||
@ -1263,7 +1259,7 @@ TEST_F(ShellTest,
|
||||
SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false);
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
end_frame_latch.Wait();
|
||||
|
||||
// Threads should not be merged.
|
||||
@ -1302,7 +1298,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) {
|
||||
SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false);
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
|
||||
// Threads should not be merged.
|
||||
ASSERT_FALSE(fml::TaskRunnerChecker::RunsOnTheSameThread(
|
||||
@ -1368,7 +1364,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithStaticThreadMerging) {
|
||||
SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false);
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
end_frame_latch.Wait();
|
||||
|
||||
ValidateDestroyPlatformView(shell.get());
|
||||
@ -1414,7 +1410,7 @@ TEST_F(ShellTest, GetUsedThisFrameShouldBeSetBeforeEndFrame) {
|
||||
SkPoint::Make(10, 10), MakeSizedDisplayList(80, 80), false, false);
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
end_frame_latch.Wait();
|
||||
ASSERT_FALSE(used_this_frame);
|
||||
|
||||
@ -1564,11 +1560,10 @@ TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) {
|
||||
configuration.SetEntrypoint("emptyMain");
|
||||
|
||||
RunEngine(shell.get(), std::move(configuration));
|
||||
PumpOneFrame(shell.get(), ViewContent::DummyView({1.0, 0.0, 0.0, 22, 0}));
|
||||
PumpOneFrame(shell.get(), {1.0, 0.0, 0.0, 22, 0});
|
||||
fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero());
|
||||
EXPECT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.message(), "timeout");
|
||||
EXPECT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded);
|
||||
|
||||
DestroyShell(std::move(shell));
|
||||
}
|
||||
@ -2084,7 +2079,6 @@ TEST_F(ShellTest, CanScheduleFrameFromPlatform) {
|
||||
TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) {
|
||||
bool is_on_begin_frame_called = false;
|
||||
bool is_secondary_callback_called = false;
|
||||
bool test_started = false;
|
||||
Settings settings = CreateSettingsForFixture();
|
||||
TaskRunners task_runners = GetTaskRunnersForFixture();
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
@ -2094,18 +2088,12 @@ TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) {
|
||||
fml::CountDownLatch count_down_latch(2);
|
||||
AddNativeCallback("NativeOnBeginFrame",
|
||||
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
|
||||
if (!test_started) {
|
||||
return;
|
||||
}
|
||||
EXPECT_FALSE(is_on_begin_frame_called);
|
||||
EXPECT_FALSE(is_secondary_callback_called);
|
||||
is_on_begin_frame_called = true;
|
||||
count_down_latch.CountDown();
|
||||
}));
|
||||
std::unique_ptr<Shell> shell = CreateShell({
|
||||
.settings = settings,
|
||||
.task_runners = task_runners,
|
||||
});
|
||||
std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
|
||||
ASSERT_TRUE(shell->IsSetup());
|
||||
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||
@ -2118,16 +2106,12 @@ TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) {
|
||||
fml::TaskRunner::RunNowOrPostTask(
|
||||
shell->GetTaskRunners().GetUITaskRunner(), [&]() {
|
||||
shell->GetEngine()->ScheduleSecondaryVsyncCallback(0, [&]() {
|
||||
if (!test_started) {
|
||||
return;
|
||||
}
|
||||
EXPECT_TRUE(is_on_begin_frame_called);
|
||||
EXPECT_FALSE(is_secondary_callback_called);
|
||||
is_secondary_callback_called = true;
|
||||
count_down_latch.CountDown();
|
||||
});
|
||||
shell->GetEngine()->ScheduleFrame();
|
||||
test_started = true;
|
||||
});
|
||||
count_down_latch.Wait();
|
||||
EXPECT_TRUE(is_on_begin_frame_called);
|
||||
@ -2172,7 +2156,7 @@ TEST_F(ShellTest, Screenshot) {
|
||||
root->Add(display_list_layer);
|
||||
};
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::ImplicitView(100, 100, builder));
|
||||
PumpOneFrame(shell.get(), 100, 100, builder);
|
||||
firstFrameLatch.Wait();
|
||||
|
||||
std::promise<Rasterizer::Screenshot> screenshot_promise;
|
||||
@ -2557,13 +2541,7 @@ TEST_F(ShellTest, OnServiceProtocolRenderFrameWithRasterStatsWorks) {
|
||||
configuration.SetEntrypoint("scene_with_red_box");
|
||||
|
||||
RunEngine(shell.get(), std::move(configuration));
|
||||
// Set a non-zero viewport metrics, otherwise the scene would be discarded.
|
||||
PostSync(shell->GetTaskRunners().GetUITaskRunner(),
|
||||
[engine = shell->GetEngine()]() {
|
||||
engine->SetViewportMetrics(kImplicitViewId,
|
||||
ViewportMetrics{1, 1, 1, 22, 0});
|
||||
});
|
||||
PumpOneFrame(shell.get(), ViewContent::NoViews());
|
||||
PumpOneFrame(shell.get());
|
||||
|
||||
ServiceProtocol::Handler::ServiceProtocolMap empty_params;
|
||||
rapidjson::Document document;
|
||||
@ -2675,7 +2653,7 @@ TEST_F(ShellTest, OnServiceProtocolRenderFrameWithRasterStatsDisableImpeller) {
|
||||
configuration.SetEntrypoint("scene_with_red_box");
|
||||
|
||||
RunEngine(shell.get(), std::move(configuration));
|
||||
PumpOneFrame(shell.get(), ViewContent::NoViews());
|
||||
PumpOneFrame(shell.get());
|
||||
|
||||
ServiceProtocol::Handler::ServiceProtocolMap empty_params;
|
||||
rapidjson::Document document;
|
||||
@ -2739,16 +2717,14 @@ TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) {
|
||||
|
||||
RunEngine(shell.get(), std::move(configuration));
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::DummyView(
|
||||
static_cast<double>(wrong_size.width()),
|
||||
static_cast<double>(wrong_size.height())));
|
||||
PumpOneFrame(shell.get(), static_cast<double>(wrong_size.width()),
|
||||
static_cast<double>(wrong_size.height()));
|
||||
end_frame_latch.Wait();
|
||||
// Wrong size, no frames are submitted.
|
||||
ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::DummyView(
|
||||
static_cast<double>(expected_size.width()),
|
||||
static_cast<double>(expected_size.height())));
|
||||
PumpOneFrame(shell.get(), static_cast<double>(expected_size.width()),
|
||||
static_cast<double>(expected_size.height()));
|
||||
end_frame_latch.Wait();
|
||||
// Expected size, 1 frame submitted.
|
||||
ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
|
||||
@ -2819,9 +2795,8 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) {
|
||||
|
||||
RunEngine(shell.get(), std::move(configuration));
|
||||
|
||||
PumpOneFrame(shell.get(), ViewContent::DummyView(
|
||||
static_cast<double>(origin_size.width()),
|
||||
static_cast<double>(origin_size.height())));
|
||||
PumpOneFrame(shell.get(), static_cast<double>(origin_size.width()),
|
||||
static_cast<double>(origin_size.height()));
|
||||
|
||||
end_frame_latch.Wait();
|
||||
ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
|
||||
@ -2842,9 +2817,8 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) {
|
||||
ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
|
||||
|
||||
// Threads will be merged at the end of this frame.
|
||||
PumpOneFrame(shell.get(),
|
||||
ViewContent::DummyView(static_cast<double>(new_size.width()),
|
||||
static_cast<double>(new_size.height())));
|
||||
PumpOneFrame(shell.get(), static_cast<double>(new_size.width()),
|
||||
static_cast<double>(new_size.height()));
|
||||
|
||||
end_frame_latch.Wait();
|
||||
ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
|
||||
|
||||
@ -10,12 +10,10 @@ void main() {
|
||||
test('PlatformView layers do not emit errors from tester', () async {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
builder.addPlatformView(1);
|
||||
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
|
||||
final Scene scene = builder.build();
|
||||
PlatformDispatcher.instance.implicitView!.render(scene);
|
||||
scene.dispose();
|
||||
};
|
||||
PlatformDispatcher.instance.scheduleFrame();
|
||||
final Scene scene = builder.build();
|
||||
|
||||
PlatformDispatcher.instance.implicitView!.render(scene);
|
||||
scene.dispose();
|
||||
// Test harness asserts that this does not emit an error from the shell logs.
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user