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); }
