Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Linux platform support for fetching refresh rate on startup. #52934

Merged
merged 7 commits into from
May 28, 2024
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
20 changes: 20 additions & 0 deletions shell/platform/linux/fl_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 573,26 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
g_warning("Failed to enable accessibility features on Flutter engine");
}

gdouble refresh_rate = fl_renderer_get_refresh_rate(self->renderer);
// FlutterEngineDisplay::refresh_rate expects 0 if the refresh rate is
// unknown.
if (refresh_rate <= 0.0) {
refresh_rate = 0.0;
}
FlutterEngineDisplay display = {};
display.struct_size = sizeof(FlutterEngineDisplay);
display.display_id = 0;
display.single_display = true;
display.refresh_rate = refresh_rate;

std::vector displays = {display};
result = self->embedder_api.NotifyDisplayUpdate(
self->engine, kFlutterEngineDisplaysUpdateTypeStartup, displays.data(),
displays.size());
if (result != kSuccess) {
g_warning("Failed to notify display update to Flutter engine: %d", result);
}

return TRUE;
}

Expand Down
5 changes: 5 additions & 0 deletions shell/platform/linux/fl_renderer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 153,11 @@ void fl_renderer_clear_current(FlRenderer* self) {
FL_RENDERER_GET_CLASS(self)->clear_current(self);
}

gdouble fl_renderer_get_refresh_rate(FlRenderer* self) {
g_return_val_if_fail(FL_IS_RENDERER(self), -1.0);
return FL_RENDERER_GET_CLASS(self)->get_refresh_rate(self);
}

guint32 fl_renderer_get_fbo(FlRenderer* self) {
g_return_val_if_fail(FL_IS_RENDERER(self), 0);

Expand Down
19 changes: 19 additions & 0 deletions shell/platform/linux/fl_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 80,16 @@ struct _FlRendererClass {
*/
gboolean (*collect_backing_store)(FlRenderer* renderer,
const FlutterBackingStore* backing_store);

/**
* Virtual method called when Flutter wants to get the refresh rate of the
* renderer.
* @renderer: an #FlRenderer.
*
* Returns: The refresh rate of the display in Hz. If the refresh rate is
* not available, returns -1.0.
*/
gdouble (*get_refresh_rate)(FlRenderer* renderer);
};

/**
Expand Down Expand Up @@ -223,6 233,15 @@ void fl_renderer_render(FlRenderer* renderer, int width, int height);
*/
void fl_renderer_cleanup(FlRenderer* renderer);

/**
* fl_renderer_get_refresh_rate:
* @renderer: an #FlRenderer.
*
* Returns: The refresh rate of the display in Hz. If the refresh rate is
* not available, returns -1.0.
*/
gdouble fl_renderer_get_refresh_rate(FlRenderer* renderer);

G_END_DECLS

#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_
18 changes: 18 additions & 0 deletions shell/platform/linux/fl_renderer_gdk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 36,23 @@ static void fl_renderer_gdk_clear_current(FlRenderer* renderer) {
gdk_gl_context_clear_current();
}

static gdouble fl_renderer_gdk_get_refresh_rate(FlRenderer* renderer) {
FlRendererGdk* self = FL_RENDERER_GDK(renderer);
GdkDisplay* display = gdk_window_get_display(self->window);
GdkMonitor* monitor =
gdk_display_get_monitor_at_window(display, self->window);
if (monitor == nullptr) {
return -1.0;
}

int refresh_rate = gdk_monitor_get_refresh_rate(monitor);
if (refresh_rate <= 0) {
return -1.0;
}
// the return value is in milli-hertz, convert to hertz
return static_cast<gdouble>(refresh_rate) / 1000.0;
}

static void fl_renderer_gdk_dispose(GObject* object) {
FlRendererGdk* self = FL_RENDERER_GDK(object);

Expand All @@ -52,6 69,7 @@ static void fl_renderer_gdk_class_init(FlRendererGdkClass* klass) {
FL_RENDERER_CLASS(klass)->make_resource_current =
fl_renderer_gdk_make_resource_current;
FL_RENDERER_CLASS(klass)->clear_current = fl_renderer_gdk_clear_current;
FL_RENDERER_CLASS(klass)->get_refresh_rate = fl_renderer_gdk_get_refresh_rate;
}

static void fl_renderer_gdk_init(FlRendererGdk* self) {}
Expand Down
7 changes: 7 additions & 0 deletions shell/platform/linux/fl_renderer_headless.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 19,18 @@ static void fl_renderer_headless_make_resource_current(FlRenderer* renderer) {}
// Implements FlRenderer::clear_current.
static void fl_renderer_headless_clear_current(FlRenderer* renderer) {}

// Implements FlRenderer::get_refresh_rate.
static gdouble fl_renderer_headless_get_refresh_rate(FlRenderer* renderer) {
return -1.0;
}

static void fl_renderer_headless_class_init(FlRendererHeadlessClass* klass) {
FL_RENDERER_CLASS(klass)->make_current = fl_renderer_headless_make_current;
FL_RENDERER_CLASS(klass)->make_resource_current =
fl_renderer_headless_make_resource_current;
FL_RENDERER_CLASS(klass)->clear_current = fl_renderer_headless_clear_current;
FL_RENDERER_CLASS(klass)->get_refresh_rate =
fl_renderer_headless_get_refresh_rate;
}

static void fl_renderer_headless_init(FlRendererHeadless* self) {}
Expand Down
16 changes: 16 additions & 0 deletions shell/platform/linux/fl_renderer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 51,19 @@ TEST(FlRendererTest, RestoresGLState) {

g_object_ref_sink(view);
}

static constexpr double kExpectedRefreshRate = 120.0;
static gdouble renderer_get_refresh_rate(FlRenderer* renderer) {
return kExpectedRefreshRate;
}

TEST(FlRendererTest, RefreshRate) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
g_autoptr(FlMockRenderer) renderer =
fl_mock_renderer_new(&renderer_get_refresh_rate);

gdouble result_refresh_rate =
fl_renderer_get_refresh_rate(FL_RENDERER(renderer));
EXPECT_DOUBLE_EQ(result_refresh_rate, kExpectedRefreshRate);
}
9 changes: 9 additions & 0 deletions shell/platform/linux/testing/mock_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 513,14 @@ FlutterEngineResult FlutterEngineUnregisterExternalTexture(
return kSuccess;
}

FlutterEngineResult FlutterEngineNotifyDisplayUpdate(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterEngineDisplaysUpdateType update_type,
const FlutterEngineDisplay* displays,
size_t display_count) {
return kSuccess;
}

} // namespace

FlutterEngineResult FlutterEngineGetProcAddresses(
Expand Down Expand Up @@ -552,5 560,6 @@ FlutterEngineResult FlutterEngineGetProcAddresses(
table->UnregisterExternalTexture = &FlutterEngineUnregisterExternalTexture;
table->UpdateAccessibilityFeatures =
&FlutterEngineUpdateAccessibilityFeatures;
table->NotifyDisplayUpdate = &FlutterEngineNotifyDisplayUpdate;
return kSuccess;
}
20 changes: 18 additions & 2 deletions shell/platform/linux/testing/mock_renderer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 6,7 @@

struct _FlMockRenderer {
FlRenderer parent_instance;
FlMockRendererGetRefreshRate get_refresh_rate;
};

G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type())
Expand All @@ -19,16 20,31 @@ static void fl_mock_renderer_make_resource_current(FlRenderer* renderer) {}
// Implements FlRenderer::clear_current.
static void fl_mock_renderer_clear_current(FlRenderer* renderer) {}

// Implements FlRenderer::get_refresh_rate.
static gdouble fl_mock_renderer_default_get_refresh_rate(FlRenderer* renderer) {
FlMockRenderer* self = FL_MOCK_RENDERER(renderer);
if (self->get_refresh_rate != nullptr) {
return self->get_refresh_rate(renderer);
}
return -1.0;
}

static void fl_mock_renderer_class_init(FlMockRendererClass* klass) {
FL_RENDERER_CLASS(klass)->make_current = fl_mock_renderer_make_current;
FL_RENDERER_CLASS(klass)->make_resource_current =
fl_mock_renderer_make_resource_current;
FL_RENDERER_CLASS(klass)->clear_current = fl_mock_renderer_clear_current;
FL_RENDERER_CLASS(klass)->get_refresh_rate =
fl_mock_renderer_default_get_refresh_rate;
}

static void fl_mock_renderer_init(FlMockRenderer* self) {}

// Creates a stub renderer
FlMockRenderer* fl_mock_renderer_new() {
return FL_MOCK_RENDERER(g_object_new(fl_mock_renderer_get_type(), nullptr));
FlMockRenderer* fl_mock_renderer_new(
FlMockRendererGetRefreshRate get_refresh_rate) {
FlMockRenderer* fl_mock_renderer = FL_MOCK_RENDERER(
g_object_new_valist(fl_mock_renderer_get_type(), nullptr, nullptr));
fl_mock_renderer->get_refresh_rate = get_refresh_rate;
return fl_mock_renderer;
}
5 changes: 4 additions & 1 deletion shell/platform/linux/testing/mock_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 15,10 @@ G_DECLARE_FINAL_TYPE(FlMockRenderer,
MOCK_RENDERER,
FlRenderer)

FlMockRenderer* fl_mock_renderer_new();
typedef gdouble (*FlMockRendererGetRefreshRate)(FlRenderer* renderer);

FlMockRenderer* fl_mock_renderer_new(
FlMockRendererGetRefreshRate get_refresh_rate = nullptr);

G_END_DECLS

Expand Down