mesa/src/glx/glxext.c

1144 lines
33 KiB
C
Raw Normal View History

/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* 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 including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* 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
* SILICON GRAPHICS, INC. 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.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/**
* \file glxext.c
* GLX protocol interface boot-strap code.
*
* Direct rendering support added by Precision Insight, Inc.
*
* \author Kevin E. Martin <kevin@precisioninsight.com>
*/
#include <assert.h>
#include <stdbool.h>
#include <stdarg.h>
#include "glxclient.h"
#include <X11/extensions/Xext.h>
#include <X11/extensions/extutil.h>
#ifdef GLX_USE_APPLEGL
#include "apple/apple_glx.h"
#include "apple/apple_visual.h"
#endif
#include "glxextensions.h"
#include "util/debug.h"
#ifndef GLX_USE_APPLEGL
#include "dri_common.h"
#endif
2006-10-09 15:55:46 +01:00
#include <X11/Xlib-xcb.h>
#include <xcb/xcb.h>
#include <xcb/glx.h>
2005-05-07 18:14:50 +01:00
#define __GLX_MIN_CONFIG_PROPS 18
#define __GLX_EXT_CONFIG_PROPS 32
/*
** Since we send all non-core visual properties as token, value pairs,
** we require 2 words across the wire. In order to maintain backwards
** compatibility, we need to send the total number of words that the
** VisualConfigs are sent back in so old libraries can simply "ignore"
** the new properties.
*/
#define __GLX_TOTAL_CONFIG \
(__GLX_MIN_CONFIG_PROPS + 2 * __GLX_EXT_CONFIG_PROPS)
_X_HIDDEN void
glx_message(int level, const char *f, ...)
{
va_list args;
int threshold = _LOADER_WARNING;
const char *libgl_debug;
libgl_debug = getenv("LIBGL_DEBUG");
if (libgl_debug) {
if (strstr(libgl_debug, "quiet"))
threshold = _LOADER_FATAL;
else if (strstr(libgl_debug, "verbose"))
threshold = _LOADER_DEBUG;
}
/* Note that the _LOADER_* levels are lower numbers for more severe. */
if (level <= threshold) {
fprintf(stderr, "libGL%s: ", level <= _LOADER_WARNING ? " error" : "");
va_start(args, f);
vfprintf(stderr, f, args);
va_end(args);
}
}
/*
** You can set this cell to 1 to force the gl drawing stuff to be
** one command per packet
*/
_X_HIDDEN int __glXDebug = 0;
/* Extension required boiler plate */
static const char __glXExtensionName[] = GLX_EXTENSION_NAME;
static struct glx_display *glx_displays;
static /* const */ char *error_list[] = {
"GLXBadContext",
"GLXBadContextState",
"GLXBadDrawable",
"GLXBadPixmap",
"GLXBadContextTag",
"GLXBadCurrentWindow",
"GLXBadRenderRequest",
"GLXBadLargeRequest",
"GLXUnsupportedPrivateRequest",
"GLXBadFBConfig",
"GLXBadPbuffer",
"GLXBadCurrentDrawable",
"GLXBadWindow",
"GLXBadProfileARB",
};
#ifdef GLX_USE_APPLEGL
static char *__glXErrorString(Display *dpy, int code, XExtCodes *codes,
char *buf, int n);
#endif
static
XEXT_GENERATE_ERROR_STRING(__glXErrorString, __glXExtensionName,
__GLX_NUMBER_ERRORS, error_list)
/*
* GLX events are a bit funky. We don't stuff the X event code into
* our user exposed (via XNextEvent) structure. Instead we use the GLX
* private event code namespace (and hope it doesn't conflict). Clients
* have to know that bit 15 in the event type field means they're getting
* a GLX event, and then handle the various sub-event types there, rather
* than simply checking the event code and handling it directly.
*/
static Bool
__glXWireToEvent(Display *dpy, XEvent *event, xEvent *wire)
{
struct glx_display *glx_dpy = __glXInitialize(dpy);
if (glx_dpy == NULL)
return False;
switch ((wire->u.u.type & 0x7f) - glx_dpy->codes.first_event) {
case GLX_PbufferClobber:
{
GLXPbufferClobberEvent *aevent = (GLXPbufferClobberEvent *)event;
xGLXPbufferClobberEvent *awire = (xGLXPbufferClobberEvent *)wire;
aevent->event_type = awire->type;
aevent->serial = awire->sequenceNumber;
aevent->event_type = awire->event_type;
aevent->draw_type = awire->draw_type;
aevent->drawable = awire->drawable;
aevent->buffer_mask = awire->buffer_mask;
aevent->aux_buffer = awire->aux_buffer;
aevent->x = awire->x;
aevent->y = awire->y;
aevent->width = awire->width;
aevent->height = awire->height;
aevent->count = awire->count;
return True;
}
case GLX_BufferSwapComplete:
{
GLXBufferSwapComplete *aevent = (GLXBufferSwapComplete *)event;
xGLXBufferSwapComplete2 *awire = (xGLXBufferSwapComplete2 *)wire;
struct glx_drawable *glxDraw = GetGLXDrawable(dpy, awire->drawable);
if (!glxDraw)
return False;
aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire);
aevent->send_event = (awire->type & 0x80) != 0;
aevent->display = dpy;
aevent->event_type = awire->event_type;
aevent->drawable = glxDraw->xDrawable;
aevent->ust = ((CARD64)awire->ust_hi << 32) | awire->ust_lo;
aevent->msc = ((CARD64)awire->msc_hi << 32) | awire->msc_lo;
glx: Handle out-of-sequence swap completion events correctly. (v2) The code for emitting INTEL_swap_events swap completion events needs to translate from 32-Bit sbc on the wire to 64-Bit sbc for the events and handle wraparound accordingly. It assumed that events would be sent by the server in the order their corresponding swap requests were emitted from the client, iow. sbc count should be always increasing. This was correct for DRI2. This is not always the case under the DRI3/Present backend, where the Present extension can execute presents and send out completion events in a different order than the submission order of the present requests, due to client code specifying targetMSC target vblank counts which are not strictly monotonically increasing. This confused the wraparound handling. This patch fixes the problem by handling 32-Bit wraparound in both directions. As long as successive swap completion events real 64-Bit sbc's don't differ by more than 2^30, this should be able to do the right thing. How this is supposed to work: awire->sbc contains the low 32-Bits of the true 64-Bit sbc of the current swap event, transmitted over the wire. glxDraw->lastEventSbc contains the low 32-Bits of the 64-Bit sbc of the most recently processed swap event. glxDraw->eventSbcWrap is a 64-Bit offset which tracks the upper 32-Bits of the current sbc. The final 64-Bit output sbc aevent->sbc is computed from the sum of awire->sbc and glxDraw->eventSbcWrap. Under DRI3/Present, swap completion events can be received slightly out of order due to non-monotic targetMsc specified by client code, e.g., present request submission: Submission sbc: 1 2 3 targetMsc: 10 11 9 Reception of completion events: Completion sbc: 3 1 2 The completion sequence 3, 1, 2 would confuse the old wraparound handling made for DRI2 as 1 < 3 --> Assumes a 32-Bit wraparound has happened when it hasn't. The client can queue multiple present requests, in the case of Mesa up to n requests for n-buffered rendering, e.g., n = 2-4 in the current Mesa GLX DRI3/Present implementation. In the case of direct Pixmap presents via xcb_present_pixmap() the number n is limited by the amount of memory available. We reasonably assume that the number of outstanding requests n is much less than 2 billion due to memory contraints and common sense. Therefore while the order of received sbc's can be a bit scrambled, successive 64-Bit sbc's won't deviate by much, a given sbc may be a few counts lower or higher than the previous received sbc. Therefore any large difference between the incoming awire->sbc and the last recorded glxDraw->lastEventSbc will be due to 32-Bit wraparound and we need to adapt glxDraw->eventSbcWrap accordingly to adjust the upper 32-Bits of the sbc. Two cases, correponding to the two if-statements in the patch: a) Previous sbc event was below the last 2^32 boundary, in the previous glxDraw->eventSbcWrap epoch, the new sbc event is in the next 2^32 epoch, therefore the low 32-Bit awire->sbc wrapped around to zero, or close to zero --> awire->sbc is apparently much lower than the glxDraw->lastEventSbc recorded for the previous epoch --> We need to increment glxDraw->eventSbcWrap by 2^32 to adjust the current epoch to be one higher than the previous one. --> Case a) also handles the old DRI2 behaviour. b) Previous sbc event was above closest 2^32 boundary, but now a late event from the previous 2^32 epoch arrives, with a true sbc that belongs to the previous 2^32 segment, so the awire->sbc of this late event has a high count close to 2^32, whereas glxDraw->lastEventSbc is closer to zero --> awire->sbc is much greater than glXDraw->lastEventSbc. --> We need to decrement glxDraw->eventSbcWrap by 2^32 to adjust the current epoch back to the previous lower epoch of this late completion event. We assume such a wraparound to a higher (a) epoch or lower (b) epoch has happened if awire->sbc and glxDraw->lastEventSbc differ by more than 2^30 counts, as such a difference can only happen on wraparound, or if somehow 2^30 present requests would be pending for a given drawable inside the server, which is rather unlikely. v2: Explain the reason for this patch and the new wraparound handling much more extensive in commit message, no code change wrt. initial version. Cc: "10.3 10.4 10.5" <mesa-stable@lists.freedesktop.org> Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Reviewed-by: Michel Dänzer <michel.daenzer@amd.com>
2015-03-12 19:34:06 +00:00
/* Handle 32-Bit wire sbc wraparound in both directions to cope with out
* of sequence 64-Bit sbc's
*/
if ((int64_t) awire->sbc < ((int64_t) glxDraw->lastEventSbc - 0x40000000))
glxDraw->eventSbcWrap += 0x100000000;
if ((int64_t) awire->sbc > ((int64_t) glxDraw->lastEventSbc + 0x40000000))
glxDraw->eventSbcWrap -= 0x100000000;
glxDraw->lastEventSbc = awire->sbc;
aevent->sbc = awire->sbc + glxDraw->eventSbcWrap;
return True;
}
default:
/* client doesn't support server event */
break;
}
return False;
}
/* We don't actually support this. It doesn't make sense for clients to
* send each other GLX events.
*/
static Status
__glXEventToWire(Display *dpy, XEvent *event, xEvent *wire)
{
struct glx_display *glx_dpy = __glXInitialize(dpy);
if (glx_dpy == NULL)
return False;
switch (event->type) {
case GLX_DAMAGED:
break;
case GLX_SAVED:
break;
case GLX_EXCHANGE_COMPLETE_INTEL:
break;
case GLX_COPY_COMPLETE_INTEL:
break;
case GLX_FLIP_COMPLETE_INTEL:
break;
default:
/* client doesn't support server event */
break;
}
return Success;
}
/************************************************************************/
/*
** Free the per screen configs data as well as the array of
** __glXScreenConfigs.
*/
2008-10-18 16:00:13 +01:00
static void
FreeScreenConfigs(struct glx_display * priv)
{
struct glx_screen *psc;
GLint i, screens;
/* Free screen configuration information */
screens = ScreenCount(priv->dpy);
for (i = 0; i < screens; i++) {
psc = priv->screens[i];
glx: Fix SEGV due to dereferencing a NULL ptr from XCB-GLX. When run in optirun, applications that linked to `libGLX.so` and then proceeded to querying Mesa for extension strings caused a SEGV in Mesa. `glXQueryExtensionsString` was calling a chain of functions that eventually led to `__glXQueryServerString`. This function would call `xcb_glx_query_server_string` then `xcb_glx_query_server_string_reply`. The latter for some unknown reason returned `NULL`. Passing this `NULL` to `xcb_glx_query_server_string_string_length` would cause a SEGV as the function tried to dereference it. The reason behind the function returning `NULL` is yet to be determined, however, simply checking that the ptr is not `NULL` resolves this. A similar check has been added to `__glXGetString` for completeness sake, although not immediately necessary. In addition to that, we stumbled into a similar problem in `AllocAndFetchScreenConfigs` which tries to access the configs to free them if `__glXQueryServerString` fails. This, of course, SEGVs, because the configs are yet to have been allocated. Simply continuing past the configs if their config ptrs are `NULL` resolves this. We also switch to `calloc` to make sure that the config ptrs are `NULL` by default, and not some uninitialized value. Cc: mesa-stable@lists.freedesktop.org Fixes: 24b8a8cfe821 "glx: implement __glXGetString, hide __glXGetStringFromServer" Fixes: cb3610e37c4c "Import the GLX client side library, formerly from xc/lib/GL/glx. Build it " Reviewed-by: Adam Jackson <ajax@redhat.com> Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
2019-07-25 22:40:50 +01:00
if (!psc)
continue;
glx_screen_cleanup(psc);
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
if (psc->driScreen) {
psc->driScreen->destroyScreen(psc);
} else {
free(psc);
}
#else
free(psc);
#endif
}
free((char *) priv->screens);
priv->screens = NULL;
}
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
static void
free_zombie_glx_drawable(struct set_entry *entry)
{
__GLXDRIdrawable *pdraw = (__GLXDRIdrawable *)entry->key;
pdraw->destroyDrawable(pdraw);
}
#endif
static void
glx_display_free(struct glx_display *priv)
{
struct glx_context *gc;
gc = __glXGetCurrentContext();
if (priv->dpy == gc->currentDpy) {
gc->vtable->destroy(gc);
__glXSetCurrentContextNull();
}
/* Needs to be done before free screen. */
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
_mesa_set_destroy(priv->zombieGLXDrawable, free_zombie_glx_drawable);
#endif
FreeScreenConfigs(priv);
__glxHashDestroy(priv->glXDrawHash);
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
__glxHashDestroy(priv->drawHash);
/* Free the direct rendering per display data */
if (priv->driswDisplay)
(*priv->driswDisplay->destroyDisplay) (priv->driswDisplay);
priv->driswDisplay = NULL;
#if defined (GLX_USE_DRM)
if (priv->dri2Display)
(*priv->dri2Display->destroyDisplay) (priv->dri2Display);
priv->dri2Display = NULL;
if (priv->dri3Display)
(*priv->dri3Display->destroyDisplay) (priv->dri3Display);
priv->dri3Display = NULL;
#endif /* GLX_USE_DRM */
#if defined(GLX_USE_WINDOWSGL)
if (priv->windowsdriDisplay)
(*priv->windowsdriDisplay->destroyDisplay) (priv->windowsdriDisplay);
priv->windowsdriDisplay = NULL;
#endif /* GLX_USE_WINDOWSGL */
#endif /* GLX_DIRECT_RENDERING && !GLX_USE_APPLEGL */
free((char *) priv);
}
static int
__glXCloseDisplay(Display * dpy, XExtCodes * codes)
{
struct glx_display *priv, **prev;
_XLockMutex(_Xglobal_lock);
prev = &glx_displays;
for (priv = glx_displays; priv; prev = &priv->next, priv = priv->next) {
if (priv->dpy == dpy) {
*prev = priv->next;
break;
}
}
_XUnlockMutex(_Xglobal_lock);
if (priv != NULL)
glx_display_free(priv);
return 1;
}
/*
** Query the version of the GLX extension. This procedure works even if
** the client extension is not completely set up.
*/
static Bool
QueryVersion(Display * dpy, int opcode, int *major, int *minor)
{
2008-10-18 16:00:13 +01:00
xcb_connection_t *c = XGetXCBConnection(dpy);
xcb_glx_query_version_reply_t *reply = xcb_glx_query_version_reply(c,
xcb_glx_query_version
(c,
GLX_MAJOR_VERSION,
GLX_MINOR_VERSION),
NULL);
if (!reply)
return GL_FALSE;
if (reply->major_version != GLX_MAJOR_VERSION) {
2008-10-18 16:00:13 +01:00
free(reply);
return GL_FALSE;
}
*major = reply->major_version;
*minor = min(reply->minor_version, GLX_MINOR_VERSION);
free(reply);
return GL_TRUE;
}
/*
* We don't want to enable this GLX_OML_swap_method in glxext.h,
* because we can't support it. The X server writes it out though,
* so we should handle it somehow, to avoid false warnings.
*/
enum {
IGNORE_GLX_SWAP_METHOD_OML = 0x8060
};
static GLint
convert_from_x_visual_type(int visualType)
{
static const int glx_visual_types[] = {
[StaticGray] = GLX_STATIC_GRAY,
[GrayScale] = GLX_GRAY_SCALE,
[StaticColor] = GLX_STATIC_COLOR,
[PseudoColor] = GLX_PSEUDO_COLOR,
[TrueColor] = GLX_TRUE_COLOR,
[DirectColor] = GLX_DIRECT_COLOR,
};
if (visualType < ARRAY_SIZE(glx_visual_types))
return glx_visual_types[visualType];
return GLX_NONE;
}
/*
* getVisualConfigs uses the !tagged_only path.
* getFBConfigs uses the tagged_only path.
*/
_X_HIDDEN void
__glXInitializeVisualConfigFromTags(struct glx_config * config, int count,
const INT32 * bp, Bool tagged_only,
Bool fbconfig_style_tags)
{
int i;
if (!tagged_only) {
/* Copy in the first set of properties */
config->visualID = *bp++;
config->visualType = convert_from_x_visual_type(*bp++);
config->renderType = *bp++ ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT;
config->redBits = *bp++;
config->greenBits = *bp++;
config->blueBits = *bp++;
config->alphaBits = *bp++;
config->accumRedBits = *bp++;
config->accumGreenBits = *bp++;
config->accumBlueBits = *bp++;
config->accumAlphaBits = *bp++;
config->doubleBufferMode = *bp++;
config->stereoMode = *bp++;
config->rgbBits = *bp++;
config->depthBits = *bp++;
config->stencilBits = *bp++;
config->numAuxBuffers = *bp++;
config->level = *bp++;
#ifdef GLX_USE_APPLEGL
/* AppleSGLX supports pixmap and pbuffers with all config. */
config->drawableType = GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT;
/* Unfortunately this can create an ABI compatibility problem. */
count -= 18;
#else
count -= __GLX_MIN_CONFIG_PROPS;
#endif
}
/*
** Additional properties may be in a list at the end
** of the reply. They are in pairs of property type
** and property value.
*/
#define FETCH_OR_SET(tag) \
config-> tag = ( fbconfig_style_tags ) ? *bp++ : 1
for (i = 0; i < count; i += 2) {
long int tag = *bp++;
switch (tag) {
case GLX_RGBA:
if (fbconfig_style_tags)
config->renderType = *bp++ ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT;
else
config->renderType = GLX_RGBA_BIT;
break;
case GLX_BUFFER_SIZE:
config->rgbBits = *bp++;
break;
case GLX_LEVEL:
config->level = *bp++;
break;
case GLX_DOUBLEBUFFER:
FETCH_OR_SET(doubleBufferMode);
break;
case GLX_STEREO:
FETCH_OR_SET(stereoMode);
break;
case GLX_AUX_BUFFERS:
config->numAuxBuffers = *bp++;
break;
case GLX_RED_SIZE:
config->redBits = *bp++;
break;
case GLX_GREEN_SIZE:
config->greenBits = *bp++;
break;
case GLX_BLUE_SIZE:
config->blueBits = *bp++;
break;
case GLX_ALPHA_SIZE:
config->alphaBits = *bp++;
break;
case GLX_DEPTH_SIZE:
config->depthBits = *bp++;
break;
case GLX_STENCIL_SIZE:
config->stencilBits = *bp++;
break;
case GLX_ACCUM_RED_SIZE:
config->accumRedBits = *bp++;
break;
case GLX_ACCUM_GREEN_SIZE:
config->accumGreenBits = *bp++;
break;
case GLX_ACCUM_BLUE_SIZE:
config->accumBlueBits = *bp++;
break;
case GLX_ACCUM_ALPHA_SIZE:
config->accumAlphaBits = *bp++;
break;
case GLX_VISUAL_CAVEAT_EXT:
config->visualRating = *bp++;
break;
case GLX_X_VISUAL_TYPE:
config->visualType = *bp++;
break;
case GLX_TRANSPARENT_TYPE:
config->transparentPixel = *bp++;
break;
case GLX_TRANSPARENT_INDEX_VALUE:
config->transparentIndex = *bp++;
break;
case GLX_TRANSPARENT_RED_VALUE:
config->transparentRed = *bp++;
break;
case GLX_TRANSPARENT_GREEN_VALUE:
config->transparentGreen = *bp++;
break;
case GLX_TRANSPARENT_BLUE_VALUE:
config->transparentBlue = *bp++;
break;
case GLX_TRANSPARENT_ALPHA_VALUE:
config->transparentAlpha = *bp++;
break;
case GLX_VISUAL_ID:
config->visualID = *bp++;
break;
case GLX_DRAWABLE_TYPE:
config->drawableType = *bp++;
#ifdef GLX_USE_APPLEGL
/* AppleSGLX supports pixmap and pbuffers with all config. */
config->drawableType |= GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT;
#endif
break;
case GLX_RENDER_TYPE: /* fbconfig render type bits */
config->renderType = *bp++;
break;
case GLX_X_RENDERABLE:
config->xRenderable = *bp++;
break;
case GLX_FBCONFIG_ID:
config->fbconfigID = *bp++;
break;
case GLX_MAX_PBUFFER_WIDTH:
config->maxPbufferWidth = *bp++;
break;
case GLX_MAX_PBUFFER_HEIGHT:
config->maxPbufferHeight = *bp++;
break;
case GLX_MAX_PBUFFER_PIXELS:
config->maxPbufferPixels = *bp++;
break;
#ifndef GLX_USE_APPLEGL
case GLX_OPTIMAL_PBUFFER_WIDTH_SGIX:
config->optimalPbufferWidth = *bp++;
break;
case GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX:
config->optimalPbufferHeight = *bp++;
break;
case GLX_VISUAL_SELECT_GROUP_SGIX:
config->visualSelectGroup = *bp++;
break;
case GLX_SWAP_METHOD_OML:
if (*bp == GLX_SWAP_UNDEFINED_OML ||
*bp == GLX_SWAP_COPY_OML ||
*bp == GLX_SWAP_EXCHANGE_OML) {
config->swapMethod = *bp++;
} else {
/* X servers with old HW drivers may return any value here, so
* assume GLX_SWAP_METHOD_UNDEFINED.
*/
config->swapMethod = GLX_SWAP_UNDEFINED_OML;
bp++;
}
break;
#endif
case GLX_SAMPLE_BUFFERS_SGIS:
config->sampleBuffers = *bp++;
break;
case GLX_SAMPLES_SGIS:
config->samples = *bp++;
break;
#ifdef GLX_USE_APPLEGL
case IGNORE_GLX_SWAP_METHOD_OML:
/* We ignore this tag. See the comment above this function. */
++bp;
break;
#else
case GLX_BIND_TO_TEXTURE_RGB_EXT:
config->bindToTextureRgb = *bp++;
break;
case GLX_BIND_TO_TEXTURE_RGBA_EXT:
config->bindToTextureRgba = *bp++;
break;
case GLX_BIND_TO_MIPMAP_TEXTURE_EXT:
config->bindToMipmapTexture = *bp++;
break;
case GLX_BIND_TO_TEXTURE_TARGETS_EXT:
config->bindToTextureTargets = *bp++;
break;
case GLX_Y_INVERTED_EXT:
config->yInverted = *bp++;
break;
#endif
case GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT:
config->sRGBCapable = *bp++;
break;
case GLX_USE_GL:
if (fbconfig_style_tags)
bp++;
break;
case GLX_FLOAT_COMPONENTS_NV:
config->floatComponentsNV = *bp++;
break;
case None:
i = count;
break;
default: {
long int tagvalue = *bp++;
DebugMessageF("WARNING: unknown fbconfig attribute from server: "
"tag 0x%lx value 0x%lx\n", tag, tagvalue);
break;
}
}
}
}
static struct glx_config *
createConfigsFromProperties(Display * dpy, int nvisuals, int nprops,
int screen, GLboolean tagged_only)
{
INT32 buf[__GLX_TOTAL_CONFIG], *props;
unsigned prop_size;
struct glx_config *modes, *m;
int i;
if (nprops == 0)
return NULL;
/* Check number of properties */
if (nprops < __GLX_MIN_CONFIG_PROPS)
return NULL;
/* Allocate memory for our config structure */
modes = glx_config_create_list(nvisuals);
if (!modes)
return NULL;
prop_size = nprops * __GLX_SIZE_INT32;
if (prop_size <= sizeof(buf))
props = buf;
else
props = malloc(prop_size);
/* Read each config structure and convert it into our format */
m = modes;
for (i = 0; i < nvisuals; i++) {
_XRead(dpy, (char *) props, prop_size);
#ifdef GLX_USE_APPLEGL
/* Older X servers don't send this so we default it here. */
m->drawableType = GLX_WINDOW_BIT;
#else
/*
* The XQuartz 2.3.2.1 X server doesn't set this properly, so
* set the proper bits here.
* AppleSGLX supports windows, pixmaps, and pbuffers with all config.
*/
m->drawableType = GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT;
#endif
__glXInitializeVisualConfigFromTags(m, nprops, props,
tagged_only, GL_TRUE);
m->screen = screen;
m = m->next;
}
if (props != buf)
free(props);
return modes;
}
static GLboolean
getVisualConfigs(struct glx_screen *psc,
struct glx_display *priv, int screen)
{
xGLXGetVisualConfigsReq *req;
xGLXGetVisualConfigsReply reply;
Display *dpy = priv->dpy;
LockDisplay(dpy);
psc->visuals = NULL;
GetReq(GLXGetVisualConfigs, req);
req->reqType = priv->codes.major_opcode;
req->glxCode = X_GLXGetVisualConfigs;
req->screen = screen;
if (!_XReply(dpy, (xReply *) & reply, 0, False))
goto out;
psc->visuals = createConfigsFromProperties(dpy,
reply.numVisuals,
reply.numProps,
screen, GL_FALSE);
out:
UnlockDisplay(dpy);
return psc->visuals != NULL;
}
static GLboolean
getFBConfigs(struct glx_screen *psc, struct glx_display *priv, int screen)
{
xGLXGetFBConfigsReq *fb_req;
xGLXGetFBConfigsSGIXReq *sgi_req;
xGLXVendorPrivateWithReplyReq *vpreq;
xGLXGetFBConfigsReply reply;
Display *dpy = priv->dpy;
psc->serverGLXexts = __glXQueryServerString(dpy, screen, GLX_EXTENSIONS);
if (psc->serverGLXexts == NULL) {
return GL_FALSE;
}
LockDisplay(dpy);
psc->configs = NULL;
if (priv->minorVersion >= 3) {
GetReq(GLXGetFBConfigs, fb_req);
fb_req->reqType = priv->codes.major_opcode;
fb_req->glxCode = X_GLXGetFBConfigs;
fb_req->screen = screen;
}
else if (strstr(psc->serverGLXexts, "GLX_SGIX_fbconfig") != NULL) {
GetReqExtra(GLXVendorPrivateWithReply,
sz_xGLXGetFBConfigsSGIXReq -
sz_xGLXVendorPrivateWithReplyReq, vpreq);
sgi_req = (xGLXGetFBConfigsSGIXReq *) vpreq;
sgi_req->reqType = priv->codes.major_opcode;
sgi_req->glxCode = X_GLXVendorPrivateWithReply;
sgi_req->vendorCode = X_GLXvop_GetFBConfigsSGIX;
sgi_req->screen = screen;
}
else
goto out;
if (!_XReply(dpy, (xReply *) & reply, 0, False))
goto out;
psc->configs = createConfigsFromProperties(dpy,
reply.numFBConfigs,
reply.numAttribs * 2,
screen, GL_TRUE);
out:
UnlockDisplay(dpy);
return psc->configs != NULL;
}
_X_HIDDEN Bool
glx_screen_init(struct glx_screen *psc,
int screen, struct glx_display * priv)
{
/* Initialize per screen dynamic client GLX extensions */
psc->ext_list_first_time = GL_TRUE;
psc->scr = screen;
psc->dpy = priv->dpy;
psc->display = priv;
if (!getVisualConfigs(psc, priv, screen))
return GL_FALSE;
if (!getFBConfigs(psc, priv, screen))
return GL_FALSE;
return GL_TRUE;
}
_X_HIDDEN void
glx_screen_cleanup(struct glx_screen *psc)
{
if (psc->configs) {
glx_config_destroy_list(psc->configs);
free(psc->effectiveGLXexts);
psc->configs = NULL; /* NOTE: just for paranoia */
}
if (psc->visuals) {
glx_config_destroy_list(psc->visuals);
psc->visuals = NULL; /* NOTE: just for paranoia */
}
free((char *) psc->serverGLXexts);
free((char *) psc->serverGLXvendor);
free((char *) psc->serverGLXversion);
}
/*
** Allocate the memory for the per screen configs for each screen.
** If that works then fetch the per screen configs data.
*/
static Bool
AllocAndFetchScreenConfigs(Display * dpy, struct glx_display * priv)
{
struct glx_screen *psc;
GLint i, screens;
/*
** First allocate memory for the array of per screen configs.
*/
screens = ScreenCount(dpy);
glx: Fix SEGV due to dereferencing a NULL ptr from XCB-GLX. When run in optirun, applications that linked to `libGLX.so` and then proceeded to querying Mesa for extension strings caused a SEGV in Mesa. `glXQueryExtensionsString` was calling a chain of functions that eventually led to `__glXQueryServerString`. This function would call `xcb_glx_query_server_string` then `xcb_glx_query_server_string_reply`. The latter for some unknown reason returned `NULL`. Passing this `NULL` to `xcb_glx_query_server_string_string_length` would cause a SEGV as the function tried to dereference it. The reason behind the function returning `NULL` is yet to be determined, however, simply checking that the ptr is not `NULL` resolves this. A similar check has been added to `__glXGetString` for completeness sake, although not immediately necessary. In addition to that, we stumbled into a similar problem in `AllocAndFetchScreenConfigs` which tries to access the configs to free them if `__glXQueryServerString` fails. This, of course, SEGVs, because the configs are yet to have been allocated. Simply continuing past the configs if their config ptrs are `NULL` resolves this. We also switch to `calloc` to make sure that the config ptrs are `NULL` by default, and not some uninitialized value. Cc: mesa-stable@lists.freedesktop.org Fixes: 24b8a8cfe821 "glx: implement __glXGetString, hide __glXGetStringFromServer" Fixes: cb3610e37c4c "Import the GLX client side library, formerly from xc/lib/GL/glx. Build it " Reviewed-by: Adam Jackson <ajax@redhat.com> Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
2019-07-25 22:40:50 +01:00
priv->screens = calloc(screens, sizeof *priv->screens);
if (!priv->screens)
return GL_FALSE;
for (i = 0; i < screens; i++, psc++) {
psc = NULL;
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
#if defined(GLX_USE_DRM)
#if defined(HAVE_DRI3)
if (priv->dri3Display)
psc = (*priv->dri3Display->createScreen) (i, priv);
#endif /* HAVE_DRI3 */
if (psc == NULL && priv->dri2Display)
psc = (*priv->dri2Display->createScreen) (i, priv);
#endif /* GLX_USE_DRM */
#ifdef GLX_USE_WINDOWSGL
if (psc == NULL && priv->windowsdriDisplay)
psc = (*priv->windowsdriDisplay->createScreen) (i, priv);
#endif
if (psc == NULL && priv->driswDisplay)
psc = (*priv->driswDisplay->createScreen) (i, priv);
#endif /* GLX_DIRECT_RENDERING && !GLX_USE_APPLEGL */
#if defined(GLX_USE_APPLEGL)
if (psc == NULL)
psc = applegl_create_screen(i, priv);
#else
if (psc == NULL)
psc = indirect_create_screen(i, priv);
#endif
priv->screens[i] = psc;
}
SyncHandle();
return GL_TRUE;
}
/*
** Initialize the client side extension code.
*/
_X_HIDDEN struct glx_display *
__glXInitialize(Display * dpy)
{
XExtCodes *codes;
struct glx_display *dpyPriv, *d;
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
Bool glx_direct, glx_accel;
#endif
int i, majorVersion = 0;
_XLockMutex(_Xglobal_lock);
for (dpyPriv = glx_displays; dpyPriv; dpyPriv = dpyPriv->next) {
if (dpyPriv->dpy == dpy) {
_XUnlockMutex(_Xglobal_lock);
return dpyPriv;
}
}
/* Drop the lock while we create the display private. */
_XUnlockMutex(_Xglobal_lock);
dpyPriv = calloc(1, sizeof *dpyPriv);
if (!dpyPriv)
return NULL;
codes = XInitExtension(dpy, __glXExtensionName);
if (!codes) {
free(dpyPriv);
return NULL;
}
dpyPriv->codes = *codes;
dpyPriv->dpy = dpy;
/* This GLX implementation requires X_GLXQueryExtensionsString
* and X_GLXQueryServerString, which are new in GLX 1.1.
*/
if (!QueryVersion(dpy, dpyPriv->codes.major_opcode,
&majorVersion, &dpyPriv->minorVersion)
|| (majorVersion != 1)
|| (majorVersion == 1 && dpyPriv->minorVersion < 1)) {
free(dpyPriv);
return NULL;
}
for (i = 0; i < __GLX_NUMBER_EVENTS; i++) {
XESetWireToEvent(dpy, dpyPriv->codes.first_event + i, __glXWireToEvent);
XESetEventToWire(dpy, dpyPriv->codes.first_event + i, __glXEventToWire);
}
XESetCloseDisplay(dpy, dpyPriv->codes.extension, __glXCloseDisplay);
XESetErrorString (dpy, dpyPriv->codes.extension, __glXErrorString);
dpyPriv->glXDrawHash = __glxHashCreate();
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
glx_direct = !env_var_as_boolean("LIBGL_ALWAYS_INDIRECT", false);
glx_accel = !env_var_as_boolean("LIBGL_ALWAYS_SOFTWARE", false);
Bool zink;
const char *env = getenv("MESA_LOADER_DRIVER_OVERRIDE");
zink = env && !strcmp(env, "zink");
dpyPriv->drawHash = __glxHashCreate();
dpyPriv->zombieGLXDrawable = _mesa_pointer_set_create(NULL);
#ifndef GLX_USE_APPLEGL
/* Set the logger before the *CreateDisplay functions. */
loader_set_logger(glx_message);
#endif
/*
** Initialize the direct rendering per display data and functions.
** Note: This _must_ be done before calling any other DRI routines
** (e.g., those called in AllocAndFetchScreenConfigs).
*/
#if defined(GLX_USE_DRM)
if (glx_direct && glx_accel && !zink) {
#if defined(HAVE_DRI3)
if (!env_var_as_boolean("LIBGL_DRI3_DISABLE", false))
dpyPriv->dri3Display = dri3_create_display(dpy);
#endif /* HAVE_DRI3 */
if (!env_var_as_boolean("LIBGL_DRI2_DISABLE", false))
dpyPriv->dri2Display = dri2CreateDisplay(dpy);
}
#endif /* GLX_USE_DRM */
if (glx_direct)
dpyPriv->driswDisplay = driswCreateDisplay(dpy, zink);
#endif /* GLX_DIRECT_RENDERING && !GLX_USE_APPLEGL */
#ifdef GLX_USE_APPLEGL
if (!applegl_create_display(dpyPriv)) {
free(dpyPriv);
return NULL;
}
#endif
#ifdef GLX_USE_WINDOWSGL
if (glx_direct && glx_accel)
dpyPriv->windowsdriDisplay = driwindowsCreateDisplay(dpy);
#endif
if (!AllocAndFetchScreenConfigs(dpy, dpyPriv)) {
free(dpyPriv);
return NULL;
}
__glX_send_client_info(dpyPriv);
/* Grab the lock again and add the dispay private, unless somebody
* beat us to initializing on this display in the meantime. */
_XLockMutex(_Xglobal_lock);
for (d = glx_displays; d; d = d->next) {
if (d->dpy == dpy) {
_XUnlockMutex(_Xglobal_lock);
glx_display_free(dpyPriv);
return d;
}
}
dpyPriv->next = glx_displays;
glx_displays = dpyPriv;
_XUnlockMutex(_Xglobal_lock);
return dpyPriv;
}
/*
** Setup for sending a GLX command on dpy. Make sure the extension is
** initialized. Try to avoid calling __glXInitialize as its kinda slow.
*/
_X_HIDDEN CARD8
__glXSetupForCommand(Display * dpy)
{
struct glx_context *gc;
struct glx_display *priv;
/* If this thread has a current context, flush its rendering commands */
gc = __glXGetCurrentContext();
if (gc->currentDpy) {
/* Flush rendering buffer of the current context, if any */
(void) __glXFlushRenderBuffer(gc, gc->pc);
if (gc->currentDpy == dpy) {
/* Use opcode from gc because its right */
return gc->majorOpcode;
}
else {
/*
** Have to get info about argument dpy because it might be to
** a different server
*/
}
}
/* Forced to lookup extension via the slow initialize route */
priv = __glXInitialize(dpy);
if (!priv) {
return 0;
}
return priv->codes.major_opcode;
}
/**
* Flush the drawing command transport buffer.
*
* \param ctx Context whose transport buffer is to be flushed.
* \param pc Pointer to first unused buffer location.
*
* \todo
* Modify this function to use \c ctx->pc instead of the explicit
* \c pc parameter.
*/
_X_HIDDEN GLubyte *
__glXFlushRenderBuffer(struct glx_context * ctx, GLubyte * pc)
{
Display *const dpy = ctx->currentDpy;
xcb_connection_t *c = XGetXCBConnection(dpy);
const GLint size = pc - ctx->buf;
if ((dpy != NULL) && (size > 0)) {
xcb_glx_render(c, ctx->currentContextTag, size,
(const uint8_t *) ctx->buf);
}
/* Reset pointer and return it */
ctx->pc = ctx->buf;
return ctx->pc;
}
/**
* Send a portion of a GLXRenderLarge command to the server. The advantage of
* this function over \c __glXSendLargeCommand is that callers can use the
* data buffer in the GLX context and may be able to avoid allocating an
* extra buffer. The disadvantage is the clients will have to do more
* GLX protocol work (i.e., calculating \c totalRequests, etc.).
*
* \sa __glXSendLargeCommand
*
* \param gc GLX context
* \param requestNumber Which part of the whole command is this? The first
* request is 1.
* \param totalRequests How many requests will there be?
* \param data Command data.
* \param dataLen Size, in bytes, of the command data.
*/
_X_HIDDEN void
__glXSendLargeChunk(struct glx_context * gc, GLint requestNumber,
GLint totalRequests, const GLvoid * data, GLint dataLen)
{
Display *dpy = gc->currentDpy;
xcb_connection_t *c = XGetXCBConnection(dpy);
xcb_glx_render_large(c, gc->currentContextTag, requestNumber,
totalRequests, dataLen, data);
}
/**
* Send a command that is too large for the GLXRender protocol request.
*
* Send a large command, one that is too large for some reason to
* send using the GLXRender protocol request. One reason to send
* a large command is to avoid copying the data.
*
* \param ctx GLX context
* \param header Header data.
* \param headerLen Size, in bytes, of the header data. It is assumed that
* the header data will always be small enough to fit in
* a single X protocol packet.
* \param data Command data.
* \param dataLen Size, in bytes, of the command data.
*/
_X_HIDDEN void
__glXSendLargeCommand(struct glx_context * ctx,
const GLvoid * header, GLint headerLen,
const GLvoid * data, GLint dataLen)
{
GLint maxSize;
GLint totalRequests, requestNumber;
/*
** Calculate the maximum amount of data can be stuffed into a single
** packet. sz_xGLXRenderReq is added because bufSize is the maximum
** packet size minus sz_xGLXRenderReq.
*/
maxSize = (ctx->bufSize + sz_xGLXRenderReq) - sz_xGLXRenderLargeReq;
totalRequests = 1 + (dataLen / maxSize);
if (dataLen % maxSize)
totalRequests++;
/*
** Send all of the command, except the large array, as one request.
*/
assert(headerLen <= maxSize);
__glXSendLargeChunk(ctx, 1, totalRequests, header, headerLen);
/*
** Send enough requests until the whole array is sent.
*/
for (requestNumber = 2; requestNumber <= (totalRequests - 1);
requestNumber++) {
__glXSendLargeChunk(ctx, requestNumber, totalRequests, data, maxSize);
data = (const GLvoid *) (((const GLubyte *) data) + maxSize);
dataLen -= maxSize;
assert(dataLen > 0);
}
assert(dataLen <= maxSize);
__glXSendLargeChunk(ctx, requestNumber, totalRequests, data, dataLen);
}