summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2014-06-09 08:58:15 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2014-06-09 14:57:57 +0100
commit3932e97057fca16615adaefbc1eb25a0d51a1d8b (patch)
tree34cfc923aadfba5599a4e6ff32b0ffe896de85c2
parentaf3130cbba8375b9060a3a8cef5b03189d01c419 (diff)
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>
-rw-r--r--src/sna/kgem.c83
-rw-r--r--src/sna/sna.h8
-rw-r--r--src/sna/sna_accel.c2
-rw-r--r--src/sna/sna_display.c295
-rw-r--r--src/sna/sna_dri2.c280
5 files changed, 573 insertions, 95 deletions
diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index e90959fa..45a2572d 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -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,
- tiling, pitch)) {
- first->tiling = tiling;
- first->pitch = pitch;
- }
- }
+ assert(bo->scanout);
- if (first->tiling == tiling && first->pitch == pitch) {
- struct drm_mode_fb_cmd arg;
+ if (size > num_pages(bo) || num_pages(bo) > 2*size)
+ continue;
- 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;
+ if (flags & CREATE_INACTIVE && bo->rq)
+ continue;
- kgem_bo_rmfb(kgem, first);
- if (do_ioctl(kgem->fd, DRM_IOCTL_MODE_ADDFB, &arg)) {
- kgem_bo_free(kgem, first);
+ 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)) {
+ 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;
}
}
diff --git a/src/sna/sna.h b/src/sna/sna.h
index 31f80a0d..2238e803 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -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);
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index e9c1ef23..78f422db 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -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);
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 51875151..8df3a90c 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -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);
diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 8553169b..5e1e7eaa 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -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,