Module: Mesa
Branch: staging/22.3
Commit: 9c91081dc0dc52ee5a84e0bdf798cefc774f2dbc
URL:    
http://cgit.freedesktop.org/mesa/mesa/commit/?id=9c91081dc0dc52ee5a84e0bdf798cefc774f2dbc

Author: Kenneth Graunke <[email protected]>
Date:   Wed Jan 11 15:37:22 2023 -0800

egl: Rewrite eglGetMscRateANGLE to avoid probes and handle multi-monitor

RRGetScreenInfo re-probes connector status, which may result in an EDID
transfer for every output, which according to Adam Jackson can be on the
order of 100ms for a single EDID block.  So our previous implementation
of this eglGetMscRateANGLE was blocking for excessive periods of time
instead of being a quick query of the refresh rate like users expect.

This changes our eglGetMscRateANGLE implementation from using
RRGetScreenInfo to RRGetScreenResourcesCurrent and RRGetCrtcInfo.
This obtains the same monitor info without re-probing connectors.

Fixes a severe performance regression in Chromium WebGL performance.

While we're re-implementing the extension, we also implement proper
multi-monitor support: if there are multiple active CRTCs, we determine
which contains the largest portion of the surface, as specified in the
EGL_ANGLE_sync_control_rate extension.

We also now report fractional refresh rates correctly rather than
rounding to the nearest Hz.

Fixes: 47526556494 ("egl/x11: implement ANGLE_sync_control_rate")
Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/6996
Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/7038
Reviewed-by: Adam Jackson <[email protected]>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/20665>
(cherry picked from commit 41d5f0ee09ccc6406d9b57351fa95edf30b03298)

---

 .pick_status.json                   |  2 +-
 src/egl/drivers/dri2/egl_dri2.h     |  1 +
 src/egl/drivers/dri2/platform_x11.c | 83 +++++++++++++++++++++++++++++++++----
 3 files changed, 76 insertions(+), 10 deletions(-)

diff --git a/.pick_status.json b/.pick_status.json
index 5e3e21b30df..3153cbaf913 100644
--- a/.pick_status.json
+++ b/.pick_status.json
@@ -400,7 +400,7 @@
         "description": "egl: Rewrite eglGetMscRateANGLE to avoid probes and 
handle multi-monitor",
         "nominated": true,
         "nomination_type": 1,
-        "resolution": 0,
+        "resolution": 1,
         "main_sha": null,
         "because_sha": "47526556494f18cd2c02f978bccac7e2ba73adcd"
     },
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index 2e283cba0e7..40c2a14d8d8 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -274,6 +274,7 @@ struct dri2_egl_display
    int present_major_version;
    int present_minor_version;
    struct loader_dri3_extensions loader_dri3_ext;
+   struct loader_dri3_screen_resources screen_resources;
 #endif
 #endif
 
diff --git a/src/egl/drivers/dri2/platform_x11.c 
b/src/egl/drivers/dri2/platform_x11.c
index 77c414823ac..15be86dc026 100644
--- a/src/egl/drivers/dri2/platform_x11.c
+++ b/src/egl/drivers/dri2/platform_x11.c
@@ -1191,23 +1191,82 @@ dri2_x11_get_sync_values(_EGLDisplay *display, 
_EGLSurface *surface,
    return EGL_TRUE;
 }
 
+static int
+box_intersection_area(int16_t a_x, int16_t a_y,
+                      int16_t a_width, int16_t a_height,
+                      int16_t b_x, int16_t b_y,
+                      int16_t b_width, int16_t b_height)
+{
+   int w = MIN2(a_x + a_width,  b_x + b_width)  - MAX2(a_x, b_x);
+   int h = MIN2(a_y + a_height, b_y + b_height) - MAX2(a_y, b_y);
+
+   return (w < 0 || h < 0) ? 0 : w * h;
+}
+
 EGLBoolean
 dri2_x11_get_msc_rate(_EGLDisplay *display, _EGLSurface *surface,
                       EGLint *numerator, EGLint *denominator)
 {
    struct dri2_egl_display *dri2_dpy = dri2_egl_display(display);
-   xcb_randr_get_screen_info_cookie_t cookie;
-   xcb_randr_get_screen_info_reply_t *reply;
 
-   cookie = xcb_randr_get_screen_info_unchecked(dri2_dpy->conn, 
dri2_dpy->screen->root);
-   reply = xcb_randr_get_screen_info_reply(dri2_dpy->conn, cookie, NULL);
+   loader_dri3_update_screen_resources(&dri2_dpy->screen_resources);
 
-   if (!reply)
-      return _eglError(EGL_BAD_ACCESS, "eglGetMscRateANGLE");
+   if (dri2_dpy->screen_resources.num_crtcs == 0) {
+      /* If there's no CRTC active, use the present fake vblank of 1Hz */
+      *numerator = 1;
+      *denominator = 1;
+      return EGL_TRUE;
+   }
 
-   *numerator = reply->rate;
-   *denominator = 1;
-   free(reply);
+   /* Default to the first CRTC in the list */
+   *numerator = dri2_dpy->screen_resources.crtcs[0].refresh_numerator;
+   *denominator = dri2_dpy->screen_resources.crtcs[0].refresh_denominator;
+
+   /* If there's only one active CRTC, we're done */
+   if (dri2_dpy->screen_resources.num_crtcs == 1)
+      return EGL_TRUE;
+
+   /* In a multi-monitor setup, look at each CRTC and perform a box
+    * intersection between the CRTC and surface.  Use the CRTC whose
+    * box intersection has the largest area.
+    */
+   if (surface->Type != EGL_WINDOW_BIT)
+      return EGL_TRUE;
+
+   xcb_window_t window = (uintptr_t) surface->NativeSurface;
+
+   xcb_translate_coordinates_cookie_t cookie =
+      xcb_translate_coordinates_unchecked(dri2_dpy->conn, window,
+                                          dri2_dpy->screen->root, 0, 0);
+   xcb_translate_coordinates_reply_t *reply =
+      xcb_translate_coordinates_reply(dri2_dpy->conn, cookie, NULL);
+
+   if (!reply) {
+      _eglError(EGL_BAD_SURFACE,
+                "eglGetMscRateANGLE failed to translate coordinates");
+      return EGL_FALSE;
+   }
+
+   int area = 0;
+
+   for (unsigned c = 0; c < dri2_dpy->screen_resources.num_crtcs; c++) {
+      struct loader_dri3_crtc_info *crtc =
+         &dri2_dpy->screen_resources.crtcs[c];
+
+      int c_area = box_intersection_area(reply->dst_x, reply->dst_y,
+                                        surface->Width, surface->Height,
+                                        crtc->x, crtc->y,
+                                        crtc->width, crtc->height);
+      if (c_area > area) {
+         *numerator = crtc->refresh_numerator;
+         *denominator = crtc->refresh_denominator;
+         area = c_area;
+      }
+   }
+
+   /* If the window is entirely off-screen, then area will still be 0.
+    * We defaulted to the first CRTC in the list's refresh rate, earlier.
+    */
 
    return EGL_TRUE;
 }
@@ -1582,6 +1641,9 @@ dri2_initialize_x11_dri3(_EGLDisplay *disp)
    if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp, false))
       goto cleanup;
 
+   loader_dri3_init_screen_resources(&dri2_dpy->screen_resources,
+                                     dri2_dpy->conn, dri2_dpy->screen);
+
    dri2_dpy->loader_dri3_ext.core = dri2_dpy->core;
    dri2_dpy->loader_dri3_ext.image_driver = dri2_dpy->image_driver;
    dri2_dpy->loader_dri3_ext.flush = dri2_dpy->flush;
@@ -1729,6 +1791,9 @@ dri2_initialize_x11(_EGLDisplay *disp)
 void
 dri2_teardown_x11(struct dri2_egl_display *dri2_dpy)
 {
+   if (dri2_dpy->dri2_major >= 3)
+      loader_dri3_destroy_screen_resources(&dri2_dpy->screen_resources);
+
    if (dri2_dpy->own_device)
       xcb_disconnect(dri2_dpy->conn);
 }

Reply via email to