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_XRGB8888: return __DRI_IMAGE_FORMAT_XRGB8888;
49 case DRM_FORMAT_ARGB8888: return __DRI_IMAGE_FORMAT_ARGB8888;
50 case DRM_FORMAT_ABGR8888: return __DRI_IMAGE_FORMAT_ABGR8888;
51 case DRM_FORMAT_XBGR8888: return __DRI_IMAGE_FORMAT_XBGR8888;
52 case DRM_FORMAT_XRGB2101010: return __DRI_IMAGE_FORMAT_XRGB2101010;
53 case DRM_FORMAT_ARGB2101010: return __DRI_IMAGE_FORMAT_ARGB2101010;
54 case DRM_FORMAT_XBGR2101010: return __DRI_IMAGE_FORMAT_XBGR2101010;
55 case DRM_FORMAT_ABGR2101010: return __DRI_IMAGE_FORMAT_ABGR2101010;
56 case DRM_FORMAT_ABGR16161616: return __DRI_IMAGE_FORMAT_ABGR16161616;
57 case DRM_FORMAT_XBGR16161616: return __DRI_IMAGE_FORMAT_XBGR16161616;
58 case DRM_FORMAT_XBGR16161616F: return __DRI_IMAGE_FORMAT_XBGR16161616F;
59 case DRM_FORMAT_ABGR16161616F: return __DRI_IMAGE_FORMAT_ABGR16161616F;
60 }
61 return 0;
62 }
63
64 int
loader_image_format_to_fourcc(int format)65 loader_image_format_to_fourcc(int format)
66 {
67 /* Convert from __DRI_IMAGE_FORMAT to DRM_FORMAT (sigh) */
68 switch (format) {
69 case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
70 case __DRI_IMAGE_FORMAT_SABGR8: return __DRI_IMAGE_FOURCC_SABGR8888;
71 case __DRI_IMAGE_FORMAT_SXRGB8: return __DRI_IMAGE_FOURCC_SXRGB8888;
72 case __DRI_IMAGE_FORMAT_RGB565: return DRM_FORMAT_RGB565;
73 case __DRI_IMAGE_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
74 case __DRI_IMAGE_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
75 case __DRI_IMAGE_FORMAT_ABGR8888: return DRM_FORMAT_ABGR8888;
76 case __DRI_IMAGE_FORMAT_XBGR8888: return DRM_FORMAT_XBGR8888;
77 case __DRI_IMAGE_FORMAT_XRGB2101010: return DRM_FORMAT_XRGB2101010;
78 case __DRI_IMAGE_FORMAT_ARGB2101010: return DRM_FORMAT_ARGB2101010;
79 case __DRI_IMAGE_FORMAT_XBGR2101010: return DRM_FORMAT_XBGR2101010;
80 case __DRI_IMAGE_FORMAT_ABGR2101010: return DRM_FORMAT_ABGR2101010;
81 case __DRI_IMAGE_FORMAT_ABGR16161616: return DRM_FORMAT_ABGR16161616;
82 case __DRI_IMAGE_FORMAT_XBGR16161616: return DRM_FORMAT_XBGR16161616;
83 case __DRI_IMAGE_FORMAT_XBGR16161616F: return DRM_FORMAT_XBGR16161616F;
84 case __DRI_IMAGE_FORMAT_ABGR16161616F: return DRM_FORMAT_ABGR16161616F;
85 }
86 return 0;
87 }
88
89 #ifdef HAVE_X11_PLATFORM
90 void
loader_init_screen_resources(struct loader_screen_resources * res,xcb_connection_t * conn,xcb_screen_t * screen)91 loader_init_screen_resources(struct loader_screen_resources *res,
92 xcb_connection_t *conn,
93 xcb_screen_t *screen)
94 {
95 res->conn = conn;
96 res->screen = screen;
97 res->crtcs = NULL;
98
99 mtx_init(&res->mtx, mtx_plain);
100 }
101
102 void
loader_destroy_screen_resources(struct loader_screen_resources * res)103 loader_destroy_screen_resources(struct loader_screen_resources *res)
104 {
105 mtx_destroy(&res->mtx);
106 }
107
108 static unsigned
gcd_u32(unsigned a,unsigned b)109 gcd_u32(unsigned a, unsigned b)
110 {
111 assert(a > 0 || b > 0);
112
113 while (b != 0) {
114 unsigned remainder = a % b;
115 a = b;
116 b = remainder;
117 }
118
119 return a;
120 }
121
122 static void
calculate_refresh_rate(const xcb_randr_mode_info_t * mode,unsigned * numerator,unsigned * denominator)123 calculate_refresh_rate(const xcb_randr_mode_info_t *mode,
124 unsigned *numerator, unsigned *denominator)
125 {
126 unsigned vtotal = mode->vtotal;
127
128 /* Double-scan doubles the number of lines */
129 if (mode->mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
130 vtotal *= 2;
131
132 /* Interlace splits the frame into two fields; typically the monitor
133 * reports field rate.
134 */
135 if (mode->mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
136 vtotal /= 2;
137
138 uint32_t dots = mode->htotal * vtotal;
139
140 if (dots == 0) {
141 *numerator = 0;
142 *denominator = 1;
143 } else {
144 uint32_t gcd = gcd_u32(mode->dot_clock, dots);
145
146 *numerator = mode->dot_clock / gcd;
147 *denominator = dots / gcd;
148 }
149 }
150
151 bool
loader_update_screen_resources(struct loader_screen_resources * res)152 loader_update_screen_resources(struct loader_screen_resources *res)
153 {
154 xcb_randr_get_crtc_info_cookie_t *crtc_cookies;
155
156 /* If we have cached screen resources information, check each CRTC to
157 * see if it's up to date. Ideally, we'd watch PresentConfigureNotify
158 * events on the root window to see if something changed, but those only
159 * fire if the geometry changes. It misses CRTC changes which only
160 * alter the refresh rate. We also can't watch RandR events internally
161 * because they aren't XGE events. So, we just check every CRTC for now.
162 */
163 bool config_unchanged = res->crtcs != NULL;
164
165 crtc_cookies = malloc(res->num_crtcs * sizeof(*crtc_cookies));
166
167 for (unsigned c = 0; c < res->num_crtcs; c++) {
168 crtc_cookies[c] =
169 xcb_randr_get_crtc_info_unchecked(res->conn, res->crtcs[c].id,
170 res->config_timestamp);
171 }
172
173 for (unsigned c = 0; c < res->num_crtcs; c++) {
174 xcb_randr_get_crtc_info_reply_t *reply =
175 xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
176
177 /* Although randrproto 1.4.0 says that RRGetCrtcInfo is supposed to
178 * return InvalidConfigTime if config_timestamp is out of date, the
179 * implementation in xserver as of 21.x doesn't actually do so. To
180 * detect changes in refresh rate, we check the returned timestamp
181 * on each tracked CRTC.
182 */
183 if (!reply ||
184 reply->status == XCB_RANDR_SET_CONFIG_INVALID_CONFIG_TIME ||
185 reply->timestamp != res->crtcs[c].timestamp) {
186 config_unchanged = false;
187 /* continue to consume all replies */
188 }
189
190 free(reply);
191 }
192
193 free(crtc_cookies);
194
195 if (config_unchanged)
196 return false;
197
198 /* Do RRGetScreenResourcesCurrent to query the list of CRTCs and modes,
199 * then RRGetCrtcInfo on each CRTC to determine what mode each uses, and
200 * use the mode to calculate the refresh rate.
201 */
202 mtx_lock(&res->mtx);
203
204 xcb_randr_get_screen_resources_current_cookie_t cookie =
205 xcb_randr_get_screen_resources_current_unchecked(res->conn,
206 res->screen->root);
207 xcb_randr_get_screen_resources_current_reply_t *reply =
208 xcb_randr_get_screen_resources_current_reply(res->conn, cookie, NULL);
209
210 xcb_randr_crtc_t *new_crtcs =
211 xcb_randr_get_screen_resources_current_crtcs(reply);
212
213 xcb_randr_mode_info_t *new_modes =
214 xcb_randr_get_screen_resources_current_modes(reply);
215
216 res->config_timestamp = reply->config_timestamp;
217
218 free(res->crtcs);
219 res->crtcs = calloc(reply->num_crtcs, sizeof(*res->crtcs));
220
221 crtc_cookies = malloc(reply->num_crtcs * sizeof(*crtc_cookies));
222
223 for (unsigned c = 0; c < reply->num_crtcs; c++) {
224 crtc_cookies[c] =
225 xcb_randr_get_crtc_info_unchecked(res->conn, new_crtcs[c],
226 res->config_timestamp);
227 }
228
229 unsigned i = 0;
230 for (unsigned c = 0; c < reply->num_crtcs; c++) {
231 xcb_randr_get_crtc_info_reply_t *crtc_info =
232 xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
233
234 if (!crtc_info || crtc_info->mode == XCB_NONE)
235 continue;
236
237 res->crtcs[i].id = new_crtcs[c];
238 res->crtcs[i].timestamp = crtc_info->timestamp;
239 res->crtcs[i].x = crtc_info->x;
240 res->crtcs[i].y = crtc_info->y;
241 res->crtcs[i].width = crtc_info->width;
242 res->crtcs[i].height = crtc_info->height;
243
244 for (int m = 0; m < reply->num_modes; m++) {
245 if (new_modes[m].id == crtc_info->mode) {
246 calculate_refresh_rate(&new_modes[m],
247 &res->crtcs[i].refresh_numerator,
248 &res->crtcs[i].refresh_denominator);
249 break;
250 }
251 }
252
253 i++;
254 free(crtc_info);
255 }
256
257 res->num_crtcs = i;
258
259 free(crtc_cookies);
260 free(reply);
261
262 mtx_unlock(&res->mtx);
263 return true;
264 }
265 #endif
266