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> /* dri_interface needs GL types */
27 #include <GL/internal/dri_interface.h>
28
29 #include "drm-uapi/drm_fourcc.h"
30 #include "loader_dri_helper.h"
31 #include "util/driconf.h"
32
loader_dri_create_image(__DRIscreen * screen,const __DRIimageExtension * image,uint32_t width,uint32_t height,uint32_t dri_format,uint32_t dri_usage,const uint64_t * modifiers,unsigned int modifiers_count,void * loaderPrivate)33 __DRIimage *loader_dri_create_image(__DRIscreen *screen,
34 const __DRIimageExtension *image,
35 uint32_t width, uint32_t height,
36 uint32_t dri_format, uint32_t dri_usage,
37 const uint64_t *modifiers,
38 unsigned int modifiers_count,
39 void *loaderPrivate)
40 {
41 if (modifiers && modifiers_count > 0 &&
42 image->base.version > 14 && image->createImageWithModifiers) {
43 bool has_valid_modifier = false;
44 int i;
45
46 /* It's acceptable to create an image with INVALID modifier in the list,
47 * but it cannot be on the only modifier (since it will certainly fail
48 * later). While we could easily catch this after modifier creation, doing
49 * the check here is a convenient debug check likely pointing at whatever
50 * interface the client is using to build its modifier list.
51 */
52 for (i = 0; i < modifiers_count; i++) {
53 if (modifiers[i] != DRM_FORMAT_MOD_INVALID) {
54 has_valid_modifier = true;
55 break;
56 }
57 }
58 if (!has_valid_modifier)
59 return NULL;
60
61 if (image->base.version >= 19 && image->createImageWithModifiers2)
62 return image->createImageWithModifiers2(screen, width, height,
63 dri_format, modifiers,
64 modifiers_count, dri_usage,
65 loaderPrivate);
66 else
67 return image->createImageWithModifiers(screen, width, height,
68 dri_format, modifiers,
69 modifiers_count, loaderPrivate);
70 }
71
72 /* No modifier given or fallback to the legacy createImage allowed */
73 return image->createImage(screen, width, height, dri_format, dri_usage,
74 loaderPrivate);
75 }
76
dri_vblank_mode(__DRIscreen * driScreen,const __DRI2configQueryExtension * config)77 static int dri_vblank_mode(__DRIscreen *driScreen, const __DRI2configQueryExtension *config)
78 {
79 GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
80
81 if (config)
82 config->configQueryi(driScreen, "vblank_mode", &vblank_mode);
83
84 return vblank_mode;
85 }
86
dri_get_initial_swap_interval(__DRIscreen * driScreen,const __DRI2configQueryExtension * config)87 int dri_get_initial_swap_interval(__DRIscreen *driScreen,
88 const __DRI2configQueryExtension *config)
89 {
90 int vblank_mode = dri_vblank_mode(driScreen, config);
91
92 switch (vblank_mode) {
93 case DRI_CONF_VBLANK_NEVER:
94 case DRI_CONF_VBLANK_DEF_INTERVAL_0:
95 return 0;
96 case DRI_CONF_VBLANK_DEF_INTERVAL_1:
97 case DRI_CONF_VBLANK_ALWAYS_SYNC:
98 default:
99 return 1;
100 }
101 }
102
dri_valid_swap_interval(__DRIscreen * driScreen,const __DRI2configQueryExtension * config,int interval)103 bool dri_valid_swap_interval(__DRIscreen *driScreen,
104 const __DRI2configQueryExtension *config, int interval)
105 {
106 int vblank_mode = dri_vblank_mode(driScreen, config);
107
108 switch (vblank_mode) {
109 case DRI_CONF_VBLANK_NEVER:
110 if (interval != 0)
111 return false;
112 break;
113 case DRI_CONF_VBLANK_ALWAYS_SYNC:
114 if (interval <= 0)
115 return false;
116 break;
117 default:
118 break;
119 }
120
121 return true;
122 }
123
124 /* the DRIimage createImage function takes __DRI_IMAGE_FORMAT codes, while
125 * the createImageFromFds call takes DRM_FORMAT codes. To avoid
126 * complete confusion, just deal in __DRI_IMAGE_FORMAT codes for now and
127 * translate to DRM_FORMAT codes in the call to createImageFromFds
128 */
129 int
loader_image_format_to_fourcc(int format)130 loader_image_format_to_fourcc(int format)
131 {
132
133 /* Convert from __DRI_IMAGE_FORMAT to DRM_FORMAT (sigh) */
134 switch (format) {
135 case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
136 case __DRI_IMAGE_FORMAT_SABGR8: return __DRI_IMAGE_FOURCC_SABGR8888;
137 case __DRI_IMAGE_FORMAT_SXRGB8: return __DRI_IMAGE_FOURCC_SXRGB8888;
138 case __DRI_IMAGE_FORMAT_RGB565: return DRM_FORMAT_RGB565;
139 case __DRI_IMAGE_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
140 case __DRI_IMAGE_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
141 case __DRI_IMAGE_FORMAT_ABGR8888: return DRM_FORMAT_ABGR8888;
142 case __DRI_IMAGE_FORMAT_XBGR8888: return DRM_FORMAT_XBGR8888;
143 case __DRI_IMAGE_FORMAT_XRGB2101010: return DRM_FORMAT_XRGB2101010;
144 case __DRI_IMAGE_FORMAT_ARGB2101010: return DRM_FORMAT_ARGB2101010;
145 case __DRI_IMAGE_FORMAT_XBGR2101010: return DRM_FORMAT_XBGR2101010;
146 case __DRI_IMAGE_FORMAT_ABGR2101010: return DRM_FORMAT_ABGR2101010;
147 case __DRI_IMAGE_FORMAT_ABGR16161616: return DRM_FORMAT_ABGR16161616;
148 case __DRI_IMAGE_FORMAT_XBGR16161616: return DRM_FORMAT_XBGR16161616;
149 case __DRI_IMAGE_FORMAT_XBGR16161616F: return DRM_FORMAT_XBGR16161616F;
150 case __DRI_IMAGE_FORMAT_ABGR16161616F: return DRM_FORMAT_ABGR16161616F;
151 }
152 return 0;
153 }
154
155 #ifdef HAVE_X11_PLATFORM
156 void
loader_init_screen_resources(struct loader_screen_resources * res,xcb_connection_t * conn,xcb_screen_t * screen)157 loader_init_screen_resources(struct loader_screen_resources *res,
158 xcb_connection_t *conn,
159 xcb_screen_t *screen)
160 {
161 res->conn = conn;
162 res->screen = screen;
163 res->crtcs = NULL;
164
165 mtx_init(&res->mtx, mtx_plain);
166 }
167
168 void
loader_destroy_screen_resources(struct loader_screen_resources * res)169 loader_destroy_screen_resources(struct loader_screen_resources *res)
170 {
171 mtx_destroy(&res->mtx);
172 }
173
174 static unsigned
gcd_u32(unsigned a,unsigned b)175 gcd_u32(unsigned a, unsigned b)
176 {
177 assert(a > 0 || b > 0);
178
179 while (b != 0) {
180 unsigned remainder = a % b;
181 a = b;
182 b = remainder;
183 }
184
185 return a;
186 }
187
188 static void
calculate_refresh_rate(const xcb_randr_mode_info_t * mode,unsigned * numerator,unsigned * denominator)189 calculate_refresh_rate(const xcb_randr_mode_info_t *mode,
190 unsigned *numerator, unsigned *denominator)
191 {
192 unsigned vtotal = mode->vtotal;
193
194 /* Double-scan doubles the number of lines */
195 if (mode->mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
196 vtotal *= 2;
197
198 /* Interlace splits the frame into two fields; typically the monitor
199 * reports field rate.
200 */
201 if (mode->mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
202 vtotal /= 2;
203
204 uint32_t dots = mode->htotal * vtotal;
205
206 if (dots == 0) {
207 *numerator = 0;
208 *denominator = 1;
209 } else {
210 uint32_t gcd = gcd_u32(mode->dot_clock, dots);
211
212 *numerator = mode->dot_clock / gcd;
213 *denominator = dots / gcd;
214 }
215 }
216
217 bool
loader_update_screen_resources(struct loader_screen_resources * res)218 loader_update_screen_resources(struct loader_screen_resources *res)
219 {
220 xcb_randr_get_crtc_info_cookie_t *crtc_cookies;
221
222 /* If we have cached screen resources information, check each CRTC to
223 * see if it's up to date. Ideally, we'd watch PresentConfigureNotify
224 * events on the root window to see if something changed, but those only
225 * fire if the geometry changes. It misses CRTC changes which only
226 * alter the refresh rate. We also can't watch RandR events internally
227 * because they aren't XGE events. So, we just check every CRTC for now.
228 */
229 bool config_unchanged = res->crtcs != NULL;
230
231 crtc_cookies = malloc(res->num_crtcs * sizeof(*crtc_cookies));
232
233 for (unsigned c = 0; c < res->num_crtcs; c++) {
234 crtc_cookies[c] =
235 xcb_randr_get_crtc_info_unchecked(res->conn, res->crtcs[c].id,
236 res->config_timestamp);
237 }
238
239 for (unsigned c = 0; c < res->num_crtcs; c++) {
240 xcb_randr_get_crtc_info_reply_t *reply =
241 xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
242
243 /* Although randrproto 1.4.0 says that RRGetCrtcInfo is supposed to
244 * return InvalidConfigTime if config_timestamp is out of date, the
245 * implementation in xserver as of 21.x doesn't actually do so. To
246 * detect changes in refresh rate, we check the returned timestamp
247 * on each tracked CRTC.
248 */
249 if (!reply ||
250 reply->status == XCB_RANDR_SET_CONFIG_INVALID_CONFIG_TIME ||
251 reply->timestamp != res->crtcs[c].timestamp) {
252 config_unchanged = false;
253 /* continue to consume all replies */
254 }
255
256 free(reply);
257 }
258
259 free(crtc_cookies);
260
261 if (config_unchanged)
262 return false;
263
264 /* Do RRGetScreenResourcesCurrent to query the list of CRTCs and modes,
265 * then RRGetCrtcInfo on each CRTC to determine what mode each uses, and
266 * use the mode to calculate the refresh rate.
267 */
268 mtx_lock(&res->mtx);
269
270 xcb_randr_get_screen_resources_current_cookie_t cookie =
271 xcb_randr_get_screen_resources_current_unchecked(res->conn,
272 res->screen->root);
273 xcb_randr_get_screen_resources_current_reply_t *reply =
274 xcb_randr_get_screen_resources_current_reply(res->conn, cookie, NULL);
275
276 xcb_randr_crtc_t *new_crtcs =
277 xcb_randr_get_screen_resources_current_crtcs(reply);
278
279 xcb_randr_mode_info_t *new_modes =
280 xcb_randr_get_screen_resources_current_modes(reply);
281
282 res->config_timestamp = reply->config_timestamp;
283
284 free(res->crtcs);
285 res->crtcs = calloc(reply->num_crtcs, sizeof(*res->crtcs));
286
287 crtc_cookies = malloc(reply->num_crtcs * sizeof(*crtc_cookies));
288
289 for (unsigned c = 0; c < reply->num_crtcs; c++) {
290 crtc_cookies[c] =
291 xcb_randr_get_crtc_info_unchecked(res->conn, new_crtcs[c],
292 res->config_timestamp);
293 }
294
295 unsigned i = 0;
296 for (unsigned c = 0; c < reply->num_crtcs; c++) {
297 xcb_randr_get_crtc_info_reply_t *crtc_info =
298 xcb_randr_get_crtc_info_reply(res->conn, crtc_cookies[c], NULL);
299
300 if (!crtc_info || crtc_info->mode == XCB_NONE)
301 continue;
302
303 res->crtcs[i].id = new_crtcs[c];
304 res->crtcs[i].timestamp = crtc_info->timestamp;
305 res->crtcs[i].x = crtc_info->x;
306 res->crtcs[i].y = crtc_info->y;
307 res->crtcs[i].width = crtc_info->width;
308 res->crtcs[i].height = crtc_info->height;
309
310 for (int m = 0; m < reply->num_modes; m++) {
311 if (new_modes[m].id == crtc_info->mode) {
312 calculate_refresh_rate(&new_modes[m],
313 &res->crtcs[i].refresh_numerator,
314 &res->crtcs[i].refresh_denominator);
315 break;
316 }
317 }
318
319 i++;
320 free(crtc_info);
321 }
322
323 res->num_crtcs = i;
324
325 free(crtc_cookies);
326 free(reply);
327
328 mtx_unlock(&res->mtx);
329 return true;
330 }
331 #endif
332