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