1 /*
2 * Copyright © 2014 Jon Turney
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "glxclient.h"
25 #include "glx_error.h"
26 #include "dri_common.h"
27 #include "util/macros.h"
28 #include "windows/xwindowsdri.h"
29 #include "windows/windowsgl.h"
30
31 struct driwindows_display
32 {
33 __GLXDRIdisplay base;
34 int event_base;
35 };
36
37 struct driwindows_context
38 {
39 struct glx_context base;
40 windowsContext *windowsContext;
41 };
42
43 struct driwindows_config
44 {
45 struct glx_config base;
46 int pxfi;
47 };
48
49 struct driwindows_screen
50 {
51 struct glx_screen base;
52 __DRIscreen *driScreen;
53 __GLXDRIscreen vtable;
54 Bool copySubBuffer;
55 };
56
57 struct driwindows_drawable
58 {
59 __GLXDRIdrawable base;
60 windowsDrawable *windowsDrawable;
61 };
62
63 /**
64 * GLXDRI functions
65 */
66
67 static void
driwindows_destroy_context(struct glx_context * context)68 driwindows_destroy_context(struct glx_context *context)
69 {
70 struct driwindows_context *pcp = (struct driwindows_context *) context;
71
72 driReleaseDrawables(&pcp->base);
73
74 free((char *) context->extensions);
75
76 windows_destroy_context(pcp->windowsContext);
77
78 free(pcp);
79 }
80
81 static int
driwindows_bind_context(struct glx_context * context,struct glx_context * old,GLXDrawable draw,GLXDrawable read)82 driwindows_bind_context(struct glx_context *context, struct glx_context *old,
83 GLXDrawable draw, GLXDrawable read)
84 {
85 struct driwindows_context *pcp = (struct driwindows_context *) context;
86 struct driwindows_drawable *pdraw, *pread;
87
88 pdraw = (struct driwindows_drawable *) driFetchDrawable(context, draw);
89 pread = (struct driwindows_drawable *) driFetchDrawable(context, read);
90
91 driReleaseDrawables(old);
92
93 if (pdraw == NULL || pread == NULL)
94 return GLXBadDrawable;
95
96 if (windows_bind_context(pcp->windowsContext,
97 pdraw->windowsDrawable, pread->windowsDrawable))
98 return Success;
99
100 return GLXBadContext;
101 }
102
103 static void
driwindows_unbind_context(struct glx_context * context,struct glx_context * new)104 driwindows_unbind_context(struct glx_context *context, struct glx_context *new)
105 {
106 struct driwindows_context *pcp = (struct driwindows_context *) context;
107
108 windows_unbind_context(pcp->windowsContext);
109 }
110
111 static const struct glx_context_vtable driwindows_context_vtable = {
112 .destroy = driwindows_destroy_context,
113 .bind = driwindows_bind_context,
114 .unbind = driwindows_unbind_context,
115 .wait_gl = NULL,
116 .wait_x = NULL,
117 };
118
119 static struct glx_context *
driwindows_create_context(struct glx_screen * base,struct glx_config * config_base,struct glx_context * shareList,int renderType)120 driwindows_create_context(struct glx_screen *base,
121 struct glx_config *config_base,
122 struct glx_context *shareList, int renderType)
123 {
124 struct driwindows_context *pcp, *pcp_shared;
125 struct driwindows_config *config = (struct driwindows_config *) config_base;
126 struct driwindows_screen *psc = (struct driwindows_screen *) base;
127 windowsContext *shared = NULL;
128
129 if (!psc->base.driScreen)
130 return NULL;
131
132 /* Check the renderType value */
133 if (!validate_renderType_against_config(config_base, renderType))
134 return NULL;
135
136 if (shareList) {
137 /* If the shareList context is not on this renderer, we cannot possibly
138 * create a context that shares with it.
139 */
140 if (shareList->vtable->destroy != driwindows_destroy_context) {
141 return NULL;
142 }
143
144 pcp_shared = (struct driwindows_context *) shareList;
145 shared = pcp_shared->windowsContext;
146 }
147
148 pcp = calloc(1, sizeof *pcp);
149 if (pcp == NULL)
150 return NULL;
151
152 if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
153 free(pcp);
154 return NULL;
155 }
156
157 pcp->base.renderType = renderType;
158
159 InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
160
161 pcp->windowsContext = windows_create_context(config->pxfi, shared);
162
163 if (!pcp->windowsContext) {
164 free(pcp);
165 return NULL;
166 }
167
168 pcp->base.vtable = &driwindows_context_vtable;
169
170 return &pcp->base;
171 }
172
173 static struct glx_context *
driwindows_create_context_attribs(struct glx_screen * base,struct glx_config * config_base,struct glx_context * shareList,unsigned num_attribs,const uint32_t * attribs,unsigned * error)174 driwindows_create_context_attribs(struct glx_screen *base,
175 struct glx_config *config_base,
176 struct glx_context *shareList,
177 unsigned num_attribs,
178 const uint32_t *attribs,
179 unsigned *error)
180 {
181 struct driwindows_context *pcp, *pcp_shared;
182 struct driwindows_config *config = (struct driwindows_config *) config_base;
183 struct driwindows_screen *psc = (struct driwindows_screen *) base;
184 windowsContext *shared = NULL;
185
186 int i;
187 uint32_t renderType = GLX_RGBA_TYPE;
188
189 /* Extract renderType from attribs */
190 for (i = 0; i < num_attribs; i++) {
191 switch (attribs[i * 2]) {
192 case GLX_RENDER_TYPE:
193 renderType = attribs[i * 2 + 1];
194 break;
195 }
196 }
197
198 /*
199 Perhaps we should map GLX tokens to WGL tokens, but they appear to have
200 identical values, so far
201 */
202
203 if (!psc->base.driScreen || !config_base)
204 return NULL;
205
206 /* Check the renderType value */
207 if (!validate_renderType_against_config(config_base, renderType)) {
208 return NULL;
209 }
210
211 if (shareList) {
212 /* If the shareList context is not on this renderer, we cannot possibly
213 * create a context that shares with it.
214 */
215 if (shareList->vtable->destroy != driwindows_destroy_context) {
216 return NULL;
217 }
218
219 pcp_shared = (struct driwindows_context *) shareList;
220 shared = pcp_shared->windowsContext;
221 }
222
223 pcp = calloc(1, sizeof *pcp);
224 if (pcp == NULL)
225 return NULL;
226
227 if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
228 free(pcp);
229 return NULL;
230 }
231
232 pcp->base.renderType = renderType;
233
234 InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
235
236 pcp->windowsContext = windows_create_context_attribs(config->pxfi,
237 shared,
238 (const int *)attribs);
239 if (pcp->windowsContext == NULL) {
240 free(pcp);
241 return NULL;
242 }
243
244 pcp->base.vtable = &driwindows_context_vtable;
245
246 return &pcp->base;
247 }
248
249 static void
driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)250 driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)
251 {
252 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
253
254 windows_destroy_drawable(pdp->windowsDrawable);
255
256 free(pdp);
257 }
258
259 static __GLXDRIdrawable *
driwindowsCreateDrawable(struct glx_screen * base,XID xDrawable,GLXDrawable drawable,int type,struct glx_config * modes)260 driwindowsCreateDrawable(struct glx_screen *base, XID xDrawable,
261 GLXDrawable drawable, int type,
262 struct glx_config *modes)
263 {
264 struct driwindows_drawable *pdp;
265 struct driwindows_screen *psc = (struct driwindows_screen *) base;
266
267 pdp = calloc(1, sizeof(*pdp));
268 if (!pdp)
269 return NULL;
270
271 pdp->base.xDrawable = xDrawable;
272 pdp->base.drawable = drawable;
273 pdp->base.psc = &psc->base;
274
275 /*
276 By this stage, the X drawable already exists, but the GLX drawable may
277 not.
278
279 Query the server with the XID to find the correct HWND, HPBUFFERARB or
280 HBITMAP
281 */
282
283 unsigned int type;
284 void *handle;
285
286 if (!XWindowsDRIQueryDrawable(psc->base.dpy, base->scr, drawable, &type, &handle))
287 {
288 free(pdp);
289 return NULL;
290 }
291
292 /* No handle found is a failure */
293 if (!handle) {
294 free(pdp);
295 return NULL;
296 }
297
298 /* Create a new drawable */
299 pdp->windowsDrawable = windows_create_drawable(type, handle);
300
301 if (!pdp->windowsDrawable) {
302 free(pdp);
303 return NULL;
304 }
305
306 pdp->base.destroyDrawable = driwindowsDestroyDrawable;
307
308 return &pdp->base;
309 }
310
311 static int64_t
driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,int64_t target_msc,int64_t divisor,int64_t remainder,Bool flush)312 driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,
313 int64_t target_msc, int64_t divisor, int64_t remainder,
314 Bool flush)
315 {
316 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
317
318 (void) target_msc;
319 (void) divisor;
320 (void) remainder;
321
322 if (flush) {
323 glFlush();
324 }
325
326 windows_swap_buffers(pdp->windowsDrawable);
327
328 return 0;
329 }
330
331 static void
driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,int x,int y,int width,int height,Bool flush)332 driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,
333 int x, int y, int width, int height, Bool flush)
334 {
335 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
336
337 if (flush) {
338 glFlush();
339 }
340
341 windows_copy_subbuffer(pdp->windowsDrawable, x, y, width, height);
342 }
343
344 static void
driwindowsDestroyScreen(struct glx_screen * base)345 driwindowsDestroyScreen(struct glx_screen *base)
346 {
347 struct driwindows_screen *psc = (struct driwindows_screen *) base;
348
349 /* Free the direct rendering per screen data */
350 psc->driScreen = NULL;
351 free(psc);
352 }
353
354 static const struct glx_screen_vtable driwindows_screen_vtable = {
355 .create_context = driwindows_create_context,
356 .create_context_attribs = driwindows_create_context_attribs,
357 .query_renderer_integer = NULL,
358 .query_renderer_string = NULL,
359 };
360
361 static Bool
driwindowsBindExtensions(struct driwindows_screen * psc)362 driwindowsBindExtensions(struct driwindows_screen *psc)
363 {
364 Bool result = 1;
365
366 const struct
367 {
368 char *wglext;
369 char *glxext;
370 Bool mandatory;
371 } extensionMap[] = {
372 { "WGL_ARB_make_current_read", "GLX_SGI_make_current_read", 0 },
373 { "WGL_EXT_swap_control", "GLX_SGI_swap_control", 0 },
374 { "WGL_EXT_swap_control", "GLX_MESA_swap_control", 0 },
375 // { "WGL_ARB_render_texture", "GLX_EXT_texture_from_pixmap", 0 },
376 // Not exactly equivalent, needs some more glue to be written
377 { "WGL_ARB_pbuffer", "GLX_SGIX_pbuffer", 1 },
378 { "WGL_ARB_multisample", "GLX_ARB_multisample", 1 },
379 { "WGL_ARB_multisample", "GLX_SGIS_multisample", 1 },
380 { "WGL_ARB_create_context", "GLX_ARB_create_context", 0 },
381 { "WGL_ARB_create_context_profile", "GLX_ARB_create_context_profile", 0 },
382 { "WGL_ARB_create_context_robustness", "GLX_ARB_create_context_robustness", 0 },
383 { "WGL_EXT_create_context_es2_profile", "GLX_EXT_create_context_es2_profile", 0 },
384 };
385
386 char *wgl_extensions;
387 char *gl_extensions;
388 int i;
389
390 windows_extensions(&gl_extensions, &wgl_extensions);
391
392 for (i = 0; i < ARRAY_SIZE(extensionMap); i++) {
393 if (strstr(wgl_extensions, extensionMap[i].wglext)) {
394 __glXEnableDirectExtension(&psc->base, extensionMap[i].glxext);
395 InfoMessageF("enabled %s\n", extensionMap[i].glxext);
396 }
397 else if (extensionMap[i].mandatory) {
398 ErrorMessageF("required WGL extension %s is missing\n", extensionMap[i].wglext);
399 result = 0;
400 }
401 }
402
403 /*
404 Because it pre-dates WGL_EXT_extensions_string, GL_WIN_swap_hint might
405 only be in GL_EXTENSIONS
406 */
407 if (strstr(gl_extensions, "GL_WIN_swap_hint")) {
408 psc->copySubBuffer = 1;
409 __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
410 InfoMessageF("enabled GLX_MESA_copy_sub_buffer\n");
411 }
412
413 free(gl_extensions);
414 free(wgl_extensions);
415
416 return result;
417 }
418
419 static struct glx_config *
driwindowsMapConfigs(struct glx_display * priv,int screen,struct glx_config * configs,struct glx_config * fbconfigs)420 driwindowsMapConfigs(struct glx_display *priv, int screen, struct glx_config *configs, struct glx_config *fbconfigs)
421 {
422 struct glx_config head, *tail, *m;
423
424 tail = &head;
425 head.next = NULL;
426
427 for (m = configs; m; m = m->next) {
428 int fbconfigID = GLX_DONT_CARE;
429 if (fbconfigs) {
430 /*
431 visuals have fbconfigID of GLX_DONT_CARE, so search for a fbconfig
432 with matching visualID and get the fbconfigID from there
433 */
434 struct glx_config *f;
435 for (f = fbconfigs; f; f = f->next) {
436 if (f->visualID == m->visualID)
437 fbconfigID = f->fbconfigID;
438 }
439 }
440 else {
441 fbconfigID = m->fbconfigID;
442 }
443
444 int pxfi;
445 XWindowsDRIFBConfigToPixelFormat(priv->dpy, screen, fbconfigID, &pxfi);
446 if (pxfi == 0)
447 continue;
448
449 struct driwindows_config *config = malloc(sizeof(*config));
450
451 tail->next = &config->base;
452 if (tail->next == NULL)
453 continue;
454
455 config->base = *m;
456 config->pxfi = pxfi;
457
458 tail = tail->next;
459 }
460
461 return head.next;
462 }
463
464 static struct glx_screen *
driwindowsCreateScreen(int screen,struct glx_display * priv)465 driwindowsCreateScreen(int screen, struct glx_display *priv)
466 {
467 __GLXDRIscreen *psp;
468 struct driwindows_screen *psc;
469 struct glx_config *configs = NULL, *visuals = NULL;
470 int directCapable;
471
472 psc = calloc(1, sizeof *psc);
473 if (psc == NULL)
474 return NULL;
475
476 if (!glx_screen_init(&psc->base, screen, priv)) {
477 free(psc);
478 return NULL;
479 }
480
481 if (!XWindowsDRIQueryDirectRenderingCapable(psc->base.dpy, screen, &directCapable) ||
482 !directCapable) {
483 ErrorMessageF("Screen is not Windows-DRI capable\n");
484 goto handle_error;
485 }
486
487 /* discover native supported extensions */
488 if (!driwindowsBindExtensions(psc)) {
489 goto handle_error;
490 }
491
492 /* Augment configs with pxfi information */
493 configs = driwindowsMapConfigs(priv, screen, psc->base.configs, NULL);
494 visuals = driwindowsMapConfigs(priv, screen, psc->base.visuals, configs);
495
496 if (!configs || !visuals) {
497 ErrorMessageF("No fbConfigs or visuals found\n");
498 goto handle_error;
499 }
500
501 glx_config_destroy_list(psc->base.configs);
502 psc->base.configs = configs;
503 glx_config_destroy_list(psc->base.visuals);
504 psc->base.visuals = visuals;
505
506 psc->base.vtable = &driwindows_screen_vtable;
507 psp = &psc->vtable;
508 psc->base.driScreen = psp;
509 psp->destroyScreen = driwindowsDestroyScreen;
510 psp->createDrawable = driwindowsCreateDrawable;
511 psp->swapBuffers = driwindowsSwapBuffers;
512
513 if (psc->copySubBuffer)
514 psp->copySubBuffer = driwindowsCopySubBuffer;
515
516 return &psc->base;
517
518 handle_error:
519 glx_screen_cleanup(&psc->base);
520
521 return NULL;
522 }
523
524 /* Called from __glXFreeDisplayPrivate.
525 */
526 static void
driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)527 driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)
528 {
529 free(dpy);
530 }
531
532 /*
533 * Allocate, initialize and return a __GLXDRIdisplay object.
534 * This is called from __glXInitialize() when we are given a new
535 * display pointer.
536 */
537 _X_HIDDEN __GLXDRIdisplay *
driwindowsCreateDisplay(Display * dpy)538 driwindowsCreateDisplay(Display * dpy)
539 {
540 struct driwindows_display *pdpyp;
541
542 int eventBase, errorBase;
543 int major, minor, patch;
544
545 /* Verify server has Windows-DRI extension */
546 if (!XWindowsDRIQueryExtension(dpy, &eventBase, &errorBase)) {
547 ErrorMessageF("Windows-DRI extension not available\n");
548 return NULL;
549 }
550
551 if (!XWindowsDRIQueryVersion(dpy, &major, &minor, &patch)) {
552 ErrorMessageF("Fetching Windows-DRI extension version failed\n");
553 return NULL;
554 }
555
556 if (!windows_check_renderer()) {
557 ErrorMessageF("Windows-DRI extension disabled for GDI Generic renderer\n");
558 return NULL;
559 }
560
561 pdpyp = malloc(sizeof *pdpyp);
562 if (pdpyp == NULL)
563 return NULL;
564
565 pdpyp->base.destroyDisplay = driwindowsDestroyDisplay;
566 pdpyp->base.createScreen = driwindowsCreateScreen;
567
568 pdpyp->event_base = eventBase;
569
570 return &pdpyp->base;
571 }
572