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(&pcp->base);
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 void
driwindows_bind_tex_image(Display * dpy,GLXDrawable drawable,int buffer,const int * attrib_list)112 driwindows_bind_tex_image(Display * dpy,
113 GLXDrawable drawable,
114 int buffer, const int *attrib_list)
115 {
116 struct glx_context *gc = __glXGetCurrentContext();
117 struct driwindows_context *pcp = (struct driwindows_context *) gc;
118 __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
119 struct driwindows_drawable *pdraw = (struct driwindows_drawable *) base;
120
121 __glXInitialize(dpy);
122
123 if (pdraw != NULL) {
124 windows_setTexBuffer(pcp->windowsContext,
125 pdraw->base.textureTarget,
126 pdraw->base.textureFormat,
127 pdraw->windowsDrawable);
128 }
129 }
130
131 static void
driwindows_release_tex_image(Display * dpy,GLXDrawable drawable,int buffer)132 driwindows_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
133 {
134 struct glx_context *gc = __glXGetCurrentContext();
135 struct driwindows_context *pcp = (struct driwindows_context *) gc;
136 __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
137 struct glx_display *dpyPriv = __glXInitialize(dpy);
138 struct driwindows_drawable *pdraw = (struct driwindows_drawable *) base;
139
140 if (dpyPriv != NULL && pdraw != NULL) {
141 windows_releaseTexBuffer(pcp->windowsContext,
142 pdraw->base.textureTarget,
143 pdraw->windowsDrawable);
144 }
145 }
146
147 static const struct glx_context_vtable driwindows_context_vtable = {
148 .destroy = driwindows_destroy_context,
149 .bind = driwindows_bind_context,
150 .unbind = driwindows_unbind_context,
151 .wait_gl = NULL,
152 .wait_x = NULL,
153 .use_x_font = DRI_glXUseXFont,
154 .bind_tex_image = driwindows_bind_tex_image,
155 .release_tex_image = driwindows_release_tex_image,
156 .get_proc_address = NULL,
157 };
158
159 static struct glx_context *
driwindows_create_context(struct glx_screen * base,struct glx_config * config_base,struct glx_context * shareList,int renderType)160 driwindows_create_context(struct glx_screen *base,
161 struct glx_config *config_base,
162 struct glx_context *shareList, int renderType)
163 {
164 struct driwindows_context *pcp, *pcp_shared;
165 struct driwindows_config *config = (struct driwindows_config *) config_base;
166 struct driwindows_screen *psc = (struct driwindows_screen *) base;
167 windowsContext *shared = NULL;
168
169 if (!psc->base.driScreen)
170 return NULL;
171
172 /* Check the renderType value */
173 if (!validate_renderType_against_config(config_base, renderType))
174 return NULL;
175
176 if (shareList) {
177 /* If the shareList context is not on this renderer, we cannot possibly
178 * create a context that shares with it.
179 */
180 if (shareList->vtable->destroy != driwindows_destroy_context) {
181 return NULL;
182 }
183
184 pcp_shared = (struct driwindows_context *) shareList;
185 shared = pcp_shared->windowsContext;
186 }
187
188 pcp = calloc(1, sizeof *pcp);
189 if (pcp == NULL)
190 return NULL;
191
192 if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
193 free(pcp);
194 return NULL;
195 }
196
197 pcp->base.renderType = renderType;
198
199 InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
200
201 pcp->windowsContext = windows_create_context(config->pxfi, shared);
202
203 if (!pcp->windowsContext) {
204 free(pcp);
205 return NULL;
206 }
207
208 pcp->base.vtable = &driwindows_context_vtable;
209
210 return &pcp->base;
211 }
212
213 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)214 driwindows_create_context_attribs(struct glx_screen *base,
215 struct glx_config *config_base,
216 struct glx_context *shareList,
217 unsigned num_attribs,
218 const uint32_t *attribs,
219 unsigned *error)
220 {
221 struct driwindows_context *pcp, *pcp_shared;
222 struct driwindows_config *config = (struct driwindows_config *) config_base;
223 struct driwindows_screen *psc = (struct driwindows_screen *) base;
224 windowsContext *shared = NULL;
225
226 int i;
227 uint32_t renderType = GLX_RGBA_TYPE;
228
229 /* Extract renderType from attribs */
230 for (i = 0; i < num_attribs; i++) {
231 switch (attribs[i * 2]) {
232 case GLX_RENDER_TYPE:
233 renderType = attribs[i * 2 + 1];
234 break;
235 }
236 }
237
238 /*
239 Perhaps we should map GLX tokens to WGL tokens, but they appear to have
240 identical values, so far
241 */
242
243 if (!psc->base.driScreen)
244 return NULL;
245
246 /* Check the renderType value */
247 if (!validate_renderType_against_config(config_base, renderType)) {
248 return NULL;
249 }
250
251 if (shareList) {
252 /* If the shareList context is not on this renderer, we cannot possibly
253 * create a context that shares with it.
254 */
255 if (shareList->vtable->destroy != driwindows_destroy_context) {
256 return NULL;
257 }
258
259 pcp_shared = (struct driwindows_context *) shareList;
260 shared = pcp_shared->windowsContext;
261 }
262
263 pcp = calloc(1, sizeof *pcp);
264 if (pcp == NULL)
265 return NULL;
266
267 if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
268 free(pcp);
269 return NULL;
270 }
271
272 pcp->base.renderType = renderType;
273
274 InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
275
276 pcp->windowsContext = windows_create_context_attribs(config->pxfi,
277 shared,
278 (const int *)attribs);
279 if (pcp->windowsContext == NULL) {
280 free(pcp);
281 return NULL;
282 }
283
284 pcp->base.vtable = &driwindows_context_vtable;
285
286 return &pcp->base;
287 }
288
289 static void
driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)290 driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)
291 {
292 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
293
294 windows_destroy_drawable(pdp->windowsDrawable);
295
296 free(pdp);
297 }
298
299 static __GLXDRIdrawable *
driwindowsCreateDrawable(struct glx_screen * base,XID xDrawable,GLXDrawable drawable,struct glx_config * modes)300 driwindowsCreateDrawable(struct glx_screen *base, XID xDrawable,
301 GLXDrawable drawable, struct glx_config *modes)
302 {
303 struct driwindows_drawable *pdp;
304 struct driwindows_screen *psc = (struct driwindows_screen *) base;
305
306 pdp = calloc(1, sizeof(*pdp));
307 if (!pdp)
308 return NULL;
309
310 pdp->base.xDrawable = xDrawable;
311 pdp->base.drawable = drawable;
312 pdp->base.psc = &psc->base;
313
314 /*
315 By this stage, the X drawable already exists, but the GLX drawable may
316 not.
317
318 Query the server with the XID to find the correct HWND, HPBUFFERARB or
319 HBITMAP
320 */
321
322 unsigned int type;
323 void *handle;
324
325 if (!XWindowsDRIQueryDrawable(psc->base.dpy, base->scr, drawable, &type, &handle))
326 {
327 free(pdp);
328 return NULL;
329 }
330
331 /* No handle found is a failure */
332 if (!handle) {
333 free(pdp);
334 return NULL;
335 }
336
337 /* Create a new drawable */
338 pdp->windowsDrawable = windows_create_drawable(type, handle);
339
340 if (!pdp->windowsDrawable) {
341 free(pdp);
342 return NULL;
343 }
344
345 pdp->base.destroyDrawable = driwindowsDestroyDrawable;
346
347 return &pdp->base;
348 }
349
350 static int64_t
driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,int64_t target_msc,int64_t divisor,int64_t remainder,Bool flush)351 driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,
352 int64_t target_msc, int64_t divisor, int64_t remainder,
353 Bool flush)
354 {
355 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
356
357 (void) target_msc;
358 (void) divisor;
359 (void) remainder;
360
361 if (flush) {
362 glFlush();
363 }
364
365 windows_swap_buffers(pdp->windowsDrawable);
366
367 return 0;
368 }
369
370 static void
driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,int x,int y,int width,int height,Bool flush)371 driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,
372 int x, int y, int width, int height, Bool flush)
373 {
374 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
375
376 if (flush) {
377 glFlush();
378 }
379
380 windows_copy_subbuffer(pdp->windowsDrawable, x, y, width, height);
381 }
382
383 static void
driwindowsDestroyScreen(struct glx_screen * base)384 driwindowsDestroyScreen(struct glx_screen *base)
385 {
386 struct driwindows_screen *psc = (struct driwindows_screen *) base;
387
388 /* Free the direct rendering per screen data */
389 psc->driScreen = NULL;
390 free(psc);
391 }
392
393 static const struct glx_screen_vtable driwindows_screen_vtable = {
394 .create_context = driwindows_create_context,
395 .create_context_attribs = driwindows_create_context_attribs,
396 .query_renderer_integer = NULL,
397 .query_renderer_string = NULL,
398 };
399
400 static Bool
driwindowsBindExtensions(struct driwindows_screen * psc)401 driwindowsBindExtensions(struct driwindows_screen *psc)
402 {
403 Bool result = 1;
404
405 const struct
406 {
407 char *wglext;
408 char *glxext;
409 Bool mandatory;
410 } extensionMap[] = {
411 { "WGL_ARB_make_current_read", "GLX_SGI_make_current_read", 0 },
412 { "WGL_EXT_swap_control", "GLX_SGI_swap_control", 0 },
413 { "WGL_EXT_swap_control", "GLX_MESA_swap_control", 0 },
414 // { "WGL_ARB_render_texture", "GLX_EXT_texture_from_pixmap", 0 },
415 // Not exactly equivalent, needs some more glue to be written
416 { "WGL_ARB_pbuffer", "GLX_SGIX_pbuffer", 1 },
417 { "WGL_ARB_multisample", "GLX_ARB_multisample", 1 },
418 { "WGL_ARB_multisample", "GLX_SGIS_multisample", 1 },
419 { "WGL_ARB_create_context", "GLX_ARB_create_context", 0 },
420 { "WGL_ARB_create_context_profile", "GLX_ARB_create_context_profile", 0 },
421 { "WGL_ARB_create_context_robustness", "GLX_ARB_create_context_robustness", 0 },
422 { "WGL_EXT_create_context_es2_profile", "GLX_EXT_create_context_es2_profile", 0 },
423 };
424
425 char *wgl_extensions;
426 char *gl_extensions;
427 int i;
428
429 windows_extensions(&gl_extensions, &wgl_extensions);
430
431 for (i = 0; i < ARRAY_SIZE(extensionMap); i++) {
432 if (strstr(wgl_extensions, extensionMap[i].wglext)) {
433 __glXEnableDirectExtension(&psc->base, extensionMap[i].glxext);
434 InfoMessageF("enabled %s\n", extensionMap[i].glxext);
435 }
436 else if (extensionMap[i].mandatory) {
437 ErrorMessageF("required WGL extension %s is missing\n", extensionMap[i].wglext);
438 result = 0;
439 }
440 }
441
442 /*
443 Because it pre-dates WGL_EXT_extensions_string, GL_WIN_swap_hint might
444 only be in GL_EXTENSIONS
445 */
446 if (strstr(gl_extensions, "GL_WIN_swap_hint")) {
447 psc->copySubBuffer = 1;
448 __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
449 InfoMessageF("enabled GLX_MESA_copy_sub_buffer\n");
450 }
451
452 free(gl_extensions);
453 free(wgl_extensions);
454
455 return result;
456 }
457
458 static struct glx_config *
driwindowsMapConfigs(struct glx_display * priv,int screen,struct glx_config * configs,struct glx_config * fbconfigs)459 driwindowsMapConfigs(struct glx_display *priv, int screen, struct glx_config *configs, struct glx_config *fbconfigs)
460 {
461 struct glx_config head, *tail, *m;
462
463 tail = &head;
464 head.next = NULL;
465
466 for (m = configs; m; m = m->next) {
467 int fbconfigID = GLX_DONT_CARE;
468 if (fbconfigs) {
469 /*
470 visuals have fbconfigID of GLX_DONT_CARE, so search for a fbconfig
471 with matching visualID and get the fbconfigID from there
472 */
473 struct glx_config *f;
474 for (f = fbconfigs; f; f = f->next) {
475 if (f->visualID == m->visualID)
476 fbconfigID = f->fbconfigID;
477 }
478 }
479 else {
480 fbconfigID = m->fbconfigID;
481 }
482
483 int pxfi;
484 XWindowsDRIFBConfigToPixelFormat(priv->dpy, screen, fbconfigID, &pxfi);
485 if (pxfi == 0)
486 continue;
487
488 struct driwindows_config *config = malloc(sizeof(*config));
489
490 tail->next = &config->base;
491 if (tail->next == NULL)
492 continue;
493
494 config->base = *m;
495 config->pxfi = pxfi;
496
497 tail = tail->next;
498 }
499
500 return head.next;
501 }
502
503 static struct glx_screen *
driwindowsCreateScreen(int screen,struct glx_display * priv)504 driwindowsCreateScreen(int screen, struct glx_display *priv)
505 {
506 __GLXDRIscreen *psp;
507 struct driwindows_screen *psc;
508 struct glx_config *configs = NULL, *visuals = NULL;
509 int directCapable;
510
511 psc = calloc(1, sizeof *psc);
512 if (psc == NULL)
513 return NULL;
514
515 if (!glx_screen_init(&psc->base, screen, priv)) {
516 free(psc);
517 return NULL;
518 }
519
520 if (!XWindowsDRIQueryDirectRenderingCapable(psc->base.dpy, screen, &directCapable) ||
521 !directCapable) {
522 ErrorMessageF("Screen is not Windows-DRI capable\n");
523 goto handle_error;
524 }
525
526 /* discover native supported extensions */
527 if (!driwindowsBindExtensions(psc)) {
528 goto handle_error;
529 }
530
531 /* Augment configs with pxfi information */
532 configs = driwindowsMapConfigs(priv, screen, psc->base.configs, NULL);
533 visuals = driwindowsMapConfigs(priv, screen, psc->base.visuals, configs);
534
535 if (!configs || !visuals) {
536 ErrorMessageF("No fbConfigs or visuals found\n");
537 goto handle_error;
538 }
539
540 glx_config_destroy_list(psc->base.configs);
541 psc->base.configs = configs;
542 glx_config_destroy_list(psc->base.visuals);
543 psc->base.visuals = visuals;
544
545 psc->base.vtable = &driwindows_screen_vtable;
546 psp = &psc->vtable;
547 psc->base.driScreen = psp;
548 psp->destroyScreen = driwindowsDestroyScreen;
549 psp->createDrawable = driwindowsCreateDrawable;
550 psp->swapBuffers = driwindowsSwapBuffers;
551
552 if (psc->copySubBuffer)
553 psp->copySubBuffer = driwindowsCopySubBuffer;
554
555 return &psc->base;
556
557 handle_error:
558 glx_screen_cleanup(&psc->base);
559
560 return NULL;
561 }
562
563 /* Called from __glXFreeDisplayPrivate.
564 */
565 static void
driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)566 driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)
567 {
568 free(dpy);
569 }
570
571 /*
572 * Allocate, initialize and return a __GLXDRIdisplay object.
573 * This is called from __glXInitialize() when we are given a new
574 * display pointer.
575 */
576 _X_HIDDEN __GLXDRIdisplay *
driwindowsCreateDisplay(Display * dpy)577 driwindowsCreateDisplay(Display * dpy)
578 {
579 struct driwindows_display *pdpyp;
580
581 int eventBase, errorBase;
582 int major, minor, patch;
583
584 /* Verify server has Windows-DRI extension */
585 if (!XWindowsDRIQueryExtension(dpy, &eventBase, &errorBase)) {
586 ErrorMessageF("Windows-DRI extension not available\n");
587 return NULL;
588 }
589
590 if (!XWindowsDRIQueryVersion(dpy, &major, &minor, &patch)) {
591 ErrorMessageF("Fetching Windows-DRI extension version failed\n");
592 return NULL;
593 }
594
595 if (!windows_check_renderer()) {
596 ErrorMessageF("Windows-DRI extension disabled for GDI Generic renderer\n");
597 return NULL;
598 }
599
600 pdpyp = malloc(sizeof *pdpyp);
601 if (pdpyp == NULL)
602 return NULL;
603
604 pdpyp->base.destroyDisplay = driwindowsDestroyDisplay;
605 pdpyp->base.createScreen = driwindowsCreateScreen;
606
607 pdpyp->event_base = eventBase;
608
609 return &pdpyp->base;
610 }
611