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