Skip to content

Commit

Permalink
Reland 2: Multiview Pipeline (flutter#47239)
Browse files Browse the repository at this point in the history
The last attempt flutter#47234 was reverted because there was another test merged in the meantime that violates the rule added in this PR.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
dkwingsmt authored Oct 23, 2023
1 parent dd6a653 commit 0ef5caa
Show file tree
Hide file tree
Showing 28 changed files with 743 additions and 194 deletions.
24 changes: 23 additions & 1 deletion flow/frame_timings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,30 @@ 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);
FML_DCHECK(state_ == state) << "Expected state " << StateToString(state)
<< ", actual state " << StateToString(state_);
}

} // namespace flutter
3 changes: 3 additions & 0 deletions flow/frame_timings.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ 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,
Expand Down Expand Up @@ -121,6 +122,8 @@ 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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ namespace impeller {
DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() =
default;

DirectionalGaussianBlurFilterContents::
~DirectionalGaussianBlurFilterContents() = default;
DirectionalGaussianBlurFilterContents::~
DirectionalGaussianBlurFilterContents() = default;

void DirectionalGaussianBlurFilterContents::SetSigma(Sigma sigma) {
blur_sigma_ = sigma;
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/dart_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ typedef CanvasPath Path;
V(NativeStringAttribute::initSpellOutStringAttribute, 3) \
V(PlatformConfigurationNativeApi::DefaultRouteName, 0) \
V(PlatformConfigurationNativeApi::ScheduleFrame, 0) \
V(PlatformConfigurationNativeApi::Render, 1) \
V(PlatformConfigurationNativeApi::Render, 2) \
V(PlatformConfigurationNativeApi::UpdateSemantics, 1) \
V(PlatformConfigurationNativeApi::SetNeedsReportTimings, 1) \
V(PlatformConfigurationNativeApi::SetIsolateDebugName, 1) \
Expand Down
10 changes: 9 additions & 1 deletion lib/ui/painting/image_dispose_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#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"
Expand Down Expand Up @@ -57,6 +58,10 @@ 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
Expand All @@ -83,12 +88,15 @@ 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();
Expand Down
8 changes: 5 additions & 3 deletions lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -356,18 +356,20 @@ class FlutterView {
void render(Scene scene) {
// Duplicated calls or calls outside of onBeginFrame/onDrawFrame (indicated
// by _debugRenderedViews being null) are ignored. See _debugRenderedViews.
// TODO(dkwingsmt): We should change this skip into an assertion.
// https://github.com/flutter/flutter/issues/137073
bool validRender = true;
assert(() {
validRender = platformDispatcher._debugRenderedViews?.add(this) ?? false;
return true;
}());
if (validRender) {
_render(scene as _NativeScene);
_render(viewId, scene as _NativeScene);
}
}

@Native<Void Function(Pointer<Void>)>(symbol: 'PlatformConfigurationNativeApi::Render')
external static void _render(_NativeScene scene);
@Native<Void Function(Int64, Pointer<Void>)>(symbol: 'PlatformConfigurationNativeApi::Render')
external static void _render(int viewId, _NativeScene scene);

/// Change the retained semantics data about this [FlutterView].
///
Expand Down
5 changes: 3 additions & 2 deletions lib/ui/window/platform_configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,10 @@ void PlatformConfiguration::CompletePlatformMessageResponse(
response->Complete(std::make_unique<fml::DataMapping>(std::move(data)));
}

void PlatformConfigurationNativeApi::Render(Scene* scene) {
void PlatformConfigurationNativeApi::Render(int64_t view_id, Scene* scene) {
UIDartState::ThrowIfUIOperationsProhibited();
UIDartState::Current()->platform_configuration()->client()->Render(scene);
UIDartState::Current()->platform_configuration()->client()->Render(view_id,
scene);
}

void PlatformConfigurationNativeApi::SetNeedsReportTimings(bool value) {
Expand Down
4 changes: 2 additions & 2 deletions lib/ui/window/platform_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class PlatformConfigurationClient {
/// @brief Updates the client's rendering on the GPU with the newly
/// provided Scene.
///
virtual void Render(Scene* scene) = 0;
virtual void Render(int64_t view_id, Scene* scene) = 0;

//--------------------------------------------------------------------------
/// @brief Receives an updated semantics tree from the Framework.
Expand Down Expand Up @@ -557,7 +557,7 @@ class PlatformConfigurationNativeApi {

static void ScheduleFrame();

static void Render(Scene* scene);
static void Render(int64_t view_id, Scene* scene);

static void UpdateSemantics(SemanticsUpdate* update);

Expand Down
158 changes: 158 additions & 0 deletions lib/ui/window/platform_configuration_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,166 @@
#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
28 changes: 14 additions & 14 deletions runtime/dart_vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -219,22 +219,22 @@ static std::vector<const char*> ProfilingFlags(bool enable_profiling) {
// flags.
if (enable_profiling) {
return {
// This is the default. But just be explicit.
"--profiler",
// This instructs the profiler to walk C++ frames, and to include
// them in the profile.
"--profile-vm",
// This is the default. But just be explicit.
"--profiler",
// This instructs the profiler to walk C++ frames, and to include
// them in the profile.
"--profile-vm",
#if FML_OS_IOS && FML_ARCH_CPU_ARM_FAMILY && FML_ARCH_CPU_ARMEL
// Set the profiler interrupt period to 500Hz instead of the
// default 1000Hz on 32-bit iOS devices to reduce average and worst
// case frame build times.
//
// Note: profile_period is time in microseconds between sampling
// events, not frequency. Frequency is calculated 1/period (or
// 1,000,000 / 2,000 -> 500Hz in this case).
"--profile_period=2000",
// Set the profiler interrupt period to 500Hz instead of the
// default 1000Hz on 32-bit iOS devices to reduce average and worst
// case frame build times.
//
// Note: profile_period is time in microseconds between sampling
// events, not frequency. Frequency is calculated 1/period (or
// 1,000,000 / 2,000 -> 500Hz in this case).
"--profile_period=2000",
#else
"--profile_period=1000",
"--profile_period=1000",
#endif // FML_OS_IOS && FML_ARCH_CPU_ARM_FAMILY && FML_ARCH_CPU_ARMEL
};
} else {
Expand Down
7 changes: 3 additions & 4 deletions runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -341,15 +341,14 @@ void RuntimeController::ScheduleFrame() {
}

// |PlatformConfigurationClient|
void RuntimeController::Render(Scene* scene) {
// TODO(dkwingsmt): Currently only supports a single window.
int64_t view_id = kFlutterImplicitViewId;
void RuntimeController::Render(int64_t view_id, Scene* scene) {
const ViewportMetrics* view_metrics =
UIDartState::Current()->platform_configuration()->GetMetrics(view_id);
if (view_metrics == nullptr) {
return;
}
client_.Render(scene->takeLayerTree(view_metrics->physical_width,
client_.Render(view_id,
scene->takeLayerTree(view_metrics->physical_width,
view_metrics->physical_height),
view_metrics->device_pixel_ratio);
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/runtime_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ class RuntimeController : public PlatformConfigurationClient {
void ScheduleFrame() override;

// |PlatformConfigurationClient|
void Render(Scene* scene) override;
void Render(int64_t view_id, Scene* scene) override;

// |PlatformConfigurationClient|
void UpdateSemantics(SemanticsUpdate* update) override;
Expand Down
Loading

0 comments on commit 0ef5caa

Please sign in to comment.