• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Permission to use, copy, modify, distribute, and sell this software and its
3  * documentation for any purpose is hereby granted without fee, provided that
4  * the above copyright notice appear in all copies and that both that copyright
5  * notice and this permission notice appear in supporting documentation, and
6  * that the name of the copyright holders not be used in advertising or
7  * publicity pertaining to distribution of the software without specific,
8  * written prior permission.  The copyright holders make no representations
9  * about the suitability of this software for any purpose.  It is provided "as
10  * is" without express or implied warranty.
11  *
12  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
13  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
14  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
15  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
16  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
17  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
18  * OF THIS SOFTWARE.
19  */
20 
21 #include <errno.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 
26 #include <GL/gl.h> /* mesa_interface needs GL types */
27 #include "mesa_interface.h"
28 
29 #include "drm-uapi/drm_fourcc.h"
30 #include "loader_dri_helper.h"
31 #include "util/driconf.h"
32 
33 
34 /* the DRIimage createImage function takes __DRI_IMAGE_FORMAT codes, while
35  * the createImageFromDmaBufs call takes DRM_FORMAT codes. To avoid
36  * complete confusion, just deal in __DRI_IMAGE_FORMAT codes for now and
37  * translate to DRM_FORMAT codes in the call to createImageFromDmaBufs
38  */
39 int
loader_fourcc_to_image_format(int fourcc)40 loader_fourcc_to_image_format(int fourcc)
41 {
42    /* Convert from DRM_FORMAT to __DRI_IMAGE_FORMAT (sigh) */
43    switch (fourcc) {
44    case __DRI_IMAGE_FOURCC_SARGB8888: return __DRI_IMAGE_FORMAT_SARGB8;
45    case __DRI_IMAGE_FOURCC_SABGR8888: return __DRI_IMAGE_FORMAT_SABGR8;
46    case __DRI_IMAGE_FOURCC_SXRGB8888: return __DRI_IMAGE_FORMAT_SXRGB8;
47    case DRM_FORMAT_RGB565: return __DRI_IMAGE_FORMAT_RGB565;
48    case DRM_FORMAT_ARGB1555: return __DRI_IMAGE_FORMAT_ARGB1555;
49    case DRM_FORMAT_XRGB8888: return __DRI_IMAGE_FORMAT_XRGB8888;
50    case DRM_FORMAT_ARGB8888: return __DRI_IMAGE_FORMAT_ARGB8888;
51    case DRM_FORMAT_ABGR8888: return __DRI_IMAGE_FORMAT_ABGR8888;
52    case DRM_FORMAT_XBGR8888: return __DRI_IMAGE_FORMAT_XBGR8888;
53    case DRM_FORMAT_XRGB2101010: return __DRI_IMAGE_FORMAT_XRGB2101010;
54    case DRM_FORMAT_ARGB2101010: return __DRI_IMAGE_FORMAT_ARGB2101010;
55    case DRM_FORMAT_XBGR2101010: return __DRI_IMAGE_FORMAT_XBGR2101010;
56    case DRM_FORMAT_ABGR2101010: return __DRI_IMAGE_FORMAT_ABGR2101010;
57    case DRM_FORMAT_ABGR16161616: return __DRI_IMAGE_FORMAT_ABGR16161616;
58    case DRM_FORMAT_XBGR16161616: return __DRI_IMAGE_FORMAT_XBGR16161616;
59    case DRM_FORMAT_XBGR16161616F: return __DRI_IMAGE_FORMAT_XBGR16161616F;
60    case DRM_FORMAT_ABGR16161616F: return __DRI_IMAGE_FORMAT_ABGR16161616F;
61    }
62    return 0;
63 }
64 
65 int
loader_image_format_to_fourcc(int format)66 loader_image_format_to_fourcc(int format)
67 {
68    /* Convert from __DRI_IMAGE_FORMAT to DRM_FORMAT (sigh) */
69    switch (format) {
70    case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
71    case __DRI_IMAGE_FORMAT_SABGR8: return __DRI_IMAGE_FOURCC_SABGR8888;
72    case __DRI_IMAGE_FORMAT_SXRGB8: return __DRI_IMAGE_FOURCC_SXRGB8888;
73    case __DRI_IMAGE_FORMAT_RGB565: return DRM_FORMAT_RGB565;
74    case __DRI_IMAGE_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
75    case __DRI_IMAGE_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
76    case __DRI_IMAGE_FORMAT_ABGR8888: return DRM_FORMAT_ABGR8888;
77    case __DRI_IMAGE_FORMAT_XBGR8888: return DRM_FORMAT_XBGR8888;
78    case __DRI_IMAGE_FORMAT_XRGB2101010: return DRM_FORMAT_XRGB2101010;
79    case __DRI_IMAGE_FORMAT_ARGB2101010: return DRM_FORMAT_ARGB2101010;
80    case __DRI_IMAGE_FORMAT_XBGR2101010: return DRM_FORMAT_XBGR2101010;
81    case __DRI_IMAGE_FORMAT_ABGR2101010: return DRM_FORMAT_ABGR2101010;
82    case __DRI_IMAGE_FORMAT_ABGR16161616: return DRM_FORMAT_ABGR16161616;
83    case __DRI_IMAGE_FORMAT_XBGR16161616: return DRM_FORMAT_XBGR16161616;
84    case __DRI_IMAGE_FORMAT_XBGR16161616F: return DRM_FORMAT_XBGR16161616F;
85    case __DRI_IMAGE_FORMAT_ABGR16161616F: return DRM_FORMAT_ABGR16161616F;
86    case __DRI_IMAGE_FORMAT_ARGB1555: return DRM_FORMAT_ARGB1555;
87    }
88    return 0;
89 }
90 
91 #ifdef HAVE_X11_PLATFORM
92 void
loader_init_screen_resources(struct loader_screen_resources * res,xcb_connection_t * conn,xcb_screen_t * screen)93 loader_init_screen_resources(struct loader_screen_resources *res,
94                              xcb_connection_t *conn,
95                              xcb_screen_t *screen)
96 {
97    res->conn = conn;
98    res->screen = screen;
99    res->crtcs = NULL;
100 
101    mtx_init(&res->mtx, mtx_plain);
102 }
103 
104 void
loader_destroy_screen_resources(struct loader_screen_resources * res)105 loader_destroy_screen_resources(struct loader_screen_resources *res)
106 {
107    mtx_destroy(&res->mtx);
108 }
109 
110 static unsigned
gcd_u32(unsigned a,unsigned b)111 gcd_u32(unsigned a, unsigned b)
112 {
113    assert(a > 0 || b > 0);
114 
115    while (b != 0) {
116       unsigned remainder = a % b;
117       a = b;
118       b = remainder;
119    }
120 
121    return a;
122 }
123 
124 static void
calculate_refresh_rate(const xcb_randr_mode_info_t * mode,unsigned * numerator,unsigned * denominator)125 calculate_refresh_rate(const xcb_randr_mode_info_t *mode,
126                        unsigned *numerator, unsigned *denominator)
127 {
128    unsigned vtotal = mode->vtotal;
129 
130    /* Double-scan doubles the number of lines */
131    if (mode->mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
132       vtotal *= 2;
133 
134    /* Interlace splits the frame into two fields; typically the monitor
135     * reports field rate.
136     */
137    if (mode->mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
138       vtotal /= 2;
139 
140    uint32_t dots = mode->htotal * vtotal;
141 
142    if (dots == 0) {
143       *numerator = 0;
144       *denominator = 1;
145    } else {
146       uint32_t gcd = gcd_u32(mode->dot_clock, dots);
147 
148       *numerator = mode->dot_clock / gcd;
149       *denominator = dots / gcd;
150    }
151 }
152 
153 bool
loader_update_screen_resources(struct loader_screen_resources * res)154 loader_update_screen_resources(struct loader_screen_resources *res)
155 {
156    xcb_randr_get_crtc_info_cookie_t *crtc_cookies;
157 
158    /* If we have cached screen resources information, check each CRTC to
159     * see if it's up to date.  Ideally, we'd watch PresentConfigureNotify
160     * events on the root window to see if something changed, but those only
161     * fire if the geometry changes.  It misses CRTC changes which only
162     * alter the refresh rate.  We also can't watch RandR events internally
163     * because they aren't XGE events.  So, we just check every CRTC for now.
164     */
165    bool config_unchanged = res->crtcs != NULL;
166 
167    crtc_cookies = malloc(res->num_crtcs * sizeof(*crtc_cookies));
168 
169    for (unsigned c = 0; c < res->num_crtcs; c++) {
170       crtc_cookies[c] =
171          xcb_randr_get_crtc_info_unchecked(res->conn, res->crtcs[c].id,
172                                            res->config_timestamp);
173    }
174 
175    for (unsigned c = 0; c < res->num_crtcs; c++) {
176       xcb_randr_get_crtc_info_reply_t *reply =
177          xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
178 
179       /* Although randrproto 1.4.0 says that RRGetCrtcInfo is supposed to
180        * return InvalidConfigTime if config_timestamp is out of date, the
181        * implementation in xserver as of 21.x doesn't actually do so.  To
182        * detect changes in refresh rate, we check the returned timestamp
183        * on each tracked CRTC.
184        */
185       if (!reply ||
186           reply->status == XCB_RANDR_SET_CONFIG_INVALID_CONFIG_TIME ||
187           reply->timestamp != res->crtcs[c].timestamp) {
188          config_unchanged = false;
189          /* continue to consume all replies */
190       }
191 
192       free(reply);
193    }
194 
195    free(crtc_cookies);
196 
197    if (config_unchanged)
198       return false;
199 
200    /* Do RRGetScreenResourcesCurrent to query the list of CRTCs and modes,
201     * then RRGetCrtcInfo on each CRTC to determine what mode each uses, and
202     * use the mode to calculate the refresh rate.
203     */
204    mtx_lock(&res->mtx);
205 
206    xcb_randr_get_screen_resources_current_cookie_t cookie =
207       xcb_randr_get_screen_resources_current_unchecked(res->conn,
208                                                        res->screen->root);
209    xcb_randr_get_screen_resources_current_reply_t *reply =
210       xcb_randr_get_screen_resources_current_reply(res->conn, cookie, NULL);
211 
212    xcb_randr_crtc_t *new_crtcs =
213       xcb_randr_get_screen_resources_current_crtcs(reply);
214 
215    xcb_randr_mode_info_t *new_modes =
216       xcb_randr_get_screen_resources_current_modes(reply);
217 
218    res->config_timestamp = reply->config_timestamp;
219 
220    free(res->crtcs);
221    res->crtcs = calloc(reply->num_crtcs, sizeof(*res->crtcs));
222 
223    crtc_cookies = malloc(reply->num_crtcs * sizeof(*crtc_cookies));
224 
225    for (unsigned c = 0; c < reply->num_crtcs; c++) {
226       crtc_cookies[c] =
227          xcb_randr_get_crtc_info_unchecked(res->conn, new_crtcs[c],
228                                            res->config_timestamp);
229    }
230 
231    unsigned i = 0;
232    for (unsigned c = 0; c < reply->num_crtcs; c++) {
233       xcb_randr_get_crtc_info_reply_t *crtc_info =
234          xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
235 
236       if (!crtc_info || crtc_info->mode == XCB_NONE)
237          continue;
238 
239       res->crtcs[i].id = new_crtcs[c];
240       res->crtcs[i].timestamp = crtc_info->timestamp;
241       res->crtcs[i].x = crtc_info->x;
242       res->crtcs[i].y = crtc_info->y;
243       res->crtcs[i].width = crtc_info->width;
244       res->crtcs[i].height = crtc_info->height;
245 
246       for (int m = 0; m < reply->num_modes; m++) {
247          if (new_modes[m].id == crtc_info->mode) {
248             calculate_refresh_rate(&new_modes[m],
249                                    &res->crtcs[i].refresh_numerator,
250                                    &res->crtcs[i].refresh_denominator);
251             break;
252          }
253       }
254 
255       i++;
256       free(crtc_info);
257    }
258 
259    res->num_crtcs = i;
260 
261    free(crtc_cookies);
262    free(reply);
263 
264    mtx_unlock(&res->mtx);
265    return true;
266 }
267 #endif
268