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_context
32 {
33 struct glx_context base;
34 windowsContext *windowsContext;
35 };
36
37 struct driwindows_config
38 {
39 struct glx_config base;
40 int pxfi;
41 };
42
43 struct driwindows_screen
44 {
45 struct glx_screen base;
46 __DRIscreen *driScreen;
47 int event_base;
48 Bool copySubBuffer;
49 };
50
51 struct driwindows_drawable
52 {
53 __GLXDRIdrawable base;
54 windowsDrawable *windowsDrawable;
55 };
56
57 /**
58 * GLXDRI functions
59 */
60
61 static void
driwindows_destroy_context(struct glx_context * context)62 driwindows_destroy_context(struct glx_context *context)
63 {
64 struct driwindows_context *pcp = (struct driwindows_context *) context;
65
66 driReleaseDrawables(&pcp->base);
67
68 free((char *) context->extensions);
69
70 windows_destroy_context(pcp->windowsContext);
71
72 free(pcp);
73 }
74
75 static int
driwindows_bind_context(struct glx_context * context,GLXDrawable draw,GLXDrawable read)76 driwindows_bind_context(struct glx_context *context, GLXDrawable draw, GLXDrawable read)
77 {
78 struct driwindows_context *pcp = (struct driwindows_context *) context;
79 struct driwindows_drawable *pdraw, *pread;
80
81 pdraw = (struct driwindows_drawable *) driFetchDrawable(context, draw);
82 pread = (struct driwindows_drawable *) driFetchDrawable(context, read);
83
84 driReleaseDrawables(&pcp->base);
85
86 if (pdraw == NULL || pread == NULL)
87 return GLXBadDrawable;
88
89 if (windows_bind_context(pcp->windowsContext,
90 pdraw->windowsDrawable, pread->windowsDrawable))
91 return Success;
92
93 return GLXBadContext;
94 }
95
96 static void
driwindows_unbind_context(struct glx_context * context)97 driwindows_unbind_context(struct glx_context *context)
98 {
99 struct driwindows_context *pcp = (struct driwindows_context *) context;
100
101 windows_unbind_context(pcp->windowsContext);
102 }
103
104 static const struct glx_context_vtable driwindows_context_vtable = {
105 .destroy = driwindows_destroy_context,
106 .bind = driwindows_bind_context,
107 .unbind = driwindows_unbind_context,
108 .wait_gl = NULL,
109 .wait_x = NULL,
110 };
111
112 static struct glx_context *
driwindows_create_context(struct glx_screen * base,struct glx_config * config_base,struct glx_context * shareList,int renderType)113 driwindows_create_context(struct glx_screen *base,
114 struct glx_config *config_base,
115 struct glx_context *shareList, int renderType)
116 {
117 struct driwindows_context *pcp, *pcp_shared;
118 struct driwindows_config *config = (struct driwindows_config *) config_base;
119 struct driwindows_screen *psc = (struct driwindows_screen *) base;
120 windowsContext *shared = NULL;
121
122 if (!psc->base.driScreen)
123 return NULL;
124
125 /* Check the renderType value */
126 if (!validate_renderType_against_config(config_base, renderType))
127 return NULL;
128
129 if (shareList) {
130 /* If the shareList context is not on this renderer, we cannot possibly
131 * create a context that shares with it.
132 */
133 if (shareList->vtable->destroy != driwindows_destroy_context) {
134 return NULL;
135 }
136
137 pcp_shared = (struct driwindows_context *) shareList;
138 shared = pcp_shared->windowsContext;
139 }
140
141 pcp = calloc(1, sizeof *pcp);
142 if (pcp == NULL)
143 return NULL;
144
145 if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
146 free(pcp);
147 return NULL;
148 }
149
150 pcp->base.renderType = renderType;
151
152 InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
153
154 pcp->windowsContext = windows_create_context(config->pxfi, shared);
155
156 if (!pcp->windowsContext) {
157 free(pcp);
158 return NULL;
159 }
160
161 pcp->base.vtable = &driwindows_context_vtable;
162
163 return &pcp->base;
164 }
165
166 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)167 driwindows_create_context_attribs(struct glx_screen *base,
168 struct glx_config *config_base,
169 struct glx_context *shareList,
170 unsigned num_attribs,
171 const uint32_t *attribs,
172 unsigned *error)
173 {
174 struct driwindows_context *pcp, *pcp_shared;
175 struct driwindows_config *config = (struct driwindows_config *) config_base;
176 struct driwindows_screen *psc = (struct driwindows_screen *) base;
177 windowsContext *shared = NULL;
178
179 int i;
180 uint32_t renderType = GLX_RGBA_TYPE;
181
182 /* Extract renderType from attribs */
183 for (i = 0; i < num_attribs; i++) {
184 switch (attribs[i * 2]) {
185 case GLX_RENDER_TYPE:
186 renderType = attribs[i * 2 + 1];
187 break;
188 }
189 }
190
191 /*
192 Perhaps we should map GLX tokens to WGL tokens, but they appear to have
193 identical values, so far
194 */
195
196 if (!psc->base.driScreen || !config_base)
197 return NULL;
198
199 /* Check the renderType value */
200 if (!validate_renderType_against_config(config_base, renderType)) {
201 return NULL;
202 }
203
204 if (shareList) {
205 /* If the shareList context is not on this renderer, we cannot possibly
206 * create a context that shares with it.
207 */
208 if (shareList->vtable->destroy != driwindows_destroy_context) {
209 return NULL;
210 }
211
212 pcp_shared = (struct driwindows_context *) shareList;
213 shared = pcp_shared->windowsContext;
214 }
215
216 pcp = calloc(1, sizeof *pcp);
217 if (pcp == NULL)
218 return NULL;
219
220 if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
221 free(pcp);
222 return NULL;
223 }
224
225 pcp->base.renderType = renderType;
226
227 InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi);
228
229 pcp->windowsContext = windows_create_context_attribs(config->pxfi,
230 shared,
231 (const int *)attribs);
232 if (pcp->windowsContext == NULL) {
233 free(pcp);
234 return NULL;
235 }
236
237 pcp->base.vtable = &driwindows_context_vtable;
238
239 return &pcp->base;
240 }
241
242 static void
driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)243 driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw)
244 {
245 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
246
247 windows_destroy_drawable(pdp->windowsDrawable);
248
249 free(pdp);
250 }
251
252 static __GLXDRIdrawable *
driwindowsCreateDrawable(struct glx_screen * base,XID xDrawable,GLXDrawable drawable,int type,struct glx_config * modes)253 driwindowsCreateDrawable(struct glx_screen *base, XID xDrawable,
254 GLXDrawable drawable, int type,
255 struct glx_config *modes)
256 {
257 struct driwindows_drawable *pdp;
258 struct driwindows_screen *psc = (struct driwindows_screen *) base;
259
260 pdp = calloc(1, sizeof(*pdp));
261 if (!pdp)
262 return NULL;
263
264 pdp->base.xDrawable = xDrawable;
265 pdp->base.drawable = drawable;
266 pdp->base.psc = &psc->base;
267
268 /*
269 By this stage, the X drawable already exists, but the GLX drawable may
270 not.
271
272 Query the server with the XID to find the correct HWND, HPBUFFERARB or
273 HBITMAP
274 */
275
276 unsigned int type;
277 void *handle;
278
279 if (!XWindowsDRIQueryDrawable(psc->base.dpy, base->scr, drawable, &type, &handle))
280 {
281 free(pdp);
282 return NULL;
283 }
284
285 /* No handle found is a failure */
286 if (!handle) {
287 free(pdp);
288 return NULL;
289 }
290
291 /* Create a new drawable */
292 pdp->windowsDrawable = windows_create_drawable(type, handle);
293
294 if (!pdp->windowsDrawable) {
295 free(pdp);
296 return NULL;
297 }
298
299 pdp->base.destroyDrawable = driwindowsDestroyDrawable;
300
301 return &pdp->base;
302 }
303
304 static int64_t
driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,int64_t target_msc,int64_t divisor,int64_t remainder,Bool flush)305 driwindowsSwapBuffers(__GLXDRIdrawable * pdraw,
306 int64_t target_msc, int64_t divisor, int64_t remainder,
307 Bool flush)
308 {
309 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
310
311 (void) target_msc;
312 (void) divisor;
313 (void) remainder;
314
315 if (flush) {
316 glFlush();
317 }
318
319 windows_swap_buffers(pdp->windowsDrawable);
320
321 return 0;
322 }
323
324 static void
driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,int x,int y,int width,int height,Bool flush)325 driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw,
326 int x, int y, int width, int height, Bool flush)
327 {
328 struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw;
329
330 if (flush) {
331 glFlush();
332 }
333
334 windows_copy_subbuffer(pdp->windowsDrawable, x, y, width, height);
335 }
336
337 static const struct glx_screen_vtable driwindows_screen_vtable = {
338 .create_context = driwindows_create_context,
339 .create_context_attribs = driwindows_create_context_attribs,
340 .query_renderer_integer = NULL,
341 .query_renderer_string = NULL,
342 };
343
344 static Bool
driwindowsBindExtensions(struct driwindows_screen * psc)345 driwindowsBindExtensions(struct driwindows_screen *psc)
346 {
347 Bool result = 1;
348
349 const struct
350 {
351 char *wglext;
352 char *glxext;
353 Bool mandatory;
354 } extensionMap[] = {
355 { "WGL_ARB_make_current_read", "GLX_SGI_make_current_read", 0 },
356 { "WGL_EXT_swap_control", "GLX_SGI_swap_control", 0 },
357 { "WGL_EXT_swap_control", "GLX_MESA_swap_control", 0 },
358 // { "WGL_ARB_render_texture", "GLX_EXT_texture_from_pixmap", 0 },
359 // Not exactly equivalent, needs some more glue to be written
360 { "WGL_ARB_pbuffer", "GLX_SGIX_pbuffer", 1 },
361 { "WGL_ARB_multisample", "GLX_ARB_multisample", 1 },
362 { "WGL_ARB_multisample", "GLX_SGIS_multisample", 1 },
363 { "WGL_ARB_create_context", "GLX_ARB_create_context", 0 },
364 { "WGL_ARB_create_context_profile", "GLX_ARB_create_context_profile", 0 },
365 { "WGL_ARB_create_context_robustness", "GLX_ARB_create_context_robustness", 0 },
366 { "WGL_EXT_create_context_es2_profile", "GLX_EXT_create_context_es2_profile", 0 },
367 };
368
369 char *wgl_extensions;
370 char *gl_extensions;
371 int i;
372
373 windows_extensions(&gl_extensions, &wgl_extensions);
374
375 for (i = 0; i < ARRAY_SIZE(extensionMap); i++) {
376 if (strstr(wgl_extensions, extensionMap[i].wglext)) {
377 __glXEnableDirectExtension(&psc->base, extensionMap[i].glxext);
378 InfoMessageF("enabled %s\n", extensionMap[i].glxext);
379 }
380 else if (extensionMap[i].mandatory) {
381 ErrorMessageF("required WGL extension %s is missing\n", extensionMap[i].wglext);
382 result = 0;
383 }
384 }
385
386 /*
387 Because it pre-dates WGL_EXT_extensions_string, GL_WIN_swap_hint might
388 only be in GL_EXTENSIONS
389 */
390 if (strstr(gl_extensions, "GL_WIN_swap_hint")) {
391 psc->copySubBuffer = 1;
392 __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
393 InfoMessageF("enabled GLX_MESA_copy_sub_buffer\n");
394 }
395
396 free(gl_extensions);
397 free(wgl_extensions);
398
399 return result;
400 }
401
402 static struct glx_config *
driwindowsMapConfigs(struct glx_display * priv,int screen,struct glx_config * configs,struct glx_config * fbconfigs)403 driwindowsMapConfigs(struct glx_display *priv, int screen, struct glx_config *configs, struct glx_config *fbconfigs)
404 {
405 struct glx_config head, *tail, *m;
406
407 tail = &head;
408 head.next = NULL;
409
410 for (m = configs; m; m = m->next) {
411 int fbconfigID = GLX_DONT_CARE;
412 if (fbconfigs) {
413 /*
414 visuals have fbconfigID of GLX_DONT_CARE, so search for a fbconfig
415 with matching visualID and get the fbconfigID from there
416 */
417 struct glx_config *f;
418 for (f = fbconfigs; f; f = f->next) {
419 if (f->visualID == m->visualID)
420 fbconfigID = f->fbconfigID;
421 }
422 }
423 else {
424 fbconfigID = m->fbconfigID;
425 }
426
427 int pxfi;
428 XWindowsDRIFBConfigToPixelFormat(priv->dpy, screen, fbconfigID, &pxfi);
429 if (pxfi == 0)
430 continue;
431
432 struct driwindows_config *config = malloc(sizeof(*config));
433
434 tail->next = &config->base;
435 if (tail->next == NULL)
436 continue;
437
438 config->base = *m;
439 config->pxfi = pxfi;
440
441 tail = tail->next;
442 }
443
444 return head.next;
445 }
446
447 static struct glx_screen *
driwindowsCreateScreen(int screen,struct glx_display * priv,bool driver_name_is_inferred)448 driwindowsCreateScreen(int screen, struct glx_display *priv, bool driver_name_is_inferred)
449 {
450 __GLXDRIscreen *psp;
451 struct driwindows_screen *psc;
452 struct glx_config *configs = NULL, *visuals = NULL;
453 int directCapable;
454 int eventBase, errorBase;
455 int major, minor, patch;
456
457 /* Verify server has Windows-DRI extension */
458 if (!XWindowsDRIQueryExtension(dpy, &eventBase, &errorBase)) {
459 ErrorMessageF("Windows-DRI extension not available\n");
460 return NULL;
461 }
462
463 if (!XWindowsDRIQueryVersion(dpy, &major, &minor, &patch)) {
464 ErrorMessageF("Fetching Windows-DRI extension version failed\n");
465 return NULL;
466 }
467
468 if (!windows_check_renderer()) {
469 ErrorMessageF("Windows-DRI extension disabled for GDI Generic renderer\n");
470 return NULL;
471 }
472
473 psc = calloc(1, sizeof *psc);
474 if (psc == NULL)
475 return NULL;
476
477 if (!glx_screen_init(&psc->base, screen, priv)) {
478 free(psc);
479 return NULL;
480 }
481
482 if (!XWindowsDRIQueryDirectRenderingCapable(psc->base.dpy, screen, &directCapable) ||
483 !directCapable) {
484 ErrorMessageF("Screen is not Windows-DRI capable\n");
485 goto handle_error;
486 }
487
488 /* discover native supported extensions */
489 if (!driwindowsBindExtensions(psc)) {
490 goto handle_error;
491 }
492
493 /* Augment configs with pxfi information */
494 configs = driwindowsMapConfigs(priv, screen, psc->base.configs, NULL);
495 visuals = driwindowsMapConfigs(priv, screen, psc->base.visuals, configs);
496
497 if (!configs || !visuals) {
498 ErrorMessageF("No fbConfigs or visuals found\n");
499 goto handle_error;
500 }
501
502 glx_config_destroy_list(psc->base.configs);
503 psc->base.configs = configs;
504 glx_config_destroy_list(psc->base.visuals);
505 psc->base.visuals = visuals;
506
507 psc->base.vtable = &driwindows_screen_vtable;
508 psp = &psc->base.driScreen;
509 psp->createDrawable = driwindowsCreateDrawable;
510 psp->swapBuffers = driwindowsSwapBuffers;
511
512 if (psc->copySubBuffer)
513 psp->copySubBuffer = driwindowsCopySubBuffer;
514
515 priv->driver = GLX_DRIVER_WINDOWS;
516
517 return &psc->base;
518
519 handle_error:
520 glx_screen_cleanup(&psc->base);
521
522 return NULL;
523 }
524