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 31struct driwindows_display 32{ 33 __GLXDRIdisplay base; 34 int event_base; 35}; 36 37struct driwindows_context 38{ 39 struct glx_context base; 40 windowsContext *windowsContext; 41}; 42 43struct driwindows_config 44{ 45 struct glx_config base; 46 int pxfi; 47}; 48 49struct driwindows_screen 50{ 51 struct glx_screen base; 52 __DRIscreen *driScreen; 53 __GLXDRIscreen vtable; 54 Bool copySubBuffer; 55}; 56 57struct driwindows_drawable 58{ 59 __GLXDRIdrawable base; 60 windowsDrawable *windowsDrawable; 61}; 62 63/** 64 * GLXDRI functions 65 */ 66 67static void 68driwindows_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 81static int 82driwindows_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 103static void 104driwindows_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 111static 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 119static struct glx_context * 120driwindows_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 173static struct glx_context * 174driwindows_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 249static void 250driwindowsDestroyDrawable(__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 259static __GLXDRIdrawable * 260driwindowsCreateDrawable(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 310static int64_t 311driwindowsSwapBuffers(__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 330static void 331driwindowsCopySubBuffer(__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 343static void 344driwindowsDestroyScreen(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 353static 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 360static Bool 361driwindowsBindExtensions(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 418static struct glx_config * 419driwindowsMapConfigs(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 463static struct glx_screen * 464driwindowsCreateScreen(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 517handle_error: 518 glx_screen_cleanup(&psc->base); 519 520 return NULL; 521} 522 523/* Called from __glXFreeDisplayPrivate. 524 */ 525static void 526driwindowsDestroyDisplay(__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 * 537driwindowsCreateDisplay(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