diff options
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; +} |