| /* |
| * Copyright © 2014 Jon Turney |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include "glxclient.h" |
| #include "glx_error.h" |
| #include "dri_common.h" |
| #include "windows/xwindowsdri.h" |
| #include "windows/windowsgl.h" |
| |
| struct driwindows_display |
| { |
| __GLXDRIdisplay base; |
| int event_base; |
| }; |
| |
| struct driwindows_context |
| { |
| struct glx_context base; |
| windowsContext *windowsContext; |
| }; |
| |
| struct driwindows_config |
| { |
| struct glx_config base; |
| int pxfi; |
| }; |
| |
| struct driwindows_screen |
| { |
| struct glx_screen base; |
| __DRIscreen *driScreen; |
| __GLXDRIscreen vtable; |
| Bool copySubBuffer; |
| }; |
| |
| struct driwindows_drawable |
| { |
| __GLXDRIdrawable base; |
| windowsDrawable *windowsDrawable; |
| }; |
| |
| /** |
| * GLXDRI functions |
| */ |
| |
| static void |
| driwindows_destroy_context(struct glx_context *context) |
| { |
| struct driwindows_context *pcp = (struct driwindows_context *) context; |
| |
| driReleaseDrawables(&pcp->base); |
| |
| free((char *) context->extensions); |
| |
| windows_destroy_context(pcp->windowsContext); |
| |
| free(pcp); |
| } |
| |
| static int |
| driwindows_bind_context(struct glx_context *context, struct glx_context *old, |
| GLXDrawable draw, GLXDrawable read) |
| { |
| struct driwindows_context *pcp = (struct driwindows_context *) context; |
| struct driwindows_drawable *pdraw, *pread; |
| |
| pdraw = (struct driwindows_drawable *) driFetchDrawable(context, draw); |
| pread = (struct driwindows_drawable *) driFetchDrawable(context, read); |
| |
| driReleaseDrawables(&pcp->base); |
| |
| if (pdraw == NULL || pread == NULL) |
| return GLXBadDrawable; |
| |
| if (windows_bind_context(pcp->windowsContext, |
| pdraw->windowsDrawable, pread->windowsDrawable)) |
| return Success; |
| |
| return GLXBadContext; |
| } |
| |
| static void |
| driwindows_unbind_context(struct glx_context *context, struct glx_context *new) |
| { |
| struct driwindows_context *pcp = (struct driwindows_context *) context; |
| |
| windows_unbind_context(pcp->windowsContext); |
| } |
| |
| static void |
| driwindows_bind_tex_image(Display * dpy, |
| GLXDrawable drawable, |
| int buffer, const int *attrib_list) |
| { |
| struct glx_context *gc = __glXGetCurrentContext(); |
| struct driwindows_context *pcp = (struct driwindows_context *) gc; |
| __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable); |
| struct driwindows_drawable *pdraw = (struct driwindows_drawable *) base; |
| |
| __glXInitialize(dpy); |
| |
| if (pdraw != NULL) { |
| windows_setTexBuffer(pcp->windowsContext, |
| pdraw->base.textureTarget, |
| pdraw->base.textureFormat, |
| pdraw->windowsDrawable); |
| } |
| } |
| |
| static void |
| driwindows_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer) |
| { |
| struct glx_context *gc = __glXGetCurrentContext(); |
| struct driwindows_context *pcp = (struct driwindows_context *) gc; |
| __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable); |
| struct glx_display *dpyPriv = __glXInitialize(dpy); |
| struct driwindows_drawable *pdraw = (struct driwindows_drawable *) base; |
| |
| if (dpyPriv != NULL && pdraw != NULL) { |
| windows_releaseTexBuffer(pcp->windowsContext, |
| pdraw->base.textureTarget, |
| pdraw->windowsDrawable); |
| } |
| } |
| |
| static const struct glx_context_vtable driwindows_context_vtable = { |
| .destroy = driwindows_destroy_context, |
| .bind = driwindows_bind_context, |
| .unbind = driwindows_unbind_context, |
| .wait_gl = NULL, |
| .wait_x = NULL, |
| .use_x_font = DRI_glXUseXFont, |
| .bind_tex_image = driwindows_bind_tex_image, |
| .release_tex_image = driwindows_release_tex_image, |
| .get_proc_address = NULL, |
| }; |
| |
| static struct glx_context * |
| driwindows_create_context(struct glx_screen *base, |
| struct glx_config *config_base, |
| struct glx_context *shareList, int renderType) |
| { |
| struct driwindows_context *pcp, *pcp_shared; |
| struct driwindows_config *config = (struct driwindows_config *) config_base; |
| struct driwindows_screen *psc = (struct driwindows_screen *) base; |
| windowsContext *shared = NULL; |
| |
| if (!psc->base.driScreen) |
| return NULL; |
| |
| /* Check the renderType value */ |
| if (!validate_renderType_against_config(config_base, renderType)) |
| return NULL; |
| |
| if (shareList) { |
| /* If the shareList context is not on this renderer, we cannot possibly |
| * create a context that shares with it. |
| */ |
| if (shareList->vtable->destroy != driwindows_destroy_context) { |
| return NULL; |
| } |
| |
| pcp_shared = (struct driwindows_context *) shareList; |
| shared = pcp_shared->windowsContext; |
| } |
| |
| pcp = calloc(1, sizeof *pcp); |
| if (pcp == NULL) |
| return NULL; |
| |
| if (!glx_context_init(&pcp->base, &psc->base, &config->base)) { |
| free(pcp); |
| return NULL; |
| } |
| |
| pcp->base.renderType = renderType; |
| |
| InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi); |
| |
| pcp->windowsContext = windows_create_context(config->pxfi, shared); |
| |
| if (!pcp->windowsContext) { |
| free(pcp); |
| return NULL; |
| } |
| |
| pcp->base.vtable = &driwindows_context_vtable; |
| |
| return &pcp->base; |
| } |
| |
| 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) |
| { |
| struct driwindows_context *pcp, *pcp_shared; |
| struct driwindows_config *config = (struct driwindows_config *) config_base; |
| struct driwindows_screen *psc = (struct driwindows_screen *) base; |
| windowsContext *shared = NULL; |
| |
| int i; |
| uint32_t renderType = GLX_RGBA_TYPE; |
| |
| /* Extract renderType from attribs */ |
| for (i = 0; i < num_attribs; i++) { |
| switch (attribs[i * 2]) { |
| case GLX_RENDER_TYPE: |
| renderType = attribs[i * 2 + 1]; |
| break; |
| } |
| } |
| |
| /* |
| Perhaps we should map GLX tokens to WGL tokens, but they appear to have |
| identical values, so far |
| */ |
| |
| if (!psc->base.driScreen) |
| return NULL; |
| |
| /* Check the renderType value */ |
| if (!validate_renderType_against_config(config_base, renderType)) { |
| return NULL; |
| } |
| |
| if (shareList) { |
| /* If the shareList context is not on this renderer, we cannot possibly |
| * create a context that shares with it. |
| */ |
| if (shareList->vtable->destroy != driwindows_destroy_context) { |
| return NULL; |
| } |
| |
| pcp_shared = (struct driwindows_context *) shareList; |
| shared = pcp_shared->windowsContext; |
| } |
| |
| pcp = calloc(1, sizeof *pcp); |
| if (pcp == NULL) |
| return NULL; |
| |
| if (!glx_context_init(&pcp->base, &psc->base, &config->base)) { |
| free(pcp); |
| return NULL; |
| } |
| |
| pcp->base.renderType = renderType; |
| |
| InfoMessageF("visualID %x, fbConfigID %x -> pxfi %d\n", config_base->visualID, config_base->fbconfigID, config->pxfi); |
| |
| pcp->windowsContext = windows_create_context_attribs(config->pxfi, |
| shared, |
| (const int *)attribs); |
| if (pcp->windowsContext == NULL) { |
| free(pcp); |
| return NULL; |
| } |
| |
| pcp->base.vtable = &driwindows_context_vtable; |
| |
| return &pcp->base; |
| } |
| |
| static void |
| driwindowsDestroyDrawable(__GLXDRIdrawable * pdraw) |
| { |
| struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw; |
| |
| windows_destroy_drawable(pdp->windowsDrawable); |
| |
| free(pdp); |
| } |
| |
| static __GLXDRIdrawable * |
| driwindowsCreateDrawable(struct glx_screen *base, XID xDrawable, |
| GLXDrawable drawable, struct glx_config *modes) |
| { |
| struct driwindows_drawable *pdp; |
| struct driwindows_screen *psc = (struct driwindows_screen *) base; |
| |
| pdp = calloc(1, sizeof(*pdp)); |
| if (!pdp) |
| return NULL; |
| |
| pdp->base.xDrawable = xDrawable; |
| pdp->base.drawable = drawable; |
| pdp->base.psc = &psc->base; |
| |
| /* |
| By this stage, the X drawable already exists, but the GLX drawable may |
| not. |
| |
| Query the server with the XID to find the correct HWND, HPBUFFERARB or |
| HBITMAP |
| */ |
| |
| unsigned int type; |
| void *handle; |
| |
| if (!XWindowsDRIQueryDrawable(psc->base.dpy, base->scr, drawable, &type, &handle)) |
| { |
| free(pdp); |
| return NULL; |
| } |
| |
| /* No handle found is a failure */ |
| if (!handle) { |
| free(pdp); |
| return NULL; |
| } |
| |
| /* Create a new drawable */ |
| pdp->windowsDrawable = windows_create_drawable(type, handle); |
| |
| if (!pdp->windowsDrawable) { |
| free(pdp); |
| return NULL; |
| } |
| |
| pdp->base.destroyDrawable = driwindowsDestroyDrawable; |
| |
| return &pdp->base; |
| } |
| |
| static int64_t |
| driwindowsSwapBuffers(__GLXDRIdrawable * pdraw, |
| int64_t target_msc, int64_t divisor, int64_t remainder, |
| Bool flush) |
| { |
| struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw; |
| |
| (void) target_msc; |
| (void) divisor; |
| (void) remainder; |
| |
| if (flush) { |
| glFlush(); |
| } |
| |
| windows_swap_buffers(pdp->windowsDrawable); |
| |
| return 0; |
| } |
| |
| static void |
| driwindowsCopySubBuffer(__GLXDRIdrawable * pdraw, |
| int x, int y, int width, int height, Bool flush) |
| { |
| struct driwindows_drawable *pdp = (struct driwindows_drawable *) pdraw; |
| |
| if (flush) { |
| glFlush(); |
| } |
| |
| windows_copy_subbuffer(pdp->windowsDrawable, x, y, width, height); |
| } |
| |
| static void |
| driwindowsDestroyScreen(struct glx_screen *base) |
| { |
| struct driwindows_screen *psc = (struct driwindows_screen *) base; |
| |
| /* Free the direct rendering per screen data */ |
| psc->driScreen = NULL; |
| free(psc); |
| } |
| |
| static const struct glx_screen_vtable driwindows_screen_vtable = { |
| .create_context = driwindows_create_context, |
| .create_context_attribs = driwindows_create_context_attribs, |
| .query_renderer_integer = NULL, |
| .query_renderer_string = NULL, |
| }; |
| |
| static Bool |
| driwindowsBindExtensions(struct driwindows_screen *psc) |
| { |
| Bool result = 1; |
| |
| const struct |
| { |
| char *wglext; |
| char *glxext; |
| Bool mandatory; |
| } extensionMap[] = { |
| { "WGL_ARB_make_current_read", "GLX_SGI_make_current_read", 0 }, |
| { "WGL_EXT_swap_control", "GLX_SGI_swap_control", 0 }, |
| { "WGL_EXT_swap_control", "GLX_MESA_swap_control", 0 }, |
| // { "WGL_ARB_render_texture", "GLX_EXT_texture_from_pixmap", 0 }, |
| // Not exactly equivalent, needs some more glue to be written |
| { "WGL_ARB_pbuffer", "GLX_SGIX_pbuffer", 1 }, |
| { "WGL_ARB_multisample", "GLX_ARB_multisample", 1 }, |
| { "WGL_ARB_multisample", "GLX_SGIS_multisample", 1 }, |
| { "WGL_ARB_create_context", "GLX_ARB_create_context", 0 }, |
| { "WGL_ARB_create_context_profile", "GLX_ARB_create_context_profile", 0 }, |
| { "WGL_ARB_create_context_robustness", "GLX_ARB_create_context_robustness", 0 }, |
| { "WGL_EXT_create_context_es2_profile", "GLX_EXT_create_context_es2_profile", 0 }, |
| }; |
| |
| char *wgl_extensions; |
| char *gl_extensions; |
| int i; |
| |
| windows_extensions(&gl_extensions, &wgl_extensions); |
| |
| for (i = 0; i < sizeof(extensionMap)/sizeof(extensionMap[0]); i++) { |
| if (strstr(wgl_extensions, extensionMap[i].wglext)) { |
| __glXEnableDirectExtension(&psc->base, extensionMap[i].glxext); |
| InfoMessageF("enabled %s\n", extensionMap[i].glxext); |
| } |
| else if (extensionMap[i].mandatory) { |
| ErrorMessageF("required WGL extension %s is missing\n", extensionMap[i].wglext); |
| result = 0; |
| } |
| } |
| |
| /* |
| Because it pre-dates WGL_EXT_extensions_string, GL_WIN_swap_hint might |
| only be in GL_EXTENSIONS |
| */ |
| if (strstr(gl_extensions, "GL_WIN_swap_hint")) { |
| psc->copySubBuffer = 1; |
| __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer"); |
| InfoMessageF("enabled GLX_MESA_copy_sub_buffer\n"); |
| } |
| |
| free(gl_extensions); |
| free(wgl_extensions); |
| |
| return result; |
| } |
| |
| static struct glx_config * |
| driwindowsMapConfigs(struct glx_display *priv, int screen, struct glx_config *configs, struct glx_config *fbconfigs) |
| { |
| struct glx_config head, *tail, *m; |
| |
| tail = &head; |
| head.next = NULL; |
| |
| for (m = configs; m; m = m->next) { |
| int fbconfigID = GLX_DONT_CARE; |
| if (fbconfigs) { |
| /* |
| visuals have fbconfigID of GLX_DONT_CARE, so search for a fbconfig |
| with matching visualID and get the fbconfigID from there |
| */ |
| struct glx_config *f; |
| for (f = fbconfigs; f; f = f->next) { |
| if (f->visualID == m->visualID) |
| fbconfigID = f->fbconfigID; |
| } |
| } |
| else { |
| fbconfigID = m->fbconfigID; |
| } |
| |
| int pxfi; |
| XWindowsDRIFBConfigToPixelFormat(priv->dpy, screen, fbconfigID, &pxfi); |
| if (pxfi == 0) |
| continue; |
| |
| struct driwindows_config *config = malloc(sizeof(*config)); |
| |
| tail->next = &config->base; |
| if (tail->next == NULL) |
| continue; |
| |
| config->base = *m; |
| config->pxfi = pxfi; |
| |
| tail = tail->next; |
| } |
| |
| return head.next; |
| } |
| |
| static struct glx_screen * |
| driwindowsCreateScreen(int screen, struct glx_display *priv) |
| { |
| __GLXDRIscreen *psp; |
| struct driwindows_screen *psc; |
| struct glx_config *configs = NULL, *visuals = NULL; |
| int directCapable; |
| |
| psc = calloc(1, sizeof *psc); |
| if (psc == NULL) |
| return NULL; |
| |
| if (!glx_screen_init(&psc->base, screen, priv)) { |
| free(psc); |
| return NULL; |
| } |
| |
| if (!XWindowsDRIQueryDirectRenderingCapable(psc->base.dpy, screen, &directCapable) || |
| !directCapable) { |
| ErrorMessageF("Screen is not Windows-DRI capable\n"); |
| goto handle_error; |
| } |
| |
| /* discover native supported extensions */ |
| if (!driwindowsBindExtensions(psc)) { |
| goto handle_error; |
| } |
| |
| /* Augment configs with pxfi information */ |
| configs = driwindowsMapConfigs(priv, screen, psc->base.configs, NULL); |
| visuals = driwindowsMapConfigs(priv, screen, psc->base.visuals, configs); |
| |
| if (!configs || !visuals) { |
| ErrorMessageF("No fbConfigs or visuals found\n"); |
| goto handle_error; |
| } |
| |
| glx_config_destroy_list(psc->base.configs); |
| psc->base.configs = configs; |
| glx_config_destroy_list(psc->base.visuals); |
| psc->base.visuals = visuals; |
| |
| psc->base.vtable = &driwindows_screen_vtable; |
| psp = &psc->vtable; |
| psc->base.driScreen = psp; |
| psp->destroyScreen = driwindowsDestroyScreen; |
| psp->createDrawable = driwindowsCreateDrawable; |
| psp->swapBuffers = driwindowsSwapBuffers; |
| |
| if (psc->copySubBuffer) |
| psp->copySubBuffer = driwindowsCopySubBuffer; |
| |
| return &psc->base; |
| |
| handle_error: |
| glx_screen_cleanup(&psc->base); |
| |
| return NULL; |
| } |
| |
| /* Called from __glXFreeDisplayPrivate. |
| */ |
| static void |
| driwindowsDestroyDisplay(__GLXDRIdisplay * dpy) |
| { |
| free(dpy); |
| } |
| |
| /* |
| * Allocate, initialize and return a __GLXDRIdisplay object. |
| * This is called from __glXInitialize() when we are given a new |
| * display pointer. |
| */ |
| _X_HIDDEN __GLXDRIdisplay * |
| driwindowsCreateDisplay(Display * dpy) |
| { |
| struct driwindows_display *pdpyp; |
| |
| int eventBase, errorBase; |
| int major, minor, patch; |
| |
| /* Verify server has Windows-DRI extension */ |
| if (!XWindowsDRIQueryExtension(dpy, &eventBase, &errorBase)) { |
| ErrorMessageF("Windows-DRI extension not available\n"); |
| return NULL; |
| } |
| |
| if (!XWindowsDRIQueryVersion(dpy, &major, &minor, &patch)) { |
| ErrorMessageF("Fetching Windows-DRI extension version failed\n"); |
| return NULL; |
| } |
| |
| if (!windows_check_renderer()) { |
| ErrorMessageF("Windows-DRI extension disabled for GDI Generic renderer\n"); |
| return NULL; |
| } |
| |
| pdpyp = malloc(sizeof *pdpyp); |
| if (pdpyp == NULL) |
| return NULL; |
| |
| pdpyp->base.destroyDisplay = driwindowsDestroyDisplay; |
| pdpyp->base.createScreen = driwindowsCreateScreen; |
| |
| pdpyp->event_base = eventBase; |
| |
| return &pdpyp->base; |
| } |