diff --git a/gdk/wayland/gdkdragsurface-wayland.c b/gdk/wayland/gdkdragsurface-wayland.c
index 22220a49e4..a52b7e6148 100644
--- a/gdk/wayland/gdkdragsurface-wayland.c
+++ b/gdk/wayland/gdkdragsurface-wayland.c
@@ -93,7 +93,7 @@ gdk_wayland_drag_surface_compute_size (GdkSurface *surface)
       gdk_wayland_surface_update_size (surface,
                                        impl->next_layout.configured_width,
                                        impl->next_layout.configured_height,
-                                       impl->scale);
+                                       &impl->scale);
 
       impl->next_layout.surface_geometry_dirty = FALSE;
     }
diff --git a/gdk/wayland/gdksurface-wayland-private.h b/gdk/wayland/gdksurface-wayland-private.h
index 4689945cc7..e6c08a4bb4 100644
--- a/gdk/wayland/gdksurface-wayland-private.h
+++ b/gdk/wayland/gdksurface-wayland-private.h
@@ -50,7 +50,7 @@ struct _GdkWaylandSurface
   int pending_buffer_offset_y;
 
   gint64 pending_frame_counter;
-  guint32 scale;
+  GdkFractionalScale scale;
   gboolean buffer_scale_dirty;
 
   int shadow_left;
@@ -99,11 +99,11 @@ struct _GdkWaylandSurfaceClass
 
 #define GDK_WAYLAND_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurfaceClass))
 
-void gdk_wayland_surface_create_wl_surface (GdkSurface *surface);
-void gdk_wayland_surface_update_size       (GdkSurface *surface,
-                                            int32_t     width,
-                                            int32_t     height,
-                                            int         scale);
+void gdk_wayland_surface_create_wl_surface (GdkSurface               *surface);
+void gdk_wayland_surface_update_size       (GdkSurface               *surface,
+                                            int32_t                   width,
+                                            int32_t                   height,
+                                            const GdkFractionalScale *scale);
 void gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface);
 void _gdk_wayland_surface_save_size (GdkSurface *surface);
 
diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c
index 7c952af083..2581015a2e 100644
--- a/gdk/wayland/gdksurface-wayland.c
+++ b/gdk/wayland/gdksurface-wayland.c
@@ -59,11 +59,6 @@
 
 G_DEFINE_TYPE (GdkWaylandSurface, gdk_wayland_surface, GDK_TYPE_SURFACE)
 
-static void gdk_wayland_surface_maybe_resize (GdkSurface *surface,
-                                              int         width,
-                                              int         height,
-                                              int         scale);
-
 static void gdk_wayland_surface_configure (GdkSurface *surface);
 
 static void gdk_wayland_surface_sync_shadow (GdkSurface *surface);
@@ -180,7 +175,7 @@ wl_region_from_cairo_region (GdkWaylandDisplay *display,
 static void
 gdk_wayland_surface_init (GdkWaylandSurface *impl)
 {
-  impl->scale = 1;
+  impl->scale = GDK_FRACTIONAL_SCALE_INIT_INT (1);
 }
 
 void
@@ -207,30 +202,66 @@ gdk_wayland_surface_thaw_state (GdkSurface *surface)
     gdk_wayland_surface_configure (surface);
 }
 
+static void
+gdk_wayland_surface_maybe_resize (GdkSurface               *surface,
+                                  int                       width,
+                                  int                       height,
+                                  const GdkFractionalScale *scale)
+{
+  GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+  gboolean hide_temporarily;
+
+  if (surface->width == width &&
+      surface->height == height &&
+      gdk_fractional_scale_equal (&impl->scale, scale))
+    return;
+
+  /* For xdg_popup using an xdg_positioner, there is a race condition if
+   * the application tries to change the size after it's mapped, but before
+   * the initial configure is received, so hide and show the surface again
+   * force the new size onto the compositor. See bug #772505.
+   */
+  hide_temporarily = GDK_IS_WAYLAND_POPUP (surface) &&
+                     gdk_surface_get_mapped (surface) &&
+                     !impl->initial_configure_received;
+
+  if (hide_temporarily)
+    gdk_surface_hide (surface);
+
+  gdk_wayland_surface_update_size (surface, width, height, scale);
+
+  if (hide_temporarily)
+    gdk_wayland_surface_create_wl_surface (surface);
+}
+
 void
-gdk_wayland_surface_update_size (GdkSurface *surface,
-                                 int32_t     width,
-                                 int32_t     height,
-                                 int         scale)
+gdk_wayland_surface_update_size (GdkSurface               *surface,
+                                 int32_t                   width,
+                                 int32_t                   height,
+                                 const GdkFractionalScale *scale)
 {
   GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
   gboolean width_changed, height_changed, scale_changed;
 
   width_changed = surface->width != width;
   height_changed = surface->height != height;
-  scale_changed = impl->scale != scale;
+  scale_changed = !gdk_fractional_scale_equal (&impl->scale, scale);
 
   if (!width_changed && !height_changed && !scale_changed)
     return;
 
   surface->width = width;
   surface->height = height;
-  impl->scale = scale;
+  if (scale_changed)
+    {
+      impl->scale = *scale;
+      impl->buffer_scale_dirty = TRUE;
+    }
 
   if (impl->display_server.egl_window)
-    wl_egl_window_resize (impl->display_server.egl_window, width * scale, height * scale, 0, 0);
-  if (scale_changed)
-    impl->buffer_scale_dirty = TRUE;
+    wl_egl_window_resize (impl->display_server.egl_window,
+                          width * gdk_fractional_scale_to_int (scale),
+                          height * gdk_fractional_scale_to_int (scale), 0, 0);
 
   gdk_surface_invalidate_rect (surface, NULL);
 
@@ -435,27 +466,23 @@ gdk_wayland_surface_update_scale (GdkSurface *surface)
     return;
 
   if (!impl->display_server.outputs)
-    {
-      scale = impl->scale;
-    }
-  else
-    {
-      scale = 1;
-      for (l = impl->display_server.outputs; l != NULL; l = l->next)
-        {
-          struct wl_output *output = l->data;
-          uint32_t output_scale;
+    return;
 
-          output_scale = gdk_wayland_display_get_output_scale (display_wayland,
-                                                               output);
-          scale = MAX (scale, output_scale);
-        }
+  scale = 1;
+  for (l = impl->display_server.outputs; l != NULL; l = l->next)
+    {
+      struct wl_output *output = l->data;
+      uint32_t output_scale;
+
+      output_scale = gdk_wayland_display_get_output_scale (display_wayland,
+                                                           output);
+      scale = MAX (scale, output_scale);
     }
 
   /* Notify app that scale changed */
   gdk_wayland_surface_maybe_resize (surface,
                                     surface->width, surface->height,
-                                    scale);
+                                    &GDK_FRACTIONAL_SCALE_INIT_INT (scale));
 }
 
 GdkSurface *
@@ -532,10 +559,13 @@ _gdk_wayland_display_create_surface (GdkDisplay     *display,
       if (monitor)
         {
           GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+          guint32 monitor_scale = gdk_monitor_get_scale_factor (monitor);
 
-          impl->scale = gdk_monitor_get_scale_factor (monitor);
-          if (impl->scale != 1)
-            impl->buffer_scale_dirty = TRUE;
+          if (monitor_scale != 1)
+            {
+              impl->scale = GDK_FRACTIONAL_SCALE_INIT_INT (monitor_scale);
+              impl->buffer_scale_dirty = TRUE;
+            }
 
           g_object_unref (monitor);
         }
@@ -657,38 +687,6 @@ gdk_wayland_surface_finalize (GObject *object)
   G_OBJECT_CLASS (gdk_wayland_surface_parent_class)->finalize (object);
 }
 
-static void
-gdk_wayland_surface_maybe_resize (GdkSurface *surface,
-                                  int         width,
-                                  int         height,
-                                  int         scale)
-{
-  GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
-  gboolean hide_temporarily;
-
-  if (surface->width == width &&
-      surface->height == height &&
-      impl->scale == scale)
-    return;
-
-  /* For xdg_popup using an xdg_positioner, there is a race condition if
-   * the application tries to change the size after it's mapped, but before
-   * the initial configure is received, so hide and show the surface again
-   * force the new size onto the compositor. See bug #772505.
-   */
-  hide_temporarily = GDK_IS_WAYLAND_POPUP (surface) &&
-                     gdk_surface_get_mapped (surface) &&
-                     !impl->initial_configure_received;
-
-  if (hide_temporarily)
-    gdk_surface_hide (surface);
-
-  gdk_wayland_surface_update_size (surface, width, height, scale);
-
-  if (hide_temporarily)
-    gdk_wayland_surface_create_wl_surface (surface);
-}
-
 static void
 gdk_wayland_surface_sync_shadow (GdkSurface *surface)
 {
@@ -793,7 +791,8 @@ gdk_wayland_surface_sync_buffer_scale (GdkSurface *surface)
 
   /* Only set the buffer scale if supported by the compositor */
   if (wl_surface_get_version (impl->display_server.wl_surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
-    wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale);
+    wl_surface_set_buffer_scale (impl->display_server.wl_surface,
+                                 gdk_fractional_scale_to_int (&impl->scale));
 
   impl->buffer_scale_dirty = FALSE;
 }
@@ -809,7 +808,7 @@ gdk_wayland_surface_fractional_scale_preferred_scale_cb (void *data,
   /* Notify app that scale changed */
   gdk_wayland_surface_maybe_resize (surface,
                                     surface->width, surface->height,
-                                    ceil (scale / 120.0));
+                                    &GDK_FRACTIONAL_SCALE_INIT (scale));
 }
 
 static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
@@ -1062,7 +1061,7 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
   impl->has_uncommitted_ack_configure = FALSE;
   impl->input_region_dirty = TRUE;
   impl->opaque_region_dirty = TRUE;
-  if (impl->scale != 1)
+  if (!gdk_fractional_scale_equal (&impl->scale, &GDK_FRACTIONAL_SCALE_INIT_INT (1)))
     impl->buffer_scale_dirty = TRUE;
 
   impl->last_sent_window_geometry = (GdkRectangle) { 0 };
@@ -1097,7 +1096,7 @@ gdk_wayland_surface_move_resize (GdkSurface *surface,
 
   surface->x = x;
   surface->y = y;
-  gdk_wayland_surface_maybe_resize (surface, width, height, impl->scale);
+  gdk_wayland_surface_maybe_resize (surface, width, height, &impl->scale);
 }
 
 static void
@@ -1232,7 +1231,7 @@ gdk_wayland_surface_get_scale_factor (GdkSurface *surface)
   if (GDK_SURFACE_DESTROYED (surface))
     return 1;
 
-  return impl->scale;
+  return gdk_fractional_scale_to_int (&impl->scale);
 }
 
 static void
@@ -1335,8 +1334,8 @@ gdk_wayland_surface_ensure_wl_egl_window (GdkSurface *surface)
     {
       impl->display_server.egl_window =
         wl_egl_window_create (impl->display_server.wl_surface,
-                              surface->width * impl->scale,
-                              surface->height * impl->scale);
+                              surface->width * gdk_fractional_scale_to_int (&impl->scale),
+                              surface->height * gdk_fractional_scale_to_int (&impl->scale));
       gdk_surface_set_egl_native_window (surface, impl->display_server.egl_window);
     }
 }
diff --git a/gdk/wayland/gdktoplevel-wayland.c b/gdk/wayland/gdktoplevel-wayland.c
index f40f5af219..6954795f0e 100644
--- a/gdk/wayland/gdktoplevel-wayland.c
+++ b/gdk/wayland/gdktoplevel-wayland.c
@@ -434,7 +434,7 @@ gdk_wayland_toplevel_compute_size (GdkSurface *surface)
                                       width, height,
                                       &width, &height);
         }
-      gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale);
+      gdk_wayland_surface_update_size (surface, width, height, &wayland_surface->scale);
 
       if (!wayland_toplevel->next_layout.size_is_fixed)
         {
@@ -452,7 +452,7 @@ gdk_wayland_toplevel_compute_size (GdkSurface *surface)
       gdk_surface_constrain_size (&geometry, mask,
                                   width, height,
                                   &width, &height);
-      gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale);
+      gdk_wayland_surface_update_size (surface, width, height, &wayland_surface->scale);
     }
 
   wayland_surface->next_layout.surface_geometry_dirty = FALSE;