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 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,struct glx_config * modes)260 driwindowsCreateDrawable(struct glx_screen *base, XID xDrawable,
261 GLXDrawable drawable, struct glx_config *modes)
262 {
263 struct driwindows_drawable *pdp;
264 struct driwindows_screen *psc = (struct driwindows_screen *) base;
265
266 pdp = calloc(1, sizeof(*pdp));
267 if (!pdp)
268 return NULL;
269
270 pdp->base.xDrawable = xDrawable;
271 pdp->base.drawable = drawable;
272 pdp->base.psc = &psc->base;
273
274 /*
275 By this stage, the X drawable already exists, but the GLX drawable may
276 not.
277
278 Query the server with the XID to find the correct HWND, HPBUFFERARB or
279 HBITMAP
280 */
281
282 unsigned int type;
283 void *handle;
284
285 if (!XWindowsDRIQueryDrawable(psc->base.dpy, base->scr, drawable, &type, &handle))
286 {
287 free(pdp);
288 return NULL;
289 }
290
291 /* No handle found is a failure */
292 if (!handle) {
293 free(pdp);
294 return NULL;
295 }
296
297 /* Create a new drawable */
298 pdp->windowsDrawable = windows_create_drawable(type, handle);
299
300 if (!pdp->windowsDrawable) {
301 free(pdp);
302 return NULL;
303 }
304
305 pdp->base.destroyDrawable = driwindowsDestroyDrawable;
306
307 return &pdp->base;
308 }
309
310 static int64_t
driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,int64_t target_msc,int64_t divisor,int64_t remainder,Bool flush)311 driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,
312 int64_t target_msc, int64_t divisor, int64_t remainder,
313 Bool flush)
314 {
315 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
316
317 (void) target_msc;
318 (void) divisor;
319 (void) remainder;
320
321 if (flush) {
322 glFlush();
323 }
324
325 windows_swap_buffers(pdp->windowsDrawable);
326
327 return 0;
328 }
329
330 static void
driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,int x,int y,int width,int height,Bool flush)331 driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,
332 int x, int y, int width, int height, Bool flush)
333 {
334 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
335
336 if (flush) {
337 glFlush();
338 }
339
340 windows_copy_subbuffer(pdp->windowsDrawable, x, y, width, height);
341 }
342
343 static void
driwindowsDestroyScreen(struct glx_screen * base)344 driwindowsDestroyScreen(struct glx_screen *base)
345 {
346 struct driwindows_screen *psc = (struct driwindows_screen *) base;
347
348 /* Free the direct rendering per screen data */
349 psc->driScreen = NULL;
350 free(psc);
351 }
352
353 static const struct glx_screen_vtable driwindows_screen_vtable = {
354 .create_context = driwindows_create_context,
355 .create_context_attribs = driwindows_create_context_attribs,
356 .query_renderer_integer = NULL,
357 .query_renderer_string = NULL,
358 };
359
360 static Bool
driwindowsBindExtensions(struct driwindows_screen * psc)361 driwindowsBindExtensions(struct driwindows_screen *psc)
362 {
363 Bool result = 1;
364
365 const struct
366 {
367 char *wglext;
368 char *glxext;
369 Bool mandatory;
370 } extensionMap[] = {
371 { "WGL_ARB_make_current_read", "GLX_SGI_make_current_read", 0 },
372 { "WGL_EXT_swap_control", "GLX_SGI_swap_control", 0 },
373 { "WGL_EXT_swap_control", "GLX_MESA_swap_control", 0 },
374 // { "WGL_ARB_render_texture", "GLX_EXT_texture_from_pixmap", 0 },
375 // Not exactly equivalent, needs some more glue to be written
376 { "WGL_ARB_pbuffer", "GLX_SGIX_pbuffer", 1 },
377 { "WGL_ARB_multisample", "GLX_ARB_multisample", 1 },
378 { "WGL_ARB_multisample", "GLX_SGIS_multisample", 1 },
379 { "WGL_ARB_create_context", "GLX_ARB_create_context", 0 },
380 { "WGL_ARB_create_context_profile", "GLX_ARB_create_context_profile", 0 },
381 { "WGL_ARB_create_context_robustness", "GLX_ARB_create_context_robustness", 0 },
382 { "WGL_EXT_create_context_es2_profile", "GLX_EXT_create_context_es2_profile", 0 },
383 };
384
385 char *wgl_extensions;
386 char *gl_extensions;
387 int i;
388
389 windows_extensions(&gl_extensions, &wgl_extensions);
390
391 for (i = 0; i < ARRAY_SIZE(extensionMap); i++) {
392 if (strstr(wgl_extensions, extensionMap[i].wglext)) {
393 __glXEnableDirectExtension(&psc->base, extensionMap[i].glxext);
394 InfoMessageF("enabled %s\n", extensionMap[i].glxext);
395 }
396 else if (extensionMap[i].mandatory) {
397 ErrorMessageF("required WGL extension %s is missing\n", extensionMap[i].wglext);
398 result = 0;
399 }
400 }
401
402 /*
403 Because it pre-dates WGL_EXT_extensions_string, GL_WIN_swap_hint might
404 only be in GL_EXTENSIONS
405 */
406 if (strstr(gl_extensions, "GL_WIN_swap_hint")) {
407 psc->copySubBuffer = 1;
408 __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
409 InfoMessageF("enabled GLX_MESA_copy_sub_buffer\n");
410 }
411
412 free(gl_extensions);
413 free(wgl_extensions);
414
415 return result;
416 }
417
418 static struct glx_config *
driwindowsMapConfigs(struct glx_display * priv,int screen,struct glx_config * configs,struct glx_config * fbconfigs)419 driwindowsMapConfigs(struct glx_display *priv, int screen, struct glx_config *configs, struct glx_config *fbconfigs)
420 {
421 struct glx_config head, *tail, *m;
422
423 tail = &head;
424 head.next = NULL;
425
426 for (m = configs; m; m = m->next) {
427 int fbconfigID = GLX_DONT_CARE;
428 if (fbconfigs) {
429 /*
430 visuals have fbconfigID of GLX_DONT_CARE, so search for a fbconfig
431 with matching visualID and get the fbconfigID from there
432 */
433 struct glx_config *f;
434 for (f = fbconfigs; f; f = f->next) {
435 if (f->visualID == m->visualID)
436 fbconfigID = f->fbconfigID;
437 }
438 }
439 else {
440 fbconfigID = m->fbconfigID;
441 }
442
443 int pxfi;
444 XWindowsDRIFBConfigToPixelFormat(priv->dpy, screen, fbconfigID, &pxfi);
445 if (pxfi == 0)
446 continue;
447
448 struct driwindows_config *config = malloc(sizeof(*config));
449
450 tail->next = &config->base;
451 if (tail->next == NULL)
452 continue;
453
454 config->base = *m;
455 config->pxfi = pxfi;
456
457 tail = tail->next;
458 }
459
460 return head.next;
461 }
462
463 static struct glx_screen *
driwindowsCreateScreen(int screen,struct glx_display * priv)464 driwindowsCreateScreen(int screen, struct glx_display *priv)
465 {
466 __GLXDRIscreen *psp;
467 struct driwindows_screen *psc;
468 struct glx_config *configs = NULL, *visuals = NULL;
469 int directCapable;
470
471 psc = calloc(1, sizeof *psc);
472 if (psc == NULL)
473 return NULL;
474
475 if (!glx_screen_init(&psc->base, screen, priv)) {
476 free(psc);
477 return NULL;
478 }
479
480 if (!XWindowsDRIQueryDirectRenderingCapable(psc->base.dpy, screen, &directCapable) ||
481 !directCapable) {
482 ErrorMessageF("Screen is not Windows-DRI capable\n");
483 goto handle_error;
484 }
485
486 /* discover native supported extensions */
487 if (!driwindowsBindExtensions(psc)) {
488 goto handle_error;
489 }
490
491 /* Augment configs with pxfi information */
492 configs = driwindowsMapConfigs(priv, screen, psc->base.configs, NULL);
493 visuals = driwindowsMapConfigs(priv, screen, psc->base.visuals, configs);
494
495 if (!configs || !visuals) {
496 ErrorMessageF("No fbConfigs or visuals found\n");
497 goto handle_error;
498 }
499
500 glx_config_destroy_list(psc->base.configs);
501 psc->base.configs = configs;
502 glx_config_destroy_list(psc->base.visuals);
503 psc->base.visuals = visuals;
504
505 psc->base.vtable = &driwindows_screen_vtable;
506 psp = &psc->vtable;
507 psc->base.driScreen = psp;
508 psp->destroyScreen = driwindowsDestroyScreen;
509 psp->createDrawable = driwindowsCreateDrawable;
510 psp->swapBuffers = driwindowsSwapBuffers;
511
512 if (psc->copySubBuffer)
513 psp->copySubBuffer = driwindowsCopySubBuffer;
514
515 return &psc->base;
516
517 handle_error:
518 glx_screen_cleanup(&psc->base);
519
520 return NULL;
521 }
522
523 /* Called from __glXFreeDisplayPrivate.
524 */
525 static void
driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)526 driwindowsDestroyDisplay(__GLXDRIdisplay * dpy)
527 {
528 free(dpy);
529 }
530
531 /*
532 * Allocate, initialize and return a __GLXDRIdisplay object.
533 * This is called from __glXInitialize() when we are given a new
534 * display pointer.
535 */
536 _X_HIDDEN __GLXDRIdisplay *
driwindowsCreateDisplay(Display * dpy)537 driwindowsCreateDisplay(Display * dpy)
538 {
539 struct driwindows_display *pdpyp;
540
541 int eventBase, errorBase;
542 int major, minor, patch;
543
544 /* Verify server has Windows-DRI extension */
545 if (!XWindowsDRIQueryExtension(dpy, &eventBase, &errorBase)) {
546 ErrorMessageF("Windows-DRI extension not available\n");
547 return NULL;
548 }
549
550 if (!XWindowsDRIQueryVersion(dpy, &major, &minor, &patch)) {
551 ErrorMessageF("Fetching Windows-DRI extension version failed\n");
552 return NULL;
553 }
554
555 if (!windows_check_renderer()) {
556 ErrorMessageF("Windows-DRI extension disabled for GDI Generic renderer\n");
557 return NULL;
558 }
559
560 pdpyp = malloc(sizeof *pdpyp);
561 if (pdpyp == NULL)
562 return NULL;
563
564 pdpyp->base.destroyDisplay = driwindowsDestroyDisplay;
565 pdpyp->base.createScreen = driwindowsCreateScreen;
566
567 pdpyp->event_base = eventBase;
568
569 return &pdpyp->base;
570 }
571