Bug 1511011 - Clean up wayland subsurface creation/management, r=jhorak
authorMartin Stransky <stransky@redhat.com>
Mon, 03 Dec 2018 13:06:21 +0000
changeset 508453 f349e5a7d515306dcfa5d0b9eb62f676873e8e78
parent 508452 819a462d5dd04f30bc50b47d9e10930ae7b1c627
child 508454 49428012434e379316f2d831a088a27cf1b429ee
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1511011
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1511011 - Clean up wayland subsurface creation/management, r=jhorak - Don't create wl_subsurface in map event but rather when it's requested by compositor. - Don't create wl_surface at nsWindow::OnExposeEvent but check ready_to_draw instead. - Rename parent_surface_committed_handler to frame_clock_after_paint_handler. - Rename parent_surface_committed to ready_to_draw. - Rename needs_clear to surface_needs_clear. Depends on D13404 Differential Revision: https://phabricator.services.mozilla.com/D13405
widget/gtk/mozcontainer.cpp
widget/gtk/mozcontainer.h
widget/gtk/nsWindow.cpp
--- a/widget/gtk/mozcontainer.cpp
+++ b/widget/gtk/mozcontainer.cpp
@@ -142,121 +142,66 @@ void moz_container_init(MozContainer *co
   gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
   gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE);
   gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
 
 #if defined(MOZ_WAYLAND)
   container->surface = nullptr;
   container->subsurface = nullptr;
   container->eglwindow = nullptr;
-  container->parent_surface_committed = false;
-  container->needs_clear = true;
+  container->ready_to_draw = false;
+  container->surface_needs_clear = true;
 #endif
 }
 
 #if defined(MOZ_WAYLAND)
-static void moz_container_commited_handler(GdkFrameClock *clock,
+static void moz_on_frame_clock_after_paint(GdkFrameClock *clock,
                                            MozContainer *container) {
-  container->parent_surface_committed = true;
+  container->ready_to_draw = true;
   g_signal_handler_disconnect(clock,
-                              container->parent_surface_committed_handler);
-  container->parent_surface_committed_handler = 0;
+                              container->frame_clock_after_paint_handler);
+  container->frame_clock_after_paint_handler = 0;
 }
 
-/* We want to draw to GdkWindow owned by mContainer from Compositor thread but
- * Gtk+ can be used in main thread only. So we create wayland wl_surface
- * and attach it as an overlay to GdkWindow.
- *
- * see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c
- * for reference.
- */
-static gboolean moz_container_map_surface(MozContainer *container) {
-  // Available as of GTK 3.8+
-  static auto sGdkWaylandDisplayGetWlCompositor =
-      (wl_compositor * (*)(GdkDisplay *))
-          dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
-  static auto sGdkWaylandWindowGetWlSurface = (wl_surface * (*)(GdkWindow *))
-      dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
+static void moz_container_map_wayland(MozContainer *container) {
+  MOZ_ASSERT(!container->ready_to_draw, "MozContainer is already mapped.");
+  MOZ_ASSERT(!container->frame_clock_after_paint_handler,
+             "Repeated gdk_window_get_frame_clock() request?");
+
+  GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
+  if (GDK_IS_X11_DISPLAY(display)) return;
+
+  container->surface_needs_clear = true;
+
   static auto sGdkWindowGetFrameClock = (GdkFrameClock * (*)(GdkWindow *))
       dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
 
-  GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
-  if (GDK_IS_X11_DISPLAY(display)) return false;
-
-  if (container->subsurface && container->surface) {
-    return true;
-  }
-
-  if (!container->parent_surface_committed) {
-    if (!container->parent_surface_committed_handler) {
-      GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
-      GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
-      container->parent_surface_committed_handler = g_signal_connect_after(
-          clock, "after-paint", G_CALLBACK(moz_container_commited_handler),
-          container);
-    }
-    return false;
-  }
-
-  if (!container->surface) {
-    // TODO - use nsWaylandDisplay::compositor for compositor thread.
-    struct wl_compositor *compositor;
-    compositor = sGdkWaylandDisplayGetWlCompositor(display);
-    container->surface = wl_compositor_create_surface(compositor);
-  }
-
-  if (!container->subsurface) {
-    GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
-    wl_surface *gtk_surface = sGdkWaylandWindowGetWlSurface(window);
-    if (!gtk_surface) {
-      // We requested the underlying wl_surface too early when container
-      // is not realized yet. We'll try again before first rendering
-      // to mContainer.
-      return false;
-    }
-    GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
-    nsWaylandDisplay *waylandDisplay = WaylandDisplayGet(display);
-    wl_subcompositor *subcompositor = waylandDisplay->GetSubcompositor();
-    container->subsurface = wl_subcompositor_get_subsurface(
-        subcompositor, container->surface, gtk_surface);
-    WaylandDisplayRelease(waylandDisplay);
-
-    gint x, y;
-    gdk_window_get_position(window, &x, &y);
-    wl_subsurface_set_position(container->subsurface, x, y);
-    wl_subsurface_set_desync(container->subsurface);
-
-    // Route input to parent wl_surface owned by Gtk+ so we get input
-    // events from Gtk+.
-    wl_compositor *compositor = sGdkWaylandDisplayGetWlCompositor(display);
-    wl_region *region = wl_compositor_create_region(compositor);
-    wl_surface_set_input_region(container->surface, region);
-    wl_region_destroy(region);
-
-    container->needs_clear = true;
-  }
-  return true;
+  GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
+  GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
+  container->frame_clock_after_paint_handler = g_signal_connect_after(
+      clock, "after-paint", G_CALLBACK(moz_on_frame_clock_after_paint),
+      container);
 }
 
-static void moz_container_unmap_surface(MozContainer *container) {
+static void moz_container_unmap_wayland(MozContainer *container) {
   g_clear_pointer(&container->eglwindow, wl_egl_window_destroy);
   g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
   g_clear_pointer(&container->surface, wl_surface_destroy);
 
-  if (container->parent_surface_committed_handler) {
+  if (container->frame_clock_after_paint_handler) {
     static auto sGdkWindowGetFrameClock = (GdkFrameClock * (*)(GdkWindow *))
         dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
     GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
     GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
 
     g_signal_handler_disconnect(clock,
-                                container->parent_surface_committed_handler);
-    container->parent_surface_committed_handler = 0;
+                                container->frame_clock_after_paint_handler);
+    container->frame_clock_after_paint_handler = 0;
   }
-  container->parent_surface_committed = false;
+  container->ready_to_draw = false;
 }
 #endif
 
 void moz_container_map(GtkWidget *widget) {
   MozContainer *container;
   GList *tmp_list;
   GtkWidget *tmp_child;
 
@@ -273,30 +218,30 @@ void moz_container_map(GtkWidget *widget
       if (!gtk_widget_get_mapped(tmp_child)) gtk_widget_map(tmp_child);
     }
     tmp_list = tmp_list->next;
   }
 
   if (gtk_widget_get_has_window(widget)) {
     gdk_window_show(gtk_widget_get_window(widget));
 #if defined(MOZ_WAYLAND)
-    moz_container_map_surface(MOZ_CONTAINER(widget));
+    moz_container_map_wayland(MOZ_CONTAINER(widget));
 #endif
   }
 }
 
 void moz_container_unmap(GtkWidget *widget) {
   g_return_if_fail(IS_MOZ_CONTAINER(widget));
 
   gtk_widget_set_mapped(widget, FALSE);
 
   if (gtk_widget_get_has_window(widget)) {
     gdk_window_hide(gtk_widget_get_window(widget));
 #if defined(MOZ_WAYLAND)
-    moz_container_unmap_surface(MOZ_CONTAINER(widget));
+    moz_container_unmap_wayland(MOZ_CONTAINER(widget));
 #endif
   }
 }
 
 void moz_container_realize(GtkWidget *widget) {
   GdkWindow *parent = gtk_widget_get_parent_window(widget);
   GdkWindow *window;
 
@@ -479,49 +424,82 @@ MozContainerChild *moz_container_get_chi
 }
 
 static void moz_container_add(GtkContainer *container, GtkWidget *widget) {
   moz_container_put(MOZ_CONTAINER(container), widget, 0, 0);
 }
 
 #ifdef MOZ_WAYLAND
 struct wl_surface *moz_container_get_wl_surface(MozContainer *container) {
-  if (!container->subsurface || !container->surface) {
+  // We're not mapped yet.
+  if (!container->ready_to_draw) return nullptr;
+
+  if (!container->surface) {
+    // Available as of GTK 3.8+
+    static auto sGdkWaylandDisplayGetWlCompositor =
+        (wl_compositor * (*)(GdkDisplay *))
+            dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
+    static auto sGdkWaylandWindowGetWlSurface = (wl_surface * (*)(GdkWindow *))
+        dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
+    static auto sGdkWindowGetScaleFactorPtr = (gint(*)(GdkWindow *))dlsym(
+        RTLD_DEFAULT, "gdk_window_get_scale_factor");
+
     GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
-    if (!gdk_window_is_visible(window)) return nullptr;
+    GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
+
+    struct wl_compositor *compositor =
+        sGdkWaylandDisplayGetWlCompositor(display);
+    container->surface = wl_compositor_create_surface(compositor);
+
+    wl_surface *parent_surface = sGdkWaylandWindowGetWlSurface(window);
 
-    moz_container_map_surface(container);
-    // Set the scale factor for the buffer right after we create it.
-    if (container->surface) {
-      static auto sGdkWindowGetScaleFactorPtr = (gint(*)(GdkWindow *))dlsym(
-          RTLD_DEFAULT, "gdk_window_get_scale_factor");
-      if (sGdkWindowGetScaleFactorPtr && window) {
-        gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window);
-        wl_surface_set_buffer_scale(container->surface, scaleFactor);
-      }
+    nsWaylandDisplay *waylandDisplay = WaylandDisplayGet(display);
+    wl_subcompositor *subcompositor = waylandDisplay->GetSubcompositor();
+    container->subsurface = wl_subcompositor_get_subsurface(
+        subcompositor, container->surface, parent_surface);
+    WaylandDisplayRelease(waylandDisplay);
+
+    gint x, y;
+    gdk_window_get_position(window, &x, &y);
+    wl_subsurface_set_position(container->subsurface, x, y);
+    wl_subsurface_set_desync(container->subsurface);
+
+    // Route input to parent wl_surface owned by Gtk+ so we get input
+    // events from Gtk+.
+    wl_region *region = wl_compositor_create_region(compositor);
+    wl_surface_set_input_region(container->surface, region);
+    wl_region_destroy(region);
+
+    container->surface_needs_clear = true;
+
+    if (sGdkWindowGetScaleFactorPtr) {
+      gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window);
+      wl_surface_set_buffer_scale(container->surface, scaleFactor);
     }
   }
 
   return container->surface;
 }
 
 struct wl_egl_window *moz_container_get_wl_egl_window(MozContainer *container) {
   if (!container->eglwindow) {
-    struct wl_surface *wlsurf = moz_container_get_wl_surface(container);
-    if (!wlsurf) return nullptr;
+    wl_surface *surface = moz_container_get_wl_surface(container);
+    if (!surface) {
+      return nullptr;
+    }
 
     GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
     container->eglwindow = wl_egl_window_create(
-        wlsurf, gdk_window_get_width(window), gdk_window_get_height(window));
+        surface, gdk_window_get_width(window), gdk_window_get_height(window));
   }
   return container->eglwindow;
 }
 
 gboolean moz_container_has_wl_egl_window(MozContainer *container) {
   return container->eglwindow ? true : false;
 }
 
-gboolean moz_container_needs_clear(MozContainer *container) {
-  gboolean state = container->needs_clear;
-  container->needs_clear = false;
+gboolean moz_container_surface_needs_clear(MozContainer *container) {
+  gboolean state = container->surface_needs_clear;
+  container->surface_needs_clear = false;
   return state;
 }
 #endif
--- a/widget/gtk/mozcontainer.h
+++ b/widget/gtk/mozcontainer.h
@@ -70,31 +70,31 @@ struct wl_subsurface;
 struct _MozContainer {
   GtkContainer container;
   GList *children;
 
 #ifdef MOZ_WAYLAND
   struct wl_surface *surface;
   struct wl_subsurface *subsurface;
   struct wl_egl_window *eglwindow;
-  gboolean needs_clear;
-  gboolean parent_surface_committed;
-  gulong parent_surface_committed_handler;
+  gboolean surface_needs_clear;
+  gboolean ready_to_draw;
+  gulong frame_clock_after_paint_handler;
 #endif
 };
 
 struct _MozContainerClass {
   GtkContainerClass parent_class;
 };
 
 GType moz_container_get_type(void);
 GtkWidget *moz_container_new(void);
 void moz_container_put(MozContainer *container, GtkWidget *child_widget, gint x,
                        gint y);
 
 #ifdef MOZ_WAYLAND
 struct wl_surface *moz_container_get_wl_surface(MozContainer *container);
 struct wl_egl_window *moz_container_get_wl_egl_window(MozContainer *container);
 gboolean moz_container_has_wl_egl_window(MozContainer *container);
-gboolean moz_container_needs_clear(MozContainer *container);
+gboolean moz_container_surface_needs_clear(MozContainer *container);
 #endif
 
 #endif /* __MOZ_CONTAINER_H__ */
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1863,18 +1863,20 @@ gboolean nsWindow::OnExposeEvent(cairo_t
   if (mIsDestroyed) {
     return FALSE;
   }
 
   // Windows that are not visible will be painted after they become visible.
   if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel) return FALSE;
 
 #ifdef MOZ_WAYLAND
-  // Window does not have visible wl_surface yet.
-  if (!mIsX11Display && !GetWaylandSurface()) return FALSE;
+  // Window does not have visible MozContainer/wl_surface yet.
+  if (!mIsX11Display && (!mContainer || !mContainer->ready_to_draw)) {
+    return FALSE;
+  }
 #endif
 
   nsIWidgetListener *listener = GetListener();
   if (!listener) return FALSE;
 
   LayoutDeviceIntRegion exposeRegion;
   if (!ExtractExposeRegion(exposeRegion, cr)) {
     return FALSE;
@@ -6576,17 +6578,17 @@ wl_surface *nsWindow::GetWaylandSurface(
   NS_WARNING(
       "nsWindow::GetWaylandSurfaces(): We don't have any mContainer for "
       "drawing!");
   return nullptr;
 }
 
 bool nsWindow::WaylandSurfaceNeedsClear() {
   if (mContainer) {
-    return moz_container_needs_clear(MOZ_CONTAINER(mContainer));
+    return moz_container_surface_needs_clear(MOZ_CONTAINER(mContainer));
   }
 
   NS_WARNING(
       "nsWindow::WaylandSurfaceNeedsClear(): We don't have any mContainer!");
   return false;
 }
 #endif