Skip to content

Commit

Permalink
[Embedder API] Add next frame callback (flutter#35244)
Browse files Browse the repository at this point in the history
  • Loading branch information
loic-sharma authored Aug 8, 2022
1 parent 79f726c commit 55413b2
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 2 deletions.
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

0 comments on commit 55413b2

Please sign in to comment.