Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion shell/common/rasterizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe(
frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now());

std::unique_ptr<FrameDamage> damage;
// when leaf layer tracing is enabled we wish to repaing the whole frame for
// when leaf layer tracing is enabled we wish to repaint the whole frame for
// accurate performance metrics.
if (frame->framebuffer_info().supports_partial_repaint &&
!layer_tree.is_leaf_layer_tracing_enabled()) {
Expand Down
31 changes: 31 additions & 0 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,36 @@ FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine)
"Could not schedule frame.");
}

FlutterEngineResult FlutterEngineSetNextFrameCallback(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
VoidCallback callback,
void* user_data) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}

if (callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Next frame callback was null.");
}

flutter::EmbedderEngine* embedder_engine =
reinterpret_cast<flutter::EmbedderEngine*>(engine);

fml::WeakPtr<flutter::PlatformView> weak_platform_view =
embedder_engine->GetShell().GetPlatformView();

if (!weak_platform_view) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Platform view unavailable.");
}

weak_platform_view->SetNextFrameCallback(
[callback, user_data]() { callback(user_data); });

return kSuccess;
}

FlutterEngineResult FlutterEngineGetProcAddresses(
FlutterEngineProcTable* table) {
if (!table) {
Expand Down Expand Up @@ -2734,6 +2764,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses(
FlutterEnginePostCallbackOnAllNativeThreads);
SET_PROC(NotifyDisplayUpdate, FlutterEngineNotifyDisplayUpdate);
SET_PROC(ScheduleFrame, FlutterEngineScheduleFrame);
SET_PROC(SetNextFrameCallback, FlutterEngineSetNextFrameCallback);
#undef SET_PROC

return kSuccess;
Expand Down
25 changes: 25 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2510,6 +2510,26 @@ FLUTTER_EXPORT
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine)
engine);

//------------------------------------------------------------------------------
/// @brief Schedule a callback to be called after the next frame is drawn.
/// This must be called from the platform thread. The callback is
/// executed only once from the raster thread; embedders must
/// re-thread if necessary. Performing blocking calls
/// in this callback may introduce application jank.
///
/// @param[in] engine A running engine instance.
/// @param[in] callback The callback to execute.
/// @param[in] user_data A baton passed by the engine to the callback. This
/// baton is not interpreted by the engine in any way.
///
/// @return The result of the call.
///
FLUTTER_EXPORT
FlutterEngineResult FlutterEngineSetNextFrameCallback(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
VoidCallback callback,
void* user_data);

#endif // !FLUTTER_ENGINE_NO_PROTOTYPES

// Typedefs for the function pointers in FlutterEngineProcTable.
Expand Down Expand Up @@ -2628,6 +2648,10 @@ typedef FlutterEngineResult (*FlutterEngineNotifyDisplayUpdateFnPtr)(
size_t display_count);
typedef FlutterEngineResult (*FlutterEngineScheduleFrameFnPtr)(
FLUTTER_API_SYMBOL(FlutterEngine) engine);
typedef FlutterEngineResult (*FlutterEngineSetNextFrameCallbackFnPtr)(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
VoidCallback callback,
void* user_data);

/// Function-pointer-based versions of the APIs above.
typedef struct {
Expand Down Expand Up @@ -2673,6 +2697,7 @@ typedef struct {
PostCallbackOnAllNativeThreads;
FlutterEngineNotifyDisplayUpdateFnPtr NotifyDisplayUpdate;
FlutterEngineScheduleFrameFnPtr ScheduleFrame;
FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback;
} FlutterEngineProcTable;

//------------------------------------------------------------------------------
Expand Down
38 changes: 38 additions & 0 deletions shell/platform/embedder/tests/embedder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2126,6 +2126,44 @@ TEST_F(EmbedderTest, CanScheduleFrame) {
check_latch.Wait();
}

TEST_F(EmbedderTest, CanSetNextFrameCallback) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("draw_solid_red");

auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());

// Register the callback that is executed once the next frame is drawn.
fml::AutoResetWaitableEvent callback_latch;
VoidCallback callback = [](void* user_data) {
fml::AutoResetWaitableEvent* callback_latch =
static_cast<fml::AutoResetWaitableEvent*>(user_data);

callback_latch->Signal();
};

auto result = FlutterEngineSetNextFrameCallback(engine.get(), callback,
&callback_latch);
ASSERT_EQ(result, kSuccess);

// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = 800;
event.height = 600;
event.pixel_ratio = 1.0;
event.physical_view_inset_top = 0.0;
event.physical_view_inset_right = 0.0;
event.physical_view_inset_bottom = 0.0;
event.physical_view_inset_left = 0.0;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);

callback_latch.Wait();
}

#if defined(FML_OS_MACOSX)

static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ namespace flutter {
//
// In the future, this will be the API surface used for all interactions with
// the engine, rather than having them duplicated on FlutterViewController.
// For now it is only used in the rare where you need a headless Flutter engine.
// For now it is only used in the rare case where you need a headless Flutter
// engine.
class FlutterEngine : public PluginRegistry {
public:
// Creates a new engine for running the given project.
Expand Down