mirror of
https://github.com/X11Libre/xf86-video-ati.git
synced 2026-03-24 01:24:43 +00:00
Performed with: `git ls-files | xargs perl -i -p -e 's{[ \t]+$}{}'`
`git diff -w` & `git diff -b` show no diffs from this change
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Part-of: <https://gitlab.freedesktop.org/xorg/driver/xf86-video-ati/-/merge_requests/32>
1488 lines
44 KiB
C
1488 lines
44 KiB
C
/*
|
|
* Copyright 2008 Kristian Høgsberg
|
|
* Copyright 2008 Jérôme Glisse
|
|
*
|
|
* All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation on the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
|
|
* THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "radeon.h"
|
|
#include "radeon_dri2.h"
|
|
#include "radeon_video.h"
|
|
|
|
#ifdef DRI2
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include "radeon_bo_helper.h"
|
|
#include "radeon_version.h"
|
|
|
|
#include "radeon_bo_gem.h"
|
|
|
|
#include <list.h>
|
|
#include <xf86Priv.h>
|
|
#include <X11/extensions/dpmsconst.h>
|
|
|
|
#define FALLBACK_SWAP_DELAY 16
|
|
|
|
#include "radeon_glamor.h"
|
|
|
|
typedef DRI2BufferPtr BufferPtr;
|
|
|
|
struct dri2_buffer_priv {
|
|
PixmapPtr pixmap;
|
|
unsigned int attachment;
|
|
unsigned int refcnt;
|
|
};
|
|
|
|
|
|
struct dri2_window_priv {
|
|
xf86CrtcPtr crtc;
|
|
int vblank_delta;
|
|
};
|
|
|
|
static DevPrivateKeyRec dri2_window_private_key_rec;
|
|
#define dri2_window_private_key (&dri2_window_private_key_rec)
|
|
|
|
#define get_dri2_window_priv(window) \
|
|
((struct dri2_window_priv*) \
|
|
dixLookupPrivate(&(window)->devPrivates, dri2_window_private_key))
|
|
|
|
|
|
/* Get GEM flink name for a pixmap */
|
|
static Bool
|
|
radeon_get_flink_name(RADEONEntPtr pRADEONEnt, PixmapPtr pixmap, uint32_t *name)
|
|
{
|
|
struct radeon_buffer *bo = radeon_get_pixmap_bo(pixmap);
|
|
struct drm_gem_flink flink;
|
|
|
|
if (bo && !(bo->flags & RADEON_BO_FLAGS_GBM) &&
|
|
radeon_gem_get_kernel_name(bo->bo.radeon, name) == 0)
|
|
return TRUE;
|
|
|
|
if (radeon_get_pixmap_handle(pixmap, &flink.handle)) {
|
|
if (drmIoctl(pRADEONEnt->fd, DRM_IOCTL_GEM_FLINK, &flink) != 0)
|
|
return FALSE;
|
|
|
|
*name = flink.name;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BufferPtr
|
|
radeon_dri2_create_buffer2(ScreenPtr pScreen,
|
|
DrawablePtr drawable,
|
|
unsigned int attachment,
|
|
unsigned int format)
|
|
{
|
|
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
|
|
RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn);
|
|
RADEONInfoPtr info = RADEONPTR(pScrn);
|
|
BufferPtr buffers;
|
|
struct dri2_buffer_priv *privates;
|
|
PixmapPtr pixmap;
|
|
int flags;
|
|
unsigned front_width;
|
|
unsigned aligned_width = drawable->width;
|
|
unsigned height = drawable->height;
|
|
Bool is_glamor_pixmap = FALSE;
|
|
int depth;
|
|
int cpp;
|
|
|
|
if (format) {
|
|
depth = format;
|
|
|
|
switch (depth) {
|
|
case 15:
|
|
cpp = 2;
|
|
break;
|
|
case 24:
|
|
case 30:
|
|
cpp = 4;
|
|
break;
|
|
default:
|
|
cpp = depth / 8;
|
|
}
|
|
} else {
|
|
depth = drawable->depth;
|
|
cpp = drawable->bitsPerPixel / 8;
|
|
}
|
|
|
|
front_width = pScreen->GetScreenPixmap(pScreen)->drawable.width;
|
|
|
|
pixmap = NULL;
|
|
|
|
if (attachment == DRI2BufferFrontLeft) {
|
|
uint32_t handle;
|
|
|
|
pixmap = get_drawable_pixmap(drawable);
|
|
if (pScreen != pixmap->drawable.pScreen)
|
|
pixmap = NULL;
|
|
else if (info->use_glamor && !radeon_get_pixmap_handle(pixmap, &handle)) {
|
|
is_glamor_pixmap = TRUE;
|
|
aligned_width = pixmap->drawable.width;
|
|
height = pixmap->drawable.height;
|
|
pixmap = NULL;
|
|
} else
|
|
pixmap->refcnt++;
|
|
}
|
|
|
|
if (!pixmap && (is_glamor_pixmap || attachment != DRI2BufferFrontLeft)) {
|
|
/* tile the back buffer */
|
|
switch(attachment) {
|
|
case DRI2BufferDepth:
|
|
/* macro is the preferred setting, but the 2D detiling for software
|
|
* fallbacks in mesa still has issues on some configurations
|
|
*/
|
|
if (info->ChipFamily >= CHIP_FAMILY_R600) {
|
|
if (info->allowColorTiling2D) {
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
|
|
} else {
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
|
|
}
|
|
if (info->ChipFamily >= CHIP_FAMILY_CEDAR)
|
|
flags |= RADEON_CREATE_PIXMAP_SZBUFFER;
|
|
} else if (cpp == 2 && info->ChipFamily >= CHIP_FAMILY_R300)
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO_SQUARE;
|
|
else
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO;
|
|
if (IS_R200_3D || info->ChipFamily == CHIP_FAMILY_RV200 || info->ChipFamily == CHIP_FAMILY_RADEON)
|
|
flags |= RADEON_CREATE_PIXMAP_DEPTH;
|
|
break;
|
|
case DRI2BufferDepthStencil:
|
|
/* macro is the preferred setting, but the 2D detiling for software
|
|
* fallbacks in mesa still has issues on some configurations
|
|
*/
|
|
if (info->ChipFamily >= CHIP_FAMILY_R600) {
|
|
if (info->allowColorTiling2D) {
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
|
|
} else {
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
|
|
}
|
|
if (info->ChipFamily >= CHIP_FAMILY_CEDAR)
|
|
flags |= RADEON_CREATE_PIXMAP_SZBUFFER;
|
|
} else if (cpp == 2 && info->ChipFamily >= CHIP_FAMILY_R300)
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO_SQUARE;
|
|
else
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO;
|
|
if (IS_R200_3D || info->ChipFamily == CHIP_FAMILY_RV200 || info->ChipFamily == CHIP_FAMILY_RADEON)
|
|
flags |= RADEON_CREATE_PIXMAP_DEPTH;
|
|
|
|
break;
|
|
case DRI2BufferBackLeft:
|
|
case DRI2BufferBackRight:
|
|
case DRI2BufferFrontLeft:
|
|
case DRI2BufferFrontRight:
|
|
case DRI2BufferFakeFrontLeft:
|
|
case DRI2BufferFakeFrontRight:
|
|
if (info->ChipFamily >= CHIP_FAMILY_R600) {
|
|
if (info->allowColorTiling2D) {
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
|
|
} else {
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
|
|
}
|
|
} else
|
|
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
|
|
break;
|
|
default:
|
|
flags = 0;
|
|
}
|
|
|
|
if (aligned_width == front_width)
|
|
aligned_width = pScrn->virtualX;
|
|
|
|
pixmap = (*pScreen->CreatePixmap)(pScreen,
|
|
aligned_width,
|
|
height,
|
|
depth,
|
|
flags | RADEON_CREATE_PIXMAP_DRI2);
|
|
}
|
|
|
|
if (!pixmap)
|
|
return NULL;
|
|
|
|
buffers = calloc(1, sizeof *buffers);
|
|
if (!buffers)
|
|
goto error;
|
|
|
|
if (!info->use_glamor) {
|
|
info->exa_force_create = TRUE;
|
|
exaMoveInPixmap(pixmap);
|
|
info->exa_force_create = FALSE;
|
|
if (!exaGetPixmapDriverPrivate(pixmap)) {
|
|
/* this happen if pixmap is non accelerable */
|
|
goto error;
|
|
}
|
|
} else if (is_glamor_pixmap) {
|
|
pixmap = radeon_glamor_set_pixmap_bo(drawable, pixmap);
|
|
pixmap->refcnt++;
|
|
|
|
/* The copy operation from radeon_glamor_set_pixmap_bo needs to
|
|
* be flushed to the kernel driver before the client starts
|
|
* using the pixmap storage for direct rendering.
|
|
*/
|
|
radeon_cs_flush_indirect(pScrn);
|
|
}
|
|
|
|
if (!radeon_get_flink_name(pRADEONEnt, pixmap, &buffers->name))
|
|
goto error;
|
|
|
|
privates = calloc(1, sizeof(struct dri2_buffer_priv));
|
|
if (!privates)
|
|
goto error;
|
|
|
|
buffers->attachment = attachment;
|
|
buffers->pitch = pixmap->devKind;
|
|
buffers->cpp = cpp;
|
|
buffers->driverPrivate = privates;
|
|
buffers->format = format;
|
|
buffers->flags = 0; /* not tiled */
|
|
privates->pixmap = pixmap;
|
|
privates->attachment = attachment;
|
|
privates->refcnt = 1;
|
|
|
|
return buffers;
|
|
|
|
error:
|
|
free(buffers);
|
|
(*pScreen->DestroyPixmap)(pixmap);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
radeon_dri2_destroy_buffer2(ScreenPtr pScreen,
|
|
DrawablePtr drawable, BufferPtr buffers)
|
|
{
|
|
if(buffers)
|
|
{
|
|
struct dri2_buffer_priv *private = buffers->driverPrivate;
|
|
|
|
/* Trying to free an already freed buffer is unlikely to end well */
|
|
if (private->refcnt == 0) {
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
|
|
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"Attempted to destroy previously destroyed buffer.\
|
|
This is a programming error\n");
|
|
return;
|
|
}
|
|
|
|
private->refcnt--;
|
|
if (private->refcnt == 0)
|
|
{
|
|
if (private->pixmap)
|
|
(*pScreen->DestroyPixmap)(private->pixmap);
|
|
|
|
free(buffers->driverPrivate);
|
|
free(buffers);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable)
|
|
{
|
|
if (drawable->type == DRAWABLE_PIXMAP)
|
|
return (PixmapPtr)drawable;
|
|
else {
|
|
struct _Window *pWin = (struct _Window *)drawable;
|
|
return drawable->pScreen->GetWindowPixmap(pWin);
|
|
}
|
|
}
|
|
static void
|
|
radeon_dri2_copy_region2(ScreenPtr pScreen,
|
|
DrawablePtr drawable,
|
|
RegionPtr region,
|
|
BufferPtr dest_buffer,
|
|
BufferPtr src_buffer)
|
|
{
|
|
struct dri2_buffer_priv *src_private = src_buffer->driverPrivate;
|
|
struct dri2_buffer_priv *dst_private = dest_buffer->driverPrivate;
|
|
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
|
|
DrawablePtr src_drawable;
|
|
DrawablePtr dst_drawable;
|
|
RegionPtr copy_clip;
|
|
GCPtr gc;
|
|
RADEONInfoPtr info = RADEONPTR(pScrn);
|
|
Bool vsync;
|
|
Bool translate = FALSE;
|
|
int off_x = 0, off_y = 0;
|
|
|
|
src_drawable = &src_private->pixmap->drawable;
|
|
dst_drawable = &dst_private->pixmap->drawable;
|
|
|
|
if (src_private->attachment == DRI2BufferFrontLeft) {
|
|
if (drawable->pScreen != pScreen) {
|
|
src_drawable = DRI2UpdatePrime(drawable, src_buffer);
|
|
if (!src_drawable)
|
|
return;
|
|
} else
|
|
src_drawable = drawable;
|
|
}
|
|
if (dst_private->attachment == DRI2BufferFrontLeft) {
|
|
if (drawable->pScreen != pScreen) {
|
|
dst_drawable = DRI2UpdatePrime(drawable, dest_buffer);
|
|
if (!dst_drawable)
|
|
return;
|
|
if (dst_drawable != drawable)
|
|
translate = TRUE;
|
|
} else
|
|
dst_drawable = drawable;
|
|
}
|
|
|
|
if (translate && drawable->type == DRAWABLE_WINDOW) {
|
|
PixmapPtr pPix = GetDrawablePixmap(drawable);
|
|
|
|
off_x = drawable->x - pPix->screen_x;
|
|
off_y = drawable->y - pPix->screen_y;
|
|
}
|
|
gc = GetScratchGC(dst_drawable->depth, pScreen);
|
|
copy_clip = REGION_CREATE(pScreen, NULL, 0);
|
|
REGION_COPY(pScreen, copy_clip, region);
|
|
|
|
if (translate) {
|
|
REGION_TRANSLATE(pScreen, copy_clip, off_x, off_y);
|
|
}
|
|
|
|
(*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0);
|
|
ValidateGC(dst_drawable, gc);
|
|
|
|
vsync = info->accel_state->vsync;
|
|
/* Driver option "SwapbuffersWait" defines if we vsync DRI2 copy-swaps. */
|
|
info->accel_state->vsync = info->swapBuffersWait;
|
|
info->accel_state->force = TRUE;
|
|
|
|
(*gc->ops->CopyArea)(src_drawable, dst_drawable, gc,
|
|
0, 0, drawable->width, drawable->height, off_x, off_y);
|
|
|
|
info->accel_state->force = FALSE;
|
|
info->accel_state->vsync = vsync;
|
|
|
|
FreeScratchGC(gc);
|
|
}
|
|
|
|
enum DRI2FrameEventType {
|
|
DRI2_SWAP,
|
|
DRI2_FLIP,
|
|
DRI2_WAITMSC,
|
|
};
|
|
|
|
typedef struct _DRI2FrameEvent {
|
|
XID drawable_id;
|
|
ClientPtr client;
|
|
enum DRI2FrameEventType type;
|
|
unsigned frame;
|
|
xf86CrtcPtr crtc;
|
|
OsTimerPtr timer;
|
|
uintptr_t drm_queue_seq;
|
|
|
|
/* for swaps & flips only */
|
|
DRI2SwapEventPtr event_complete;
|
|
void *event_data;
|
|
DRI2BufferPtr front;
|
|
DRI2BufferPtr back;
|
|
} DRI2FrameEventRec, *DRI2FrameEventPtr;
|
|
|
|
static int DRI2InfoCnt;
|
|
|
|
static void
|
|
radeon_dri2_ref_buffer(BufferPtr buffer)
|
|
{
|
|
struct dri2_buffer_priv *private = buffer->driverPrivate;
|
|
private->refcnt++;
|
|
}
|
|
|
|
static void
|
|
radeon_dri2_unref_buffer(BufferPtr buffer)
|
|
{
|
|
if (buffer) {
|
|
struct dri2_buffer_priv *private = buffer->driverPrivate;
|
|
DrawablePtr draw = &private->pixmap->drawable;
|
|
|
|
radeon_dri2_destroy_buffer2(draw->pScreen, draw, buffer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
radeon_dri2_client_state_changed(CallbackListPtr *ClientStateCallback, pointer data, pointer calldata)
|
|
{
|
|
NewClientInfoRec *clientinfo = calldata;
|
|
ClientPtr pClient = clientinfo->client;
|
|
|
|
switch (pClient->clientState) {
|
|
case ClientStateRetained:
|
|
case ClientStateGone:
|
|
radeon_drm_abort_client(pClient);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get current frame count delta for the specified drawable and CRTC
|
|
*/
|
|
static uint32_t radeon_get_msc_delta(DrawablePtr pDraw, xf86CrtcPtr crtc)
|
|
{
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
|
|
if (pDraw && pDraw->type == DRAWABLE_WINDOW)
|
|
return drmmode_crtc->interpolated_vblanks +
|
|
get_dri2_window_priv((WindowPtr)pDraw)->vblank_delta;
|
|
|
|
return drmmode_crtc->interpolated_vblanks;
|
|
}
|
|
|
|
/*
|
|
* Get current frame count and timestamp of the specified CRTC
|
|
*/
|
|
static Bool radeon_dri2_get_crtc_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
|
|
{
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
|
|
if (!radeon_crtc_is_enabled(crtc) ||
|
|
drmmode_crtc_get_ust_msc(crtc, ust, msc) != Success) {
|
|
/* CRTC is not running, extrapolate MSC and timestamp */
|
|
ScrnInfoPtr scrn = crtc->scrn;
|
|
RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
|
|
CARD64 now, delta_t, delta_seq;
|
|
|
|
if (!drmmode_crtc->dpms_last_ust)
|
|
return FALSE;
|
|
|
|
if (drmmode_get_current_ust(pRADEONEnt->fd, &now) != 0) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
|
|
"%s cannot get current time\n", __func__);
|
|
return FALSE;
|
|
}
|
|
|
|
delta_t = now - drmmode_crtc->dpms_last_ust;
|
|
delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
|
|
delta_seq /= 1000000;
|
|
*ust = drmmode_crtc->dpms_last_ust;
|
|
delta_t = delta_seq * 1000000;
|
|
delta_t /= drmmode_crtc->dpms_last_fps;
|
|
*ust += delta_t;
|
|
*msc = drmmode_crtc->dpms_last_seq;
|
|
*msc += delta_seq;
|
|
}
|
|
|
|
*msc += drmmode_crtc->interpolated_vblanks;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
xf86CrtcPtr radeon_dri2_drawable_crtc(DrawablePtr pDraw)
|
|
{
|
|
ScreenPtr pScreen = pDraw->pScreen;
|
|
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
|
|
xf86CrtcPtr crtc = radeon_pick_best_crtc(pScrn, TRUE,
|
|
pDraw->x, pDraw->x + pDraw->width,
|
|
pDraw->y, pDraw->y + pDraw->height);
|
|
|
|
if (pDraw->type == DRAWABLE_WINDOW) {
|
|
struct dri2_window_priv *priv = get_dri2_window_priv((WindowPtr)pDraw);
|
|
|
|
if (!crtc) {
|
|
crtc = priv->crtc;
|
|
} else if (priv->crtc && priv->crtc != crtc) {
|
|
CARD64 ust, mscold, mscnew;
|
|
|
|
if (radeon_dri2_get_crtc_msc(priv->crtc, &ust, &mscold) &&
|
|
radeon_dri2_get_crtc_msc(crtc, &ust, &mscnew))
|
|
priv->vblank_delta += mscold - mscnew;
|
|
}
|
|
|
|
priv->crtc = crtc;
|
|
}
|
|
|
|
return crtc;
|
|
}
|
|
|
|
static void
|
|
radeon_dri2_flip_event_abort(xf86CrtcPtr crtc, void *event_data)
|
|
{
|
|
if (crtc)
|
|
RADEONPTR(crtc->scrn)->drmmode.dri2_flipping = FALSE;
|
|
|
|
free(event_data);
|
|
}
|
|
|
|
static void
|
|
radeon_dri2_flip_event_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
|
|
void *event_data)
|
|
{
|
|
DRI2FrameEventPtr flip = event_data;
|
|
ScrnInfoPtr scrn = crtc->scrn;
|
|
unsigned tv_sec, tv_usec;
|
|
DrawablePtr drawable;
|
|
ScreenPtr screen;
|
|
int status;
|
|
PixmapPtr pixmap;
|
|
|
|
status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient,
|
|
M_ANY, DixWriteAccess);
|
|
if (status != Success)
|
|
goto abort;
|
|
|
|
frame += radeon_get_msc_delta(drawable, crtc);
|
|
|
|
screen = scrn->pScreen;
|
|
pixmap = screen->GetScreenPixmap(screen);
|
|
xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
|
|
"%s:%d fevent[%p] width %d pitch %d (/4 %d)\n",
|
|
__func__, __LINE__, flip, pixmap->drawable.width, pixmap->devKind, pixmap->devKind/4);
|
|
|
|
tv_sec = usec / 1000000;
|
|
tv_usec = usec % 1000000;
|
|
|
|
/* We assume our flips arrive in order, so we don't check the frame */
|
|
switch (flip->type) {
|
|
case DRI2_SWAP:
|
|
/* Check for too small vblank count of pageflip completion, taking wraparound
|
|
* into account. This usually means some defective kms pageflip completion,
|
|
* causing wrong (msc, ust) return values and possible visual corruption.
|
|
*/
|
|
if ((frame < flip->frame) && (flip->frame - frame < 5)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"%s: Pageflip completion event has impossible msc %u < target_msc %u\n",
|
|
__func__, frame, flip->frame);
|
|
/* All-Zero values signal failure of (msc, ust) timestamping to client. */
|
|
frame = tv_sec = tv_usec = 0;
|
|
}
|
|
|
|
DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec,
|
|
DRI2_FLIP_COMPLETE, flip->event_complete,
|
|
flip->event_data);
|
|
break;
|
|
default:
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __func__);
|
|
/* Unknown type */
|
|
break;
|
|
}
|
|
|
|
abort:
|
|
radeon_dri2_flip_event_abort(crtc, event_data);
|
|
}
|
|
|
|
static Bool
|
|
radeon_dri2_schedule_flip(xf86CrtcPtr crtc, ClientPtr client,
|
|
DrawablePtr draw, DRI2BufferPtr front,
|
|
DRI2BufferPtr back, DRI2SwapEventPtr func,
|
|
void *data, unsigned int target_msc)
|
|
{
|
|
ScrnInfoPtr scrn = crtc->scrn;
|
|
RADEONInfoPtr info = RADEONPTR(scrn);
|
|
struct dri2_buffer_priv *back_priv;
|
|
DRI2FrameEventPtr flip_info;
|
|
|
|
flip_info = calloc(1, sizeof(DRI2FrameEventRec));
|
|
if (!flip_info)
|
|
return FALSE;
|
|
|
|
flip_info->drawable_id = draw->id;
|
|
flip_info->client = client;
|
|
flip_info->type = DRI2_SWAP;
|
|
flip_info->event_complete = func;
|
|
flip_info->event_data = data;
|
|
flip_info->frame = target_msc;
|
|
flip_info->crtc = crtc;
|
|
|
|
xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
|
|
"%s:%d fevent[%p]\n", __func__, __LINE__, flip_info);
|
|
|
|
/* Page flip the full screen buffer */
|
|
back_priv = back->driverPrivate;
|
|
if (radeon_do_pageflip(scrn, client, back_priv->pixmap,
|
|
RADEON_DRM_QUEUE_ID_DEFAULT, flip_info, crtc,
|
|
radeon_dri2_flip_event_handler,
|
|
radeon_dri2_flip_event_abort, FLIP_VSYNC,
|
|
target_msc - radeon_get_msc_delta(draw, crtc))) {
|
|
info->drmmode.dri2_flipping = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static Bool
|
|
update_front(DrawablePtr draw, DRI2BufferPtr front)
|
|
{
|
|
PixmapPtr pixmap;
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(draw->pScreen);
|
|
RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
|
|
RADEONInfoPtr info = RADEONPTR(scrn);
|
|
struct dri2_buffer_priv *priv = front->driverPrivate;
|
|
|
|
pixmap = get_drawable_pixmap(draw);
|
|
pixmap->refcnt++;
|
|
|
|
if (!info->use_glamor)
|
|
exaMoveInPixmap(pixmap);
|
|
if (!radeon_get_flink_name(pRADEONEnt, pixmap, &front->name)) {
|
|
(*draw->pScreen->DestroyPixmap)(pixmap);
|
|
return FALSE;
|
|
}
|
|
(*draw->pScreen->DestroyPixmap)(priv->pixmap);
|
|
front->pitch = pixmap->devKind;
|
|
front->cpp = pixmap->drawable.bitsPerPixel / 8;
|
|
priv->pixmap = pixmap;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
can_exchange(ScrnInfoPtr pScrn, DrawablePtr draw,
|
|
DRI2BufferPtr front, DRI2BufferPtr back)
|
|
{
|
|
struct dri2_buffer_priv *front_priv = front->driverPrivate;
|
|
struct dri2_buffer_priv *back_priv = back->driverPrivate;
|
|
PixmapPtr front_pixmap;
|
|
PixmapPtr back_pixmap = back_priv->pixmap;
|
|
|
|
if (!update_front(draw, front))
|
|
return FALSE;
|
|
|
|
front_pixmap = front_priv->pixmap;
|
|
|
|
if (front_pixmap->drawable.width != back_pixmap->drawable.width)
|
|
return FALSE;
|
|
|
|
if (front_pixmap->drawable.height != back_pixmap->drawable.height)
|
|
return FALSE;
|
|
|
|
if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel)
|
|
return FALSE;
|
|
|
|
if (front_pixmap->devKind != back_pixmap->devKind)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
can_flip(xf86CrtcPtr crtc, DrawablePtr draw,
|
|
DRI2BufferPtr front, DRI2BufferPtr back)
|
|
{
|
|
ScrnInfoPtr pScrn = crtc->scrn;
|
|
RADEONInfoPtr info = RADEONPTR(pScrn);
|
|
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
|
|
int num_crtcs_on;
|
|
int i;
|
|
|
|
if (draw->type != DRAWABLE_WINDOW ||
|
|
!info->allowPageFlip ||
|
|
info->sprites_visible > 0 ||
|
|
info->drmmode.present_flipping ||
|
|
!pScrn->vtSema ||
|
|
!DRI2CanFlip(draw))
|
|
return FALSE;
|
|
|
|
for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) {
|
|
if (drmmode_crtc_can_flip(config->crtc[i]))
|
|
num_crtcs_on++;
|
|
}
|
|
|
|
return num_crtcs_on > 0 && can_exchange(pScrn, draw, front, back);
|
|
}
|
|
|
|
static void
|
|
radeon_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back)
|
|
{
|
|
struct dri2_buffer_priv *front_priv = front->driverPrivate;
|
|
struct dri2_buffer_priv *back_priv = back->driverPrivate;
|
|
#ifdef USE_GLAMOR
|
|
RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(draw->pScreen));
|
|
#endif
|
|
RegionRec region;
|
|
int tmp;
|
|
|
|
region.extents.x1 = region.extents.y1 = 0;
|
|
region.extents.x2 = front_priv->pixmap->drawable.width;
|
|
region.extents.y2 = front_priv->pixmap->drawable.height;
|
|
region.data = NULL;
|
|
DamageRegionAppend(&front_priv->pixmap->drawable, ®ion);
|
|
|
|
/* Swap BO names so DRI works */
|
|
tmp = front->name;
|
|
front->name = back->name;
|
|
back->name = tmp;
|
|
|
|
/* Swap pixmap privates */
|
|
#ifdef USE_GLAMOR
|
|
if (info->use_glamor) {
|
|
struct radeon_pixmap *front_pix, *back_pix;
|
|
|
|
front_pix = radeon_get_pixmap_private(front_priv->pixmap);
|
|
back_pix = radeon_get_pixmap_private(back_priv->pixmap);
|
|
radeon_set_pixmap_private(front_priv->pixmap, back_pix);
|
|
radeon_set_pixmap_private(back_priv->pixmap, front_pix);
|
|
|
|
radeon_glamor_exchange_buffers(front_priv->pixmap, back_priv->pixmap);
|
|
} else
|
|
#endif
|
|
{
|
|
struct radeon_exa_pixmap_priv driver_priv = *(struct radeon_exa_pixmap_priv*)
|
|
exaGetPixmapDriverPrivate(front_priv->pixmap);
|
|
|
|
*(struct radeon_exa_pixmap_priv*)exaGetPixmapDriverPrivate(front_priv->pixmap) =
|
|
*(struct radeon_exa_pixmap_priv*)exaGetPixmapDriverPrivate(back_priv->pixmap);
|
|
*(struct radeon_exa_pixmap_priv*)exaGetPixmapDriverPrivate(back_priv->pixmap) =
|
|
driver_priv;
|
|
}
|
|
|
|
DamageRegionProcessPending(&front_priv->pixmap->drawable);
|
|
}
|
|
|
|
static void radeon_dri2_frame_event_abort(xf86CrtcPtr crtc, void *event_data)
|
|
{
|
|
DRI2FrameEventPtr event = event_data;
|
|
|
|
TimerCancel(event->timer);
|
|
TimerFree(event->timer);
|
|
radeon_dri2_unref_buffer(event->front);
|
|
radeon_dri2_unref_buffer(event->back);
|
|
free(event);
|
|
}
|
|
|
|
static void radeon_dri2_frame_event_handler(xf86CrtcPtr crtc, uint32_t seq,
|
|
uint64_t usec, void *event_data)
|
|
{
|
|
DRI2FrameEventPtr event = event_data;
|
|
ScrnInfoPtr scrn = crtc->scrn;
|
|
DrawablePtr drawable;
|
|
int status;
|
|
int swap_type;
|
|
BoxRec box;
|
|
RegionRec region;
|
|
|
|
status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
|
|
M_ANY, DixWriteAccess);
|
|
if (status != Success)
|
|
goto cleanup;
|
|
|
|
seq += radeon_get_msc_delta(drawable, crtc);
|
|
|
|
switch (event->type) {
|
|
case DRI2_FLIP:
|
|
if (can_flip(crtc, drawable, event->front, event->back) &&
|
|
radeon_dri2_schedule_flip(crtc,
|
|
event->client,
|
|
drawable,
|
|
event->front,
|
|
event->back,
|
|
event->event_complete,
|
|
event->event_data,
|
|
event->frame)) {
|
|
radeon_dri2_exchange_buffers(drawable, event->front, event->back);
|
|
break;
|
|
}
|
|
/* else fall through to exchange/blit */
|
|
case DRI2_SWAP:
|
|
if (DRI2CanExchange(drawable) &&
|
|
can_exchange(scrn, drawable, event->front, event->back)) {
|
|
radeon_dri2_exchange_buffers(drawable, event->front, event->back);
|
|
swap_type = DRI2_EXCHANGE_COMPLETE;
|
|
} else {
|
|
box.x1 = 0;
|
|
box.y1 = 0;
|
|
box.x2 = drawable->width;
|
|
box.y2 = drawable->height;
|
|
REGION_INIT(pScreen, ®ion, &box, 0);
|
|
radeon_dri2_copy_region2(drawable->pScreen, drawable, ®ion,
|
|
event->front, event->back);
|
|
swap_type = DRI2_BLIT_COMPLETE;
|
|
}
|
|
|
|
DRI2SwapComplete(event->client, drawable, seq, usec / 1000000,
|
|
usec % 1000000, swap_type, event->event_complete,
|
|
event->event_data);
|
|
|
|
break;
|
|
case DRI2_WAITMSC:
|
|
DRI2WaitMSCComplete(event->client, drawable, seq, usec / 1000000,
|
|
usec % 1000000);
|
|
break;
|
|
default:
|
|
/* Unknown type */
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"%s: unknown vblank event received\n", __func__);
|
|
break;
|
|
}
|
|
|
|
cleanup:
|
|
radeon_dri2_frame_event_abort(crtc, event_data);
|
|
}
|
|
|
|
/*
|
|
* This function should be called on a disabled CRTC only (i.e., CRTC
|
|
* in DPMS-off state). It will calculate the delay necessary to reach
|
|
* target_msc from present time if the CRTC were running.
|
|
*/
|
|
static
|
|
CARD32 radeon_dri2_extrapolate_msc_delay(xf86CrtcPtr crtc, CARD64 *target_msc,
|
|
CARD64 divisor, CARD64 remainder)
|
|
{
|
|
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
|
|
ScrnInfoPtr pScrn = crtc->scrn;
|
|
RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn);
|
|
int nominal_frame_rate = drmmode_crtc->dpms_last_fps;
|
|
CARD64 last_vblank_ust = drmmode_crtc->dpms_last_ust;
|
|
uint32_t last_vblank_seq = drmmode_crtc->dpms_last_seq;
|
|
CARD64 now, target_time, delta_t;
|
|
int64_t d, delta_seq;
|
|
int ret;
|
|
CARD32 d_ms;
|
|
|
|
if (!last_vblank_ust) {
|
|
*target_msc = 0;
|
|
return FALLBACK_SWAP_DELAY;
|
|
}
|
|
ret = drmmode_get_current_ust(pRADEONEnt->fd, &now);
|
|
if (ret) {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
|
|
"%s cannot get current time\n", __func__);
|
|
*target_msc = 0;
|
|
return FALLBACK_SWAP_DELAY;
|
|
}
|
|
delta_seq = *target_msc - last_vblank_seq;
|
|
delta_seq *= 1000000;
|
|
target_time = last_vblank_ust;
|
|
target_time += delta_seq / nominal_frame_rate;
|
|
d = target_time - now;
|
|
if (d < 0) {
|
|
/* we missed the event, adjust target_msc, do the divisor magic */
|
|
CARD64 current_msc = last_vblank_seq;
|
|
|
|
delta_t = now - last_vblank_ust;
|
|
delta_seq = delta_t * nominal_frame_rate;
|
|
current_msc += delta_seq / 1000000;
|
|
current_msc &= 0xffffffff;
|
|
if (divisor == 0) {
|
|
*target_msc = current_msc;
|
|
d = 0;
|
|
} else {
|
|
*target_msc = current_msc - (current_msc % divisor) + remainder;
|
|
if ((current_msc % divisor) >= remainder)
|
|
*target_msc += divisor;
|
|
*target_msc &= 0xffffffff;
|
|
delta_seq = *target_msc - last_vblank_seq;
|
|
delta_seq *= 1000000;
|
|
target_time = last_vblank_ust;
|
|
target_time += delta_seq / nominal_frame_rate;
|
|
d = target_time - now;
|
|
}
|
|
}
|
|
/*
|
|
* convert delay to milliseconds and add margin to prevent the client
|
|
* from coming back early (due to timer granularity and rounding
|
|
* errors) and getting the same MSC it just got
|
|
*/
|
|
d_ms = (CARD32)d / 1000;
|
|
if ((CARD32)d - d_ms * 1000 > 0)
|
|
d_ms += 2;
|
|
else
|
|
d_ms++;
|
|
return d_ms;
|
|
}
|
|
|
|
/*
|
|
* Get current interpolated frame count and frame count timestamp, based on
|
|
* drawable's crtc.
|
|
*/
|
|
static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
|
|
{
|
|
xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw);
|
|
|
|
/* Drawable not displayed, make up a value */
|
|
if (!crtc) {
|
|
*ust = 0;
|
|
*msc = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!radeon_dri2_get_crtc_msc(crtc, ust, msc))
|
|
return FALSE;
|
|
|
|
if (draw && draw->type == DRAWABLE_WINDOW)
|
|
*msc += get_dri2_window_priv((WindowPtr)draw)->vblank_delta;
|
|
*msc &= 0xffffffff;
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
CARD32 radeon_dri2_deferred_event(OsTimerPtr timer, CARD32 now, pointer data)
|
|
{
|
|
DRI2FrameEventPtr event_info = (DRI2FrameEventPtr)data;
|
|
xf86CrtcPtr crtc = event_info->crtc;
|
|
ScrnInfoPtr scrn;
|
|
RADEONEntPtr pRADEONEnt;
|
|
CARD64 drm_now;
|
|
int ret;
|
|
CARD64 delta_t, delta_seq, frame;
|
|
drmmode_crtc_private_ptr drmmode_crtc;
|
|
|
|
/*
|
|
* This is emulated event, so its time is current time, which we
|
|
* have to get in DRM-compatible form (which is a bit messy given
|
|
* the information that we have at this point). Can't use now argument
|
|
* because DRM event time may come from monotonic clock, while
|
|
* DIX timer facility uses real-time clock.
|
|
*/
|
|
if (!event_info->crtc) {
|
|
ErrorF("%s no crtc\n", __func__);
|
|
if (event_info->drm_queue_seq)
|
|
radeon_drm_abort_entry(event_info->drm_queue_seq);
|
|
else
|
|
radeon_dri2_frame_event_abort(NULL, data);
|
|
return 0;
|
|
}
|
|
|
|
scrn = crtc->scrn;
|
|
pRADEONEnt = RADEONEntPriv(scrn);
|
|
drmmode_crtc = event_info->crtc->driver_private;
|
|
ret = drmmode_get_current_ust(pRADEONEnt->fd, &drm_now);
|
|
if (ret) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
|
|
"%s cannot get current time\n", __func__);
|
|
|
|
if (event_info->drm_queue_seq) {
|
|
drmmode_crtc->drmmode->event_context.
|
|
vblank_handler(pRADEONEnt->fd, 0, 0, 0,
|
|
(void*)event_info->drm_queue_seq);
|
|
drmmode_crtc->wait_flip_nesting_level++;
|
|
radeon_drm_queue_handle_deferred(crtc);
|
|
|
|
} else {
|
|
radeon_dri2_frame_event_handler(crtc, 0, 0, data);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
/*
|
|
* calculate the frame number from current time
|
|
* that would come from CRTC if it were running
|
|
*/
|
|
delta_t = drm_now - (CARD64)drmmode_crtc->dpms_last_ust;
|
|
delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
|
|
delta_seq /= 1000000;
|
|
frame = (CARD64)drmmode_crtc->dpms_last_seq + delta_seq;
|
|
|
|
if (event_info->drm_queue_seq) {
|
|
drmmode_crtc->drmmode->event_context.
|
|
vblank_handler(pRADEONEnt->fd, frame, drm_now / 1000000,
|
|
drm_now % 1000000,
|
|
(void*)event_info->drm_queue_seq);
|
|
drmmode_crtc->wait_flip_nesting_level++;
|
|
radeon_drm_queue_handle_deferred(crtc);
|
|
} else {
|
|
radeon_dri2_frame_event_handler(crtc, frame, drm_now, data);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void radeon_dri2_schedule_event(CARD32 delay, DRI2FrameEventPtr event_info)
|
|
{
|
|
event_info->timer = TimerSet(NULL, 0, delay, radeon_dri2_deferred_event,
|
|
event_info);
|
|
if (delay == 0) {
|
|
CARD32 now = GetTimeInMillis();
|
|
radeon_dri2_deferred_event(event_info->timer, now, event_info);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Request a DRM event when the requested conditions will be satisfied.
|
|
*
|
|
* We need to handle the event and ask the server to wake up the client when
|
|
* we receive it.
|
|
*/
|
|
static int radeon_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw,
|
|
CARD64 target_msc, CARD64 divisor,
|
|
CARD64 remainder)
|
|
{
|
|
ScreenPtr screen = draw->pScreen;
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
|
|
DRI2FrameEventPtr wait_info = NULL;
|
|
uintptr_t drm_queue_seq = 0;
|
|
xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw);
|
|
uint32_t msc_delta;
|
|
uint32_t seq;
|
|
CARD64 current_msc;
|
|
|
|
/* Truncate to match kernel interfaces; means occasional overflow
|
|
* misses, but that's generally not a big deal */
|
|
target_msc &= 0xffffffff;
|
|
divisor &= 0xffffffff;
|
|
remainder &= 0xffffffff;
|
|
|
|
/* Drawable not visible, return immediately */
|
|
if (!crtc)
|
|
goto out_complete;
|
|
|
|
msc_delta = radeon_get_msc_delta(draw, crtc);
|
|
|
|
wait_info = calloc(1, sizeof(DRI2FrameEventRec));
|
|
if (!wait_info)
|
|
goto out_complete;
|
|
|
|
wait_info->drawable_id = draw->id;
|
|
wait_info->client = client;
|
|
wait_info->type = DRI2_WAITMSC;
|
|
wait_info->crtc = crtc;
|
|
|
|
/*
|
|
* CRTC is in DPMS off state, calculate wait time from current time,
|
|
* target_msc and last vblank time/sequence when CRTC was turned off
|
|
*/
|
|
if (!radeon_crtc_is_enabled(crtc)) {
|
|
CARD32 delay;
|
|
target_msc -= msc_delta;
|
|
delay = radeon_dri2_extrapolate_msc_delay(crtc, &target_msc,
|
|
divisor, remainder);
|
|
radeon_dri2_schedule_event(delay, wait_info);
|
|
DRI2BlockClient(client, draw);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Get current count */
|
|
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"get vblank counter failed: %s\n", strerror(errno));
|
|
goto out_complete;
|
|
}
|
|
|
|
current_msc = seq + msc_delta;
|
|
current_msc &= 0xffffffff;
|
|
|
|
drm_queue_seq = radeon_drm_queue_alloc(crtc, client, RADEON_DRM_QUEUE_ID_DEFAULT,
|
|
wait_info, radeon_dri2_frame_event_handler,
|
|
radeon_dri2_frame_event_abort, FALSE);
|
|
if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"Allocating DRM queue event entry failed.\n");
|
|
goto out_complete;
|
|
}
|
|
wait_info->drm_queue_seq = drm_queue_seq;
|
|
|
|
/*
|
|
* If divisor is zero, or current_msc is smaller than target_msc,
|
|
* we just need to make sure target_msc passes before waking up the
|
|
* client.
|
|
*/
|
|
if (divisor == 0 || current_msc < target_msc) {
|
|
/* If target_msc already reached or passed, set it to
|
|
* current_msc to ensure we return a reasonable value back
|
|
* to the caller. This keeps the client from continually
|
|
* sending us MSC targets from the past by forcibly updating
|
|
* their count on this call.
|
|
*/
|
|
if (current_msc >= target_msc)
|
|
target_msc = current_msc;
|
|
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT,
|
|
target_msc - msc_delta, drm_queue_seq, NULL,
|
|
NULL)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"get vblank counter failed: %s\n", strerror(errno));
|
|
goto out_complete;
|
|
}
|
|
|
|
DRI2BlockClient(client, draw);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* If we get here, target_msc has already passed or we don't have one,
|
|
* so we queue an event that will satisfy the divisor/remainder equation.
|
|
*/
|
|
target_msc = current_msc - (current_msc % divisor) + remainder - msc_delta;
|
|
|
|
/*
|
|
* If calculated remainder is larger than requested remainder,
|
|
* it means we've passed the last point where
|
|
* seq % divisor == remainder, so we need to wait for the next time
|
|
* that will happen.
|
|
*/
|
|
if ((current_msc % divisor) >= remainder)
|
|
target_msc += divisor;
|
|
|
|
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT,
|
|
target_msc, drm_queue_seq, NULL, NULL)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"get vblank counter failed: %s\n", strerror(errno));
|
|
goto out_complete;
|
|
}
|
|
|
|
DRI2BlockClient(client, draw);
|
|
|
|
return TRUE;
|
|
|
|
out_complete:
|
|
if (wait_info)
|
|
radeon_dri2_deferred_event(NULL, 0, wait_info);
|
|
else
|
|
DRI2WaitMSCComplete(client, draw, 0, 0, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ScheduleSwap is responsible for requesting a DRM vblank event for the
|
|
* appropriate frame.
|
|
*
|
|
* In the case of a blit (e.g. for a windowed swap) or buffer exchange,
|
|
* the vblank requested can simply be the last queued swap frame + the swap
|
|
* interval for the drawable.
|
|
*
|
|
* In the case of a page flip, we request an event for the last queued swap
|
|
* frame + swap interval - 1, since we'll need to queue the flip for the frame
|
|
* immediately following the received event.
|
|
*
|
|
* The client will be blocked if it tries to perform further GL commands
|
|
* after queueing a swap, though in the Intel case after queueing a flip, the
|
|
* client is free to queue more commands; they'll block in the kernel if
|
|
* they access buffers busy with the flip.
|
|
*
|
|
* When the swap is complete, the driver should call into the server so it
|
|
* can send any swap complete events that have been requested.
|
|
*/
|
|
static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
|
|
DRI2BufferPtr front, DRI2BufferPtr back,
|
|
CARD64 *target_msc, CARD64 divisor,
|
|
CARD64 remainder, DRI2SwapEventPtr func,
|
|
void *data)
|
|
{
|
|
ScreenPtr screen = draw->pScreen;
|
|
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
|
|
xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw);
|
|
uint32_t msc_delta;
|
|
drmVBlankSeqType type;
|
|
uint32_t seq;
|
|
int flip = 0;
|
|
DRI2FrameEventPtr swap_info = NULL;
|
|
uintptr_t drm_queue_seq;
|
|
CARD64 current_msc, event_msc;
|
|
BoxRec box;
|
|
RegionRec region;
|
|
|
|
/* Truncate to match kernel interfaces; means occasional overflow
|
|
* misses, but that's generally not a big deal */
|
|
*target_msc &= 0xffffffff;
|
|
divisor &= 0xffffffff;
|
|
remainder &= 0xffffffff;
|
|
|
|
/* radeon_dri2_frame_event_handler will get called some unknown time in the
|
|
* future with these buffers. Take a reference to ensure that they won't
|
|
* get destroyed before then.
|
|
*/
|
|
radeon_dri2_ref_buffer(front);
|
|
radeon_dri2_ref_buffer(back);
|
|
|
|
/* either off-screen or CRTC not usable... just complete the swap */
|
|
if (!crtc)
|
|
goto blit_fallback;
|
|
|
|
msc_delta = radeon_get_msc_delta(draw, crtc);
|
|
|
|
swap_info = calloc(1, sizeof(DRI2FrameEventRec));
|
|
if (!swap_info)
|
|
goto blit_fallback;
|
|
|
|
swap_info->type = DRI2_SWAP;
|
|
swap_info->drawable_id = draw->id;
|
|
swap_info->client = client;
|
|
swap_info->event_complete = func;
|
|
swap_info->event_data = data;
|
|
swap_info->front = front;
|
|
swap_info->back = back;
|
|
swap_info->crtc = crtc;
|
|
|
|
drm_queue_seq = radeon_drm_queue_alloc(crtc, client, RADEON_DRM_QUEUE_ID_DEFAULT,
|
|
swap_info, radeon_dri2_frame_event_handler,
|
|
radeon_dri2_frame_event_abort, FALSE);
|
|
if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"Allocating DRM queue entry failed.\n");
|
|
goto blit_fallback;
|
|
}
|
|
swap_info->drm_queue_seq = drm_queue_seq;
|
|
|
|
/*
|
|
* CRTC is in DPMS off state, fallback to blit, but calculate
|
|
* wait time from current time, target_msc and last vblank
|
|
* time/sequence when CRTC was turned off
|
|
*/
|
|
if (!radeon_crtc_is_enabled(crtc)) {
|
|
CARD32 delay;
|
|
*target_msc -= msc_delta;
|
|
delay = radeon_dri2_extrapolate_msc_delay(crtc, target_msc,
|
|
divisor, remainder);
|
|
*target_msc += msc_delta;
|
|
*target_msc &= 0xffffffff;
|
|
radeon_dri2_schedule_event(delay, swap_info);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Get current count */
|
|
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"first get vblank counter failed: %s\n",
|
|
strerror(errno));
|
|
goto blit_fallback;
|
|
}
|
|
|
|
current_msc = seq + msc_delta;
|
|
current_msc &= 0xffffffff;
|
|
|
|
/* Flips need to be submitted one frame before */
|
|
if (can_flip(crtc, draw, front, back)) {
|
|
swap_info->type = DRI2_FLIP;
|
|
flip = 1;
|
|
}
|
|
|
|
/* Correct target_msc by 'flip' if swap_info->type == DRI2_FLIP.
|
|
* Do it early, so handling of different timing constraints
|
|
* for divisor, remainder and msc vs. target_msc works.
|
|
*/
|
|
if (*target_msc > 0)
|
|
*target_msc -= flip;
|
|
|
|
/*
|
|
* If divisor is zero, or current_msc is smaller than target_msc
|
|
* we just need to make sure target_msc passes before initiating
|
|
* the swap.
|
|
*/
|
|
if (divisor == 0 || current_msc < *target_msc) {
|
|
type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
|
|
/* If non-pageflipping, but blitting/exchanging, we need to use
|
|
* DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
|
|
* on.
|
|
*/
|
|
if (flip == 0)
|
|
type |= DRM_VBLANK_NEXTONMISS;
|
|
|
|
/* If target_msc already reached or passed, set it to
|
|
* current_msc to ensure we return a reasonable value back
|
|
* to the caller. This makes swap_interval logic more robust.
|
|
*/
|
|
if (current_msc >= *target_msc)
|
|
*target_msc = current_msc;
|
|
|
|
if (!drmmode_wait_vblank(crtc, type, *target_msc - msc_delta,
|
|
drm_queue_seq, NULL, &seq)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"divisor 0 get vblank counter failed: %s\n",
|
|
strerror(errno));
|
|
goto blit_fallback;
|
|
}
|
|
|
|
*target_msc = seq + flip + msc_delta;
|
|
swap_info->frame = *target_msc;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* If we get here, target_msc has already passed or we don't have one,
|
|
* and we need to queue an event that will satisfy the divisor/remainder
|
|
* equation.
|
|
*/
|
|
type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
|
|
if (flip == 0)
|
|
type |= DRM_VBLANK_NEXTONMISS;
|
|
|
|
event_msc = current_msc - (current_msc % divisor) + remainder - msc_delta;
|
|
|
|
/*
|
|
* If the calculated deadline vbl.request.sequence is smaller than
|
|
* or equal to current_msc, it means we've passed the last point
|
|
* when effective onset frame seq could satisfy
|
|
* seq % divisor == remainder, so we need to wait for the next time
|
|
* this will happen.
|
|
|
|
* This comparison takes the 1 frame swap delay in pageflipping mode
|
|
* into account, as well as a potential DRM_VBLANK_NEXTONMISS delay
|
|
* if we are blitting/exchanging instead of flipping.
|
|
*/
|
|
if (event_msc <= current_msc)
|
|
event_msc += divisor;
|
|
|
|
/* Account for 1 frame extra pageflip delay if flip > 0 */
|
|
event_msc -= flip;
|
|
|
|
if (!drmmode_wait_vblank(crtc, type, event_msc, drm_queue_seq, NULL, &seq)) {
|
|
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
|
|
"final get vblank counter failed: %s\n",
|
|
strerror(errno));
|
|
goto blit_fallback;
|
|
}
|
|
|
|
/* Adjust returned value for 1 fame pageflip offset of flip > 0 */
|
|
*target_msc = seq + flip + msc_delta;
|
|
*target_msc &= 0xffffffff;
|
|
swap_info->frame = *target_msc;
|
|
|
|
return TRUE;
|
|
|
|
blit_fallback:
|
|
if (swap_info) {
|
|
swap_info->type = DRI2_SWAP;
|
|
radeon_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info);
|
|
} else {
|
|
box.x1 = 0;
|
|
box.y1 = 0;
|
|
box.x2 = draw->width;
|
|
box.y2 = draw->height;
|
|
REGION_INIT(pScreen, ®ion, &box, 0);
|
|
|
|
radeon_dri2_copy_region2(draw->pScreen, draw, ®ion, front, back);
|
|
|
|
DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
|
|
|
|
radeon_dri2_unref_buffer(front);
|
|
radeon_dri2_unref_buffer(back);
|
|
}
|
|
|
|
*target_msc = 0; /* offscreen, so zero out target vblank count */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
Bool
|
|
radeon_dri2_screen_init(ScreenPtr pScreen)
|
|
{
|
|
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
|
|
RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn);
|
|
RADEONInfoPtr info = RADEONPTR(pScrn);
|
|
DRI2InfoRec dri2_info = { 0 };
|
|
const char *driverNames[2];
|
|
Bool scheduling_works = TRUE;
|
|
|
|
if (!info->dri2.available)
|
|
return FALSE;
|
|
|
|
info->dri2.device_name = drmGetDeviceNameFromFd(pRADEONEnt->fd);
|
|
|
|
if ( (info->ChipFamily >= CHIP_FAMILY_TAHITI) ) {
|
|
dri2_info.driverName = SI_DRIVER_NAME;
|
|
} else if ( (info->ChipFamily >= CHIP_FAMILY_R600) ) {
|
|
dri2_info.driverName = R600_DRIVER_NAME;
|
|
} else if ( (info->ChipFamily >= CHIP_FAMILY_R300) ) {
|
|
dri2_info.driverName = R300_DRIVER_NAME;
|
|
} else if ( info->ChipFamily >= CHIP_FAMILY_R200 ) {
|
|
dri2_info.driverName = R200_DRIVER_NAME;
|
|
} else {
|
|
dri2_info.driverName = RADEON_DRIVER_NAME;
|
|
}
|
|
dri2_info.fd = pRADEONEnt->fd;
|
|
dri2_info.deviceName = info->dri2.device_name;
|
|
|
|
if (info->dri2.pKernelDRMVersion->version_minor < 4) {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel for "
|
|
"sync extension\n");
|
|
scheduling_works = FALSE;
|
|
}
|
|
|
|
if (scheduling_works && info->drmmode.count_crtcs > 2) {
|
|
#ifdef DRM_CAP_VBLANK_HIGH_CRTC
|
|
uint64_t cap_value;
|
|
|
|
if (drmGetCap(pRADEONEnt->fd, DRM_CAP_VBLANK_HIGH_CRTC, &cap_value)) {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel "
|
|
"for VBLANKs on CRTC > 1\n");
|
|
scheduling_works = FALSE;
|
|
} else if (!cap_value) {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Your kernel does not "
|
|
"handle VBLANKs on CRTC > 1\n");
|
|
scheduling_works = FALSE;
|
|
}
|
|
#else
|
|
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need to rebuild against a "
|
|
"newer libdrm to handle VBLANKs on CRTC > 1\n");
|
|
scheduling_works = FALSE;
|
|
#endif
|
|
}
|
|
|
|
if (scheduling_works) {
|
|
dri2_info.ScheduleSwap = radeon_dri2_schedule_swap;
|
|
dri2_info.GetMSC = radeon_dri2_get_msc;
|
|
dri2_info.ScheduleWaitMSC = radeon_dri2_schedule_wait_msc;
|
|
dri2_info.numDrivers = ARRAY_SIZE(driverNames);
|
|
dri2_info.driverNames = driverNames;
|
|
driverNames[0] = dri2_info.driverName;
|
|
|
|
if (info->ChipFamily >= CHIP_FAMILY_R300)
|
|
driverNames[1] = driverNames[0];
|
|
else
|
|
driverNames[1] = NULL; /* no VDPAU support */
|
|
|
|
if (DRI2InfoCnt == 0) {
|
|
if (!dixRegisterPrivateKey(dri2_window_private_key,
|
|
PRIVATE_WINDOW,
|
|
sizeof(struct dri2_window_priv))) {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
|
|
"Failed to get DRI2 window private\n");
|
|
return FALSE;
|
|
}
|
|
|
|
AddCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
|
|
}
|
|
|
|
DRI2InfoCnt++;
|
|
}
|
|
|
|
dri2_info.version = 9;
|
|
dri2_info.CreateBuffer2 = radeon_dri2_create_buffer2;
|
|
dri2_info.DestroyBuffer2 = radeon_dri2_destroy_buffer2;
|
|
dri2_info.CopyRegion2 = radeon_dri2_copy_region2;
|
|
|
|
info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info);
|
|
return info->dri2.enabled;
|
|
}
|
|
|
|
void radeon_dri2_close_screen(ScreenPtr pScreen)
|
|
{
|
|
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
|
|
RADEONInfoPtr info = RADEONPTR(pScrn);
|
|
|
|
if (--DRI2InfoCnt == 0)
|
|
DeleteCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
|
|
|
|
DRI2CloseScreen(pScreen);
|
|
drmFree(info->dri2.device_name);
|
|
}
|
|
|
|
#endif /* DRI2 */
|
|
|