sna/dri2: Allow TearFree flipping to individual CRTC

Baby step. We first take advantage of TearFree to allow us to redirect a
single CRTC to the DRI2 frontbuffer and so allow a fullscreen game
covering a single monitor to avoid expensive blits when running in a
multi-monitor setup.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2014-06-09 08:58:15 +01:00
parent af3130cbba
commit 3932e97057
5 changed files with 575 additions and 97 deletions

View File

@ -4400,7 +4400,7 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem,
bucket = cache_bucket(size);
if (flags & CREATE_SCANOUT) {
struct kgem_bo *last = NULL, *first = NULL;
struct kgem_bo *last = NULL;
list_for_each_entry_reverse(bo, &kgem->scanout, list) {
assert(bo->scanout);
@ -4414,11 +4414,8 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem,
/* No tiling/pitch without recreating fb */
continue;
if (bo->delta && !check_scanout_size(kgem, bo, width, height)) {
if (first == NULL)
first = bo;
if (bo->delta && !check_scanout_size(kgem, bo, width, height))
continue;
}
if (flags & CREATE_INACTIVE && bo->rq) {
last = bo;
@ -4448,44 +4445,60 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem,
return last;
}
if (first) {
ScrnInfoPtr scrn =
container_of(kgem, struct sna, kgem)->scrn;
if (container_of(kgem, struct sna, kgem)->scrn->vtSema) {
ScrnInfoPtr scrn = container_of(kgem, struct sna, kgem)->scrn;
if (scrn->vtSema) {
DBG(("%s: recreate fb %dx%d@%d/%d\n",
__FUNCTION__, width, height, scrn->depth, scrn->bitsPerPixel));
list_for_each_entry_reverse(bo, &kgem->scanout, list) {
struct drm_mode_fb_cmd arg;
if (first->tiling != tiling ||
(tiling != I915_TILING_NONE && first->pitch != pitch)) {
if (gem_set_tiling(kgem->fd, first->handle,
assert(bo->scanout);
if (size > num_pages(bo) || num_pages(bo) > 2*size)
continue;
if (flags & CREATE_INACTIVE && bo->rq)
continue;
list_del(&bo->list);
if (bo->tiling != tiling || bo->pitch != pitch) {
if (bo->delta) {
kgem_bo_rmfb(kgem, bo);
bo->delta = 0;
}
if (gem_set_tiling(kgem->fd, bo->handle,
tiling, pitch)) {
first->tiling = tiling;
first->pitch = pitch;
}
}
if (first->tiling == tiling && first->pitch == pitch) {
struct drm_mode_fb_cmd arg;
VG_CLEAR(arg);
arg.width = width;
arg.height = height;
arg.pitch = first->pitch;
arg.bpp = scrn->bitsPerPixel;
arg.depth = scrn->depth;
arg.handle = first->handle;
kgem_bo_rmfb(kgem, first);
if (do_ioctl(kgem->fd, DRM_IOCTL_MODE_ADDFB, &arg)) {
kgem_bo_free(kgem, first);
bo->tiling = tiling;
bo->pitch = pitch;
} else {
DBG(("%s: attached fb=%d to handle=%d\n",
__FUNCTION__, arg.fb_id, arg.handle));
first->delta = arg.fb_id;
return first;
kgem_bo_free(kgem, bo);
break;
}
}
VG_CLEAR(arg);
arg.width = width;
arg.height = height;
arg.pitch = bo->pitch;
arg.bpp = scrn->bitsPerPixel;
arg.depth = scrn->depth;
arg.handle = bo->handle;
if (do_ioctl(kgem->fd, DRM_IOCTL_MODE_ADDFB, &arg)) {
kgem_bo_free(kgem, bo);
break;
}
bo->delta = arg.fb_id;
bo->unique_id = kgem_get_unique_id(kgem);
DBG((" 2:from scanout: pitch=%d, tiling=%d, handle=%d, id=%d\n",
bo->pitch, bo->tiling, bo->handle, bo->unique_id));
assert(bo->pitch*kgem_aligned_height(kgem, height, bo->tiling) <= kgem_bo_size(bo));
assert_tiling(kgem, bo);
bo->refcnt = 1;
return bo;
}
}

View File

@ -291,6 +291,8 @@ struct sna {
int max_crtc_width, max_crtc_height;
RegionRec shadow_region;
struct list shadow_crtc;
bool shadow_dirty;
unsigned num_real_crtc;
unsigned num_real_output;
@ -424,6 +426,8 @@ extern void sna_mode_check(struct sna *sna);
extern void sna_mode_reset(struct sna *sna);
extern void sna_mode_wakeup(struct sna *sna);
extern void sna_mode_redisplay(struct sna *sna);
extern void sna_shadow_set_crtc(struct sna *sna, xf86CrtcPtr crtc, struct kgem_bo *bo);
extern void sna_shadow_unset_crtc(struct sna *sna, xf86CrtcPtr crtc);
extern void sna_pixmap_discard_shadow_damage(struct sna_pixmap *priv,
RegionPtr region);
extern void sna_mode_close(struct sna *sna);
@ -570,7 +574,8 @@ extern bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation);
extern int sna_crtc_to_pipe(xf86CrtcPtr crtc);
extern uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc);
extern uint32_t sna_crtc_id(xf86CrtcPtr crtc);
extern int sna_crtc_is_on(xf86CrtcPtr crtc);
extern bool sna_crtc_is_on(xf86CrtcPtr crtc);
extern bool sna_crtc_is_transformed(xf86CrtcPtr crtc);
CARD32 sna_format_for_depth(int depth);
CARD32 sna_render_format_for_depth(int depth);
@ -688,6 +693,7 @@ sna_pixmap_undo_cow(struct sna *sna, struct sna_pixmap *priv, unsigned flags);
#define MOVE_WHOLE_HINT 0x20
#define __MOVE_FORCE 0x40
#define __MOVE_DRI 0x80
#define __MOVE_SCANOUT 0x100
struct sna_pixmap *
sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int flags);

View File

@ -16868,7 +16868,7 @@ static void sna_scanout_flush(struct sna *sna)
if (priv &&
sna_pixmap_force_to_gpu(priv->pixmap,
MOVE_READ | MOVE_ASYNC_HINT))
MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT))
kgem_scanout_flush(&sna->kgem, priv->gpu_bo);
sna_mode_redisplay(sna);

View File

@ -99,12 +99,14 @@ union compat_mode_get_connector{
extern XF86ConfigPtr xf86configptr;
struct sna_crtc {
xf86CrtcPtr base;
struct drm_mode_modeinfo kmode;
int dpms_mode;
PixmapPtr scanout_pixmap;
struct kgem_bo *bo, *shadow_bo;
struct sna_cursor *cursor;
uint32_t sprite;
uint32_t offset;
bool shadow;
bool fallback_shadow;
bool transform;
@ -125,6 +127,8 @@ struct sna_crtc {
sna_flip_handler_t flip_handler;
struct kgem_bo *flip_bo;
void *flip_data;
struct list shadow_link;
};
struct sna_property {
@ -234,12 +238,18 @@ uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc)
return to_sna_crtc(crtc)->sprite;
}
int sna_crtc_is_on(xf86CrtcPtr crtc)
bool sna_crtc_is_on(xf86CrtcPtr crtc)
{
assert(to_sna_crtc(crtc));
return to_sna_crtc(crtc)->bo != NULL;
}
bool sna_crtc_is_transformed(xf86CrtcPtr crtc)
{
assert(to_sna_crtc(crtc));
return to_sna_crtc(crtc)->transform;
}
static inline uint64_t msc64(struct sna_crtc *sna_crtc, uint32_t seq)
{
if (seq < sna_crtc->last_seq) {
@ -988,9 +998,11 @@ sna_crtc_apply(xf86CrtcPtr crtc)
if (sna_crtc->transform) {
arg.x = 0;
arg.y = 0;
sna_crtc->offset = 0;
} else {
arg.x = crtc->x;
arg.y = crtc->y;
sna_crtc->offset = arg.y << 16 | arg.x;
}
arg.set_connectors_ptr = (uintptr_t)output_ids;
arg.count_connectors = output_count;
@ -1032,6 +1044,34 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f
if (flags == 0 || pixmap != sna->front || !sna->mode.shadow_damage)
goto done;
if ((flags & __MOVE_SCANOUT) == 0) {
while (!list_is_empty(&sna->mode.shadow_crtc)) {
struct sna_crtc *crtc =
list_first_entry(&sna->mode.shadow_crtc, struct sna_crtc, shadow_link);
RegionRec region;
DBG(("%s: copying replaced CRTC: (%d, %d), (%d, %d)\n",
__FUNCTION__,
crtc->base->bounds.x1,
crtc->base->bounds.y1,
crtc->base->bounds.x2,
crtc->base->bounds.y2));
ret = sna->render.copy_boxes(sna, GXcopy,
pixmap, crtc->shadow_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
pixmap, priv->gpu_bo, 0, 0,
&crtc->base->bounds, 1,
0);
kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
crtc->shadow_bo = NULL;
list_del(&crtc->shadow_link);
region.extents = crtc->base->bounds;
region.data = NULL;
RegionSubtract(&sna->mode.shadow_region, &sna->mode.shadow_region, &region);
}
}
if ((flags & MOVE_WRITE) == 0)
return true;
@ -1123,7 +1163,8 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f
sna_dri2_pixmap_update_bo(sna, pixmap, bo);
done:
RegionUninit(&sna->mode.shadow_region);
RegionEmpty(&sna->mode.shadow_region);
sna->mode.shadow_dirty = false;
priv->move_to_gpu_data = NULL;
priv->move_to_gpu = NULL;
@ -1199,6 +1240,7 @@ static void sna_mode_disable_shadow(struct sna *sna)
}
assert(sna->mode.shadow_active == 0);
sna->mode.shadow_dirty = false;
}
static bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc)
@ -1235,6 +1277,13 @@ static void sna_crtc_disable_shadow(struct sna *sna, struct sna_crtc *crtc)
sna_mode_disable_shadow(sna);
if (crtc->shadow_bo) {
if (!crtc->transform) {
sna->render.copy_boxes(sna, GXcopy,
sna->front, crtc->shadow_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
sna->front, __sna_pixmap_get_bo(sna->front), 0, 0,
&crtc->base->bounds, 1, 0);
list_del(&crtc->shadow_link);
}
kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
crtc->shadow_bo = NULL;
}
@ -1556,9 +1605,6 @@ static void set_shadow(struct sna *sna, RegionPtr region)
assert((priv->pinned & PIN_PRIME) == 0);
assert(sna->mode.shadow != priv->gpu_bo);
assert(priv->move_to_gpu == NULL);
RegionNull(&sna->mode.shadow_region);
RegionCopy(&sna->mode.shadow_region, region);
priv->move_to_gpu = wait_for_shadow;
@ -1635,7 +1681,7 @@ static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
return NULL;
if (sna->flags & SNA_TEAR_FREE) {
DBG(("%s: creating TearFree shadow\n", __FUNCTION__));
DBG(("%s: enabling TearFree shadow\n", __FUNCTION__));
if (!sna_crtc_enable_shadow(sna, sna_crtc))
return NULL;
@ -1643,6 +1689,8 @@ static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
RegionRec region;
struct kgem_bo *shadow;
DBG(("%s: creating TearFree shadow bo\n", __FUNCTION__));
region.extents.x1 = 0;
region.extents.y1 = 0;
region.extents.x2 = sna->scrn->virtualX;
@ -1778,6 +1826,12 @@ sna_crtc_damage(xf86CrtcPtr crtc)
assert(sna->mode.shadow_damage && sna->mode.shadow_active);
damage = DamageRegion(sna->mode.shadow_damage);
RegionUnion(damage, damage, &region);
DBG(("%s: damage now %ldx[(%d, %d), (%d, %d)]\n",
__FUNCTION__,
(long)RegionNumRects(damage),
damage->extents.x1, damage->extents.y1,
damage->extents.x2, damage->extents.y2));
}
static char *outputs_for_crtc(xf86CrtcPtr crtc, char *outputs, int max)
@ -1831,6 +1885,7 @@ sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct kgem_bo *saved_bo, *bo;
struct drm_mode_modeinfo saved_kmode;
uint32_t saved_offset;
bool saved_transform;
char outputs[256];
@ -1859,6 +1914,7 @@ sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
saved_kmode = sna_crtc->kmode;
saved_bo = sna_crtc->bo;
saved_transform = sna_crtc->transform;
saved_offset = sna_crtc->offset;
sna_crtc->fallback_shadow = false;
retry: /* Attach per-crtc pixmap or direct */
@ -1881,6 +1937,7 @@ retry: /* Attach per-crtc pixmap or direct */
xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
"failed to set mode: %s\n", strerror(errno));
sna_crtc->offset = saved_offset;
sna_crtc->transform = saved_transform;
sna_crtc->bo = saved_bo;
sna_crtc->kmode = saved_kmode;
@ -2112,6 +2169,8 @@ sna_crtc_add(ScrnInfoPtr scrn, int id)
sna_crtc->pipe = get_pipe.pipe;
sna_crtc->sprite = sna_crtc_find_sprite(sna, sna_crtc->pipe);
list_init(&sna_crtc->shadow_link);
if (xf86IsEntityShared(scrn->entityList[0]) &&
scrn->confScreen->device->screen != sna_crtc->pipe) {
free(sna_crtc);
@ -2128,6 +2187,7 @@ sna_crtc_add(ScrnInfoPtr scrn, int id)
sna_crtc_init__cursor(sna, sna_crtc);
crtc->driver_private = sna_crtc;
sna_crtc->base = crtc;
DBG(("%s: attached crtc[%d] pipe=%d\n",
__FUNCTION__, id, sna_crtc->pipe));
@ -4873,6 +4933,9 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
sna->mode.max_crtc_width = res->max_width;
sna->mode.max_crtc_height = res->max_height;
RegionEmpty(&sna->mode.shadow_region);
list_init(&sna->mode.shadow_crtc);
drmModeFreeResources(res);
sna_cursor_pre_init(sna);
@ -5689,6 +5752,121 @@ sna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region)
#define shadow_flip_handler (sna_flip_handler_t)sna_mode_redisplay
void sna_shadow_set_crtc(struct sna *sna,
xf86CrtcPtr crtc,
struct kgem_bo *bo)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
struct sna_pixmap *priv;
DBG(("%s: setting shadow override for CRTC:%d to handle=%d\n",
__FUNCTION__, sna_crtc->id, bo->handle));
assert(sna->flags & SNA_TEAR_FREE);
assert(sna_crtc);
assert(!sna_crtc->transform);
if (sna_crtc->shadow_bo != bo) {
if (sna_crtc->shadow_bo)
kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo);
sna_crtc->shadow_bo = kgem_bo_reference(bo);
sna_crtc_damage(crtc);
}
list_move(&sna_crtc->shadow_link, &sna->mode.shadow_crtc);
sna->mode.shadow_dirty = true;
priv = sna_pixmap(sna->front);
assert(priv->gpu_bo);
priv->move_to_gpu = wait_for_shadow;
priv->move_to_gpu_data = sna;
}
void sna_shadow_unset_crtc(struct sna *sna,
xf86CrtcPtr crtc)
{
struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
DBG(("%s: clearin shadow override for CRTC:%d\n",
__FUNCTION__, sna_crtc->id));
if (sna_crtc->shadow_bo == NULL)
return;
kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo);
sna_crtc->shadow_bo = NULL;
list_del(&sna_crtc->shadow_link);
sna->mode.shadow_dirty = true;
sna_crtc_damage(crtc);
}
static bool
sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
struct kgem_bo *bo = crtc->shadow_bo ? crtc->shadow_bo : crtc->bo;
struct drm_mode_crtc arg;
uint32_t output_ids[32];
int output_count = 0;
int i;
DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, crtc->id, crtc->pipe, bo->handle));
assert(config->num_output < ARRAY_SIZE(output_ids));
for (i = 0; i < config->num_output; i++) {
xf86OutputPtr output = config->output[i];
if (output->crtc != crtc->base)
continue;
DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n",
__FUNCTION__, output->name, i, to_connector_id(output),
crtc->id, crtc->pipe,
(uint32_t)output->possible_crtcs,
(uint32_t)output->possible_clones));
assert(output->possible_crtcs & (1 << crtc->pipe) ||
xf86IsEntityShared(sna->scrn->entityList[0]));
output_ids[output_count] = to_connector_id(output);
if (++output_count == ARRAY_SIZE(output_ids))
return false;
}
VG_CLEAR(arg);
arg.crtc_id = crtc->id;
arg.fb_id = fb_id(bo);
assert(arg.fb_id);
if (bo != crtc->bo) {
arg.x = 0;
arg.y = 0;
crtc->offset = 0;
} else {
arg.x = crtc->base->x;
arg.y = crtc->base->y;
crtc->offset = arg.y << 16 | arg.x;
}
arg.set_connectors_ptr = (uintptr_t)output_ids;
arg.count_connectors = output_count;
arg.mode = crtc->kmode;
arg.mode_valid = 1;
DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d%s update to %d outputs [%d...]\n",
__FUNCTION__, crtc->id, crtc->pipe,
arg.mode.hdisplay,
arg.mode.vdisplay,
arg.x, arg.y,
arg.mode.clock,
arg.fb_id,
bo != crtc->bo ? " [shadow]" : "",
output_count, output_count ? output_ids[0] : 0));
return drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg) == 0;
}
void sna_mode_redisplay(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
@ -5729,7 +5907,8 @@ void sna_mode_redisplay(struct sna *sna)
if (sna->mode.flip_active)
return;
if (wedged(sna) || !sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT)) {
if (wedged(sna) || !sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT)) {
DBG(("%s: forcing scanout update using the CPU\n", __FUNCTION__));
if (!sna_pixmap_move_to_cpu(sna->front, MOVE_READ))
return;
@ -5764,12 +5943,14 @@ void sna_mode_redisplay(struct sna *sna)
assert(priv != NULL);
if (priv->move_to_gpu) {
if (priv->move_to_gpu == wait_for_shadow) {
if (priv->move_to_gpu == wait_for_shadow &&
!sna->mode.shadow_dirty) {
/* No damage written to new scanout
* (backbuffer), ignore redisplay request
* and continue with the current intact
* scanout (frontbuffer).
*/
DBG(("%s: shadow idle, skipping update\n", __FUNCTION__));
RegionEmpty(region);
return;
}
@ -5875,31 +6056,33 @@ disable1:
struct kgem_bo *new = __sna_pixmap_get_bo(sna->front);
struct kgem_bo *old = sna->mode.shadow;
struct drm_mode_crtc_page_flip arg;
uint32_t fb_id;
DBG(("%s: flipping tear-free outputs, current scanout handle=%d [active?=%d], new handle=%d [active=%d]\n",
__FUNCTION__, old->handle, old->active_scanout, new->handle, new->active_scanout));
assert(new != old);
assert(new->refcnt);
assert(new->active_scanout == 0);
arg.fb_id = get_fb(sna, new,
sna->scrn->virtualX,
sna->scrn->virtualY);
if (arg.fb_id == 0) {
BoxRec box;
fb_id = get_fb(sna, new,
sna->scrn->virtualX,
sna->scrn->virtualY);
if (fb_id == 0) {
fixup_shadow:
box.x1 = 0;
box.y1 = 0;
box.x2 = sna->scrn->virtualX;
box.y2 = sna->scrn->virtualY;
if (sna->render.copy_boxes(sna, GXcopy,
sna->front, new, 0, 0,
sna->front, old, 0, 0,
&box, 1, COPY_LAST)) {
kgem_submit(&sna->kgem);
RegionEmpty(region);
if (sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT)) {
BoxRec box;
box.x1 = 0;
box.y1 = 0;
box.x2 = sna->scrn->virtualX;
box.y2 = sna->scrn->virtualY;
if (sna->render.copy_boxes(sna, GXcopy,
sna->front, __sna_pixmap_get_bo(sna->front), 0, 0,
sna->front, old, 0, 0,
&box, 1, COPY_LAST)) {
kgem_submit(&sna->kgem);
RegionEmpty(region);
}
}
return;
@ -5912,6 +6095,8 @@ fixup_shadow:
for (i = 0; i < sna->mode.num_real_crtc; i++) {
struct sna_crtc *crtc = config->crtc[i]->driver_private;
struct kgem_bo *flip_bo;
uint32_t crtc_offset = 0;
assert(crtc != NULL);
DBG(("%s: crtc %d [%d, pipe=%d] active? %d, transformed? %d\n",
@ -5925,23 +6110,64 @@ fixup_shadow:
arg.crtc_id = crtc->id;
arg.user_data = (uintptr_t)crtc;
if (crtc->shadow_bo) {
DBG(("%s: apply shadow override bo for CRTC:%d on pipe=%d, handle=%d\n",
__FUNCTION__, crtc->id, crtc->pipe, crtc->shadow_bo->handle));
arg.fb_id = get_fb(sna, crtc->shadow_bo,
crtc->base->mode.HDisplay,
crtc->base->mode.VDisplay);
flip_bo = crtc->shadow_bo;
crtc_offset = 0;
} else {
arg.fb_id = fb_id;
flip_bo = new;
crtc_offset = crtc->base->y << 16 | crtc->base->x;
}
if (crtc->bo == flip_bo)
continue;
if (flip_bo->pitch != crtc->bo->pitch || crtc_offset != crtc->offset) {
DBG(("%s: changing pitch (%d == %d) or offset (%x == %x)\n",
__FUNCTION__,
flip_bo->pitch, crtc->bo->pitch,
crtc_offset, crtc->offset));
fixup_flip:
if (sna_crtc_flip(sna, crtc)) {
assert(crtc->bo->active_scanout);
assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
crtc->bo->active_scanout--;
kgem_bo_destroy(&sna->kgem, crtc->bo);
crtc->bo = kgem_bo_reference(flip_bo);
crtc->bo->active_scanout++;
if (crtc->shadow_bo)
sna_shadow_set_crtc(sna, crtc->base, flip_bo);
} else {
if (sna->mode.flip_active == 0) {
DBG(("%s: abandoning flip attempt\n", __FUNCTION__));
goto fixup_shadow;
}
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n",
__FUNCTION__, crtc->id, crtc->pipe);
sna_crtc_disable(crtc->base);
}
continue;
}
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
ERR(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
__FUNCTION__, arg.fb_id, i, crtc->id, crtc->pipe, errno));
if (sna->mode.flip_active == 0)
goto fixup_shadow;
xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
"%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n",
__FUNCTION__, crtc->id, crtc->pipe);
sna_crtc_disable(config->crtc[i]);
continue;
goto fixup_flip;
}
sna->mode.flip_active++;
assert(crtc->flip_bo == NULL);
crtc->flip_handler = shadow_flip_handler;
crtc->flip_bo = kgem_bo_reference(new);
crtc->flip_bo = kgem_bo_reference(flip_bo);
crtc->flip_bo->active_scanout++;
}
@ -6012,6 +6238,7 @@ void sna_mode_wakeup(struct sna *sna)
crtc->flip_bo = NULL;
}
DBG(("%s: flip complete, pending? %d\n", __FUNCTION__, sna->mode.flip_active));
assert(sna->mode.flip_active);
if (--sna->mode.flip_active == 0)
crtc->flip_handler(sna, vbl, crtc->flip_data);

View File

@ -72,7 +72,7 @@ static inline void unref(struct kgem_bo *bo)
struct sna_dri2_private {
PixmapPtr pixmap;
struct kgem_bo *bo;
bool scanout;
DRI2Buffer2Ptr proxy;
bool stale;
uint32_t size;
int refcnt;
@ -129,6 +129,7 @@ static void sna_dri2_flip_event(struct sna *sna,
static void
sna_dri2_get_back(struct sna *sna,
DrawablePtr draw,
DRI2BufferPtr back,
struct sna_dri2_event *info)
{
@ -160,12 +161,11 @@ sna_dri2_get_back(struct sna *sna,
}
}
if (bo == NULL) {
DrawablePtr draw = &sna->front->drawable;
DBG(("%s: allocating new backbuffer\n", __FUNCTION__));
bo = kgem_create_2d(&sna->kgem,
draw->width, draw->height, draw->bitsPerPixel,
get_private(back)->bo->tiling,
CREATE_SCANOUT);
get_private(back)->bo->scanout ? CREATE_SCANOUT : 0);
if (bo == NULL)
return;
@ -203,6 +203,7 @@ sna_dri2_get_back(struct sna *sna,
}
struct dri2_window {
DRI2BufferPtr front;
struct sna_dri2_event *chain;
xf86CrtcPtr crtc;
int64_t msc_delta;
@ -248,7 +249,7 @@ sna_dri2_reuse_buffer(DrawablePtr draw, DRI2BufferPtr buffer)
if (buffer->attachment == DRI2BufferBackLeft &&
draw->type != DRAWABLE_PIXMAP) {
DBG(("%s: replacing back buffer\n", __FUNCTION__));
sna_dri2_get_back(to_sna_from_drawable(draw), buffer, dri2_chain(draw));
sna_dri2_get_back(to_sna_from_drawable(draw), draw, buffer, dri2_chain(draw));
assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name);
assert(get_private(buffer)->bo->active_scanout == 0);
@ -409,22 +410,30 @@ sna_dri2_create_buffer(DrawablePtr draw,
switch (attachment) {
case DRI2BufferFrontLeft:
pixmap = get_drawable_pixmap(draw);
buffer = sna_pixmap_get_buffer(pixmap);
buffer = NULL;
if (draw->type != DRAWABLE_PIXMAP)
buffer = dri2_window((WindowPtr)draw)->front;
if (buffer == NULL)
buffer = sna_pixmap_get_buffer(pixmap);
if (buffer) {
private = get_private(buffer);
DBG(("%s: reusing front buffer attachment, pixmap=%ld, handle=%d, name=%d\n",
__FUNCTION__, pixmap->drawable.serialNumber,
DBG(("%s: reusing front buffer attachment, win=%lu %dx%d, pixmap=%ld %dx%d, handle=%d, name=%d\n",
__FUNCTION__,
draw->type != DRAWABLE_PIXMAP ? (long)draw->id : (long)0,
draw->width, draw->height,
pixmap->drawable.serialNumber,
pixmap->drawable.width,
pixmap->drawable.height,
private->bo->handle, buffer->name));
assert(private->pixmap == pixmap);
assert(private->bo->flush);
assert(sna_pixmap(pixmap)->flush);
assert(sna_pixmap(pixmap)->gpu_bo == private->bo);
assert(sna_pixmap(pixmap)->pinned & PIN_DRI2);
assert(private->proxy != NULL || sna_pixmap(pixmap)->gpu_bo == private->bo);
assert(kgem_bo_flink(&sna->kgem, private->bo) == buffer->name);
assert(8*private->bo->pitch >= pixmap->drawable.width * pixmap->drawable.bitsPerPixel);
assert(private->bo->pitch * pixmap->drawable.height <= kgem_bo_size(private->bo));
buffer->attachment = DRI2BufferFrontLeft;
private->refcnt++;
return buffer;
@ -448,10 +457,14 @@ sna_dri2_create_buffer(DrawablePtr draw,
break;
case DRI2BufferBackLeft:
if (draw->width == sna->front->drawable.width &&
draw->height == sna->front->drawable.height &&
(sna->flags & (SNA_NO_WAIT | SNA_NO_FLIP)) == 0)
flags |= CREATE_SCANOUT;
if (draw->type != DRAWABLE_PIXMAP) {
if (dri2_window((WindowPtr)draw)->front)
flags |= CREATE_SCANOUT;
if (draw->width == sna->front->drawable.width &&
draw->height == sna->front->drawable.height &&
(sna->flags & (SNA_NO_WAIT | SNA_NO_FLIP)) == 0)
flags |= CREATE_SCANOUT;
}
case DRI2BufferBackRight:
case DRI2BufferFrontRight:
case DRI2BufferFakeFrontLeft:
@ -533,7 +546,6 @@ sna_dri2_create_buffer(DrawablePtr draw,
private->refcnt = 1;
private->bo = bo;
private->pixmap = pixmap;
private->scanout = !!(flags & CREATE_SCANOUT);
private->size = size;
if (buffer->name == 0)
@ -593,6 +605,12 @@ static void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer)
return;
assert(private->bo);
if (private->proxy) {
_sna_dri2_destroy_buffer(sna, private->proxy);
private->pixmap = NULL;
}
if (private->pixmap) {
PixmapPtr pixmap = private->pixmap;
struct sna_pixmap *priv = sna_pixmap(pixmap);
@ -1078,6 +1096,7 @@ draw_current_msc(DrawablePtr draw, xf86CrtcPtr crtc, uint64_t msc)
if (priv == NULL) {
priv = malloc(sizeof(*priv));
if (priv != NULL) {
priv->front = NULL;
priv->crtc = crtc;
priv->msc_delta = 0;
priv->chain = NULL;
@ -1226,6 +1245,7 @@ sna_dri2_event_free(struct sna *sna,
void sna_dri2_destroy_window(WindowPtr win)
{
struct sna *sna;
struct dri2_window *priv;
priv = dri2_window(win);
@ -1233,9 +1253,15 @@ void sna_dri2_destroy_window(WindowPtr win)
return;
DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.serialNumber));
sna = to_sna_from_drawable(&win->drawable);
if (priv->front) {
assert(priv->crtc);
sna_shadow_unset_crtc(sna, priv->crtc);
_sna_dri2_destroy_buffer(sna, priv->front);
}
if (priv->chain) {
struct sna *sna = to_sna_from_drawable(&win->drawable);
struct sna_dri2_event *info, *chain;
DBG(("%s: freeing chain\n", __FUNCTION__));
@ -1377,7 +1403,7 @@ can_flip(struct sna * sna,
assert(get_private(front)->pixmap == sna->front);
assert(sna_pixmap(sna->front)->gpu_bo == get_private(front)->bo);
if (!get_private(back)->scanout) {
if (!get_private(back)->bo->scanout) {
DBG(("%s: no, DRI2 drawable was too small at time of creation)\n",
__FUNCTION__));
return false;
@ -1513,6 +1539,107 @@ can_xchg(struct sna * sna,
return true;
}
static bool
overlaps_other_crtc(struct sna *sna, xf86CrtcPtr desired)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int c;
for (c = 0; c < sna->mode.num_real_crtc; c++) {
xf86CrtcPtr crtc = config->crtc[c];
if (crtc == desired)
continue;
if (!crtc->enabled)
continue;
if (desired->bounds.x1 < crtc->bounds.x2 &&
desired->bounds.x2 > crtc->bounds.x1 &&
desired->bounds.y1 < crtc->bounds.y2 &&
desired->bounds.y2 > crtc->bounds.y1)
return true;
}
return false;
}
static bool
can_xchg_one(struct sna *sna,
DrawablePtr draw,
DRI2BufferPtr front,
DRI2BufferPtr back,
xf86CrtcPtr crtc)
{
WindowPtr win = (WindowPtr)draw;
PixmapPtr pixmap;
if ((sna->flags & SNA_TEAR_FREE) == 0) {
DBG(("%s: no, requires TearFree\n",
__FUNCTION__));
return false;
}
if (draw->type == DRAWABLE_PIXMAP)
return false;
if (front->format != back->format) {
DBG(("%s: no, format mismatch, front = %d, back = %d\n",
__FUNCTION__, front->format, back->format));
return false;
}
if (front->attachment != DRI2BufferFrontLeft) {
DBG(("%s: no, front attachment [%d] is not FrontLeft [%d]\n",
__FUNCTION__,
front->attachment,
DRI2BufferFrontLeft));
return false;
}
if (sna_crtc_is_transformed(crtc)) {
DBG(("%s: no, CRTC is rotated\n", __FUNCTION__));
return false;
}
pixmap = get_window_pixmap(win);
if (pixmap != sna->front) {
DBG(("%s: no, not attached to front buffer\n", __FUNCTION__));
return false;
}
DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d\n",
__FUNCTION__,
win->drawable.width, win->drawable.height,
win->clipList.extents.x1, win->clipList.extents.y1,
win->clipList.extents.x2, win->clipList.extents.y2,
RegionNumRects(&win->clipList)));
if (is_clipped(&win->clipList, &win->drawable)) {
DBG(("%s: no, %dx%d window is clipped: clip region=(%d, %d), (%d, %d)\n",
__FUNCTION__,
draw->width, draw->height,
win->clipList.extents.x1,
win->clipList.extents.y1,
win->clipList.extents.x2,
win->clipList.extents.y2));
return false;
}
if (overlaps_other_crtc(sna, crtc)) {
DBG(("%s: no, overlaps other CRTC\n", __FUNCTION__));
return false;
}
if (get_private(back)->size != (draw->height << 16 | draw->width)) {
DBG(("%s: no, DRI2 buffers does not fit window\n",
__FUNCTION__));
return false;
}
DBG(("%s: yes\n", __FUNCTION__));
return true;
}
static void
sna_dri2_exchange_buffers(DrawablePtr draw,
DRI2BufferPtr front,
@ -1900,7 +2027,7 @@ sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_event *info)
return false;
if (!XORG_CAN_TRIPLE_BUFFER) {
sna_dri2_get_back(sna, info->back, info);
sna_dri2_get_back(sna, info->draw, info->back, info);
DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
frame_swap_complete(sna, info, DRI2_FLIP_COMPLETE);
}
@ -2180,7 +2307,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
if (type >= FLIP_COMPLETE) {
new_back:
if (!XORG_CAN_TRIPLE_BUFFER)
sna_dri2_get_back(sna, back, info);
sna_dri2_get_back(sna, draw, back, info);
DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
frame_swap_complete(sna, info, DRI2_EXCHANGE_COMPLETE);
if (info->type == FLIP_ASYNC)
@ -2314,6 +2441,94 @@ complete:
return true;
}
static bool
sna_dri2_schedule_xchg_one(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
DRI2BufferPtr front, DRI2BufferPtr back,
CARD64 *target_msc, CARD64 divisor, CARD64 remainder,
DRI2SwapEventPtr func, void *data)
{
struct sna *sna = to_sna_from_drawable(draw);
uint64_t current_msc;
bool sync, event;
if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, &current_msc))
return false;
sync = current_msc < *target_msc;
event = dri2_chain(draw) == NULL;
if (!sync || event) {
WindowPtr win = (WindowPtr)draw;
PixmapPtr pixmap = get_window_pixmap(win);
DBG(("%s: performing immediate xchg only on pipe %d\n",
__FUNCTION__, sna_crtc_to_pipe(crtc)));
DBG(("%s: exchange front=%d/%d and back=%d/%d, win id=%lu, pixmap=%ld %dx%d\n",
__FUNCTION__,
get_private(front)->bo->handle, front->name,
get_private(back)->bo->handle, back->name,
win->drawable.id,
pixmap->drawable.serialNumber,
pixmap->drawable.width,
pixmap->drawable.height));
sna_shadow_set_crtc(sna, crtc, get_private(back)->bo);
DamageRegionProcessPending(&win->drawable);
if (get_private(front)->size == (draw->height << 16 | draw->width)) {
front->attachment = DRI2BufferBackLeft;
get_private(front)->stale = true;
} else
front->attachment = -1;
back->attachment = DRI2BufferFrontLeft;
if (get_private(back)->proxy == NULL) {
get_private(back)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(pixmap));
get_private(back)->pixmap = pixmap;
}
assert(dri2_window(win)->front == NULL);
dri2_window(win)->front = sna_dri2_reference_buffer(back);
}
if (sync) {
struct sna_dri2_event *info;
info = sna_dri2_add_event(draw, client);
if (!info)
goto complete;
info->event_complete = func;
info->event_data = data;
info->front = sna_dri2_reference_buffer(front);
info->back = sna_dri2_reference_buffer(back);
info->type = SWAP_THROTTLE;
if (event) {
union drm_wait_vblank vbl;
VG_CLEAR(vbl);
vbl.request.type =
DRM_VBLANK_RELATIVE |
DRM_VBLANK_EVENT;
vbl.request.sequence = 1;
vbl.request.signal = (uintptr_t)info;
info->queued = true;
if (sna_wait_vblank(sna, &vbl, info->pipe)) {
sna_dri2_event_free(sna, draw, info);
goto complete;
}
swap_limit(draw, 2);
}
} else {
complete:
fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data);
}
*target_msc = current_msc + 1;
return true;
}
static bool has_pending_events(struct sna *sna)
{
struct pollfd pfd;
@ -2353,9 +2568,12 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
struct sna_dri2_event *info = NULL;
CARD64 current_msc;
DBG(("%s: draw=%ld, pixmap=%ld, back=%u (refs=%d/%d, flush=%d) , fron=%u (refs=%d/%d, flush=%d)\n",
DBG(("%s: draw=%lu %dx%d, pixmap=%ld %dx%d, back=%u (refs=%d/%d, flush=%d) , front=%u (refs=%d/%d, flush=%d)\n",
__FUNCTION__,
(long)draw->id, get_drawable_pixmap(draw)->drawable.serialNumber,
(long)draw->id, draw->width, draw->height,
get_drawable_pixmap(draw)->drawable.serialNumber,
get_drawable_pixmap(draw)->drawable.width,
get_drawable_pixmap(draw)->drawable.height,
get_private(back)->bo->handle,
get_private(back)->refcnt,
get_private(back)->bo->refcnt,
@ -2375,10 +2593,18 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
assert(get_private(back)->refcnt);
assert(get_private(front)->bo->refcnt);
assert(get_private(front)->bo->flush);
assert(get_private(back)->bo->refcnt);
if (draw->type != DRAWABLE_PIXMAP) {
struct dri2_window *priv = dri2_window((WindowPtr)draw);
if (priv->front) {
assert(front == priv->front);
assert(get_private(priv->front)->refcnt > 1);
get_private(priv->front)->refcnt--;
priv->front = NULL;
}
}
if (get_private(front)->pixmap != get_drawable_pixmap(draw))
goto skip;
@ -2408,6 +2634,12 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
func, data))
return TRUE;
if (can_xchg_one(sna, draw, front, back, crtc) &&
sna_dri2_schedule_xchg_one(client, draw, crtc, front, back,
target_msc, divisor, remainder,
func, data))
return TRUE;
if (can_flip(sna, draw, front, back, crtc) &&
sna_dri2_schedule_flip(client, draw, crtc, front, back,
target_msc, divisor, remainder,