• 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> /* 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