2013-11-05 02:15:51 +00:00
|
|
|
/*
|
|
|
|
* Copyright © 2013 Keith Packard
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
|
|
* the above copyright notice appear in all copies and that both that copyright
|
|
|
|
* notice and this permission notice appear in supporting documentation, and
|
|
|
|
* that the name of the copyright holders not be used in advertising or
|
|
|
|
* publicity pertaining to distribution of the software without specific,
|
|
|
|
* written prior permission. The copyright holders make no representations
|
|
|
|
* about the suitability of this software for any purpose. It is provided "as
|
|
|
|
* is" without express or implied warranty.
|
|
|
|
*
|
|
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
|
|
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
|
|
* OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Portions of this code were adapted from dri2_glx.c which carries the
|
|
|
|
* following copyright:
|
|
|
|
*
|
|
|
|
* Copyright © 2008 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the "Soft-
|
|
|
|
* ware"), to deal in the Software without restriction, including without
|
|
|
|
* limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
* Software is furnished to do so, provided that the above copyright
|
|
|
|
* notice(s) and this permission notice appear in all copies of the Soft-
|
|
|
|
* ware and that both the above copyright notice(s) and this permission
|
|
|
|
* notice appear in supporting documentation.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
|
|
|
|
* ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
|
|
|
|
* RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
|
|
|
|
* THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
|
|
|
|
* QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
|
|
|
|
* MANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
* Except as contained in this notice, the name of a copyright holder shall
|
|
|
|
* not be used in advertising or otherwise to promote the sale, use or
|
|
|
|
* other dealings in this Software without prior written authorization of
|
|
|
|
* the copyright holder.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Kristian Høgsberg (krh@redhat.com)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/extensions/Xfixes.h>
|
|
|
|
#include <X11/Xlib-xcb.h>
|
|
|
|
#include <X11/xshmfence.h>
|
|
|
|
#include <xcb/xcb.h>
|
|
|
|
#include <xcb/dri3.h>
|
|
|
|
#include <xcb/present.h>
|
|
|
|
#include <GL/gl.h>
|
|
|
|
#include "glxclient.h"
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include "dri_common.h"
|
|
|
|
#include "dri3_priv.h"
|
2014-01-11 02:24:43 +00:00
|
|
|
#include "loader.h"
|
2014-02-20 19:47:14 +00:00
|
|
|
#include "dri2.h"
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
static const struct glx_context_vtable dri3_context_vtable;
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
dri3_fence_reset(xcb_connection_t *c, struct dri3_buffer *buffer)
|
|
|
|
{
|
|
|
|
xshmfence_reset(buffer->shm_fence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
dri3_fence_set(struct dri3_buffer *buffer)
|
|
|
|
{
|
|
|
|
xshmfence_trigger(buffer->shm_fence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
dri3_fence_trigger(xcb_connection_t *c, struct dri3_buffer *buffer)
|
|
|
|
{
|
|
|
|
xcb_sync_trigger_fence(c, buffer->sync_fence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
dri3_fence_await(xcb_connection_t *c, struct dri3_buffer *buffer)
|
|
|
|
{
|
|
|
|
xcb_flush(c);
|
|
|
|
xshmfence_await(buffer->shm_fence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline Bool
|
|
|
|
dri3_fence_triggered(struct dri3_buffer *buffer)
|
|
|
|
{
|
|
|
|
return xshmfence_query(buffer->shm_fence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_destroy_context(struct glx_context *context)
|
|
|
|
{
|
|
|
|
struct dri3_context *pcp = (struct dri3_context *) context;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) context->psc;
|
|
|
|
|
|
|
|
driReleaseDrawables(&pcp->base);
|
|
|
|
|
|
|
|
free((char *) context->extensions);
|
|
|
|
|
|
|
|
(*psc->core->destroyContext) (pcp->driContext);
|
|
|
|
|
|
|
|
free(pcp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
dri3_bind_context(struct glx_context *context, struct glx_context *old,
|
|
|
|
GLXDrawable draw, GLXDrawable read)
|
|
|
|
{
|
|
|
|
struct dri3_context *pcp = (struct dri3_context *) context;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc;
|
|
|
|
struct dri3_drawable *pdraw, *pread;
|
|
|
|
|
|
|
|
pdraw = (struct dri3_drawable *) driFetchDrawable(context, draw);
|
|
|
|
pread = (struct dri3_drawable *) driFetchDrawable(context, read);
|
|
|
|
|
|
|
|
driReleaseDrawables(&pcp->base);
|
|
|
|
|
|
|
|
if (pdraw == NULL || pread == NULL)
|
|
|
|
return GLXBadDrawable;
|
|
|
|
|
|
|
|
if (!(*psc->core->bindContext) (pcp->driContext,
|
|
|
|
pdraw->driDrawable, pread->driDrawable))
|
|
|
|
return GLXBadContext;
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_unbind_context(struct glx_context *context, struct glx_context *new)
|
|
|
|
{
|
|
|
|
struct dri3_context *pcp = (struct dri3_context *) context;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc;
|
|
|
|
|
|
|
|
(*psc->core->unbindContext) (pcp->driContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct glx_context *
|
|
|
|
dri3_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 dri3_context *pcp = NULL;
|
|
|
|
struct dri3_context *pcp_shared = NULL;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) base;
|
|
|
|
__GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
|
|
|
|
__DRIcontext *shared = NULL;
|
|
|
|
|
|
|
|
uint32_t minor_ver = 1;
|
|
|
|
uint32_t major_ver = 2;
|
|
|
|
uint32_t flags = 0;
|
|
|
|
unsigned api;
|
|
|
|
int reset = __DRI_CTX_RESET_NO_NOTIFICATION;
|
|
|
|
uint32_t ctx_attribs[2 * 5];
|
|
|
|
unsigned num_ctx_attribs = 0;
|
|
|
|
uint32_t render_type;
|
|
|
|
|
|
|
|
/* Remap the GLX tokens to DRI2 tokens.
|
|
|
|
*/
|
|
|
|
if (!dri2_convert_glx_attribs(num_attribs, attribs,
|
|
|
|
&major_ver, &minor_ver,
|
|
|
|
&render_type, &flags, &api,
|
|
|
|
&reset, error))
|
|
|
|
goto error_exit;
|
|
|
|
|
|
|
|
/* Check the renderType value */
|
|
|
|
if (!validate_renderType_against_config(config_base, render_type))
|
|
|
|
goto error_exit;
|
|
|
|
|
|
|
|
if (shareList) {
|
|
|
|
pcp_shared = (struct dri3_context *) shareList;
|
|
|
|
shared = pcp_shared->driContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcp = calloc(1, sizeof *pcp);
|
|
|
|
if (pcp == NULL) {
|
|
|
|
*error = __DRI_CTX_ERROR_NO_MEMORY;
|
|
|
|
goto error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!glx_context_init(&pcp->base, &psc->base, &config->base))
|
|
|
|
goto error_exit;
|
|
|
|
|
|
|
|
ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MAJOR_VERSION;
|
|
|
|
ctx_attribs[num_ctx_attribs++] = major_ver;
|
|
|
|
ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MINOR_VERSION;
|
|
|
|
ctx_attribs[num_ctx_attribs++] = minor_ver;
|
|
|
|
|
|
|
|
/* Only send a value when the non-default value is requested. By doing
|
|
|
|
* this we don't have to check the driver's DRI3 version before sending the
|
|
|
|
* default value.
|
|
|
|
*/
|
|
|
|
if (reset != __DRI_CTX_RESET_NO_NOTIFICATION) {
|
|
|
|
ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_RESET_STRATEGY;
|
|
|
|
ctx_attribs[num_ctx_attribs++] = reset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags != 0) {
|
|
|
|
ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_FLAGS;
|
|
|
|
|
|
|
|
/* The current __DRI_CTX_FLAG_* values are identical to the
|
|
|
|
* GLX_CONTEXT_*_BIT values.
|
|
|
|
*/
|
|
|
|
ctx_attribs[num_ctx_attribs++] = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcp->driContext =
|
|
|
|
(*psc->image_driver->createContextAttribs) (psc->driScreen,
|
|
|
|
api,
|
|
|
|
config->driConfig,
|
|
|
|
shared,
|
|
|
|
num_ctx_attribs / 2,
|
|
|
|
ctx_attribs,
|
|
|
|
error,
|
|
|
|
pcp);
|
|
|
|
|
|
|
|
if (pcp->driContext == NULL)
|
|
|
|
goto error_exit;
|
|
|
|
|
|
|
|
pcp->base.vtable = &dri3_context_vtable;
|
|
|
|
|
|
|
|
return &pcp->base;
|
|
|
|
|
|
|
|
error_exit:
|
|
|
|
free(pcp);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-12 00:35:35 +00:00
|
|
|
static struct glx_context *
|
|
|
|
dri3_create_context(struct glx_screen *base,
|
|
|
|
struct glx_config *config_base,
|
|
|
|
struct glx_context *shareList, int renderType)
|
|
|
|
{
|
|
|
|
unsigned int error;
|
|
|
|
|
|
|
|
return dri3_create_context_attribs(base, config_base, shareList,
|
|
|
|
0, NULL, &error);
|
|
|
|
}
|
|
|
|
|
2013-11-22 05:30:07 +00:00
|
|
|
static void
|
|
|
|
dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer);
|
|
|
|
|
2014-01-27 00:14:29 +00:00
|
|
|
static void
|
|
|
|
dri3_update_num_back(struct dri3_drawable *priv)
|
|
|
|
{
|
|
|
|
priv->num_back = 1;
|
glx/dri3: Use four buffers until X driver supports async flips
A driver which doesn't have async flip support will queue up flips without any
way to replace them afterwards. This means we've got a scanout buffer pinned
as soon as we schedule a flip and so we need another buffer to keep from
stalling.
When vblank_mode=0, if there are only three buffers we do:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC 2.
Now we block after this, waiting for one of the three buffers to become idle.
We can't use buffer 0 because it is the scanout buffer. We can't use buffer 1
because it's sitting in the kernel waiting to become the next scanout buffer
and we can't use buffer 2 because that's the most recent frame which will
become the next scanout buffer if the application doesn't manage to generate
another complete frame by MSC 2.
With four buffers, we get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC
2. The X server will queue this swap until buffer 1 becomes
the scanout buffer.
Render frame 3 to buffer 3
PresentPixmap for buffer 3 at MSC 1
As soon as the X server sees this, it will replace the pending
buffer 2 swap with this swap and release buffer 2 back to the
application
Render frame 4 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Now we're in a steady state, flipping between buffer 2 and 3
waiting for one of them to be queued to the kernel.
...
current scanout buffer = 1 at MSC 1
Now buffer 0 is free and (e.g.) buffer 2 is queued in
the kernel to be the scanout buffer at MSC 2
Render frames, flipping between buffer 0 and 3
When the system can replace a queued buffer, and we update Present to take
advantage of that, we can use three buffers and get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting waiting for vblank to become the next scanout
buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Queue this for display at MSC 1
1. There are three possible results:
1) We're still before MSC 1. Buffer 1 is released,
buffer 2 is queued waiting for MSC 1.
2) We're now after MSC 1. Buffer 0 was released at MSC 1.
Buffer 1 is the current scanout buffer.
a) If the user asked for a tearing update, we swap
scanout from buffer 1 to buffer 2 and release buffer
1.
b) If the user asked for non-tearing update, we
queue buffer 2 for the MSC 2.
In all three cases, we have a buffer released (call it 'n'),
ready to receive the next frame.
Render frame 3 to buffer n
PresentPixmap for buffer n
If we're still before MSC 1, then we'll ask to present at MSC
1. Otherwise, we'll ask to present at MSC 2.
Present already does this if the driver offers async flips, however it does
this by waiting for the right vblank event and sending an async flip right at
that point.
I've hacked the intel driver to offer this, but I get tearing at the top of
the screen. I think this is because flips are always done from within the
ring, and so the latency between the vblank event and the async flip happening
can cause tearing at the top of the screen.
That's why I'm keying the need for the extra buffer on the lack of 2D
driver support for async flips.
Signed-off-by: Keith Packard <keithp@keithp.com>
Acked-by: Jason Ekstrand <jason.ekstrand@intel.com>
Tested-by: Dylan Baker <baker.dylan.c@gmail.com>
2014-07-02 21:26:22 +01:00
|
|
|
if (priv->flipping) {
|
|
|
|
if (!priv->is_pixmap && !(priv->present_capabilities & XCB_PRESENT_CAPABILITY_ASYNC))
|
|
|
|
priv->num_back++;
|
2014-01-27 00:14:29 +00:00
|
|
|
priv->num_back++;
|
glx/dri3: Use four buffers until X driver supports async flips
A driver which doesn't have async flip support will queue up flips without any
way to replace them afterwards. This means we've got a scanout buffer pinned
as soon as we schedule a flip and so we need another buffer to keep from
stalling.
When vblank_mode=0, if there are only three buffers we do:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC 2.
Now we block after this, waiting for one of the three buffers to become idle.
We can't use buffer 0 because it is the scanout buffer. We can't use buffer 1
because it's sitting in the kernel waiting to become the next scanout buffer
and we can't use buffer 2 because that's the most recent frame which will
become the next scanout buffer if the application doesn't manage to generate
another complete frame by MSC 2.
With four buffers, we get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC
2. The X server will queue this swap until buffer 1 becomes
the scanout buffer.
Render frame 3 to buffer 3
PresentPixmap for buffer 3 at MSC 1
As soon as the X server sees this, it will replace the pending
buffer 2 swap with this swap and release buffer 2 back to the
application
Render frame 4 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Now we're in a steady state, flipping between buffer 2 and 3
waiting for one of them to be queued to the kernel.
...
current scanout buffer = 1 at MSC 1
Now buffer 0 is free and (e.g.) buffer 2 is queued in
the kernel to be the scanout buffer at MSC 2
Render frames, flipping between buffer 0 and 3
When the system can replace a queued buffer, and we update Present to take
advantage of that, we can use three buffers and get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting waiting for vblank to become the next scanout
buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Queue this for display at MSC 1
1. There are three possible results:
1) We're still before MSC 1. Buffer 1 is released,
buffer 2 is queued waiting for MSC 1.
2) We're now after MSC 1. Buffer 0 was released at MSC 1.
Buffer 1 is the current scanout buffer.
a) If the user asked for a tearing update, we swap
scanout from buffer 1 to buffer 2 and release buffer
1.
b) If the user asked for non-tearing update, we
queue buffer 2 for the MSC 2.
In all three cases, we have a buffer released (call it 'n'),
ready to receive the next frame.
Render frame 3 to buffer n
PresentPixmap for buffer n
If we're still before MSC 1, then we'll ask to present at MSC
1. Otherwise, we'll ask to present at MSC 2.
Present already does this if the driver offers async flips, however it does
this by waiting for the right vblank event and sending an async flip right at
that point.
I've hacked the intel driver to offer this, but I get tearing at the top of
the screen. I think this is because flips are always done from within the
ring, and so the latency between the vblank event and the async flip happening
can cause tearing at the top of the screen.
That's why I'm keying the need for the extra buffer on the lack of 2D
driver support for async flips.
Signed-off-by: Keith Packard <keithp@keithp.com>
Acked-by: Jason Ekstrand <jason.ekstrand@intel.com>
Tested-by: Dylan Baker <baker.dylan.c@gmail.com>
2014-07-02 21:26:22 +01:00
|
|
|
}
|
2014-01-27 00:14:29 +00:00
|
|
|
if (priv->swap_interval == 0)
|
|
|
|
priv->num_back++;
|
|
|
|
}
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
static void
|
|
|
|
dri3_destroy_drawable(__GLXDRIdrawable *base)
|
|
|
|
{
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) base->psc;
|
|
|
|
struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
|
2013-11-22 05:30:07 +00:00
|
|
|
xcb_connection_t *c = XGetXCBConnection(pdraw->base.psc->dpy);
|
|
|
|
int i;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
(*psc->core->destroyDrawable) (pdraw->driDrawable);
|
|
|
|
|
2013-11-22 05:30:07 +00:00
|
|
|
for (i = 0; i < DRI3_NUM_BUFFERS; i++) {
|
|
|
|
if (pdraw->buffers[i])
|
|
|
|
dri3_free_render_buffer(pdraw, pdraw->buffers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pdraw->special_event)
|
|
|
|
xcb_unregister_for_special_event(c, pdraw->special_event);
|
2013-11-05 02:15:51 +00:00
|
|
|
free(pdraw);
|
|
|
|
}
|
|
|
|
|
|
|
|
static __GLXDRIdrawable *
|
|
|
|
dri3_create_drawable(struct glx_screen *base, XID xDrawable,
|
|
|
|
GLXDrawable drawable, struct glx_config *config_base)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *pdraw;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) base;
|
|
|
|
__GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
|
|
|
|
GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
|
|
|
|
|
|
|
|
pdraw = calloc(1, sizeof(*pdraw));
|
|
|
|
if (!pdraw)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pdraw->base.destroyDrawable = dri3_destroy_drawable;
|
|
|
|
pdraw->base.xDrawable = xDrawable;
|
|
|
|
pdraw->base.drawable = drawable;
|
|
|
|
pdraw->base.psc = &psc->base;
|
|
|
|
pdraw->swap_interval = 1; /* default may be overridden below */
|
|
|
|
pdraw->have_back = 0;
|
|
|
|
pdraw->have_fake_front = 0;
|
|
|
|
|
|
|
|
if (psc->config)
|
|
|
|
psc->config->configQueryi(psc->driScreen,
|
|
|
|
"vblank_mode", &vblank_mode);
|
|
|
|
|
|
|
|
switch (vblank_mode) {
|
|
|
|
case DRI_CONF_VBLANK_NEVER:
|
|
|
|
case DRI_CONF_VBLANK_DEF_INTERVAL_0:
|
|
|
|
pdraw->swap_interval = 0;
|
|
|
|
break;
|
|
|
|
case DRI_CONF_VBLANK_DEF_INTERVAL_1:
|
|
|
|
case DRI_CONF_VBLANK_ALWAYS_SYNC:
|
|
|
|
default:
|
|
|
|
pdraw->swap_interval = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-01-27 00:14:29 +00:00
|
|
|
dri3_update_num_back(pdraw);
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
(void) __glXInitialize(psc->base.dpy);
|
|
|
|
|
|
|
|
/* Create a new drawable */
|
|
|
|
pdraw->driDrawable =
|
|
|
|
(*psc->image_driver->createNewDrawable) (psc->driScreen,
|
|
|
|
config->driConfig, pdraw);
|
|
|
|
|
|
|
|
if (!pdraw->driDrawable) {
|
|
|
|
free(pdraw);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure server has the same swap interval we do for the new
|
|
|
|
* drawable.
|
|
|
|
*/
|
|
|
|
if (psc->vtable.setSwapInterval)
|
|
|
|
psc->vtable.setSwapInterval(&pdraw->base, pdraw->swap_interval);
|
|
|
|
|
|
|
|
return &pdraw->base;
|
|
|
|
}
|
|
|
|
|
2014-03-15 01:53:42 +00:00
|
|
|
static void
|
|
|
|
show_fps(struct dri3_drawable *draw, uint64_t current_ust)
|
|
|
|
{
|
|
|
|
const uint64_t interval =
|
|
|
|
((struct dri3_screen *) draw->base.psc)->show_fps_interval;
|
|
|
|
|
|
|
|
draw->frames++;
|
|
|
|
|
|
|
|
/* DRI3+Present together uses microseconds for UST. */
|
|
|
|
if (draw->previous_ust + interval * 1000000 <= current_ust) {
|
|
|
|
if (draw->previous_ust) {
|
|
|
|
fprintf(stderr, "libGL: FPS = %.1f\n",
|
|
|
|
((uint64_t) draw->frames * 1000000) /
|
|
|
|
(double)(current_ust - draw->previous_ust));
|
|
|
|
}
|
|
|
|
draw->frames = 0;
|
|
|
|
draw->previous_ust = current_ust;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
/*
|
|
|
|
* Process one Present event
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_t *ge)
|
|
|
|
{
|
2014-03-15 01:53:42 +00:00
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
switch (ge->evtype) {
|
|
|
|
case XCB_PRESENT_CONFIGURE_NOTIFY: {
|
|
|
|
xcb_present_configure_notify_event_t *ce = (void *) ge;
|
|
|
|
|
|
|
|
priv->width = ce->width;
|
|
|
|
priv->height = ce->height;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case XCB_PRESENT_COMPLETE_NOTIFY: {
|
|
|
|
xcb_present_complete_notify_event_t *ce = (void *) ge;
|
|
|
|
|
2013-11-26 05:14:55 +00:00
|
|
|
/* Compute the processed SBC number from the received 32-bit serial number merged
|
|
|
|
* with the upper 32-bits of the sent 64-bit serial number while checking for
|
|
|
|
* wrap
|
|
|
|
*/
|
|
|
|
if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
|
|
|
|
priv->recv_sbc = (priv->send_sbc & 0xffffffff00000000LL) | ce->serial;
|
|
|
|
if (priv->recv_sbc > priv->send_sbc)
|
|
|
|
priv->recv_sbc -= 0x100000000;
|
2014-01-27 00:14:29 +00:00
|
|
|
switch (ce->mode) {
|
|
|
|
case XCB_PRESENT_COMPLETE_MODE_FLIP:
|
|
|
|
priv->flipping = true;
|
|
|
|
break;
|
|
|
|
case XCB_PRESENT_COMPLETE_MODE_COPY:
|
|
|
|
priv->flipping = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dri3_update_num_back(priv);
|
2014-03-15 01:53:42 +00:00
|
|
|
|
|
|
|
if (psc->show_fps_interval)
|
|
|
|
show_fps(priv, ce->ust);
|
glx/dri3: Track separate (ust, msc) for PresentPixmap vs. PresentNotifyMsc (v2)
Prevent calls to glXGetSyncValuesOML() and glXWaitForMscOML()
from overwriting the (ust,msc) values of the last successfull
swapbuffers call (PresentPixmapCompleteNotify event), as
glXWaitForSbcOML() relies on those values corresponding to
the most recent completed swap, not to whatever was last
returned from the server.
Problematic call sequence without this patch would have been, e.g.,
glXSwapBuffers()
... wait ...
swap completes -> PresentPixmapComplete event -> (ust,msc)
updated to reflect swap completion time and count.
... wait for at least 1 video refresh cycle/vblank increment.
glXGetSyncValuesOML()
-> PresentNotifyMsc event overwrites (ust,msc) of swap
completion with (ust,msc) of most recent vblank
glXWaitForSbcOML()
-> Returns sbc of last completed swap but (ust,msc) of last
completed vblank, not of last completed swap.
-> Client is confused.
Do this by tracking a separate set of (ust, msc) for the
dri3_wait_for_msc() call than for the dri3_wait_for_sbc()
call.
This makes the glXWaitForSbcOML() call robust again and restores
consistent behaviour with the DRI2 implementation.
Fixes applications originally written and tested against
DRI2 which also rely on this not regressing under DRI3/Present,
e.g., Neuro-Science software like Psychtoolbox-3.
This patch fixes the problem.
v2: Rename vblank_msc/ust to notify_msc/ust as suggested by
Axel Davy for better clarity.
Cc: "10.3 10.4" <mesa-stable@lists.freedesktop.org>
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
2014-12-05 07:42:30 +00:00
|
|
|
|
|
|
|
priv->ust = ce->ust;
|
|
|
|
priv->msc = ce->msc;
|
2013-11-26 05:14:55 +00:00
|
|
|
} else {
|
|
|
|
priv->recv_msc_serial = ce->serial;
|
glx/dri3: Track separate (ust, msc) for PresentPixmap vs. PresentNotifyMsc (v2)
Prevent calls to glXGetSyncValuesOML() and glXWaitForMscOML()
from overwriting the (ust,msc) values of the last successfull
swapbuffers call (PresentPixmapCompleteNotify event), as
glXWaitForSbcOML() relies on those values corresponding to
the most recent completed swap, not to whatever was last
returned from the server.
Problematic call sequence without this patch would have been, e.g.,
glXSwapBuffers()
... wait ...
swap completes -> PresentPixmapComplete event -> (ust,msc)
updated to reflect swap completion time and count.
... wait for at least 1 video refresh cycle/vblank increment.
glXGetSyncValuesOML()
-> PresentNotifyMsc event overwrites (ust,msc) of swap
completion with (ust,msc) of most recent vblank
glXWaitForSbcOML()
-> Returns sbc of last completed swap but (ust,msc) of last
completed vblank, not of last completed swap.
-> Client is confused.
Do this by tracking a separate set of (ust, msc) for the
dri3_wait_for_msc() call than for the dri3_wait_for_sbc()
call.
This makes the glXWaitForSbcOML() call robust again and restores
consistent behaviour with the DRI2 implementation.
Fixes applications originally written and tested against
DRI2 which also rely on this not regressing under DRI3/Present,
e.g., Neuro-Science software like Psychtoolbox-3.
This patch fixes the problem.
v2: Rename vblank_msc/ust to notify_msc/ust as suggested by
Axel Davy for better clarity.
Cc: "10.3 10.4" <mesa-stable@lists.freedesktop.org>
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
2014-12-05 07:42:30 +00:00
|
|
|
priv->notify_ust = ce->ust;
|
|
|
|
priv->notify_msc = ce->msc;
|
2013-11-26 05:14:55 +00:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
|
|
|
|
xcb_present_idle_notify_event_t *ie = (void *) ge;
|
|
|
|
int b;
|
|
|
|
|
|
|
|
for (b = 0; b < sizeof (priv->buffers) / sizeof (priv->buffers[0]); b++) {
|
|
|
|
struct dri3_buffer *buf = priv->buffers[b];
|
|
|
|
|
|
|
|
if (buf && buf->pixmap == ie->pixmap) {
|
|
|
|
buf->busy = 0;
|
2014-01-27 00:14:29 +00:00
|
|
|
if (priv->num_back <= b && b < DRI3_MAX_BACK) {
|
|
|
|
dri3_free_render_buffer(priv, buf);
|
|
|
|
priv->buffers[b] = NULL;
|
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(ge);
|
|
|
|
}
|
|
|
|
|
2013-11-26 05:21:40 +00:00
|
|
|
static bool
|
|
|
|
dri3_wait_for_event(__GLXDRIdrawable *pdraw)
|
|
|
|
{
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
|
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
|
|
|
xcb_generic_event_t *ev;
|
|
|
|
xcb_present_generic_event_t *ge;
|
|
|
|
|
2013-11-26 06:57:42 +00:00
|
|
|
xcb_flush(c);
|
2013-11-26 05:21:40 +00:00
|
|
|
ev = xcb_wait_for_special_event(c, priv->special_event);
|
|
|
|
if (!ev)
|
|
|
|
return false;
|
|
|
|
ge = (void *) ev;
|
|
|
|
dri3_handle_present_event(priv, ge);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_wait_for_msc
|
|
|
|
*
|
|
|
|
* Get the X server to send an event when the target msc/divisor/remainder is
|
|
|
|
* reached.
|
|
|
|
*/
|
2013-11-05 02:15:51 +00:00
|
|
|
static int
|
|
|
|
dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
|
|
|
|
int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc)
|
|
|
|
{
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
|
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
2013-11-26 05:14:55 +00:00
|
|
|
uint32_t msc_serial;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
/* Ask for the an event for the target MSC */
|
2013-11-26 05:14:55 +00:00
|
|
|
msc_serial = ++priv->send_msc_serial;
|
2013-11-05 02:15:51 +00:00
|
|
|
xcb_present_notify_msc(c,
|
|
|
|
priv->base.xDrawable,
|
2013-11-26 05:14:55 +00:00
|
|
|
msc_serial,
|
2013-11-05 02:15:51 +00:00
|
|
|
target_msc,
|
|
|
|
divisor,
|
|
|
|
remainder);
|
|
|
|
|
|
|
|
xcb_flush(c);
|
|
|
|
|
|
|
|
/* Wait for the event */
|
|
|
|
if (priv->special_event) {
|
2013-11-26 05:14:55 +00:00
|
|
|
while ((int32_t) (msc_serial - priv->recv_msc_serial) > 0) {
|
2013-11-26 05:21:40 +00:00
|
|
|
if (!dri3_wait_for_event(pdraw))
|
|
|
|
return 0;
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
glx/dri3: Track separate (ust, msc) for PresentPixmap vs. PresentNotifyMsc (v2)
Prevent calls to glXGetSyncValuesOML() and glXWaitForMscOML()
from overwriting the (ust,msc) values of the last successfull
swapbuffers call (PresentPixmapCompleteNotify event), as
glXWaitForSbcOML() relies on those values corresponding to
the most recent completed swap, not to whatever was last
returned from the server.
Problematic call sequence without this patch would have been, e.g.,
glXSwapBuffers()
... wait ...
swap completes -> PresentPixmapComplete event -> (ust,msc)
updated to reflect swap completion time and count.
... wait for at least 1 video refresh cycle/vblank increment.
glXGetSyncValuesOML()
-> PresentNotifyMsc event overwrites (ust,msc) of swap
completion with (ust,msc) of most recent vblank
glXWaitForSbcOML()
-> Returns sbc of last completed swap but (ust,msc) of last
completed vblank, not of last completed swap.
-> Client is confused.
Do this by tracking a separate set of (ust, msc) for the
dri3_wait_for_msc() call than for the dri3_wait_for_sbc()
call.
This makes the glXWaitForSbcOML() call robust again and restores
consistent behaviour with the DRI2 implementation.
Fixes applications originally written and tested against
DRI2 which also rely on this not regressing under DRI3/Present,
e.g., Neuro-Science software like Psychtoolbox-3.
This patch fixes the problem.
v2: Rename vblank_msc/ust to notify_msc/ust as suggested by
Axel Davy for better clarity.
Cc: "10.3 10.4" <mesa-stable@lists.freedesktop.org>
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
2014-12-05 07:42:30 +00:00
|
|
|
*ust = priv->notify_ust;
|
|
|
|
*msc = priv->notify_msc;
|
2013-11-26 05:14:55 +00:00
|
|
|
*sbc = priv->recv_sbc;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-11-26 05:21:40 +00:00
|
|
|
/** dri3_drawable_get_msc
|
|
|
|
*
|
|
|
|
* Return the current UST/MSC/SBC triplet by asking the server
|
|
|
|
* for an event
|
|
|
|
*/
|
2013-11-05 02:15:51 +00:00
|
|
|
static int
|
|
|
|
dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw,
|
|
|
|
int64_t *ust, int64_t *msc, int64_t *sbc)
|
|
|
|
{
|
|
|
|
return dri3_wait_for_msc(pdraw, 0, 0, 0, ust, msc,sbc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_wait_for_sbc
|
|
|
|
*
|
2013-11-26 05:21:40 +00:00
|
|
|
* Wait for the completed swap buffer count to reach the specified
|
|
|
|
* target. Presumably the application knows that this will be reached with
|
|
|
|
* outstanding complete events, or we're going to be here awhile.
|
2013-11-05 02:15:51 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust,
|
|
|
|
int64_t *msc, int64_t *sbc)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
|
|
|
|
2014-12-05 07:42:29 +00:00
|
|
|
/* From the GLX_OML_sync_control spec:
|
|
|
|
*
|
|
|
|
* "If <target_sbc> = 0, the function will block until all previous
|
|
|
|
* swaps requested with glXSwapBuffersMscOML for that window have
|
|
|
|
* completed."
|
|
|
|
*/
|
|
|
|
if (!target_sbc)
|
|
|
|
target_sbc = priv->send_sbc;
|
|
|
|
|
2013-11-26 05:21:40 +00:00
|
|
|
while (priv->recv_sbc < target_sbc) {
|
|
|
|
if (!dri3_wait_for_event(pdraw))
|
|
|
|
return 0;
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
2013-11-26 05:21:40 +00:00
|
|
|
|
|
|
|
*ust = priv->ust;
|
|
|
|
*msc = priv->msc;
|
|
|
|
*sbc = priv->recv_sbc;
|
|
|
|
return 1;
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Asks the driver to flush any queued work necessary for serializing with the
|
|
|
|
* X command stream, and optionally the slightly more strict requirement of
|
|
|
|
* glFlush() equivalence (which would require flushing even if nothing had
|
|
|
|
* been drawn to a window system framebuffer, for example).
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_flush(struct dri3_screen *psc,
|
|
|
|
struct dri3_drawable *draw,
|
|
|
|
unsigned flags,
|
|
|
|
enum __DRI2throttleReason throttle_reason)
|
|
|
|
{
|
|
|
|
struct glx_context *gc = __glXGetCurrentContext();
|
|
|
|
|
|
|
|
if (gc) {
|
|
|
|
struct dri3_context *dri3Ctx = (struct dri3_context *)gc;
|
|
|
|
|
|
|
|
(*psc->f->flush_with_flags)(dri3Ctx->driContext, draw->driDrawable, flags, throttle_reason);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static xcb_gcontext_t
|
|
|
|
dri3_drawable_gc(struct dri3_drawable *priv)
|
|
|
|
{
|
|
|
|
if (!priv->gc) {
|
|
|
|
uint32_t v;
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
|
|
|
|
|
|
|
|
v = 0;
|
|
|
|
xcb_create_gc(c,
|
|
|
|
(priv->gc = xcb_generate_id(c)),
|
|
|
|
priv->base.xDrawable,
|
|
|
|
XCB_GC_GRAPHICS_EXPOSURES,
|
|
|
|
&v);
|
|
|
|
}
|
|
|
|
return priv->gc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dri3_buffer *
|
|
|
|
dri3_back_buffer(struct dri3_drawable *priv)
|
|
|
|
{
|
|
|
|
return priv->buffers[DRI3_BACK_ID(priv->cur_back)];
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dri3_buffer *
|
|
|
|
dri3_fake_front_buffer(struct dri3_drawable *priv)
|
|
|
|
{
|
|
|
|
return priv->buffers[DRI3_FRONT_ID];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_copy_area (xcb_connection_t *c /**< */,
|
|
|
|
xcb_drawable_t src_drawable /**< */,
|
|
|
|
xcb_drawable_t dst_drawable /**< */,
|
|
|
|
xcb_gcontext_t gc /**< */,
|
|
|
|
int16_t src_x /**< */,
|
|
|
|
int16_t src_y /**< */,
|
|
|
|
int16_t dst_x /**< */,
|
|
|
|
int16_t dst_y /**< */,
|
|
|
|
uint16_t width /**< */,
|
|
|
|
uint16_t height /**< */)
|
|
|
|
{
|
|
|
|
xcb_void_cookie_t cookie;
|
|
|
|
|
|
|
|
cookie = xcb_copy_area_checked(c,
|
|
|
|
src_drawable,
|
|
|
|
dst_drawable,
|
|
|
|
gc,
|
|
|
|
src_x,
|
|
|
|
src_y,
|
|
|
|
dst_x,
|
|
|
|
dst_y,
|
|
|
|
width,
|
|
|
|
height);
|
|
|
|
xcb_discard_reply(c, cookie.sequence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y,
|
|
|
|
int width, int height,
|
|
|
|
Bool flush)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) pdraw->psc;
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext();
|
2013-11-05 02:15:51 +00:00
|
|
|
xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_buffer *back;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
2014-05-17 17:12:11 +01:00
|
|
|
unsigned flags = __DRI2_FLUSH_DRAWABLE;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
/* Check we have the right attachments */
|
|
|
|
if (!priv->have_back || priv->is_pixmap)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (flush)
|
|
|
|
flags |= __DRI2_FLUSH_CONTEXT;
|
|
|
|
dri3_flush(psc, priv, flags, __DRI2_THROTTLE_SWAPBUFFER);
|
|
|
|
|
2014-05-17 17:12:11 +01:00
|
|
|
back = dri3_back_buffer(priv);
|
2013-11-05 02:15:51 +00:00
|
|
|
y = priv->height - y - height;
|
|
|
|
|
2014-05-17 17:12:11 +01:00
|
|
|
if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) {
|
|
|
|
/* Update the linear buffer part of the back buffer
|
|
|
|
* for the dri3_copy_area operation
|
|
|
|
*/
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
back->linear_buffer,
|
|
|
|
back->image,
|
|
|
|
0, 0, back->width,
|
|
|
|
back->height,
|
|
|
|
0, 0, back->width,
|
|
|
|
back->height, __BLIT_FLAG_FLUSH);
|
|
|
|
/* We use blitImage to update our fake front,
|
|
|
|
*/
|
|
|
|
if (priv->have_fake_front)
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
dri3_fake_front_buffer(priv)->image,
|
|
|
|
back->image,
|
|
|
|
x, y, width, height,
|
|
|
|
x, y, width, height, __BLIT_FLAG_FLUSH);
|
|
|
|
}
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
dri3_fence_reset(c, back);
|
|
|
|
dri3_copy_area(c,
|
|
|
|
dri3_back_buffer(priv)->pixmap,
|
|
|
|
priv->base.xDrawable,
|
|
|
|
dri3_drawable_gc(priv),
|
|
|
|
x, y, x, y, width, height);
|
|
|
|
dri3_fence_trigger(c, back);
|
|
|
|
/* Refresh the fake front (if present) after we just damaged the real
|
|
|
|
* front.
|
|
|
|
*/
|
2014-05-17 17:12:11 +01:00
|
|
|
if (priv->have_fake_front && !psc->is_different_gpu) {
|
2013-11-05 02:15:51 +00:00
|
|
|
dri3_fence_reset(c, dri3_fake_front_buffer(priv));
|
|
|
|
dri3_copy_area(c,
|
|
|
|
dri3_back_buffer(priv)->pixmap,
|
|
|
|
dri3_fake_front_buffer(priv)->pixmap,
|
|
|
|
dri3_drawable_gc(priv),
|
|
|
|
x, y, x, y, width, height);
|
|
|
|
dri3_fence_trigger(c, dri3_fake_front_buffer(priv));
|
|
|
|
dri3_fence_await(c, dri3_fake_front_buffer(priv));
|
|
|
|
}
|
|
|
|
dri3_fence_await(c, back);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src)
|
|
|
|
{
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
|
|
|
|
|
|
|
|
dri3_flush(psc, priv, __DRI2_FLUSH_DRAWABLE, 0);
|
|
|
|
|
|
|
|
dri3_fence_reset(c, dri3_fake_front_buffer(priv));
|
|
|
|
dri3_copy_area(c,
|
|
|
|
src, dest,
|
|
|
|
dri3_drawable_gc(priv),
|
|
|
|
0, 0, 0, 0, priv->width, priv->height);
|
|
|
|
dri3_fence_trigger(c, dri3_fake_front_buffer(priv));
|
|
|
|
dri3_fence_await(c, dri3_fake_front_buffer(priv));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_wait_x(struct glx_context *gc)
|
|
|
|
{
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_context *pcp = (struct dri3_context *) gc;
|
2013-11-05 02:15:51 +00:00
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *)
|
|
|
|
GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_screen *psc;
|
|
|
|
struct dri3_buffer *front;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
if (priv == NULL || !priv->have_fake_front)
|
|
|
|
return;
|
|
|
|
|
2014-05-17 17:12:11 +01:00
|
|
|
psc = (struct dri3_screen *) priv->base.psc;
|
|
|
|
front = dri3_fake_front_buffer(priv);
|
|
|
|
|
|
|
|
dri3_copy_drawable(priv, front->pixmap, priv->base.xDrawable);
|
|
|
|
|
|
|
|
/* In the psc->is_different_gpu case, the linear buffer has been updated,
|
|
|
|
* but not yet the tiled buffer.
|
|
|
|
* Copy back to the tiled buffer we use for rendering.
|
|
|
|
* Note that we don't need flushing.
|
|
|
|
*/
|
|
|
|
if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base)
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
front->image,
|
|
|
|
front->linear_buffer,
|
|
|
|
0, 0, front->width,
|
|
|
|
front->height,
|
|
|
|
0, 0, front->width,
|
|
|
|
front->height, 0);
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_wait_gl(struct glx_context *gc)
|
|
|
|
{
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_context *pcp = (struct dri3_context *) gc;
|
2013-11-05 02:15:51 +00:00
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *)
|
|
|
|
GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_screen *psc;
|
|
|
|
struct dri3_buffer *front;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
if (priv == NULL || !priv->have_fake_front)
|
|
|
|
return;
|
|
|
|
|
2014-05-17 17:12:11 +01:00
|
|
|
psc = (struct dri3_screen *) priv->base.psc;
|
|
|
|
front = dri3_fake_front_buffer(priv);
|
|
|
|
|
|
|
|
/* In the psc->is_different_gpu case, we update the linear_buffer
|
|
|
|
* before updating the real front.
|
|
|
|
*/
|
|
|
|
if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base)
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
front->linear_buffer,
|
|
|
|
front->image,
|
|
|
|
0, 0, front->width,
|
|
|
|
front->height,
|
|
|
|
0, 0, front->width,
|
|
|
|
front->height, __BLIT_FLAG_FLUSH);
|
|
|
|
dri3_copy_drawable(priv, priv->base.xDrawable, front->pixmap);
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called by the driver when it needs to update the real front buffer with the
|
|
|
|
* contents of its fake front buffer.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
|
|
|
|
{
|
|
|
|
struct glx_context *gc;
|
|
|
|
struct dri3_drawable *pdraw = loaderPrivate;
|
|
|
|
struct dri3_screen *psc;
|
|
|
|
|
|
|
|
if (!pdraw)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!pdraw->base.psc)
|
|
|
|
return;
|
|
|
|
|
|
|
|
psc = (struct dri3_screen *) pdraw->base.psc;
|
|
|
|
|
|
|
|
(void) __glXInitialize(psc->base.dpy);
|
|
|
|
|
|
|
|
gc = __glXGetCurrentContext();
|
|
|
|
|
|
|
|
dri3_flush(psc, pdraw, __DRI2_FLUSH_DRAWABLE, __DRI2_THROTTLE_FLUSHFRONT);
|
|
|
|
|
|
|
|
dri3_wait_gl(gc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
dri3_cpp_for_format(uint32_t format) {
|
|
|
|
switch (format) {
|
|
|
|
case __DRI_IMAGE_FORMAT_R8:
|
|
|
|
return 1;
|
|
|
|
case __DRI_IMAGE_FORMAT_RGB565:
|
|
|
|
case __DRI_IMAGE_FORMAT_GR88:
|
|
|
|
return 2;
|
|
|
|
case __DRI_IMAGE_FORMAT_XRGB8888:
|
|
|
|
case __DRI_IMAGE_FORMAT_ARGB8888:
|
|
|
|
case __DRI_IMAGE_FORMAT_ABGR8888:
|
|
|
|
case __DRI_IMAGE_FORMAT_XBGR8888:
|
|
|
|
case __DRI_IMAGE_FORMAT_XRGB2101010:
|
|
|
|
case __DRI_IMAGE_FORMAT_ARGB2101010:
|
|
|
|
case __DRI_IMAGE_FORMAT_SARGB8:
|
|
|
|
return 4;
|
|
|
|
case __DRI_IMAGE_FORMAT_NONE:
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** dri3_alloc_render_buffer
|
|
|
|
*
|
|
|
|
* Use the driver createImage function to construct a __DRIimage, then
|
|
|
|
* get a file descriptor for that and create an X pixmap from that
|
|
|
|
*
|
|
|
|
* Allocate an xshmfence for synchronization
|
|
|
|
*/
|
|
|
|
static struct dri3_buffer *
|
|
|
|
dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw,
|
|
|
|
unsigned int format, int width, int height, int depth)
|
|
|
|
{
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) glx_screen;
|
|
|
|
Display *dpy = glx_screen->dpy;
|
|
|
|
struct dri3_buffer *buffer;
|
2014-05-17 17:12:11 +01:00
|
|
|
__DRIimage *pixmap_buffer;
|
2013-11-05 02:15:51 +00:00
|
|
|
xcb_connection_t *c = XGetXCBConnection(dpy);
|
|
|
|
xcb_pixmap_t pixmap;
|
|
|
|
xcb_sync_fence_t sync_fence;
|
2013-11-22 04:22:16 +00:00
|
|
|
struct xshmfence *shm_fence;
|
2013-11-05 02:15:51 +00:00
|
|
|
int buffer_fd, fence_fd;
|
|
|
|
int stride;
|
|
|
|
|
|
|
|
/* Create an xshmfence object and
|
|
|
|
* prepare to send that to the X server
|
|
|
|
*/
|
|
|
|
|
|
|
|
fence_fd = xshmfence_alloc_shm();
|
2014-10-01 04:03:29 +01:00
|
|
|
if (fence_fd < 0) {
|
|
|
|
ErrorMessageF("DRI3 Fence object allocation failure %s\n", strerror(errno));
|
2013-11-05 02:15:51 +00:00
|
|
|
return NULL;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
shm_fence = xshmfence_map_shm(fence_fd);
|
2014-10-01 04:03:29 +01:00
|
|
|
if (shm_fence == NULL) {
|
|
|
|
ErrorMessageF("DRI3 Fence object map failure %s\n", strerror(errno));
|
2013-11-05 02:15:51 +00:00
|
|
|
goto no_shm_fence;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
/* Allocate the image from the driver
|
|
|
|
*/
|
|
|
|
buffer = calloc(1, sizeof (struct dri3_buffer));
|
|
|
|
if (!buffer)
|
|
|
|
goto no_buffer;
|
|
|
|
|
|
|
|
buffer->cpp = dri3_cpp_for_format(format);
|
2014-10-01 04:03:29 +01:00
|
|
|
if (!buffer->cpp) {
|
|
|
|
ErrorMessageF("DRI3 buffer format %d invalid\n", format);
|
2013-11-05 02:15:51 +00:00
|
|
|
goto no_image;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
|
2014-05-17 17:12:11 +01:00
|
|
|
if (!psc->is_different_gpu) {
|
|
|
|
buffer->image = (*psc->image->createImage) (psc->driScreen,
|
|
|
|
width, height,
|
|
|
|
format,
|
|
|
|
__DRI_IMAGE_USE_SHARE |
|
|
|
|
__DRI_IMAGE_USE_SCANOUT,
|
|
|
|
buffer);
|
|
|
|
pixmap_buffer = buffer->image;
|
|
|
|
|
2014-10-01 04:03:29 +01:00
|
|
|
if (!buffer->image) {
|
|
|
|
ErrorMessageF("DRI3 gpu image creation failure\n");
|
2014-05-17 17:12:11 +01:00
|
|
|
goto no_image;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2014-05-17 17:12:11 +01:00
|
|
|
} else {
|
|
|
|
buffer->image = (*psc->image->createImage) (psc->driScreen,
|
|
|
|
width, height,
|
|
|
|
format,
|
|
|
|
0,
|
|
|
|
buffer);
|
|
|
|
|
2014-10-01 04:03:29 +01:00
|
|
|
if (!buffer->image) {
|
|
|
|
ErrorMessageF("DRI3 other gpu image creation failure\n");
|
2014-05-17 17:12:11 +01:00
|
|
|
goto no_image;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2014-05-17 17:12:11 +01:00
|
|
|
|
|
|
|
buffer->linear_buffer = (*psc->image->createImage) (psc->driScreen,
|
|
|
|
width, height,
|
|
|
|
format,
|
|
|
|
__DRI_IMAGE_USE_SHARE |
|
|
|
|
__DRI_IMAGE_USE_LINEAR,
|
|
|
|
buffer);
|
|
|
|
pixmap_buffer = buffer->linear_buffer;
|
|
|
|
|
2014-10-01 04:03:29 +01:00
|
|
|
if (!buffer->linear_buffer) {
|
|
|
|
ErrorMessageF("DRI3 gpu linear image creation failure\n");
|
2014-05-17 17:12:11 +01:00
|
|
|
goto no_linear_buffer;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2014-05-17 17:12:11 +01:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
/* X wants the stride, so ask the image for it
|
|
|
|
*/
|
2014-10-01 04:03:29 +01:00
|
|
|
if (!(*psc->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE, &stride)) {
|
|
|
|
ErrorMessageF("DRI3 get image stride failed\n");
|
2013-11-05 02:15:51 +00:00
|
|
|
goto no_buffer_attrib;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
buffer->pitch = stride;
|
|
|
|
|
2014-10-01 04:03:29 +01:00
|
|
|
if (!(*psc->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD, &buffer_fd)) {
|
|
|
|
ErrorMessageF("DRI3 get image FD failed\n");
|
2013-11-05 02:15:51 +00:00
|
|
|
goto no_buffer_attrib;
|
2014-10-01 04:03:29 +01:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
xcb_dri3_pixmap_from_buffer(c,
|
|
|
|
(pixmap = xcb_generate_id(c)),
|
|
|
|
draw,
|
|
|
|
buffer->size,
|
|
|
|
width, height, buffer->pitch,
|
|
|
|
depth, buffer->cpp * 8,
|
|
|
|
buffer_fd);
|
|
|
|
|
|
|
|
xcb_dri3_fence_from_fd(c,
|
|
|
|
pixmap,
|
|
|
|
(sync_fence = xcb_generate_id(c)),
|
|
|
|
false,
|
|
|
|
fence_fd);
|
|
|
|
|
|
|
|
buffer->pixmap = pixmap;
|
2013-11-22 05:30:07 +00:00
|
|
|
buffer->own_pixmap = true;
|
2013-11-05 02:15:51 +00:00
|
|
|
buffer->sync_fence = sync_fence;
|
|
|
|
buffer->shm_fence = shm_fence;
|
|
|
|
buffer->width = width;
|
|
|
|
buffer->height = height;
|
|
|
|
|
|
|
|
/* Mark the buffer as idle
|
|
|
|
*/
|
|
|
|
dri3_fence_set(buffer);
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
|
|
|
|
no_buffer_attrib:
|
2014-05-17 17:12:11 +01:00
|
|
|
(*psc->image->destroyImage)(pixmap_buffer);
|
|
|
|
no_linear_buffer:
|
|
|
|
if (psc->is_different_gpu)
|
|
|
|
(*psc->image->destroyImage)(buffer->image);
|
2013-11-05 02:15:51 +00:00
|
|
|
no_image:
|
|
|
|
free(buffer);
|
|
|
|
no_buffer:
|
|
|
|
xshmfence_unmap_shm(shm_fence);
|
|
|
|
no_shm_fence:
|
|
|
|
close(fence_fd);
|
2014-10-01 04:03:29 +01:00
|
|
|
ErrorMessageF("DRI3 alloc_render_buffer failed\n");
|
2013-11-05 02:15:51 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_free_render_buffer
|
|
|
|
*
|
|
|
|
* Free everything associated with one render buffer including pixmap, fence
|
|
|
|
* stuff and the driver image
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer)
|
|
|
|
{
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) pdraw->base.psc;
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(pdraw->base.psc->dpy);
|
|
|
|
|
2013-11-22 05:30:07 +00:00
|
|
|
if (buffer->own_pixmap)
|
|
|
|
xcb_free_pixmap(c, buffer->pixmap);
|
2013-11-05 02:15:51 +00:00
|
|
|
xcb_sync_destroy_fence(c, buffer->sync_fence);
|
|
|
|
xshmfence_unmap_shm(buffer->shm_fence);
|
|
|
|
(*psc->image->destroyImage)(buffer->image);
|
2014-05-17 17:12:11 +01:00
|
|
|
if (buffer->linear_buffer)
|
|
|
|
(*psc->image->destroyImage)(buffer->linear_buffer);
|
2013-11-05 02:15:51 +00:00
|
|
|
free(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** dri3_flush_present_events
|
|
|
|
*
|
|
|
|
* Process any present events that have been received from the X server
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_flush_present_events(struct dri3_drawable *priv)
|
|
|
|
{
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
|
|
|
|
|
|
|
|
/* Check to see if any configuration changes have occurred
|
|
|
|
* since we were last invoked
|
|
|
|
*/
|
|
|
|
if (priv->special_event) {
|
|
|
|
xcb_generic_event_t *ev;
|
|
|
|
|
|
|
|
while ((ev = xcb_poll_for_special_event(c, priv->special_event)) != NULL) {
|
|
|
|
xcb_present_generic_event_t *ge = (void *) ev;
|
|
|
|
dri3_handle_present_event(priv, ge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_update_drawable
|
|
|
|
*
|
|
|
|
* Called the first time we use the drawable and then
|
|
|
|
* after we receive present configure notify events to
|
|
|
|
* track the geometry of the drawable
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *priv = loaderPrivate;
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
|
|
|
|
|
|
|
|
/* First time through, go get the current drawable geometry
|
|
|
|
*/
|
|
|
|
if (priv->width == 0 || priv->height == 0 || priv->depth == 0) {
|
|
|
|
xcb_get_geometry_cookie_t geom_cookie;
|
|
|
|
xcb_get_geometry_reply_t *geom_reply;
|
|
|
|
xcb_void_cookie_t cookie;
|
|
|
|
xcb_generic_error_t *error;
|
glx/dri3: Use four buffers until X driver supports async flips
A driver which doesn't have async flip support will queue up flips without any
way to replace them afterwards. This means we've got a scanout buffer pinned
as soon as we schedule a flip and so we need another buffer to keep from
stalling.
When vblank_mode=0, if there are only three buffers we do:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC 2.
Now we block after this, waiting for one of the three buffers to become idle.
We can't use buffer 0 because it is the scanout buffer. We can't use buffer 1
because it's sitting in the kernel waiting to become the next scanout buffer
and we can't use buffer 2 because that's the most recent frame which will
become the next scanout buffer if the application doesn't manage to generate
another complete frame by MSC 2.
With four buffers, we get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC
2. The X server will queue this swap until buffer 1 becomes
the scanout buffer.
Render frame 3 to buffer 3
PresentPixmap for buffer 3 at MSC 1
As soon as the X server sees this, it will replace the pending
buffer 2 swap with this swap and release buffer 2 back to the
application
Render frame 4 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Now we're in a steady state, flipping between buffer 2 and 3
waiting for one of them to be queued to the kernel.
...
current scanout buffer = 1 at MSC 1
Now buffer 0 is free and (e.g.) buffer 2 is queued in
the kernel to be the scanout buffer at MSC 2
Render frames, flipping between buffer 0 and 3
When the system can replace a queued buffer, and we update Present to take
advantage of that, we can use three buffers and get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting waiting for vblank to become the next scanout
buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Queue this for display at MSC 1
1. There are three possible results:
1) We're still before MSC 1. Buffer 1 is released,
buffer 2 is queued waiting for MSC 1.
2) We're now after MSC 1. Buffer 0 was released at MSC 1.
Buffer 1 is the current scanout buffer.
a) If the user asked for a tearing update, we swap
scanout from buffer 1 to buffer 2 and release buffer
1.
b) If the user asked for non-tearing update, we
queue buffer 2 for the MSC 2.
In all three cases, we have a buffer released (call it 'n'),
ready to receive the next frame.
Render frame 3 to buffer n
PresentPixmap for buffer n
If we're still before MSC 1, then we'll ask to present at MSC
1. Otherwise, we'll ask to present at MSC 2.
Present already does this if the driver offers async flips, however it does
this by waiting for the right vblank event and sending an async flip right at
that point.
I've hacked the intel driver to offer this, but I get tearing at the top of
the screen. I think this is because flips are always done from within the
ring, and so the latency between the vblank event and the async flip happening
can cause tearing at the top of the screen.
That's why I'm keying the need for the extra buffer on the lack of 2D
driver support for async flips.
Signed-off-by: Keith Packard <keithp@keithp.com>
Acked-by: Jason Ekstrand <jason.ekstrand@intel.com>
Tested-by: Dylan Baker <baker.dylan.c@gmail.com>
2014-07-02 21:26:22 +01:00
|
|
|
xcb_present_query_capabilities_cookie_t present_capabilities_cookie;
|
|
|
|
xcb_present_query_capabilities_reply_t *present_capabilities_reply;
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
/* Try to select for input on the window.
|
|
|
|
*
|
|
|
|
* If the drawable is a window, this will get our events
|
|
|
|
* delivered.
|
|
|
|
*
|
|
|
|
* Otherwise, we'll get a BadWindow error back from this request which
|
|
|
|
* will let us know that the drawable is a pixmap instead.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
cookie = xcb_present_select_input_checked(c,
|
|
|
|
(priv->eid = xcb_generate_id(c)),
|
|
|
|
priv->base.xDrawable,
|
|
|
|
XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY|
|
|
|
|
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
|
|
|
|
XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
|
|
|
|
|
glx/dri3: Use four buffers until X driver supports async flips
A driver which doesn't have async flip support will queue up flips without any
way to replace them afterwards. This means we've got a scanout buffer pinned
as soon as we schedule a flip and so we need another buffer to keep from
stalling.
When vblank_mode=0, if there are only three buffers we do:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC 2.
Now we block after this, waiting for one of the three buffers to become idle.
We can't use buffer 0 because it is the scanout buffer. We can't use buffer 1
because it's sitting in the kernel waiting to become the next scanout buffer
and we can't use buffer 2 because that's the most recent frame which will
become the next scanout buffer if the application doesn't manage to generate
another complete frame by MSC 2.
With four buffers, we get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC
2. The X server will queue this swap until buffer 1 becomes
the scanout buffer.
Render frame 3 to buffer 3
PresentPixmap for buffer 3 at MSC 1
As soon as the X server sees this, it will replace the pending
buffer 2 swap with this swap and release buffer 2 back to the
application
Render frame 4 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Now we're in a steady state, flipping between buffer 2 and 3
waiting for one of them to be queued to the kernel.
...
current scanout buffer = 1 at MSC 1
Now buffer 0 is free and (e.g.) buffer 2 is queued in
the kernel to be the scanout buffer at MSC 2
Render frames, flipping between buffer 0 and 3
When the system can replace a queued buffer, and we update Present to take
advantage of that, we can use three buffers and get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting waiting for vblank to become the next scanout
buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Queue this for display at MSC 1
1. There are three possible results:
1) We're still before MSC 1. Buffer 1 is released,
buffer 2 is queued waiting for MSC 1.
2) We're now after MSC 1. Buffer 0 was released at MSC 1.
Buffer 1 is the current scanout buffer.
a) If the user asked for a tearing update, we swap
scanout from buffer 1 to buffer 2 and release buffer
1.
b) If the user asked for non-tearing update, we
queue buffer 2 for the MSC 2.
In all three cases, we have a buffer released (call it 'n'),
ready to receive the next frame.
Render frame 3 to buffer n
PresentPixmap for buffer n
If we're still before MSC 1, then we'll ask to present at MSC
1. Otherwise, we'll ask to present at MSC 2.
Present already does this if the driver offers async flips, however it does
this by waiting for the right vblank event and sending an async flip right at
that point.
I've hacked the intel driver to offer this, but I get tearing at the top of
the screen. I think this is because flips are always done from within the
ring, and so the latency between the vblank event and the async flip happening
can cause tearing at the top of the screen.
That's why I'm keying the need for the extra buffer on the lack of 2D
driver support for async flips.
Signed-off-by: Keith Packard <keithp@keithp.com>
Acked-by: Jason Ekstrand <jason.ekstrand@intel.com>
Tested-by: Dylan Baker <baker.dylan.c@gmail.com>
2014-07-02 21:26:22 +01:00
|
|
|
present_capabilities_cookie = xcb_present_query_capabilities(c, priv->base.xDrawable);
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
/* Create an XCB event queue to hold present events outside of the usual
|
|
|
|
* application event queue
|
|
|
|
*/
|
|
|
|
priv->special_event = xcb_register_for_special_xge(c,
|
|
|
|
&xcb_present_id,
|
|
|
|
priv->eid,
|
|
|
|
priv->stamp);
|
|
|
|
|
|
|
|
geom_cookie = xcb_get_geometry(c, priv->base.xDrawable);
|
|
|
|
|
|
|
|
geom_reply = xcb_get_geometry_reply(c, geom_cookie, NULL);
|
|
|
|
|
|
|
|
if (!geom_reply)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
priv->width = geom_reply->width;
|
|
|
|
priv->height = geom_reply->height;
|
|
|
|
priv->depth = geom_reply->depth;
|
|
|
|
priv->is_pixmap = false;
|
|
|
|
|
|
|
|
free(geom_reply);
|
|
|
|
|
|
|
|
/* Check to see if our select input call failed. If it failed with a
|
|
|
|
* BadWindow error, then assume the drawable is a pixmap. Destroy the
|
|
|
|
* special event queue created above and mark the drawable as a pixmap
|
|
|
|
*/
|
|
|
|
|
|
|
|
error = xcb_request_check(c, cookie);
|
|
|
|
|
glx/dri3: Use four buffers until X driver supports async flips
A driver which doesn't have async flip support will queue up flips without any
way to replace them afterwards. This means we've got a scanout buffer pinned
as soon as we schedule a flip and so we need another buffer to keep from
stalling.
When vblank_mode=0, if there are only three buffers we do:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC 2.
Now we block after this, waiting for one of the three buffers to become idle.
We can't use buffer 0 because it is the scanout buffer. We can't use buffer 1
because it's sitting in the kernel waiting to become the next scanout buffer
and we can't use buffer 2 because that's the most recent frame which will
become the next scanout buffer if the application doesn't manage to generate
another complete frame by MSC 2.
With four buffers, we get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting down in the kernel waiting for vblank to
become the next scanout buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
This cannot be displayed at MSC 1 because the
kernel doesn't have any way to replace buffer 1 as the pending
scanout buffer. So, best case this will get displayed at MSC
2. The X server will queue this swap until buffer 1 becomes
the scanout buffer.
Render frame 3 to buffer 3
PresentPixmap for buffer 3 at MSC 1
As soon as the X server sees this, it will replace the pending
buffer 2 swap with this swap and release buffer 2 back to the
application
Render frame 4 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Now we're in a steady state, flipping between buffer 2 and 3
waiting for one of them to be queued to the kernel.
...
current scanout buffer = 1 at MSC 1
Now buffer 0 is free and (e.g.) buffer 2 is queued in
the kernel to be the scanout buffer at MSC 2
Render frames, flipping between buffer 0 and 3
When the system can replace a queued buffer, and we update Present to take
advantage of that, we can use three buffers and get:
current scanout buffer = 0 at MSC 0
Render frame 1 to buffer 1
PresentPixmap for buffer 1 at MSC 1
This is sitting waiting for vblank to become the next scanout
buffer
Render frame 2 to buffer 2
PresentPixmap for buffer 2 at MSC 1
Queue this for display at MSC 1
1. There are three possible results:
1) We're still before MSC 1. Buffer 1 is released,
buffer 2 is queued waiting for MSC 1.
2) We're now after MSC 1. Buffer 0 was released at MSC 1.
Buffer 1 is the current scanout buffer.
a) If the user asked for a tearing update, we swap
scanout from buffer 1 to buffer 2 and release buffer
1.
b) If the user asked for non-tearing update, we
queue buffer 2 for the MSC 2.
In all three cases, we have a buffer released (call it 'n'),
ready to receive the next frame.
Render frame 3 to buffer n
PresentPixmap for buffer n
If we're still before MSC 1, then we'll ask to present at MSC
1. Otherwise, we'll ask to present at MSC 2.
Present already does this if the driver offers async flips, however it does
this by waiting for the right vblank event and sending an async flip right at
that point.
I've hacked the intel driver to offer this, but I get tearing at the top of
the screen. I think this is because flips are always done from within the
ring, and so the latency between the vblank event and the async flip happening
can cause tearing at the top of the screen.
That's why I'm keying the need for the extra buffer on the lack of 2D
driver support for async flips.
Signed-off-by: Keith Packard <keithp@keithp.com>
Acked-by: Jason Ekstrand <jason.ekstrand@intel.com>
Tested-by: Dylan Baker <baker.dylan.c@gmail.com>
2014-07-02 21:26:22 +01:00
|
|
|
present_capabilities_reply = xcb_present_query_capabilities_reply(c,
|
|
|
|
present_capabilities_cookie,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (present_capabilities_reply) {
|
|
|
|
priv->present_capabilities = present_capabilities_reply->capabilities;
|
|
|
|
free(present_capabilities_reply);
|
|
|
|
} else
|
|
|
|
priv->present_capabilities = 0;
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
if (error) {
|
|
|
|
if (error->error_code != BadWindow) {
|
|
|
|
free(error);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
priv->is_pixmap = true;
|
|
|
|
xcb_unregister_for_special_event(c, priv->special_event);
|
|
|
|
priv->special_event = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dri3_flush_present_events(priv);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the DRIimage createImage function takes __DRI_IMAGE_FORMAT codes, while
|
|
|
|
* the createImageFromFds call takes __DRI_IMAGE_FOURCC codes. To avoid
|
|
|
|
* complete confusion, just deal in __DRI_IMAGE_FORMAT codes for now and
|
|
|
|
* translate to __DRI_IMAGE_FOURCC codes in the call to createImageFromFds
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
image_format_to_fourcc(int format)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Convert from __DRI_IMAGE_FORMAT to __DRI_IMAGE_FOURCC (sigh) */
|
|
|
|
switch (format) {
|
2013-11-22 04:08:35 +00:00
|
|
|
case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
|
2013-11-05 02:15:51 +00:00
|
|
|
case __DRI_IMAGE_FORMAT_RGB565: return __DRI_IMAGE_FOURCC_RGB565;
|
|
|
|
case __DRI_IMAGE_FORMAT_XRGB8888: return __DRI_IMAGE_FOURCC_XRGB8888;
|
|
|
|
case __DRI_IMAGE_FORMAT_ARGB8888: return __DRI_IMAGE_FOURCC_ARGB8888;
|
|
|
|
case __DRI_IMAGE_FORMAT_ABGR8888: return __DRI_IMAGE_FOURCC_ABGR8888;
|
|
|
|
case __DRI_IMAGE_FORMAT_XBGR8888: return __DRI_IMAGE_FOURCC_XBGR8888;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_get_pixmap_buffer
|
|
|
|
*
|
|
|
|
* Get the DRM object for a pixmap from the X server and
|
|
|
|
* wrap that with a __DRIimage structure using createImageFromFds
|
|
|
|
*/
|
|
|
|
static struct dri3_buffer *
|
|
|
|
dri3_get_pixmap_buffer(__DRIdrawable *driDrawable,
|
|
|
|
unsigned int format,
|
|
|
|
enum dri3_buffer_type buffer_type,
|
|
|
|
void *loaderPrivate)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *pdraw = loaderPrivate;
|
2013-11-08 03:01:48 +00:00
|
|
|
int buf_id = dri3_pixmap_buf_id(buffer_type);
|
2013-11-05 02:15:51 +00:00
|
|
|
struct dri3_buffer *buffer = pdraw->buffers[buf_id];
|
|
|
|
Pixmap pixmap;
|
|
|
|
xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
|
|
|
|
xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
|
|
|
|
int *fds;
|
|
|
|
Display *dpy;
|
|
|
|
struct dri3_screen *psc;
|
|
|
|
xcb_connection_t *c;
|
|
|
|
xcb_sync_fence_t sync_fence;
|
2013-11-22 04:22:16 +00:00
|
|
|
struct xshmfence *shm_fence;
|
2013-11-05 02:15:51 +00:00
|
|
|
int fence_fd;
|
|
|
|
__DRIimage *image_planar;
|
|
|
|
int stride, offset;
|
|
|
|
|
|
|
|
if (buffer)
|
|
|
|
return buffer;
|
|
|
|
|
|
|
|
pixmap = pdraw->base.xDrawable;
|
|
|
|
psc = (struct dri3_screen *) pdraw->base.psc;
|
|
|
|
dpy = psc->base.dpy;
|
|
|
|
c = XGetXCBConnection(dpy);
|
|
|
|
|
|
|
|
buffer = calloc(1, sizeof (struct dri3_buffer));
|
|
|
|
if (!buffer)
|
|
|
|
goto no_buffer;
|
|
|
|
|
|
|
|
fence_fd = xshmfence_alloc_shm();
|
|
|
|
if (fence_fd < 0)
|
|
|
|
goto no_fence;
|
|
|
|
shm_fence = xshmfence_map_shm(fence_fd);
|
|
|
|
if (shm_fence == NULL) {
|
|
|
|
close (fence_fd);
|
|
|
|
goto no_fence;
|
|
|
|
}
|
|
|
|
|
|
|
|
xcb_dri3_fence_from_fd(c,
|
|
|
|
pixmap,
|
|
|
|
(sync_fence = xcb_generate_id(c)),
|
|
|
|
false,
|
|
|
|
fence_fd);
|
|
|
|
|
|
|
|
/* Get an FD for the pixmap object
|
|
|
|
*/
|
|
|
|
bp_cookie = xcb_dri3_buffer_from_pixmap(c, pixmap);
|
|
|
|
bp_reply = xcb_dri3_buffer_from_pixmap_reply(c, bp_cookie, NULL);
|
|
|
|
if (!bp_reply)
|
|
|
|
goto no_image;
|
|
|
|
fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, bp_reply);
|
|
|
|
|
|
|
|
stride = bp_reply->stride;
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
/* createImageFromFds creates a wrapper __DRIimage structure which
|
|
|
|
* can deal with multiple planes for things like Yuv images. So, once
|
|
|
|
* we've gotten the planar wrapper, pull the single plane out of it and
|
|
|
|
* discard the wrapper.
|
|
|
|
*/
|
|
|
|
image_planar = (*psc->image->createImageFromFds) (psc->driScreen,
|
|
|
|
bp_reply->width,
|
|
|
|
bp_reply->height,
|
|
|
|
image_format_to_fourcc(format),
|
|
|
|
fds, 1,
|
|
|
|
&stride, &offset, buffer);
|
|
|
|
close(fds[0]);
|
|
|
|
if (!image_planar)
|
|
|
|
goto no_image;
|
|
|
|
|
|
|
|
buffer->image = (*psc->image->fromPlanar)(image_planar, 0, buffer);
|
|
|
|
|
|
|
|
(*psc->image->destroyImage)(image_planar);
|
|
|
|
|
|
|
|
if (!buffer->image)
|
|
|
|
goto no_image;
|
|
|
|
|
|
|
|
buffer->pixmap = pixmap;
|
2013-11-22 05:30:07 +00:00
|
|
|
buffer->own_pixmap = false;
|
2013-11-05 02:15:51 +00:00
|
|
|
buffer->width = bp_reply->width;
|
|
|
|
buffer->height = bp_reply->height;
|
|
|
|
buffer->buffer_type = buffer_type;
|
|
|
|
buffer->shm_fence = shm_fence;
|
|
|
|
buffer->sync_fence = sync_fence;
|
|
|
|
|
|
|
|
pdraw->buffers[buf_id] = buffer;
|
|
|
|
return buffer;
|
|
|
|
|
|
|
|
no_image:
|
|
|
|
xcb_sync_destroy_fence(c, sync_fence);
|
|
|
|
xshmfence_unmap_shm(shm_fence);
|
|
|
|
no_fence:
|
|
|
|
free(buffer);
|
|
|
|
no_buffer:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_find_back
|
|
|
|
*
|
|
|
|
* Find an idle back buffer. If there isn't one, then
|
|
|
|
* wait for a present idle notify event from the X server
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dri3_find_back(xcb_connection_t *c, struct dri3_drawable *priv)
|
|
|
|
{
|
|
|
|
int b;
|
|
|
|
xcb_generic_event_t *ev;
|
|
|
|
xcb_present_generic_event_t *ge;
|
|
|
|
|
|
|
|
for (;;) {
|
2014-01-27 00:14:29 +00:00
|
|
|
for (b = 0; b < priv->num_back; b++) {
|
2014-03-07 23:56:06 +00:00
|
|
|
int id = DRI3_BACK_ID((b + priv->cur_back) % priv->num_back);
|
2014-01-27 00:14:29 +00:00
|
|
|
struct dri3_buffer *buffer = priv->buffers[id];
|
2013-11-05 02:15:51 +00:00
|
|
|
|
2014-03-07 23:56:06 +00:00
|
|
|
if (!buffer || !buffer->busy) {
|
|
|
|
priv->cur_back = id;
|
|
|
|
return id;
|
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
2013-11-26 06:57:42 +00:00
|
|
|
xcb_flush(c);
|
2013-11-05 02:15:51 +00:00
|
|
|
ev = xcb_wait_for_special_event(c, priv->special_event);
|
|
|
|
if (!ev)
|
|
|
|
return -1;
|
|
|
|
ge = (void *) ev;
|
|
|
|
dri3_handle_present_event(priv, ge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_get_buffer
|
|
|
|
*
|
|
|
|
* Find a front or back buffer, allocating new ones as necessary
|
|
|
|
*/
|
|
|
|
static struct dri3_buffer *
|
|
|
|
dri3_get_buffer(__DRIdrawable *driDrawable,
|
|
|
|
unsigned int format,
|
|
|
|
enum dri3_buffer_type buffer_type,
|
|
|
|
void *loaderPrivate)
|
|
|
|
{
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext();
|
2013-11-05 02:15:51 +00:00
|
|
|
struct dri3_drawable *priv = loaderPrivate;
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
|
2013-11-05 02:15:51 +00:00
|
|
|
xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
|
|
|
|
struct dri3_buffer *buffer;
|
|
|
|
int buf_id;
|
|
|
|
|
|
|
|
if (buffer_type == dri3_buffer_back) {
|
2014-03-07 23:56:06 +00:00
|
|
|
buf_id = dri3_find_back(c, priv);
|
2013-11-05 02:15:51 +00:00
|
|
|
|
2014-03-07 23:56:06 +00:00
|
|
|
if (buf_id < 0)
|
2013-11-05 02:15:51 +00:00
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
buf_id = DRI3_FRONT_ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer = priv->buffers[buf_id];
|
|
|
|
|
|
|
|
/* Allocate a new buffer if there isn't an old one, or if that
|
|
|
|
* old one is the wrong size
|
|
|
|
*/
|
|
|
|
if (!buffer || buffer->width != priv->width || buffer->height != priv->height) {
|
|
|
|
struct dri3_buffer *new_buffer;
|
|
|
|
|
|
|
|
/* Allocate the new buffers
|
|
|
|
*/
|
|
|
|
new_buffer = dri3_alloc_render_buffer(priv->base.psc,
|
|
|
|
priv->base.xDrawable,
|
|
|
|
format, priv->width, priv->height, priv->depth);
|
|
|
|
if (!new_buffer)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* When resizing, copy the contents of the old buffer, waiting for that
|
|
|
|
* copy to complete using our fences before proceeding
|
|
|
|
*/
|
|
|
|
switch (buffer_type) {
|
|
|
|
case dri3_buffer_back:
|
|
|
|
if (buffer) {
|
2014-05-17 17:12:11 +01:00
|
|
|
if (!buffer->linear_buffer) {
|
|
|
|
dri3_fence_reset(c, new_buffer);
|
|
|
|
dri3_fence_await(c, buffer);
|
|
|
|
dri3_copy_area(c,
|
|
|
|
buffer->pixmap,
|
|
|
|
new_buffer->pixmap,
|
|
|
|
dri3_drawable_gc(priv),
|
|
|
|
0, 0, 0, 0, priv->width, priv->height);
|
2013-11-05 02:15:51 +00:00
|
|
|
dri3_fence_trigger(c, new_buffer);
|
2014-05-17 17:12:11 +01:00
|
|
|
} else if ((&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) {
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
new_buffer->image,
|
|
|
|
buffer->image,
|
|
|
|
0, 0, priv->width,
|
|
|
|
priv->height,
|
|
|
|
0, 0, priv->width,
|
|
|
|
priv->height, 0);
|
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
dri3_free_render_buffer(priv, buffer);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case dri3_buffer_front:
|
|
|
|
dri3_fence_reset(c, new_buffer);
|
|
|
|
dri3_copy_area(c,
|
|
|
|
priv->base.xDrawable,
|
|
|
|
new_buffer->pixmap,
|
|
|
|
dri3_drawable_gc(priv),
|
|
|
|
0, 0, 0, 0, priv->width, priv->height);
|
|
|
|
dri3_fence_trigger(c, new_buffer);
|
2014-05-17 17:12:11 +01:00
|
|
|
|
|
|
|
if (new_buffer->linear_buffer && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) {
|
|
|
|
dri3_fence_await(c, new_buffer);
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
new_buffer->image,
|
|
|
|
new_buffer->linear_buffer,
|
|
|
|
0, 0, priv->width,
|
|
|
|
priv->height,
|
|
|
|
0, 0, priv->width,
|
|
|
|
priv->height, 0);
|
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
buffer = new_buffer;
|
|
|
|
buffer->buffer_type = buffer_type;
|
|
|
|
priv->buffers[buf_id] = buffer;
|
|
|
|
}
|
|
|
|
dri3_fence_await(c, buffer);
|
|
|
|
|
|
|
|
/* Return the requested buffer */
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_free_buffers
|
|
|
|
*
|
|
|
|
* Free the front bufffer or all of the back buffers. Used
|
|
|
|
* when the application changes which buffers it needs
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_free_buffers(__DRIdrawable *driDrawable,
|
|
|
|
enum dri3_buffer_type buffer_type,
|
|
|
|
void *loaderPrivate)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *priv = loaderPrivate;
|
|
|
|
struct dri3_buffer *buffer;
|
|
|
|
int first_id;
|
|
|
|
int n_id;
|
|
|
|
int buf_id;
|
|
|
|
|
|
|
|
switch (buffer_type) {
|
|
|
|
case dri3_buffer_back:
|
|
|
|
first_id = DRI3_BACK_ID(0);
|
2014-01-27 00:14:29 +00:00
|
|
|
n_id = DRI3_MAX_BACK;
|
2013-11-05 02:15:51 +00:00
|
|
|
break;
|
|
|
|
case dri3_buffer_front:
|
|
|
|
first_id = DRI3_FRONT_ID;
|
|
|
|
n_id = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (buf_id = first_id; buf_id < first_id + n_id; buf_id++) {
|
|
|
|
buffer = priv->buffers[buf_id];
|
|
|
|
if (buffer) {
|
|
|
|
dri3_free_render_buffer(priv, buffer);
|
|
|
|
priv->buffers[buf_id] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_get_buffers
|
|
|
|
*
|
|
|
|
* The published buffer allocation API.
|
|
|
|
* Returns all of the necessary buffers, allocating
|
|
|
|
* as needed.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dri3_get_buffers(__DRIdrawable *driDrawable,
|
|
|
|
unsigned int format,
|
|
|
|
uint32_t *stamp,
|
|
|
|
void *loaderPrivate,
|
|
|
|
uint32_t buffer_mask,
|
|
|
|
struct __DRIimageList *buffers)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *priv = loaderPrivate;
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
|
2013-11-05 02:15:51 +00:00
|
|
|
struct dri3_buffer *front, *back;
|
|
|
|
|
|
|
|
buffers->image_mask = 0;
|
|
|
|
buffers->front = NULL;
|
|
|
|
buffers->back = NULL;
|
|
|
|
|
|
|
|
front = NULL;
|
|
|
|
back = NULL;
|
|
|
|
|
|
|
|
if (!dri3_update_drawable(driDrawable, loaderPrivate))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* pixmaps always have front buffers */
|
|
|
|
if (priv->is_pixmap)
|
|
|
|
buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
|
|
|
|
|
|
|
|
if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
|
2014-05-17 17:12:11 +01:00
|
|
|
/* All pixmaps are owned by the server gpu.
|
|
|
|
* When we use a different gpu, we can't use the pixmap
|
|
|
|
* as buffer since it is potentially tiled a way
|
|
|
|
* our device can't understand. In this case, use
|
|
|
|
* a fake front buffer. Hopefully the pixmap
|
|
|
|
* content will get synced with the fake front
|
|
|
|
* buffer.
|
|
|
|
*/
|
|
|
|
if (priv->is_pixmap && !psc->is_different_gpu)
|
2013-11-05 02:15:51 +00:00
|
|
|
front = dri3_get_pixmap_buffer(driDrawable,
|
|
|
|
format,
|
|
|
|
dri3_buffer_front,
|
|
|
|
loaderPrivate);
|
|
|
|
else
|
|
|
|
front = dri3_get_buffer(driDrawable,
|
|
|
|
format,
|
|
|
|
dri3_buffer_front,
|
|
|
|
loaderPrivate);
|
|
|
|
|
|
|
|
if (!front)
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
dri3_free_buffers(driDrawable, dri3_buffer_front, loaderPrivate);
|
|
|
|
priv->have_fake_front = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
|
|
|
|
back = dri3_get_buffer(driDrawable,
|
|
|
|
format,
|
|
|
|
dri3_buffer_back,
|
|
|
|
loaderPrivate);
|
|
|
|
if (!back)
|
|
|
|
return false;
|
|
|
|
priv->have_back = 1;
|
|
|
|
} else {
|
|
|
|
dri3_free_buffers(driDrawable, dri3_buffer_back, loaderPrivate);
|
|
|
|
priv->have_back = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (front) {
|
|
|
|
buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
|
|
|
|
buffers->front = front->image;
|
2014-05-17 17:12:11 +01:00
|
|
|
priv->have_fake_front = psc->is_different_gpu || !priv->is_pixmap;
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (back) {
|
|
|
|
buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
|
|
|
|
buffers->back = back->image;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->stamp = stamp;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The image loader extension record for DRI3
|
|
|
|
*/
|
|
|
|
static const __DRIimageLoaderExtension imageLoaderExtension = {
|
2014-02-12 18:46:32 +00:00
|
|
|
.base = { __DRI_IMAGE_LOADER, 1 },
|
|
|
|
|
|
|
|
.getBuffers = dri3_get_buffers,
|
|
|
|
.flushFrontBuffer = dri3_flush_front_buffer,
|
2013-11-05 02:15:51 +00:00
|
|
|
};
|
|
|
|
|
2014-06-19 04:27:31 +01:00
|
|
|
const __DRIuseInvalidateExtension dri3UseInvalidate = {
|
|
|
|
.base = { __DRI_USE_INVALIDATE, 1 }
|
|
|
|
};
|
|
|
|
|
2014-03-16 01:17:46 +00:00
|
|
|
static const __DRIextension *loader_extensions[] = {
|
|
|
|
&imageLoaderExtension.base,
|
|
|
|
&systemTimeExtension.base,
|
2014-06-19 04:27:31 +01:00
|
|
|
&dri3UseInvalidate.base,
|
2014-03-16 01:17:46 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
/** dri3_swap_buffers
|
|
|
|
*
|
|
|
|
* Make the current back buffer visible using the present extension
|
|
|
|
*/
|
|
|
|
static int64_t
|
|
|
|
dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
|
|
|
|
int64_t remainder, Bool flush)
|
|
|
|
{
|
2014-05-17 17:12:11 +01:00
|
|
|
struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext();
|
2013-11-05 02:15:51 +00:00
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
|
|
|
|
Display *dpy = priv->base.psc->dpy;
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(dpy);
|
2014-06-27 02:47:33 +01:00
|
|
|
struct dri3_buffer *back;
|
2013-11-05 02:15:51 +00:00
|
|
|
int64_t ret = 0;
|
2014-12-05 07:42:31 +00:00
|
|
|
uint32_t options = XCB_PRESENT_OPTION_NONE;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
unsigned flags = __DRI2_FLUSH_DRAWABLE;
|
|
|
|
if (flush)
|
|
|
|
flags |= __DRI2_FLUSH_CONTEXT;
|
|
|
|
dri3_flush(psc, priv, flags, __DRI2_THROTTLE_SWAPBUFFER);
|
|
|
|
|
2014-06-27 02:47:33 +01:00
|
|
|
back = priv->buffers[DRI3_BACK_ID(priv->cur_back)];
|
2014-05-17 17:12:11 +01:00
|
|
|
if (psc->is_different_gpu && back) {
|
|
|
|
/* Update the linear buffer before presenting the pixmap */
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
back->linear_buffer,
|
|
|
|
back->image,
|
|
|
|
0, 0, back->width,
|
|
|
|
back->height,
|
|
|
|
0, 0, back->width,
|
|
|
|
back->height, __BLIT_FLAG_FLUSH);
|
|
|
|
/* Update the fake front */
|
|
|
|
if (priv->have_fake_front)
|
|
|
|
psc->image->blitImage(pcp->driContext,
|
|
|
|
priv->buffers[DRI3_FRONT_ID]->image,
|
|
|
|
back->image,
|
|
|
|
0, 0, priv->width,
|
|
|
|
priv->height,
|
|
|
|
0, 0, priv->width,
|
|
|
|
priv->height, __BLIT_FLAG_FLUSH);
|
|
|
|
}
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
dri3_flush_present_events(priv);
|
|
|
|
|
2014-06-27 02:47:33 +01:00
|
|
|
if (back && !priv->is_pixmap) {
|
|
|
|
dri3_fence_reset(c, back);
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
/* Compute when we want the frame shown by taking the last known successful
|
glx/dri3: Don't fail on glXSwapBuffersMscOML(dpy, window, 0, 0, 0) (v2)
glXSwapBuffersMscOML() with target_msc=divisor=remainder=0 gets
translated into target_msc=divisor=0 but remainder=1 by the mesa
api. This is done for server DRI2 where there needs to be a way
to tell the server-side DRI2ScheduleSwap implementation if a call
to glXSwapBuffers() or glXSwapBuffersMscOML(dpy,window,0,0,0) was
done. remainder = 1 was (ab)used as a flag to tell the server to
select proper semantic. The DRI3/Present backend ignored this
signalling, treated any target_msc=0 as glXSwapBuffers() request,
and called xcb_present_pixmap with invalid divisor=0, remainder=1
combo. The present extension responded kindly to this with a
BadValue error and dropped the request, but mesa's DRI3/Present
backend doesn't check for error codes. From there on stuff went
downhill quickly for the calling OpenGL client...
This patch fixes the problem.
v2: Change comments to be more clear, with reference to
relevant spec, as suggested by Eric Anholt.
Cc: "10.3 10.4" <mesa-stable@lists.freedesktop.org>
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
Reviewed-by: Eric Anholt <eric@anholt.net>
2014-12-05 07:42:32 +00:00
|
|
|
* MSC and adding in a swap interval for each outstanding swap request.
|
|
|
|
* target_msc=divisor=remainder=0 means "Use glXSwapBuffers() semantic"
|
2013-11-05 02:15:51 +00:00
|
|
|
*/
|
2013-11-26 05:14:55 +00:00
|
|
|
++priv->send_sbc;
|
glx/dri3: Don't fail on glXSwapBuffersMscOML(dpy, window, 0, 0, 0) (v2)
glXSwapBuffersMscOML() with target_msc=divisor=remainder=0 gets
translated into target_msc=divisor=0 but remainder=1 by the mesa
api. This is done for server DRI2 where there needs to be a way
to tell the server-side DRI2ScheduleSwap implementation if a call
to glXSwapBuffers() or glXSwapBuffersMscOML(dpy,window,0,0,0) was
done. remainder = 1 was (ab)used as a flag to tell the server to
select proper semantic. The DRI3/Present backend ignored this
signalling, treated any target_msc=0 as glXSwapBuffers() request,
and called xcb_present_pixmap with invalid divisor=0, remainder=1
combo. The present extension responded kindly to this with a
BadValue error and dropped the request, but mesa's DRI3/Present
backend doesn't check for error codes. From there on stuff went
downhill quickly for the calling OpenGL client...
This patch fixes the problem.
v2: Change comments to be more clear, with reference to
relevant spec, as suggested by Eric Anholt.
Cc: "10.3 10.4" <mesa-stable@lists.freedesktop.org>
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
Reviewed-by: Eric Anholt <eric@anholt.net>
2014-12-05 07:42:32 +00:00
|
|
|
if (target_msc == 0 && divisor == 0 && remainder == 0)
|
2013-11-26 05:14:55 +00:00
|
|
|
target_msc = priv->msc + priv->swap_interval * (priv->send_sbc - priv->recv_sbc);
|
glx/dri3: Don't fail on glXSwapBuffersMscOML(dpy, window, 0, 0, 0) (v2)
glXSwapBuffersMscOML() with target_msc=divisor=remainder=0 gets
translated into target_msc=divisor=0 but remainder=1 by the mesa
api. This is done for server DRI2 where there needs to be a way
to tell the server-side DRI2ScheduleSwap implementation if a call
to glXSwapBuffers() or glXSwapBuffersMscOML(dpy,window,0,0,0) was
done. remainder = 1 was (ab)used as a flag to tell the server to
select proper semantic. The DRI3/Present backend ignored this
signalling, treated any target_msc=0 as glXSwapBuffers() request,
and called xcb_present_pixmap with invalid divisor=0, remainder=1
combo. The present extension responded kindly to this with a
BadValue error and dropped the request, but mesa's DRI3/Present
backend doesn't check for error codes. From there on stuff went
downhill quickly for the calling OpenGL client...
This patch fixes the problem.
v2: Change comments to be more clear, with reference to
relevant spec, as suggested by Eric Anholt.
Cc: "10.3 10.4" <mesa-stable@lists.freedesktop.org>
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
Reviewed-by: Eric Anholt <eric@anholt.net>
2014-12-05 07:42:32 +00:00
|
|
|
else if (divisor == 0 && remainder > 0) {
|
|
|
|
/* From the GLX_OML_sync_control spec:
|
|
|
|
*
|
|
|
|
* "If <divisor> = 0, the swap will occur when MSC becomes
|
|
|
|
* greater than or equal to <target_msc>."
|
|
|
|
*
|
|
|
|
* Note that there's no mention of the remainder. The Present extension
|
|
|
|
* throws BadValue for remainder != 0 with divisor == 0, so just drop
|
|
|
|
* the passed in value.
|
|
|
|
*/
|
|
|
|
remainder = 0;
|
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
|
2014-12-05 07:42:31 +00:00
|
|
|
/* From the GLX_EXT_swap_control spec:
|
|
|
|
*
|
|
|
|
* "If <interval> is set to a value of 0, buffer swaps are not
|
|
|
|
* synchronized to a video frame."
|
|
|
|
*
|
|
|
|
* Implementation note: It is possible to enable triple buffering behaviour
|
|
|
|
* by not using XCB_PRESENT_OPTION_ASYNC, but this should not be the default.
|
|
|
|
*/
|
|
|
|
if (priv->swap_interval == 0)
|
|
|
|
options |= XCB_PRESENT_OPTION_ASYNC;
|
|
|
|
|
2014-06-27 02:47:33 +01:00
|
|
|
back->busy = 1;
|
|
|
|
back->last_swap = priv->send_sbc;
|
2013-11-05 02:15:51 +00:00
|
|
|
xcb_present_pixmap(c,
|
|
|
|
priv->base.xDrawable,
|
2014-06-27 02:47:33 +01:00
|
|
|
back->pixmap,
|
2013-11-26 05:14:55 +00:00
|
|
|
(uint32_t) priv->send_sbc,
|
2013-11-05 02:15:51 +00:00
|
|
|
0, /* valid */
|
|
|
|
0, /* update */
|
|
|
|
0, /* x_off */
|
|
|
|
0, /* y_off */
|
|
|
|
None, /* target_crtc */
|
|
|
|
None,
|
2014-06-27 02:47:33 +01:00
|
|
|
back->sync_fence,
|
2014-12-05 07:42:31 +00:00
|
|
|
options,
|
2013-11-05 02:15:51 +00:00
|
|
|
target_msc,
|
|
|
|
divisor,
|
|
|
|
remainder, 0, NULL);
|
2013-11-26 05:14:55 +00:00
|
|
|
ret = (int64_t) priv->send_sbc;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
/* If there's a fake front, then copy the source back buffer
|
|
|
|
* to the fake front to keep it up to date. This needs
|
|
|
|
* to reset the fence and make future users block until
|
|
|
|
* the X server is done copying the bits
|
|
|
|
*/
|
2014-05-17 17:12:11 +01:00
|
|
|
if (priv->have_fake_front && !psc->is_different_gpu) {
|
2013-11-05 02:15:51 +00:00
|
|
|
dri3_fence_reset(c, priv->buffers[DRI3_FRONT_ID]);
|
|
|
|
dri3_copy_area(c,
|
2014-06-27 02:47:33 +01:00
|
|
|
back->pixmap,
|
2013-11-05 02:15:51 +00:00
|
|
|
priv->buffers[DRI3_FRONT_ID]->pixmap,
|
|
|
|
dri3_drawable_gc(priv),
|
|
|
|
0, 0, 0, 0, priv->width, priv->height);
|
|
|
|
dri3_fence_trigger(c, priv->buffers[DRI3_FRONT_ID]);
|
|
|
|
}
|
|
|
|
xcb_flush(c);
|
|
|
|
if (priv->stamp)
|
|
|
|
++(*priv->stamp);
|
|
|
|
}
|
|
|
|
|
2014-06-19 04:27:31 +01:00
|
|
|
(*psc->f->invalidate)(priv->driDrawable);
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-02-24 19:44:42 +00:00
|
|
|
static int
|
|
|
|
dri3_get_buffer_age(__GLXDRIdrawable *pdraw)
|
|
|
|
{
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
|
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
|
|
|
int back_id = DRI3_BACK_ID(dri3_find_back(c, priv));
|
|
|
|
|
|
|
|
if (back_id < 0 || !priv->buffers[back_id])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (priv->buffers[back_id]->last_swap != 0)
|
|
|
|
return priv->send_sbc - priv->buffers[back_id]->last_swap + 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
/** dri3_open
|
|
|
|
*
|
|
|
|
* Wrapper around xcb_dri3_open
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dri3_open(Display *dpy,
|
|
|
|
Window root,
|
|
|
|
CARD32 provider)
|
|
|
|
{
|
|
|
|
xcb_dri3_open_cookie_t cookie;
|
|
|
|
xcb_dri3_open_reply_t *reply;
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(dpy);
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
cookie = xcb_dri3_open(c,
|
|
|
|
root,
|
|
|
|
provider);
|
|
|
|
|
2014-01-23 18:25:58 +00:00
|
|
|
reply = xcb_dri3_open_reply(c, cookie, NULL);
|
2013-11-05 02:15:51 +00:00
|
|
|
if (!reply)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (reply->nfd != 1) {
|
|
|
|
free(reply);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = xcb_dri3_open_reply_fds(c, reply)[0];
|
|
|
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** dri3_destroy_screen
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_destroy_screen(struct glx_screen *base)
|
|
|
|
{
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) base;
|
|
|
|
|
|
|
|
/* Free the direct rendering per screen data */
|
|
|
|
(*psc->core->destroyScreen) (psc->driScreen);
|
|
|
|
driDestroyConfigs(psc->driver_configs);
|
|
|
|
close(psc->fd);
|
|
|
|
free(psc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_set_swap_interval
|
|
|
|
*
|
|
|
|
* Record the application swap interval specification,
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
|
|
|
GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
|
|
|
|
struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
|
|
|
|
|
|
|
|
if (psc->config)
|
|
|
|
psc->config->configQueryi(psc->driScreen,
|
|
|
|
"vblank_mode", &vblank_mode);
|
|
|
|
|
|
|
|
switch (vblank_mode) {
|
|
|
|
case DRI_CONF_VBLANK_NEVER:
|
|
|
|
if (interval != 0)
|
|
|
|
return GLX_BAD_VALUE;
|
|
|
|
break;
|
|
|
|
case DRI_CONF_VBLANK_ALWAYS_SYNC:
|
|
|
|
if (interval <= 0)
|
|
|
|
return GLX_BAD_VALUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->swap_interval = interval;
|
2014-01-27 00:14:29 +00:00
|
|
|
dri3_update_num_back(priv);
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_get_swap_interval
|
|
|
|
*
|
|
|
|
* Return the stored swap interval
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dri3_get_swap_interval(__GLXDRIdrawable *pdraw)
|
|
|
|
{
|
|
|
|
struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
|
|
|
|
|
|
|
|
return priv->swap_interval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_bind_tex_image(Display * dpy,
|
|
|
|
GLXDrawable drawable,
|
|
|
|
int buffer, const int *attrib_list)
|
|
|
|
{
|
|
|
|
struct glx_context *gc = __glXGetCurrentContext();
|
|
|
|
struct dri3_context *pcp = (struct dri3_context *) gc;
|
|
|
|
__GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
|
|
|
|
struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
|
|
|
|
struct dri3_screen *psc;
|
|
|
|
|
|
|
|
if (pdraw != NULL) {
|
|
|
|
psc = (struct dri3_screen *) base->psc;
|
|
|
|
|
|
|
|
(*psc->f->invalidate)(pdraw->driDrawable);
|
|
|
|
|
|
|
|
XSync(dpy, false);
|
|
|
|
|
|
|
|
(*psc->texBuffer->setTexBuffer2) (pcp->driContext,
|
|
|
|
pdraw->base.textureTarget,
|
|
|
|
pdraw->base.textureFormat,
|
|
|
|
pdraw->driDrawable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
|
|
|
|
{
|
|
|
|
struct glx_context *gc = __glXGetCurrentContext();
|
|
|
|
struct dri3_context *pcp = (struct dri3_context *) gc;
|
|
|
|
__GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
|
|
|
|
struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
|
|
|
|
struct dri3_screen *psc;
|
|
|
|
|
|
|
|
if (pdraw != NULL) {
|
|
|
|
psc = (struct dri3_screen *) base->psc;
|
|
|
|
|
2014-02-18 03:04:03 +00:00
|
|
|
if (psc->texBuffer->base.version >= 3 &&
|
|
|
|
psc->texBuffer->releaseTexBuffer != NULL)
|
2013-11-05 02:15:51 +00:00
|
|
|
(*psc->texBuffer->releaseTexBuffer) (pcp->driContext,
|
|
|
|
pdraw->base.textureTarget,
|
|
|
|
pdraw->driDrawable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct glx_context_vtable dri3_context_vtable = {
|
2014-02-18 19:26:27 +00:00
|
|
|
.destroy = dri3_destroy_context,
|
|
|
|
.bind = dri3_bind_context,
|
|
|
|
.unbind = dri3_unbind_context,
|
|
|
|
.wait_gl = dri3_wait_gl,
|
|
|
|
.wait_x = dri3_wait_x,
|
|
|
|
.use_x_font = DRI_glXUseXFont,
|
|
|
|
.bind_tex_image = dri3_bind_tex_image,
|
|
|
|
.release_tex_image = dri3_release_tex_image,
|
|
|
|
.get_proc_address = NULL,
|
2013-11-05 02:15:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/** dri3_bind_extensions
|
|
|
|
*
|
|
|
|
* Enable all of the extensions supported on DRI3
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
|
|
|
|
const char *driverName)
|
|
|
|
{
|
|
|
|
const __DRIextension **extensions;
|
|
|
|
unsigned mask;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
extensions = psc->core->getExtensions(psc->driScreen);
|
|
|
|
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_SGI_video_sync");
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_SGI_swap_control");
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_MESA_swap_control");
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_SGI_make_current_read");
|
2013-11-26 05:24:54 +00:00
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event");
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
mask = psc->image_driver->getAPIMask(psc->driScreen);
|
|
|
|
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context");
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context_profile");
|
|
|
|
|
|
|
|
if ((mask & (1 << __DRI_API_GLES2)) != 0)
|
|
|
|
__glXEnableDirectExtension(&psc->base,
|
|
|
|
"GLX_EXT_create_context_es2_profile");
|
|
|
|
|
|
|
|
for (i = 0; extensions[i]; i++) {
|
2014-05-17 17:12:11 +01:00
|
|
|
/* when on a different gpu than the server, the server pixmaps
|
|
|
|
* can have a tiling mode we can't read. Thus we can't create
|
|
|
|
* a texture from them.
|
|
|
|
*/
|
|
|
|
if (!psc->is_different_gpu &&
|
|
|
|
(strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
|
2013-11-05 02:15:51 +00:00
|
|
|
psc->texBuffer = (__DRItexBufferExtension *) extensions[i];
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((strcmp(extensions[i]->name, __DRI2_FLUSH) == 0)) {
|
|
|
|
psc->f = (__DRI2flushExtension *) extensions[i];
|
|
|
|
/* internal driver extension, no GL extension exposed */
|
|
|
|
}
|
|
|
|
|
2014-06-27 02:38:21 +01:00
|
|
|
if (strcmp(extensions[i]->name, __DRI_IMAGE) == 0)
|
|
|
|
psc->image = (__DRIimageExtension *) extensions[i];
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
if ((strcmp(extensions[i]->name, __DRI2_CONFIG_QUERY) == 0))
|
|
|
|
psc->config = (__DRI2configQueryExtension *) extensions[i];
|
|
|
|
|
|
|
|
if (strcmp(extensions[i]->name, __DRI2_ROBUSTNESS) == 0)
|
|
|
|
__glXEnableDirectExtension(&psc->base,
|
|
|
|
"GLX_ARB_create_context_robustness");
|
2014-02-20 19:47:14 +00:00
|
|
|
|
|
|
|
if (strcmp(extensions[i]->name, __DRI2_RENDERER_QUERY) == 0) {
|
|
|
|
psc->rendererQuery = (__DRI2rendererQueryExtension *) extensions[i];
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_MESA_query_renderer");
|
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct glx_screen_vtable dri3_screen_vtable = {
|
2014-02-18 19:26:27 +00:00
|
|
|
.create_context = dri3_create_context,
|
|
|
|
.create_context_attribs = dri3_create_context_attribs,
|
2014-02-20 19:47:14 +00:00
|
|
|
.query_renderer_integer = dri3_query_renderer_integer,
|
|
|
|
.query_renderer_string = dri3_query_renderer_string,
|
2013-11-05 02:15:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/** dri3_create_screen
|
|
|
|
*
|
|
|
|
* Initialize DRI3 on the specified screen.
|
|
|
|
*
|
|
|
|
* Opens the DRI device, locates the appropriate DRI driver
|
|
|
|
* and loads that.
|
|
|
|
*
|
|
|
|
* Checks to see if the driver supports the necessary extensions
|
|
|
|
*
|
|
|
|
* Initializes the driver for the screen and sets up our structures
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct glx_screen *
|
|
|
|
dri3_create_screen(int screen, struct glx_display * priv)
|
|
|
|
{
|
2013-11-07 23:44:57 +00:00
|
|
|
xcb_connection_t *c = XGetXCBConnection(priv->dpy);
|
2013-11-05 02:15:51 +00:00
|
|
|
const __DRIconfig **driver_configs;
|
|
|
|
const __DRIextension **extensions;
|
|
|
|
const struct dri3_display *const pdp = (struct dri3_display *)
|
|
|
|
priv->dri3Display;
|
|
|
|
struct dri3_screen *psc;
|
|
|
|
__GLXDRIscreen *psp;
|
|
|
|
struct glx_config *configs = NULL, *visuals = NULL;
|
2014-03-15 01:53:42 +00:00
|
|
|
char *driverName, *deviceName, *tmp;
|
2013-11-05 02:15:51 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
psc = calloc(1, sizeof *psc);
|
|
|
|
if (psc == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
psc->fd = -1;
|
|
|
|
|
|
|
|
if (!glx_screen_init(&psc->base, screen, priv)) {
|
|
|
|
free(psc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
psc->fd = dri3_open(priv->dpy, RootWindow(priv->dpy, screen), None);
|
|
|
|
if (psc->fd < 0) {
|
2013-11-07 23:44:57 +00:00
|
|
|
int conn_error = xcb_connection_has_error(c);
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
glx_screen_cleanup(&psc->base);
|
|
|
|
free(psc);
|
|
|
|
InfoMessageF("screen %d does not appear to be DRI3 capable\n", screen);
|
2013-11-07 23:44:57 +00:00
|
|
|
|
|
|
|
if (conn_error)
|
|
|
|
ErrorMessageF("Connection closed during DRI3 initialization failure");
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-05-17 17:12:11 +01:00
|
|
|
|
|
|
|
psc->fd = loader_get_user_preferred_fd(psc->fd, &psc->is_different_gpu);
|
2013-11-05 02:15:51 +00:00
|
|
|
deviceName = NULL;
|
|
|
|
|
2014-01-11 02:24:43 +00:00
|
|
|
driverName = loader_get_driver_for_fd(psc->fd, 0);
|
2013-11-05 02:15:51 +00:00
|
|
|
if (!driverName) {
|
|
|
|
ErrorMessageF("No driver found\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
psc->driver = driOpenDriver(driverName);
|
|
|
|
if (psc->driver == NULL) {
|
|
|
|
ErrorMessageF("driver pointer missing\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
extensions = driGetDriverExtensions(psc->driver, driverName);
|
|
|
|
if (extensions == NULL)
|
|
|
|
goto handle_error;
|
|
|
|
|
|
|
|
for (i = 0; extensions[i]; i++) {
|
|
|
|
if (strcmp(extensions[i]->name, __DRI_CORE) == 0)
|
|
|
|
psc->core = (__DRIcoreExtension *) extensions[i];
|
|
|
|
if (strcmp(extensions[i]->name, __DRI_IMAGE_DRIVER) == 0)
|
|
|
|
psc->image_driver = (__DRIimageDriverExtension *) extensions[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (psc->core == NULL) {
|
|
|
|
ErrorMessageF("core dri driver extension not found\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (psc->image_driver == NULL) {
|
|
|
|
ErrorMessageF("image driver extension not found\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
psc->driScreen =
|
|
|
|
psc->image_driver->createNewScreen2(screen, psc->fd,
|
2014-03-16 01:17:46 +00:00
|
|
|
pdp->loader_extensions,
|
2013-11-05 02:15:51 +00:00
|
|
|
extensions,
|
|
|
|
&driver_configs, psc);
|
|
|
|
|
|
|
|
if (psc->driScreen == NULL) {
|
|
|
|
ErrorMessageF("failed to create dri screen\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
2014-06-27 02:38:21 +01:00
|
|
|
dri3_bind_extensions(psc, priv, driverName);
|
2013-11-05 02:15:51 +00:00
|
|
|
|
2014-06-27 02:38:21 +01:00
|
|
|
if (!psc->image || psc->image->base.version < 7 || !psc->image->createImageFromFds) {
|
|
|
|
ErrorMessageF("Version 7 or imageFromFds image extension not found\n");
|
2013-11-05 02:15:51 +00:00
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!psc->f || psc->f->base.version < 4) {
|
|
|
|
ErrorMessageF("Version 4 or later of flush extension not found\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
2014-05-17 17:12:11 +01:00
|
|
|
if (psc->is_different_gpu && psc->image->base.version < 9) {
|
|
|
|
ErrorMessageF("Different GPU, but image extension version 9 or later not found\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!psc->is_different_gpu && (
|
|
|
|
!psc->texBuffer || psc->texBuffer->base.version < 2 ||
|
|
|
|
!psc->texBuffer->setTexBuffer2
|
|
|
|
)) {
|
2013-11-05 02:15:51 +00:00
|
|
|
ErrorMessageF("Version 2 or later of texBuffer extension not found\n");
|
|
|
|
goto handle_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
configs = driConvertConfigs(psc->core, psc->base.configs, driver_configs);
|
|
|
|
visuals = driConvertConfigs(psc->core, psc->base.visuals, driver_configs);
|
|
|
|
|
2014-05-10 11:04:44 +01:00
|
|
|
if (!configs || !visuals) {
|
|
|
|
ErrorMessageF("No matching fbConfigs or visuals found\n");
|
2013-11-05 02:15:51 +00:00
|
|
|
goto handle_error;
|
2014-05-10 11:04:44 +01:00
|
|
|
}
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
glx_config_destroy_list(psc->base.configs);
|
|
|
|
psc->base.configs = configs;
|
|
|
|
glx_config_destroy_list(psc->base.visuals);
|
|
|
|
psc->base.visuals = visuals;
|
|
|
|
|
|
|
|
psc->driver_configs = driver_configs;
|
|
|
|
|
|
|
|
psc->base.vtable = &dri3_screen_vtable;
|
|
|
|
psp = &psc->vtable;
|
|
|
|
psc->base.driScreen = psp;
|
|
|
|
psp->destroyScreen = dri3_destroy_screen;
|
|
|
|
psp->createDrawable = dri3_create_drawable;
|
|
|
|
psp->swapBuffers = dri3_swap_buffers;
|
|
|
|
|
|
|
|
psp->getDrawableMSC = dri3_drawable_get_msc;
|
|
|
|
psp->waitForMSC = dri3_wait_for_msc;
|
|
|
|
psp->waitForSBC = dri3_wait_for_sbc;
|
|
|
|
psp->setSwapInterval = dri3_set_swap_interval;
|
|
|
|
psp->getSwapInterval = dri3_get_swap_interval;
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_OML_sync_control");
|
|
|
|
|
|
|
|
psp->copySubBuffer = dri3_copy_sub_buffer;
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
|
|
|
|
|
2014-02-24 19:44:42 +00:00
|
|
|
psp->getBufferAge = dri3_get_buffer_age;
|
|
|
|
__glXEnableDirectExtension(&psc->base, "GLX_EXT_buffer_age");
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
free(driverName);
|
|
|
|
free(deviceName);
|
|
|
|
|
2014-03-15 01:53:42 +00:00
|
|
|
tmp = getenv("LIBGL_SHOW_FPS");
|
|
|
|
psc->show_fps_interval = tmp ? atoi(tmp) : 0;
|
|
|
|
if (psc->show_fps_interval < 0)
|
|
|
|
psc->show_fps_interval = 0;
|
|
|
|
|
2013-11-05 02:15:51 +00:00
|
|
|
return &psc->base;
|
|
|
|
|
|
|
|
handle_error:
|
|
|
|
CriticalErrorMessageF("failed to load driver: %s\n", driverName);
|
|
|
|
|
|
|
|
if (configs)
|
|
|
|
glx_config_destroy_list(configs);
|
|
|
|
if (visuals)
|
|
|
|
glx_config_destroy_list(visuals);
|
|
|
|
if (psc->driScreen)
|
|
|
|
psc->core->destroyScreen(psc->driScreen);
|
|
|
|
psc->driScreen = NULL;
|
|
|
|
if (psc->fd >= 0)
|
|
|
|
close(psc->fd);
|
|
|
|
if (psc->driver)
|
|
|
|
dlclose(psc->driver);
|
|
|
|
|
|
|
|
free(driverName);
|
|
|
|
free(deviceName);
|
|
|
|
glx_screen_cleanup(&psc->base);
|
|
|
|
free(psc);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri_destroy_display
|
|
|
|
*
|
|
|
|
* Called from __glXFreeDisplayPrivate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dri3_destroy_display(__GLXDRIdisplay * dpy)
|
|
|
|
{
|
|
|
|
free(dpy);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** dri3_create_display
|
|
|
|
*
|
|
|
|
* Allocate, initialize and return a __DRIdisplayPrivate object.
|
|
|
|
* This is called from __glXInitialize() when we are given a new
|
|
|
|
* display pointer. This is public to that function, but hidden from
|
|
|
|
* outside of libGL.
|
|
|
|
*/
|
|
|
|
_X_HIDDEN __GLXDRIdisplay *
|
|
|
|
dri3_create_display(Display * dpy)
|
|
|
|
{
|
|
|
|
struct dri3_display *pdp;
|
|
|
|
xcb_connection_t *c = XGetXCBConnection(dpy);
|
|
|
|
xcb_dri3_query_version_cookie_t dri3_cookie;
|
|
|
|
xcb_dri3_query_version_reply_t *dri3_reply;
|
|
|
|
xcb_present_query_version_cookie_t present_cookie;
|
|
|
|
xcb_present_query_version_reply_t *present_reply;
|
|
|
|
xcb_generic_error_t *error;
|
|
|
|
const xcb_query_extension_reply_t *extension;
|
|
|
|
|
|
|
|
xcb_prefetch_extension_data(c, &xcb_dri3_id);
|
|
|
|
xcb_prefetch_extension_data(c, &xcb_present_id);
|
|
|
|
|
|
|
|
extension = xcb_get_extension_data(c, &xcb_dri3_id);
|
|
|
|
if (!(extension && extension->present))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
extension = xcb_get_extension_data(c, &xcb_present_id);
|
|
|
|
if (!(extension && extension->present))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dri3_cookie = xcb_dri3_query_version(c,
|
|
|
|
XCB_DRI3_MAJOR_VERSION,
|
|
|
|
XCB_DRI3_MINOR_VERSION);
|
|
|
|
|
|
|
|
|
|
|
|
present_cookie = xcb_present_query_version(c,
|
|
|
|
XCB_PRESENT_MAJOR_VERSION,
|
|
|
|
XCB_PRESENT_MINOR_VERSION);
|
|
|
|
|
|
|
|
pdp = malloc(sizeof *pdp);
|
|
|
|
if (pdp == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dri3_reply = xcb_dri3_query_version_reply(c, dri3_cookie, &error);
|
|
|
|
if (!dri3_reply) {
|
|
|
|
free(error);
|
|
|
|
goto no_extension;
|
|
|
|
}
|
|
|
|
|
|
|
|
pdp->dri3Major = dri3_reply->major_version;
|
|
|
|
pdp->dri3Minor = dri3_reply->minor_version;
|
|
|
|
free(dri3_reply);
|
|
|
|
|
|
|
|
present_reply = xcb_present_query_version_reply(c, present_cookie, &error);
|
|
|
|
if (!present_reply) {
|
|
|
|
free(error);
|
|
|
|
goto no_extension;
|
|
|
|
}
|
|
|
|
pdp->presentMajor = present_reply->major_version;
|
|
|
|
pdp->presentMinor = present_reply->minor_version;
|
2014-01-23 18:25:58 +00:00
|
|
|
free(present_reply);
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
pdp->base.destroyDisplay = dri3_destroy_display;
|
|
|
|
pdp->base.createScreen = dri3_create_screen;
|
|
|
|
|
2014-01-23 19:03:53 +00:00
|
|
|
loader_set_logger(dri_message);
|
2013-11-05 02:15:51 +00:00
|
|
|
|
2014-03-16 01:17:46 +00:00
|
|
|
pdp->loader_extensions = loader_extensions;
|
2013-11-05 02:15:51 +00:00
|
|
|
|
|
|
|
return &pdp->base;
|
|
|
|
no_extension:
|
|
|
|
free(pdp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* GLX_DIRECT_RENDERING */
|