mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Revert "Reland 2: Multiview Pipeline (#47239)" (flutter/engine#49238)
This reverts commit 87171e73397f7edfd4c145fbaebdd7208954debf. Internal performance test shows that this PR negatively affects startup time. I'll reland this PR in parts. b/307872797 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
parent
05268a49ed
commit
927fd200ed
@ -254,30 +254,8 @@ const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const {
|
||||
return frame_number_trace_arg_val_.c_str();
|
||||
}
|
||||
|
||||
static 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 "";
|
||||
}
|
||||
|
||||
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 opt builds, this call is a no-op.
|
||||
void AssertInState(State state) const;
|
||||
|
||||
private:
|
||||
|
||||
@ -98,7 +98,7 @@ typedef CanvasPath Path;
|
||||
V(NativeStringAttribute::initSpellOutStringAttribute, 3) \
|
||||
V(PlatformConfigurationNativeApi::DefaultRouteName, 0) \
|
||||
V(PlatformConfigurationNativeApi::ScheduleFrame, 0) \
|
||||
V(PlatformConfigurationNativeApi::Render, 2) \
|
||||
V(PlatformConfigurationNativeApi::Render, 1) \
|
||||
V(PlatformConfigurationNativeApi::UpdateSemantics, 1) \
|
||||
V(PlatformConfigurationNativeApi::SetNeedsReportTimings, 1) \
|
||||
V(PlatformConfigurationNativeApi::SetIsolateDebugName, 1) \
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -374,16 +374,14 @@ class FlutterView {
|
||||
void render(Scene scene, {Size? size}) {
|
||||
// Duplicated calls or calls outside of onBeginFrame/onDrawFrame (indicated
|
||||
// by _renderedViews being null) are ignored. See _renderedViews.
|
||||
// TODO(dkwingsmt): We should change this skip into an assertion.
|
||||
// https://github.com/flutter/flutter/issues/137073
|
||||
final bool validRender = platformDispatcher._renderedViews?.add(this) ?? false;
|
||||
if (validRender) {
|
||||
_render(viewId, scene as _NativeScene, size?.width ?? physicalSize.width, size?.height ?? physicalSize.height);
|
||||
_render(scene as _NativeScene, size?.width ?? physicalSize.width, size?.height ?? physicalSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
@Native<Void Function(Int64, Pointer<Void>, Double, Double)>(symbol: 'PlatformConfigurationNativeApi::Render')
|
||||
external static void _render(int viewId, _NativeScene scene, double width, double height);
|
||||
@Native<Void Function(Pointer<Void>, Double, Double)>(symbol: 'PlatformConfigurationNativeApi::Render')
|
||||
external static void _render(_NativeScene scene, double width, double height);
|
||||
|
||||
/// Change the retained semantics data about this [FlutterView].
|
||||
///
|
||||
|
||||
@ -449,13 +449,12 @@ void PlatformConfiguration::CompletePlatformMessageResponse(
|
||||
response->Complete(std::make_unique<fml::DataMapping>(std::move(data)));
|
||||
}
|
||||
|
||||
void PlatformConfigurationNativeApi::Render(int64_t view_id,
|
||||
Scene* scene,
|
||||
void PlatformConfigurationNativeApi::Render(Scene* scene,
|
||||
double width,
|
||||
double height) {
|
||||
UIDartState::ThrowIfUIOperationsProhibited();
|
||||
UIDartState::Current()->platform_configuration()->client()->Render(
|
||||
view_id, scene, width, height);
|
||||
scene, width, height);
|
||||
}
|
||||
|
||||
void PlatformConfigurationNativeApi::SetNeedsReportTimings(bool value) {
|
||||
|
||||
@ -69,10 +69,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.
|
||||
@ -560,10 +557,7 @@ class PlatformConfigurationNativeApi {
|
||||
|
||||
static void ScheduleFrame();
|
||||
|
||||
static void Render(int64_t view_id,
|
||||
Scene* scene,
|
||||
double width,
|
||||
double height);
|
||||
static void Render(Scene* scene, double width, double height);
|
||||
|
||||
static void UpdateSemantics(SemanticsUpdate* update);
|
||||
|
||||
|
||||
@ -15,166 +15,8 @@
|
||||
#include "flutter/shell/common/shell_test.h"
|
||||
#include "flutter/shell/common/thread_host.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr int64_t kImplicitViewId = 0;
|
||||
|
||||
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();
|
||||
latch.Signal();
|
||||
});
|
||||
latch.Wait();
|
||||
}
|
||||
|
||||
class MockRuntimeDelegate : public RuntimeDelegate {
|
||||
public:
|
||||
MOCK_METHOD(std::string, DefaultRouteName, (), (override));
|
||||
MOCK_METHOD(void, ScheduleFrame, (bool), (override));
|
||||
MOCK_METHOD(void,
|
||||
Render,
|
||||
(int64_t, std::unique_ptr<flutter::LayerTree>, float),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
UpdateSemantics,
|
||||
(SemanticsNodeUpdates, CustomAccessibilityActionUpdates),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
HandlePlatformMessage,
|
||||
(std::unique_ptr<PlatformMessage>),
|
||||
(override));
|
||||
MOCK_METHOD(FontCollection&, GetFontCollection, (), (override));
|
||||
MOCK_METHOD(std::shared_ptr<AssetManager>, GetAssetManager, (), (override));
|
||||
MOCK_METHOD(void, OnRootIsolateCreated, (), (override));
|
||||
MOCK_METHOD(void,
|
||||
UpdateIsolateDescription,
|
||||
(const std::string, int64_t),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetNeedsReportTimings, (bool), (override));
|
||||
MOCK_METHOD(std::unique_ptr<std::vector<std::string>>,
|
||||
ComputePlatformResolvedLocale,
|
||||
(const std::vector<std::string>&),
|
||||
(override));
|
||||
MOCK_METHOD(void, RequestDartDeferredLibrary, (intptr_t), (override));
|
||||
MOCK_METHOD(std::weak_ptr<PlatformMessageHandler>,
|
||||
GetPlatformMessageHandler,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(void, SendChannelUpdate, (std::string, bool), (override));
|
||||
MOCK_METHOD(double,
|
||||
GetScaledFontSize,
|
||||
(double font_size, int configuration_id),
|
||||
(const, override));
|
||||
};
|
||||
|
||||
class MockPlatformMessageHandler : public PlatformMessageHandler {
|
||||
public:
|
||||
MOCK_METHOD(void,
|
||||
HandlePlatformMessage,
|
||||
(std::unique_ptr<PlatformMessage> message),
|
||||
(override));
|
||||
MOCK_METHOD(bool,
|
||||
DoesHandlePlatformMessageOnPlatformThread,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(void,
|
||||
InvokePlatformMessageResponseCallback,
|
||||
(int response_id, std::unique_ptr<fml::Mapping> mapping),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
InvokePlatformMessageEmptyResponseCallback,
|
||||
(int response_id),
|
||||
(override));
|
||||
};
|
||||
|
||||
// A class that can launch a RuntimeController with the specified
|
||||
// RuntimeDelegate.
|
||||
//
|
||||
// To use this class, contruct this class with Create, call LaunchRootIsolate,
|
||||
// and use the controller with ControllerTaskSync().
|
||||
class RuntimeControllerContext {
|
||||
public:
|
||||
using ControllerCallback = std::function<void(RuntimeController&)>;
|
||||
|
||||
[[nodiscard]] static std::unique_ptr<RuntimeControllerContext> Create(
|
||||
Settings settings, //
|
||||
const TaskRunners& task_runners, //
|
||||
RuntimeDelegate& client) {
|
||||
auto [vm, isolate_snapshot] = Shell::InferVmInitDataFromSettings(settings);
|
||||
FML_CHECK(vm) << "Must be able to initialize the VM.";
|
||||
// Construct the class with `new` because `make_unique` has no access to the
|
||||
// private constructor.
|
||||
RuntimeControllerContext* raw_pointer = new RuntimeControllerContext(
|
||||
settings, task_runners, client, std::move(vm), isolate_snapshot);
|
||||
return std::unique_ptr<RuntimeControllerContext>(raw_pointer);
|
||||
}
|
||||
|
||||
~RuntimeControllerContext() {
|
||||
PostSync(task_runners_.GetUITaskRunner(),
|
||||
[&]() { runtime_controller_.reset(); });
|
||||
}
|
||||
|
||||
// Launch the root isolate. The post_launch callback will be executed in the
|
||||
// same UI task, which can be used to create initial views.
|
||||
void LaunchRootIsolate(RunConfiguration& configuration,
|
||||
ControllerCallback post_launch) {
|
||||
PostSync(task_runners_.GetUITaskRunner(), [&]() {
|
||||
bool launch_success = runtime_controller_->LaunchRootIsolate(
|
||||
settings_, //
|
||||
[]() {}, //
|
||||
configuration.GetEntrypoint(), //
|
||||
configuration.GetEntrypointLibrary(), //
|
||||
configuration.GetEntrypointArgs(), //
|
||||
configuration.TakeIsolateConfiguration()); //
|
||||
ASSERT_TRUE(launch_success);
|
||||
post_launch(*runtime_controller_);
|
||||
});
|
||||
}
|
||||
|
||||
// Run a task that operates the RuntimeController on the UI thread, and wait
|
||||
// for the task to end.
|
||||
void ControllerTaskSync(ControllerCallback task) {
|
||||
ASSERT_TRUE(runtime_controller_);
|
||||
ASSERT_TRUE(task);
|
||||
PostSync(task_runners_.GetUITaskRunner(),
|
||||
[&]() { task(*runtime_controller_); });
|
||||
}
|
||||
|
||||
private:
|
||||
RuntimeControllerContext(const Settings& settings,
|
||||
const TaskRunners& task_runners,
|
||||
RuntimeDelegate& client,
|
||||
DartVMRef vm,
|
||||
fml::RefPtr<const DartSnapshot> isolate_snapshot)
|
||||
: settings_(settings),
|
||||
task_runners_(task_runners),
|
||||
isolate_snapshot_(std::move(isolate_snapshot)),
|
||||
vm_(std::move(vm)),
|
||||
runtime_controller_(std::make_unique<RuntimeController>(
|
||||
client,
|
||||
&vm_,
|
||||
std::move(isolate_snapshot_),
|
||||
settings.idle_notification_callback, // idle notification callback
|
||||
flutter::PlatformData(), // platform data
|
||||
settings.isolate_create_callback, // isolate create callback
|
||||
settings.isolate_shutdown_callback, // isolate shutdown callback
|
||||
settings.persistent_isolate_data, // persistent isolate data
|
||||
UIDartState::Context{task_runners})) {}
|
||||
|
||||
Settings settings_;
|
||||
TaskRunners task_runners_;
|
||||
fml::RefPtr<const DartSnapshot> isolate_snapshot_;
|
||||
DartVMRef vm_;
|
||||
std::unique_ptr<RuntimeController> runtime_controller_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace testing {
|
||||
|
||||
class PlatformConfigurationTest : public ShellTest {};
|
||||
|
||||
@ -341,16 +341,15 @@ void RuntimeController::ScheduleFrame() {
|
||||
}
|
||||
|
||||
// |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);
|
||||
}
|
||||
|
||||
|
||||
@ -658,10 +658,7 @@ class RuntimeController : public PlatformConfigurationClient {
|
||||
void ScheduleFrame() 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;
|
||||
|
||||
@ -25,8 +25,7 @@ class RuntimeDelegate {
|
||||
|
||||
virtual void ScheduleFrame(bool regenerate_layer_trees = true) = 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,
|
||||
|
||||
@ -60,10 +60,6 @@ void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) {
|
||||
|
||||
void Animator::BeginFrame(
|
||||
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
|
||||
// Both frame_timings_recorder_ and layer_trees_tasks_ must be empty if not
|
||||
// between BeginFrame and EndFrame.
|
||||
FML_DCHECK(frame_timings_recorder_ == nullptr);
|
||||
FML_DCHECK(layer_trees_tasks_.empty());
|
||||
TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending",
|
||||
frame_request_number_);
|
||||
frame_request_number_++;
|
||||
@ -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_CHECK(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; // Ensure it's cleared.
|
||||
|
||||
if (!frame_scheduled_ && has_rendered_) {
|
||||
// Wait a tad more than 3 60hz frames before reporting a big idle period.
|
||||
@ -170,25 +139,52 @@ void Animator::EndFrame() {
|
||||
},
|
||||
kNotifyIdleTaskWaitTime);
|
||||
}
|
||||
// Both frame_timings_recorder_ and layer_trees_tasks_ must be empty if not
|
||||
// between BeginFrame and EndFrame.
|
||||
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) {
|
||||
FML_CHECK(frame_timings_recorder_ != nullptr);
|
||||
|
||||
has_rendered_ = true;
|
||||
|
||||
if (!frame_timings_recorder_) {
|
||||
// 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);
|
||||
frame_timings_recorder_->RecordBuildStart(placeholder_time);
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -260,7 +256,6 @@ void Animator::AwaitVSync() {
|
||||
self->DrawLastLayerTrees(std::move(frame_timings_recorder));
|
||||
} else {
|
||||
self->BeginFrame(std::move(frame_timings_recorder));
|
||||
self->EndFrame();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -61,8 +61,7 @@ class Animator final {
|
||||
/// (both private methods). Otherwise, an assertion will be
|
||||
/// triggered.
|
||||
///
|
||||
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;
|
||||
@ -91,13 +90,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();
|
||||
|
||||
@ -114,7 +107,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(); });
|
||||
|
||||
@ -459,8 +459,7 @@ void Engine::ScheduleFrame(bool regenerate_layer_trees) {
|
||||
animator_->RequestFrame(regenerate_layer_trees);
|
||||
}
|
||||
|
||||
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;
|
||||
@ -471,7 +470,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,
|
||||
|
||||
@ -961,8 +961,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|
|
||||
|
||||
@ -7,8 +7,6 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "flutter/runtime/dart_vm_lifecycle.h"
|
||||
#include "flutter/shell/common/shell.h"
|
||||
#include "flutter/shell/common/shell_test.h"
|
||||
#include "flutter/shell/common/thread_host.h"
|
||||
#include "flutter/testing/fixture_test.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
@ -21,19 +19,6 @@ namespace flutter {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::testing::Invoke;
|
||||
using ::testing::ReturnRef;
|
||||
|
||||
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();
|
||||
latch.Signal();
|
||||
});
|
||||
latch.Wait();
|
||||
}
|
||||
|
||||
class MockDelegate : public Engine::Delegate {
|
||||
public:
|
||||
MOCK_METHOD(void,
|
||||
@ -80,7 +65,7 @@ class MockRuntimeDelegate : public RuntimeDelegate {
|
||||
MOCK_METHOD(void, ScheduleFrame, (bool), (override));
|
||||
MOCK_METHOD(void,
|
||||
Render,
|
||||
(int64_t, std::unique_ptr<flutter::LayerTree>, float),
|
||||
(std::unique_ptr<flutter::LayerTree>, float),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
UpdateSemantics,
|
||||
@ -132,51 +117,6 @@ class MockRuntimeController : public RuntimeController {
|
||||
MOCK_METHOD(bool, NotifyIdle, (fml::TimeDelta), (override));
|
||||
};
|
||||
|
||||
class MockAnimatorDelegate : public Animator::Delegate {
|
||||
public:
|
||||
/* Animator::Delegate */
|
||||
MOCK_METHOD(void,
|
||||
OnAnimatorBeginFrame,
|
||||
(fml::TimePoint frame_target_time, uint64_t frame_number),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnAnimatorNotifyIdle,
|
||||
(fml::TimeDelta deadline),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnAnimatorUpdateLatestFrameTargetTime,
|
||||
(fml::TimePoint frame_target_time),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnAnimatorDraw,
|
||||
(std::shared_ptr<FramePipeline> pipeline),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnAnimatorDrawLastLayerTrees,
|
||||
(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder),
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockPlatformMessageHandler : public PlatformMessageHandler {
|
||||
public:
|
||||
MOCK_METHOD(void,
|
||||
HandlePlatformMessage,
|
||||
(std::unique_ptr<PlatformMessage> message),
|
||||
(override));
|
||||
MOCK_METHOD(bool,
|
||||
DoesHandlePlatformMessageOnPlatformThread,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(void,
|
||||
InvokePlatformMessageResponseCallback,
|
||||
(int response_id, std::unique_ptr<fml::Mapping> mapping),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
InvokePlatformMessageEmptyResponseCallback,
|
||||
(int response_id),
|
||||
(override));
|
||||
};
|
||||
|
||||
std::unique_ptr<PlatformMessage> MakePlatformMessage(
|
||||
const std::string& channel,
|
||||
const std::map<std::string, std::string>& values,
|
||||
@ -245,97 +185,6 @@ class EngineTest : public testing::FixtureTest {
|
||||
std::shared_ptr<fml::ConcurrentTaskRunner> image_decoder_task_runner_;
|
||||
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate_;
|
||||
};
|
||||
|
||||
// A class that can launch an Engine with the specified Engine::Delegate.
|
||||
//
|
||||
// To use this class, contruct this class with Create, call Run, and use the
|
||||
// engine with EngineTaskSync().
|
||||
class EngineContext {
|
||||
public:
|
||||
using EngineCallback = std::function<void(Engine&)>;
|
||||
|
||||
[[nodiscard]] static std::unique_ptr<EngineContext> Create(
|
||||
Engine::Delegate& delegate, //
|
||||
Settings settings, //
|
||||
const TaskRunners& task_runners, //
|
||||
std::unique_ptr<Animator> animator) {
|
||||
auto [vm, isolate_snapshot] = Shell::InferVmInitDataFromSettings(settings);
|
||||
FML_CHECK(vm) << "Must be able to initialize the VM.";
|
||||
// Construct the class with `new` because `make_unique` has no access to the
|
||||
// private constructor.
|
||||
EngineContext* raw_pointer =
|
||||
new EngineContext(delegate, settings, task_runners, std::move(animator),
|
||||
std::move(vm), isolate_snapshot);
|
||||
return std::unique_ptr<EngineContext>(raw_pointer);
|
||||
}
|
||||
|
||||
void Run(RunConfiguration configuration) {
|
||||
PostSync(task_runners_.GetUITaskRunner(), [this, &configuration] {
|
||||
Engine::RunStatus run_status = engine_->Run(std::move(configuration));
|
||||
FML_CHECK(run_status == Engine::RunStatus::Success)
|
||||
<< "Engine failed to run.";
|
||||
(void)run_status; // Suppress unused-variable warning
|
||||
});
|
||||
}
|
||||
|
||||
// Run a task that operates the Engine on the UI thread, and wait for the
|
||||
// task to end.
|
||||
//
|
||||
// If called on the UI thread, the task is executed synchronously.
|
||||
void EngineTaskSync(EngineCallback task) {
|
||||
ASSERT_TRUE(engine_);
|
||||
ASSERT_TRUE(task);
|
||||
auto runner = task_runners_.GetUITaskRunner();
|
||||
if (runner->RunsTasksOnCurrentThread()) {
|
||||
task(*engine_);
|
||||
} else {
|
||||
PostSync(task_runners_.GetUITaskRunner(), [&]() { task(*engine_); });
|
||||
}
|
||||
}
|
||||
|
||||
~EngineContext() {
|
||||
PostSync(task_runners_.GetUITaskRunner(), [this] { engine_.reset(); });
|
||||
}
|
||||
|
||||
private:
|
||||
EngineContext(Engine::Delegate& delegate, //
|
||||
Settings settings, //
|
||||
const TaskRunners& task_runners, //
|
||||
std::unique_ptr<Animator> animator, //
|
||||
DartVMRef vm, //
|
||||
fml::RefPtr<const DartSnapshot> isolate_snapshot)
|
||||
: task_runners_(task_runners), vm_(std::move(vm)) {
|
||||
PostSync(task_runners.GetUITaskRunner(), [this, &settings, &animator,
|
||||
&delegate, &isolate_snapshot] {
|
||||
auto dispatcher_maker =
|
||||
[](DefaultPointerDataDispatcher::Delegate& delegate) {
|
||||
return std::make_unique<DefaultPointerDataDispatcher>(delegate);
|
||||
};
|
||||
engine_ = std::make_unique<Engine>(
|
||||
/*delegate=*/delegate,
|
||||
/*dispatcher_maker=*/dispatcher_maker,
|
||||
/*vm=*/*&vm_,
|
||||
/*isolate_snapshot=*/std::move(isolate_snapshot),
|
||||
/*task_runners=*/task_runners_,
|
||||
/*platform_data=*/PlatformData(),
|
||||
/*settings=*/settings,
|
||||
/*animator=*/std::move(animator),
|
||||
/*io_manager=*/io_manager_,
|
||||
/*unref_queue=*/nullptr,
|
||||
/*snapshot_delegate=*/snapshot_delegate_,
|
||||
/*volatile_path_tracker=*/nullptr,
|
||||
/*gpu_disabled_switch=*/std::make_shared<fml::SyncSwitch>());
|
||||
});
|
||||
}
|
||||
|
||||
TaskRunners task_runners_;
|
||||
DartVMRef vm_;
|
||||
std::unique_ptr<Engine> engine_;
|
||||
|
||||
fml::WeakPtr<IOManager> io_manager_;
|
||||
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(EngineTest, Create) {
|
||||
@ -569,68 +418,4 @@ 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);
|
||||
});
|
||||
}));
|
||||
|
||||
static fml::AutoResetWaitableEvent callback_ready_latch;
|
||||
callback_ready_latch.Reset();
|
||||
AddNativeCallback("NotifyNative",
|
||||
[](auto args) { callback_ready_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("onBeginFrameRendersMultipleViews");
|
||||
engine_context->Run(std::move(configuration));
|
||||
|
||||
engine_context->EngineTaskSync([](Engine& engine) {
|
||||
engine.AddView(1, {1, 10, 10, 22, 0});
|
||||
engine.AddView(2, {1, 10, 10, 22, 0});
|
||||
});
|
||||
|
||||
callback_ready_latch.Wait();
|
||||
|
||||
engine_context->EngineTaskSync(
|
||||
[](Engine& engine) { engine.ScheduleFrame(); });
|
||||
draw_latch.Wait();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -524,24 +524,3 @@ void testReportViewWidths() {
|
||||
nativeReportViewWidthsCallback(getCurrentViewWidths());
|
||||
};
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void onBeginFrameRendersMultipleViews() {
|
||||
PlatformDispatcher.instance.onBeginFrame = (Duration beginTime) {
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -252,7 +252,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);
|
||||
|
||||
@ -41,7 +41,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"
|
||||
@ -875,7 +874,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);
|
||||
|
||||
@ -949,7 +948,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());
|
||||
@ -1010,7 +1009,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);
|
||||
@ -1056,12 +1055,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();
|
||||
|
||||
@ -1126,12 +1122,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(
|
||||
@ -1195,7 +1191,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(
|
||||
@ -1206,7 +1202,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:
|
||||
@ -1262,7 +1258,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.
|
||||
@ -1301,7 +1297,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(
|
||||
@ -1367,7 +1363,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());
|
||||
@ -1413,7 +1409,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);
|
||||
|
||||
@ -1563,11 +1559,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));
|
||||
}
|
||||
@ -2083,7 +2078,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;
|
||||
@ -2093,18 +2087,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);
|
||||
@ -2117,16 +2105,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);
|
||||
@ -2171,7 +2155,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;
|
||||
@ -2556,13 +2540,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;
|
||||
@ -2674,7 +2652,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;
|
||||
@ -2738,16 +2716,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());
|
||||
@ -2818,9 +2794,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());
|
||||
@ -2841,9 +2816,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