dri3_free_render_buffer(draw, draw->buffers[i]);
}
- if (draw->special_event) {
- xcb_void_cookie_t cookie =
- xcb_present_select_input_checked(draw->conn, draw->eid, draw->drawable,
- XCB_PRESENT_EVENT_MASK_NO_EVENT);
-
- xcb_discard_reply(draw->conn, cookie.sequence);
- xcb_unregister_for_special_event(draw->conn, draw->special_event);
- }
-
if (draw->region)
xcb_xfixes_destroy_region(draw->conn, draw->region);
}
}
+void
+loader_dri3_init_screen_resources(struct loader_dri3_screen_resources *res,
+ xcb_connection_t *conn,
+ xcb_screen_t *screen)
+{
+ res->conn = conn;
+ res->screen = screen;
+ res->crtcs = NULL;
+
+ mtx_init(&res->mtx, mtx_plain);
+}
+
+void
+loader_dri3_destroy_screen_resources(struct loader_dri3_screen_resources *res)
+{
+ mtx_destroy(&res->mtx);
+}
+
+static unsigned
+gcd_u32(unsigned a, unsigned b)
+{
+ assert(a > 0 || b > 0);
+
+ while (b != 0) {
+ unsigned remainder = a % b;
+ a = b;
+ b = remainder;
+ }
+
+ return a;
+}
+
+static void
+calculate_refresh_rate(const xcb_randr_mode_info_t *mode,
+ unsigned *numerator, unsigned *denominator)
+{
+ unsigned vtotal = mode->vtotal;
+
+ /* Double-scan doubles the number of lines */
+ if (mode->mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
+ vtotal *= 2;
+
+ /* Interlace splits the frame into two fields; typically the monitor
+ * reports field rate.
+ */
+ if (mode->mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
+ vtotal /= 2;
+
+ uint32_t dots = mode->htotal * vtotal;
+
+ if (dots == 0) {
+ *numerator = 0;
+ *denominator = 1;
+ } else {
+ uint32_t gcd = gcd_u32(mode->dot_clock, dots);
+
+ *numerator = mode->dot_clock / gcd;
+ *denominator = dots / gcd;
+ }
+}
+
+bool
+loader_dri3_update_screen_resources(struct loader_dri3_screen_resources *res)
+{
+ xcb_randr_get_crtc_info_cookie_t *crtc_cookies;
+
+ /* If we have cached screen resources information, check each CRTC to
+ * see if it's up to date. Ideally, we'd watch PresentConfigureNotify
+ * events on the root window to see if something changed, but those only
+ * fire if the geometry changes. It misses CRTC changes which only
+ * alter the refresh rate. We also can't watch RandR events internally
+ * because they aren't XGE events. So, we just check every CRTC for now.
+ */
+ bool config_unchanged = res->crtcs != NULL;
+
+ crtc_cookies = malloc(res->num_crtcs * sizeof(*crtc_cookies));
+
+ for (unsigned c = 0; c < res->num_crtcs; c++) {
+ crtc_cookies[c] =
+ xcb_randr_get_crtc_info_unchecked(res->conn, res->crtcs[c].id,
+ res->config_timestamp);
+ }
+
+ for (unsigned c = 0; c < res->num_crtcs; c++) {
+ xcb_randr_get_crtc_info_reply_t *reply =
+ xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
+
+ /* Although randrproto 1.4.0 says that RRGetCrtcInfo is supposed to
+ * return InvalidConfigTime if config_timestamp is out of date, the
+ * implementation in xserver as of 21.x doesn't actually do so. To
+ * detect changes in refresh rate, we check the returned timestamp
+ * on each tracked CRTC.
+ */
+ if (!reply ||
+ reply->status == XCB_RANDR_SET_CONFIG_INVALID_CONFIG_TIME ||
+ reply->timestamp != res->crtcs[c].timestamp) {
+ config_unchanged = false;
+ /* continue to consume all replies */
+ }
+
+ free(reply);
+ }
+
+ free(crtc_cookies);
+
+ if (config_unchanged)
+ return false;
+
+ /* Do RRGetScreenResourcesCurrent to query the list of CRTCs and modes,
+ * then RRGetCrtcInfo on each CRTC to determine what mode each uses, and
+ * use the mode to calculate the refresh rate.
+ */
+ mtx_lock(&res->mtx);
+
+ xcb_randr_get_screen_resources_current_cookie_t cookie =
+ xcb_randr_get_screen_resources_current_unchecked(res->conn,
+ res->screen->root);
+ xcb_randr_get_screen_resources_current_reply_t *reply =
+ xcb_randr_get_screen_resources_current_reply(res->conn, cookie, NULL);
+
+ xcb_randr_crtc_t *new_crtcs =
+ xcb_randr_get_screen_resources_current_crtcs(reply);
+
+ xcb_randr_mode_info_t *new_modes =
+ xcb_randr_get_screen_resources_current_modes(reply);
+
+ res->config_timestamp = reply->config_timestamp;
+
+ free(res->crtcs);
+ res->crtcs = calloc(reply->num_crtcs, sizeof(*res->crtcs));
+
+ crtc_cookies = malloc(reply->num_crtcs * sizeof(*crtc_cookies));
+
+ for (unsigned c = 0; c < reply->num_crtcs; c++) {
+ crtc_cookies[c] =
+ xcb_randr_get_crtc_info_unchecked(res->conn, new_crtcs[c],
+ res->config_timestamp);
+ }
+
+ unsigned i = 0;
+ for (unsigned c = 0; c < reply->num_crtcs; c++) {
+ xcb_randr_get_crtc_info_reply_t *crtc_info =
+ xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
+
+ if (!crtc_info || crtc_info->mode == XCB_NONE)
+ continue;
+
+ res->crtcs[i].id = new_crtcs[c];
+ res->crtcs[i].timestamp = crtc_info->timestamp;
+ res->crtcs[i].x = crtc_info->x;
+ res->crtcs[i].y = crtc_info->y;
+ res->crtcs[i].width = crtc_info->width;
+ res->crtcs[i].height = crtc_info->height;
+
+ for (int m = 0; m < reply->num_modes; m++) {
+ if (new_modes[m].id == crtc_info->mode) {
+ calculate_refresh_rate(&new_modes[m],
+ &res->crtcs[i].refresh_numerator,
+ &res->crtcs[i].refresh_denominator);
+ break;
+ }
+ }
+
+ i++;
+ free(crtc_info);
+ }
+
+ res->num_crtcs = i;
+
+ free(crtc_cookies);
+ free(reply);
+
+ mtx_unlock(&res->mtx);
+ return true;
+}
/**
* Make sure the server has flushed all pending swap buffers to hardware