Skip to content

Commit

Permalink
Additional changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dkwingsmt committed Feb 12, 2024
1 parent aca468a commit bc69ddc
Show file tree
Hide file tree
Showing 16 changed files with 245 additions and 168 deletions.
1 change: 1 addition & 0 deletions lib/ui/dart_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ typedef CanvasPath Path;
V(NativeStringAttribute::initSpellOutStringAttribute) \
V(PlatformConfigurationNativeApi::DefaultRouteName) \
V(PlatformConfigurationNativeApi::ScheduleFrame) \
V(PlatformConfigurationNativeApi::ForceSyncFrame) \
V(PlatformConfigurationNativeApi::Render) \
V(PlatformConfigurationNativeApi::UpdateSemantics) \
V(PlatformConfigurationNativeApi::SetNeedsReportTimings) \
Expand Down
25 changes: 25 additions & 0 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -801,11 +801,36 @@ class PlatformDispatcher {
///
/// * [SchedulerBinding], the Flutter framework class which manages the
/// scheduling of frames.
/// * [forceSyncFrame], a similar method that is used in rare cases that
/// a frame must be rendered immediately.
void scheduleFrame() => _scheduleFrame();

@Native<Void Function()>(symbol: 'PlatformConfigurationNativeApi::ScheduleFrame')
external static void _scheduleFrame();

/// Immediately render a frame by invoking the [onBeginFrame] and
/// [onDrawFrame] callbacks synchronously.
///
/// This method performs the same computation for a frame as [scheduleFrame]
/// does, but instead of doing so at an appropriate opportunity, the render is
/// completed synchronously within this call.
///
/// Prefer [scheduleFrame] to update the display in normal operation. The
/// [forceSyncFrame] method is designed for situations that require a frame is
/// rendered as soon as possible, even at the cost of rendering quality. An
/// example of using this method is [SchedulerBinding.scheduleWarmUpFrame],
/// which is called during application startup so that the first frame can be
/// presented to the screen a few extra milliseconds earlier.
///
/// See also:
///
/// * [SchedulerBinding.scheduleWarmUpFrame], which uses this method.
/// * [scheduleFrame].
void forceSyncFrame() => _forceSyncFrame();

@Native<Void Function()>(symbol: 'PlatformConfigurationNativeApi::ForceSyncFrame')
external static void _forceSyncFrame();

/// Additional accessibility features that may be enabled by the platform.
AccessibilityFeatures get accessibilityFeatures => _configuration.accessibilityFeatures;

Expand Down
8 changes: 5 additions & 3 deletions lib/ui/window/platform_configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -453,9 +453,6 @@ 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);
Expand Down Expand Up @@ -589,6 +586,11 @@ void PlatformConfigurationNativeApi::ScheduleFrame() {
UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
}

void PlatformConfigurationNativeApi::ForceSyncFrame() {
UIDartState::ThrowIfUIOperationsProhibited();
UIDartState::Current()->platform_configuration()->client()->ForceSyncFrame();
}

void PlatformConfigurationNativeApi::UpdateSemantics(SemanticsUpdate* update) {
UIDartState::ThrowIfUIOperationsProhibited();
UIDartState::Current()->platform_configuration()->client()->UpdateSemantics(
Expand Down
7 changes: 7 additions & 0 deletions lib/ui/window/platform_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ class PlatformConfigurationClient {
///
virtual void ScheduleFrame() = 0;

//--------------------------------------------------------------------------
/// @brief
///
virtual void ForceSyncFrame() = 0;

//--------------------------------------------------------------------------
/// @brief Updates the client's rendering on the GPU with the newly
/// provided Scene.
Expand Down Expand Up @@ -560,6 +565,8 @@ class PlatformConfigurationNativeApi {

static void ScheduleFrame();

static void ForceSyncFrame();

static void Render(int64_t view_id,
Scene* scene,
double width,
Expand Down
158 changes: 0 additions & 158 deletions lib/ui/window/platform_configuration_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
Expand Down
2 changes: 2 additions & 0 deletions lib/web_ui/lib/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ abstract class PlatformDispatcher {

void scheduleFrame();

void forceSyncFrame();

Future<void> render(Scene scene, [FlutterView view]);

AccessibilityFeatures get accessibilityFeatures;
Expand Down
6 changes: 6 additions & 0 deletions lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,12 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
scheduleFrameCallback!();
}

@override
void forceSyncFrame() {
// TODO(dkwingsmt): Call beginFrame and drawFrame, since the framework
// will no longer call them once it switches to forceSyncFrame.
}

/// Updates the application's rendering on the GPU with the newly provided
/// [Scene]. This function must be called within the scope of the
/// [onBeginFrame] or [onDrawFrame] callbacks being invoked. If this function
Expand Down
20 changes: 20 additions & 0 deletions runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ bool RuntimeController::SetAccessibilityFeatures(int32_t flags) {

bool RuntimeController::BeginFrame(fml::TimePoint frame_time,
uint64_t frame_number) {
MarkAsFrameBorder();
if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) {
platform_configuration->BeginFrame(frame_time, frame_number);
return true;
Expand Down Expand Up @@ -340,6 +341,10 @@ void RuntimeController::ScheduleFrame() {
client_.ScheduleFrame();
}

void RuntimeController::ForceSyncFrame() {
client_.ForceSyncFrame();
}

// |PlatformConfigurationClient|
void RuntimeController::Render(int64_t view_id,
Scene* scene,
Expand All @@ -352,6 +357,21 @@ void RuntimeController::Render(int64_t view_id,
}
client_.Render(view_id, scene->takeLayerTree(width, height),
view_metrics->device_pixel_ratio);
rendered_views_during_frame_.insert(view_id);
CheckIfAllViewsRendered();
}

void RuntimeController::MarkAsFrameBorder() {
rendered_views_during_frame_.clear();
}

void RuntimeController::CheckIfAllViewsRendered() {
if (rendered_views_during_frame_.size() != 0 &&
rendered_views_during_frame_.size() ==
platform_data_.viewport_metrics_for_views.size()) {
client_.OnAllViewsRendered();
MarkAsFrameBorder();
}
}

// |PlatformConfigurationClient|
Expand Down
18 changes: 18 additions & 0 deletions runtime/runtime_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,10 @@ class RuntimeController : public PlatformConfigurationClient {
std::string variant_code;
};

void MarkAsFrameBorder();

void CheckIfAllViewsRendered();

RuntimeDelegate& client_;
DartVM* const vm_;
fml::RefPtr<const DartSnapshot> isolate_snapshot_;
Expand All @@ -647,6 +651,17 @@ class RuntimeController : public PlatformConfigurationClient {
UIDartState::Context context_;
bool has_flushed_runtime_state_ = false;

// Tracks the views that have been called `Render` during a frame.
//
// If all registered views (see `AddView`) have been rendered, then the end of
// frame will be called immediately, submitting the views to the pipeline.
//
// This is a transitional solution to keep the warm up frame rendered in a
// multiview setup, before the Framework uses forceSyncFrame.
// TODO(dkwingsmt): Use forceSyncFrame to render the warm up frame.
// https://github.com/flutter/flutter/issues/142851.
std::unordered_set<uint64_t> rendered_views_during_frame_;

PlatformConfiguration* GetPlatformConfigurationIfAvailable();

bool FlushRuntimeStateToIsolate();
Expand All @@ -657,6 +672,9 @@ class RuntimeController : public PlatformConfigurationClient {
// |PlatformConfigurationClient|
void ScheduleFrame() override;

// |PlatformConfigurationClient|
void ForceSyncFrame() override;

// |PlatformConfigurationClient|
void Render(int64_t view_id,
Scene* scene,
Expand Down
4 changes: 4 additions & 0 deletions runtime/runtime_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ class RuntimeDelegate {

virtual void ScheduleFrame(bool regenerate_layer_trees = true) = 0;

virtual void ForceSyncFrame() = 0;

virtual void Render(int64_t view_id,
std::unique_ptr<flutter::LayerTree> layer_tree,
float device_pixel_ratio) = 0;

virtual void OnAllViewsRendered() = 0;

virtual void UpdateSemantics(SemanticsNodeUpdates update,
CustomAccessibilityActionUpdates actions) = 0;

Expand Down
Loading

0 comments on commit bc69ddc

Please sign in to comment.