/*
* 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 "util/macros.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 < ARRAY_SIZE(extensionMap); 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;
}