diff options
author | Jon Turney <[email protected]> | 2016-07-11 21:38:16 +0100 |
---|---|---|
committer | Jon Turney <[email protected]> | 2016-09-15 13:14:43 +0100 |
commit | 533b3530c1292a39ea12437d1376c77bc7e584b9 (patch) | |
tree | 9eb840d793ee592449eb6d356a10be201bca1e02 /src/glx/driwindows_glx.c | |
parent | 2ac09ac5a52b133934c2d96683eab40b9555fdb1 (diff) |
direct-to-native-GL for GLX clients on Cygwin ("Windows-DRI")
Structurally, this is very similar to the existing Apple-DRI code, except I
have chosen to implement this using the __GLXDRIdisplay, etc. vtables (as
suggested originally in [1]), rather than a maze of ifdefs. This also means
that LIBGL_ALWAYS_SOFTWARE and LIBGL_ALWAYS_INDIRECT work as expected.
[1] https://lists.freedesktop.org/archives/mesa-dev/2010-May/000756.html
This adds:
* the Windows-DRI extension protocol headers and the windowsdriproto.pc
file, for use in building the Windows-DRI extension for the X server
* a Windows-DRI extension helper client library
* a Windows-specific DRI implementation for GLX clients
The server is queried for Windows-DRI extension support on the screen before
using it (to detect the case where WGL is disabled or can't be activated).
The server is queried for fbconfigID to pixelformatindex mapping, which is
used to augment glx_config.
The server is queried for a native handle for the drawable (which is of a
different type for windows, pixmaps and pbuffers), which is used to augment
__GLXDRIdrawable.
Various GLX extensions are enabled depending on if the equivalent WGL
extension is available.
Diffstat (limited to 'src/glx/driwindows_glx.c')
-rw-r--r-- | src/glx/driwindows_glx.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/src/glx/driwindows_glx.c b/src/glx/driwindows_glx.c new file mode 100644 index 00000000000..02d95e7bfd2 --- /dev/null +++ b/src/glx/driwindows_glx.c @@ -0,0 +1,609 @@ +/* + * 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; +} |