summaryrefslogtreecommitdiff
path: root/src/sna/sna_dri2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sna/sna_dri2.c')
-rw-r--r--src/sna/sna_dri2.c673
1 files changed, 348 insertions, 325 deletions
diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 083c47b2..7a433019 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -70,12 +70,6 @@ static inline struct kgem_bo *ref(struct kgem_bo *bo)
struct sna_dri2_private {
PixmapPtr pixmap;
struct kgem_bo *bo;
- struct copy {
- struct kgem_bo *bo;
- uint32_t name;
- uint32_t size;
- uint32_t flags;
- } copy;
DRI2Buffer2Ptr proxy;
bool stale;
uint32_t size;
@@ -104,9 +98,7 @@ static inline void sna_pixmap_set_buffer(PixmapPtr pixmap, void *ptr)
enum event_type {
WAITMSC = 0,
SWAP,
- SWAP_WAIT,
- SWAP_THROTTLE,
- SWAP_ALIVE,
+ SWAP_COMPLETE,
FLIP,
FLIP_THROTTLE,
FLIP_COMPLETE,
@@ -117,7 +109,7 @@ struct dri_bo {
struct list link;
struct kgem_bo *bo;
uint32_t name;
- int flags;
+ unsigned flags;
};
struct sna_dri2_event {
@@ -129,6 +121,7 @@ struct sna_dri2_event {
int pipe;
bool queued;
bool sync;
+ bool chained;
/* for swaps & flips only */
DRI2SwapEventPtr event_complete;
@@ -137,12 +130,20 @@ struct sna_dri2_event {
DRI2BufferPtr back;
struct kgem_bo *bo;
+ struct copy {
+ struct kgem_bo *bo;
+ unsigned flags;
+ uint32_t name;
+ uint32_t size;
+ } pending;
+
struct sna_dri2_event *chain;
struct list link;
int flip_continue;
int keepalive;
+ int signal;
};
#if DRI2INFOREC_VERSION < 10
@@ -761,6 +762,67 @@ err:
return NULL;
}
+static void
+sna_dri2_cache_bo(struct sna *sna,
+ DrawablePtr draw,
+ struct kgem_bo *bo,
+ uint32_t name,
+ uint32_t size,
+ uint32_t flags)
+{
+ struct dri_bo *c;
+
+ DBG(("%s(handle=%d, name=%d)\n", __FUNCTION__, bo->handle, name));
+
+ if (draw == NULL) {
+ DBG(("%s: no draw, releasing handle=%d\n",
+ __FUNCTION__, bo->handle));
+ goto err;
+ }
+
+ if (draw->type == DRAWABLE_PIXMAP) {
+ DBG(("%s: not a window, releasing handle=%d\n",
+ __FUNCTION__, bo->handle));
+ goto err;
+ }
+
+ if (bo->refcnt > 1 + bo->active_scanout) {
+ DBG(("%s: multiple references [%d], releasing handle\n",
+ __FUNCTION__, bo->refcnt, bo->handle));
+ goto err;
+ }
+
+ if ((draw->height << 16 | draw->width) != size) {
+ DBG(("%s: wrong size [%dx%d], releasing handle\n",
+ __FUNCTION__,
+ size & 0xffff, size >> 16,
+ bo->handle));
+ goto err;
+ }
+
+ if (bo->scanout && front_pitch(draw) != bo->pitch) {
+ DBG(("%s: scanout with pitch change [%d != %d], releasing handle\n",
+ __FUNCTION__, bo->pitch, front_pitch(draw), bo->handle));
+ goto err;
+ }
+
+ c = malloc(sizeof(*c));
+ if (!c)
+ goto err;
+
+ DBG(("%s: caching handle=%d (name=%d, flags=%d, active_scanout=%d)\n", __FUNCTION__, bo->handle, name, flags, bo->active_scanout));
+
+ c->bo = bo;
+ c->name = name;
+ c->flags = flags;
+ list_add(&c->link, &dri2_window((WindowPtr)draw)->cache);
+ return;
+
+err:
+ assert(bo->active_scanout == 0 || bo->scanout);
+ kgem_bo_destroy(&sna->kgem, bo);
+}
+
static void _sna_dri2_destroy_buffer(struct sna *sna,
DrawablePtr draw,
DRI2Buffer2Ptr buffer)
@@ -785,12 +847,6 @@ static void _sna_dri2_destroy_buffer(struct sna *sna,
private->pixmap = NULL;
}
- if (private->copy.bo) {
- assert(private->copy.bo->active_scanout);
- private->copy.bo->active_scanout--;
- kgem_bo_destroy(&sna->kgem, private->copy.bo);
- }
-
if (private->pixmap) {
PixmapPtr pixmap = private->pixmap;
struct sna_pixmap *priv = sna_pixmap(pixmap);
@@ -822,7 +878,11 @@ static void _sna_dri2_destroy_buffer(struct sna *sna,
pixmap->drawable.pScreen->DestroyPixmap(pixmap);
}
- kgem_bo_destroy(&sna->kgem, private->bo);
+ sna_dri2_cache_bo(sna, draw,
+ private->bo,
+ buffer->name,
+ private->size,
+ buffer->flags);
free(buffer);
}
@@ -1425,19 +1485,69 @@ sna_dri2_get_crtc(DrawablePtr draw)
NULL);
}
+static void frame_swap_complete(struct sna_dri2_event *frame, int type)
+{
+ const struct ust_msc *swap;
+
+ assert(frame->signal);
+ frame->signal = false;
+
+ if (frame->client == NULL) {
+ DBG(("%s: client already gone\n", __FUNCTION__));
+ return;
+ }
+
+ assert(frame->draw);
+
+ swap = sna_crtc_last_swap(frame->crtc);
+ DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc=%lld], tv=%d.%06d\n",
+ __FUNCTION__, type, (long)frame->draw->id, frame->pipe,
+ (long long)swap->msc,
+ (long long)draw_current_msc(frame->draw, frame->crtc, swap->msc),
+ swap->tv_sec, swap->tv_usec));
+
+ DRI2SwapComplete(frame->client, frame->draw,
+ draw_current_msc(frame->draw, frame->crtc, swap->msc),
+ swap->tv_sec, swap->tv_usec,
+ type, frame->event_complete, frame->event_data);
+}
+
+static void fake_swap_complete(struct sna *sna, ClientPtr client,
+ DrawablePtr draw, xf86CrtcPtr crtc,
+ int type, DRI2SwapEventPtr func, void *data)
+{
+ const struct ust_msc *swap;
+
+ swap = sna_crtc_last_swap(crtc);
+ DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc %lld], tv=%d.%06d\n",
+ __FUNCTION__, type, (long)draw->id, crtc ? sna_crtc_pipe(crtc) : -1,
+ (long long)swap->msc,
+ (long long)draw_current_msc(draw, crtc, swap->msc),
+ swap->tv_sec, swap->tv_usec));
+
+ DRI2SwapComplete(client, draw,
+ draw_current_msc(draw, crtc, swap->msc),
+ swap->tv_sec, swap->tv_usec,
+ type, func, data);
+}
+
static void
-sna_dri2_remove_event(WindowPtr win, struct sna_dri2_event *info)
+sna_dri2_remove_event(struct sna_dri2_event *info)
{
+ WindowPtr win = (WindowPtr)info->draw;
struct dri2_window *priv;
struct sna_dri2_event *chain;
assert(win->drawable.type == DRAWABLE_WINDOW);
DBG(("%s: remove[%p] from window %ld, active? %d\n",
__FUNCTION__, info, (long)win->drawable.id, info->draw != NULL));
+ assert(!info->signal);
priv = dri2_window(win);
assert(priv);
assert(priv->chain != NULL);
+ assert(info->chained);
+ info->chained = false;
if (priv->chain != info) {
chain = priv->chain;
@@ -1471,14 +1581,18 @@ sna_dri2_event_free(struct sna_dri2_event *info)
DrawablePtr draw = info->draw;
DBG(("%s(draw?=%d)\n", __FUNCTION__, draw != NULL));
+ assert(!info->queued);
+ assert(!info->signal);
+ assert(info->pending.bo == NULL);
+
if (info->sna->dri2.flip_pending == info)
- info->sna->dri2.flip_pending = info->chain;
+ info->sna->dri2.flip_pending = NULL;
assert(info->sna->dri2.flip_pending != info);
- if (draw && draw->type == DRAWABLE_WINDOW)
- sna_dri2_remove_event((WindowPtr)draw, info);
+ if (info->chained)
+ sna_dri2_remove_event(info);
- _sna_dri2_destroy_buffer(info->sna, draw, info->front);
- _sna_dri2_destroy_buffer(info->sna, draw, info->back);
+ _sna_dri2_destroy_buffer(info->sna, info->draw, info->front);
+ _sna_dri2_destroy_buffer(info->sna, info->draw, info->back);
if (info->bo) {
DBG(("%s: releasing batch handle=%d\n", __FUNCTION__, info->bo->handle));
@@ -1511,15 +1625,24 @@ sna_dri2_client_gone(CallbackListPtr *list, void *closure, void *data)
event = list_first_entry(&priv->events, struct sna_dri2_event, link);
assert(event->client == client);
+ list_del(&event->link);
+ event->signal = false;
- if (event->queued) {
- if (event->draw)
- sna_dri2_remove_event((WindowPtr)event->draw,
- event);
- event->client = NULL;
- event->draw = NULL;
- list_del(&event->link);
- } else
+ if (event->pending.bo) {
+ assert(event->pending.bo->active_scanout > 0);
+ event->pending.bo->active_scanout--;
+
+ kgem_bo_destroy(&sna->kgem, event->pending.bo);
+ event->pending.bo = NULL;
+ }
+
+ if (event->chained)
+ sna_dri2_remove_event(event);
+
+ event->client = NULL;
+ event->draw = NULL;
+
+ if (!event->queued)
sna_dri2_event_free(event);
}
@@ -1577,6 +1700,7 @@ sna_dri2_add_event(struct sna *sna,
}
assert(priv->chain != info);
+ info->chained = true;
if (priv->chain == NULL) {
priv->chain = info;
@@ -1637,12 +1761,25 @@ void sna_dri2_destroy_window(WindowPtr win)
chain = priv->chain;
while ((info = chain)) {
assert(info->draw == &win->drawable);
+ if (info->signal)
+ frame_swap_complete(info, DRI2_EXCHANGE_COMPLETE);
+ if (info->pending.bo) {
+ assert(info->pending.bo->active_scanout > 0);
+ info->pending.bo->active_scanout--;
+
+ info->signal = true;
+ frame_swap_complete(info, DRI2_EXCHANGE_COMPLETE);
+
+ kgem_bo_destroy(&sna->kgem, info->pending.bo);
+ info->pending.bo = NULL;
+ }
+
info->draw = NULL;
- info->client = NULL;
list_del(&info->link);
chain = info->chain;
info->chain = NULL;
+ info->chained = false;
if (!info->queued)
sna_dri2_event_free(info);
@@ -1696,6 +1833,9 @@ sna_dri2_flip(struct sna_dri2_event *info)
info->type == FLIP_ASYNC ? NULL : info))
return false;
+ DBG(("%s: queued flip=%p\n", __FUNCTION__, info->type == FLIP_ASYNC ? NULL : info));
+ assert(info->signal || info->type != FLIP_THROTTLE);
+
assert(info->sna->dri2.flip_pending == NULL ||
info->sna->dri2.flip_pending == info);
if (info->type != FLIP_ASYNC)
@@ -2220,106 +2360,9 @@ static void sna_dri2_xchg_crtc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr cr
}
}
-static void frame_swap_complete(struct sna_dri2_event *frame, int type)
-{
- const struct ust_msc *swap;
-
- if (frame->draw == NULL)
- return;
-
- assert(frame->client);
-
- swap = sna_crtc_last_swap(frame->crtc);
- DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc=%lld], tv=%d.%06d\n",
- __FUNCTION__, type, (long)frame->draw->id, frame->pipe,
- (long long)swap->msc,
- (long long)draw_current_msc(frame->draw, frame->crtc, swap->msc),
- swap->tv_sec, swap->tv_usec));
-
- DRI2SwapComplete(frame->client, frame->draw,
- draw_current_msc(frame->draw, frame->crtc, swap->msc),
- swap->tv_sec, swap->tv_usec,
- type, frame->event_complete, frame->event_data);
-}
-
-static void fake_swap_complete(struct sna *sna, ClientPtr client,
- DrawablePtr draw, xf86CrtcPtr crtc,
- int type, DRI2SwapEventPtr func, void *data)
-{
- const struct ust_msc *swap;
-
- swap = sna_crtc_last_swap(crtc);
- DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc %lld], tv=%d.%06d\n",
- __FUNCTION__, type, (long)draw->id, crtc ? sna_crtc_pipe(crtc) : -1,
- (long long)swap->msc,
- (long long)draw_current_msc(draw, crtc, swap->msc),
- swap->tv_sec, swap->tv_usec));
-
- DRI2SwapComplete(client, draw,
- draw_current_msc(draw, crtc, swap->msc),
- swap->tv_sec, swap->tv_usec,
- type, func, data);
-}
-
-static void
-sna_dri2_cache_bo(struct sna *sna,
- DrawablePtr draw,
- struct kgem_bo *bo,
- uint32_t name,
- uint32_t size,
- uint32_t flags)
-{
- struct dri_bo *c;
-
- DBG(("%s(handle=%d, name=%d)\n", __FUNCTION__, bo->handle, name));
-
- if (draw == NULL) {
- DBG(("%s: no draw, releasing handle=%d\n",
- __FUNCTION__, bo->handle));
- goto err;
- }
-
- if (bo->refcnt > 1 + bo->active_scanout) {
- DBG(("%s: multiple references [%d], releasing handle\n",
- __FUNCTION__, bo->refcnt, bo->handle));
- goto err;
- }
-
- if ((draw->height << 16 | draw->width) != size) {
- DBG(("%s: wrong size [%dx%d], releasing handle\n",
- __FUNCTION__,
- size & 0xffff, size >> 16,
- bo->handle));
- goto err;
- }
-
- if (bo->scanout && front_pitch(draw) != bo->pitch) {
- DBG(("%s: scanout with pitch change [%d %= %d], releasing handle\n",
- __FUNCTION__, bo->pitch, front_pitch(draw), bo->handle));
- goto err;
- }
-
- c = malloc(sizeof(*c));
- if (!c)
- goto err;
-
- DBG(("%s: caching handle=%d (name=%d, flags=%d, active_scanout=%d)\n", __FUNCTION__, bo->handle, name, flags, bo->active_scanout));
-
- c->bo = bo;
- c->name = name;
- c->flags = flags;
- list_add(&c->link, &dri2_window((WindowPtr)draw)->cache);
- return;
-
-err:
- assert(bo->active_scanout == 0 || bo->scanout);
- kgem_bo_destroy(&sna->kgem, bo);
-}
-
static void chain_swap(struct sna_dri2_event *chain)
{
union drm_wait_vblank vbl;
- struct copy tmp;
DBG(("%s: draw=%ld, queued?=%d, type=%d\n",
__FUNCTION__, (long)chain->draw->id, chain->queued, chain->type));
@@ -2333,10 +2376,10 @@ static void chain_swap(struct sna_dri2_event *chain)
return;
assert(chain == dri2_chain(chain->draw));
- chain->queued = true;
+ assert(chain->signal);
switch (chain->type) {
- case SWAP_THROTTLE:
+ case SWAP_COMPLETE:
DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__));
if (chain->sna->mode.shadow &&
!chain->sna->mode.shadow_damage) {
@@ -2349,41 +2392,15 @@ static void chain_swap(struct sna_dri2_event *chain)
vbl.request.sequence = 1;
vbl.request.signal = (uintptr_t)chain;
- if (!sna_wait_vblank(chain->sna, &vbl, chain->pipe))
+ assert(!chain->queued);
+ if (!sna_wait_vblank(chain->sna, &vbl, chain->pipe)) {
+ chain->queued = true;
return;
+ }
DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno));
}
- /* We tracked the most recent completed swap in back->copy,
- * back->bo holds the buffer we last gave back to the client
- * i.e. its current render target. To simplify our swap
- * routines, we do an exchange here to emit the copy, then
- * exchange back again so that we are consistent with the
- * client once more.
- */
- tmp.bo = NULL;
- if (get_private(chain->back)->copy.bo) {
- tmp.bo = get_private(chain->back)->copy.bo;
- DBG(("%s: removing active marker [%d] from handle=%d\n",
- __FUNCTION__,
- tmp.bo->active_scanout, tmp.bo->handle));
- assert(tmp.bo->active_scanout);
- tmp.bo->active_scanout--;
-
- tmp.bo = get_private(chain->back)->bo;
- tmp.name = chain->back->name;
- tmp.size = get_private(chain->back)->size;
- tmp.flags = chain->back->flags;
-
- get_private(chain->back)->bo = get_private(chain->back)->copy.bo;
- chain->back->name = get_private(chain->back)->copy.name;
- get_private(chain->back)->size = get_private(chain->back)->copy.size;
- chain->back->flags = get_private(chain->back)->copy.flags;
- chain->back->pitch = get_private(chain->back)->copy.bo->pitch;
- get_private(chain->back)->copy.bo = NULL;
- }
-
if (can_xchg(chain->sna, chain->draw, chain->front, chain->back)) {
sna_dri2_xchg(chain->draw, chain->front, chain->back);
} else if (can_xchg_crtc(chain->sna, chain->draw, chain->crtc,
@@ -2391,25 +2408,8 @@ static void chain_swap(struct sna_dri2_event *chain)
sna_dri2_xchg_crtc(chain->sna, chain->draw, chain->crtc,
chain->front, chain->back);
} else {
- assert(chain->queued);
- __sna_dri2_copy_event(chain, DRI2_BO);
- }
-
- if (tmp.bo) {
- sna_dri2_cache_bo(chain->sna, chain->draw,
- get_private(chain->back)->bo,
- chain->back->name,
- get_private(chain->back)->size,
- chain->back->flags);
- get_private(chain->back)->copy.bo = NULL;
-
- get_private(chain->back)->bo = tmp.bo;
- chain->back->name = tmp.name;
- get_private(chain->back)->size = tmp.size;
- chain->back->flags = tmp.flags;
- chain->back->pitch = tmp.bo->pitch;
+ __sna_dri2_copy_event(chain, chain->sync | DRI2_BO);
}
-
assert(get_private(chain->back)->bo != get_private(chain->front)->bo);
case SWAP:
break;
@@ -2423,14 +2423,17 @@ static void chain_swap(struct sna_dri2_event *chain)
DRM_VBLANK_EVENT;
vbl.request.sequence = 1;
vbl.request.signal = (uintptr_t)chain;
- if ((chain->type == SWAP_THROTTLE &&
+
+ assert(!chain->queued);
+ if ((chain->type == SWAP_COMPLETE &&
!swap_limit(chain->draw, 2 + !chain->sync) &&
!chain->sync) ||
sna_wait_vblank(chain->sna, &vbl, chain->pipe)) {
DBG(("%s: vblank wait failed, unblocking client\n", __FUNCTION__));
frame_swap_complete(chain, DRI2_BLIT_COMPLETE);
sna_dri2_event_free(chain);
- }
+ } else
+ chain->queued = true;
}
static inline bool rq_is_busy(struct kgem *kgem, struct kgem_bo *bo)
@@ -2466,9 +2469,12 @@ static bool sna_dri2_blit_complete(struct sna *sna,
DRM_VBLANK_EVENT;
vbl.request.sequence = 1;
vbl.request.signal = (uintptr_t)info;
- assert(info->queued);
- if (!sna_wait_vblank(sna, &vbl, info->pipe))
+
+ assert(!info->queued);
+ if (!sna_wait_vblank(sna, &vbl, info->pipe)) {
+ info->queued = true;
return false;
+ }
}
DBG(("%s: blit finished\n", __FUNCTION__));
@@ -2484,8 +2490,10 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event)
union drm_wait_vblank vbl;
uint64_t msc;
- DBG(("%s(type=%d, sequence=%d)\n", __FUNCTION__, info->type, event->sequence));
+ DBG(("%s(type=%d, sequence=%d, draw=%ld)\n", __FUNCTION__, info->type, event->sequence, info->draw ? info->draw->serialNumber : 0));
assert(info->queued);
+ info->queued = false;
+
msc = sna_crtc_record_event(info->crtc, event);
draw = info->draw;
@@ -2497,29 +2505,28 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event)
switch (info->type) {
case FLIP:
/* If we can still flip... */
+ assert(info->signal);
if (can_flip(sna, draw, info->front, info->back, info->crtc) &&
sna_dri2_flip(info))
return;
/* else fall through to blit */
case SWAP:
- assert(info->queued);
+ assert(info->signal);
if (sna->mode.shadow && !sna->mode.shadow_damage) {
/* recursed from wait_for_shadow(), simply requeue */
DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__));
-
} else if (can_xchg(info->sna, draw, info->front, info->back)) {
sna_dri2_xchg(draw, info->front, info->back);
- info->type = SWAP_WAIT;
+ info->type = SWAP_COMPLETE;
} else if (can_xchg_crtc(sna, draw, info->crtc,
info->front, info->back)) {
sna_dri2_xchg_crtc(sna, draw, info->crtc,
info->front, info->back);
- info->type = SWAP_WAIT;
+ info->type = SWAP_COMPLETE;
} else {
- assert(info->queued);
- __sna_dri2_copy_event(info, DRI2_SYNC);
- info->type = SWAP_WAIT;
+ __sna_dri2_copy_event(info, DRI2_BO | DRI2_SYNC);
+ info->type = SWAP_COMPLETE;
}
VG_CLEAR(vbl);
@@ -2529,27 +2536,22 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event)
vbl.request.sequence = 1;
vbl.request.signal = (uintptr_t)info;
- assert(info->queued);
- if (!sna_wait_vblank(sna, &vbl, info->pipe))
+ assert(!info->queued);
+ if (!sna_wait_vblank(sna, &vbl, info->pipe)) {
+ info->queued = true;
return;
+ }
DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno));
+ assert(info->pending.bo == NULL);
+ assert(info->keepalive == 1);
/* fall through to SwapComplete */
- case SWAP_WAIT:
- if (!sna_dri2_blit_complete(sna, info))
- return;
-
- DBG(("%s: swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__,
- event->sequence, event->tv_sec, event->tv_usec));
- frame_swap_complete(info, DRI2_BLIT_COMPLETE);
- break;
-
- case SWAP_THROTTLE:
+ case SWAP_COMPLETE:
DBG(("%s: %d complete, frame=%d tv=%d.%06d\n",
__FUNCTION__, info->type,
event->sequence, event->tv_sec, event->tv_usec));
- if (xorg_can_triple_buffer()) {
+ if (info->signal) {
if (!sna_dri2_blit_complete(sna, info))
return;
@@ -2557,8 +2559,57 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event)
event->sequence, event->tv_sec, event->tv_usec));
frame_swap_complete(info, DRI2_BLIT_COMPLETE);
}
- break;
- case SWAP_ALIVE:
+
+ if (info->pending.bo) {
+ assert(info->pending.bo->active_scanout > 0);
+ info->pending.bo->active_scanout--;
+
+ sna_dri2_cache_bo(info->sna, info->draw,
+ get_private(info->back)->bo,
+ info->back->name,
+ get_private(info->back)->size,
+ info->back->flags);
+
+ get_private(info->back)->bo = info->pending.bo;
+ get_private(info->back)->size = info->pending.size;
+ info->back->name = info->pending.name;
+ info->back->pitch = info->pending.bo->pitch;
+ info->back->flags = info->pending.flags;
+ info->pending.bo = NULL;
+
+ if (can_xchg(info->sna, info->draw, info->front, info->back))
+ sna_dri2_xchg(info->draw, info->front, info->back);
+ else if (can_xchg_crtc(info->sna, info->draw, info->crtc,
+ info->front, info->back))
+ sna_dri2_xchg_crtc(info->sna, info->draw, info->crtc,
+ info->front, info->back);
+ else
+ __sna_dri2_copy_event(info, info->sync | DRI2_BO);
+
+ info->keepalive++;
+ info->signal = true;
+ }
+
+ if (--info->keepalive) {
+ VG_CLEAR(vbl);
+ vbl.request.type =
+ DRM_VBLANK_RELATIVE |
+ DRM_VBLANK_EVENT;
+ vbl.request.sequence = 1;
+ vbl.request.signal = (uintptr_t)info;
+
+ assert(!info->queued);
+ if (!sna_wait_vblank(sna, &vbl, info->pipe)) {
+ info->queued = true;
+ return;
+ }
+
+ if (info->signal) {
+ DBG(("%s: triple buffer swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__,
+ event->sequence, event->tv_sec, event->tv_usec));
+ frame_swap_complete(info, DRI2_BLIT_COMPLETE);
+ }
+ }
break;
case WAITMSC:
@@ -2577,27 +2628,8 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event)
DBG(("%s: continuing chain\n", __FUNCTION__));
assert(info->chain != info);
assert(info->draw == draw);
- sna_dri2_remove_event((WindowPtr)draw, info);
+ sna_dri2_remove_event(info);
chain_swap(info->chain);
- info->draw = NULL;
- } else if (--info->keepalive) {
- assert(info->type = SWAP_ALIVE || SWAP_THROTTLE);
- info->type = SWAP_ALIVE;
- info->queued = true;
-
- DBG(("%s: requeing keepalive=%d\n",
- __FUNCTION__, info->keepalive));
-
- VG_CLEAR(vbl);
- vbl.request.type =
- DRM_VBLANK_RELATIVE |
- DRM_VBLANK_EVENT;
- vbl.request.sequence = 1;
- vbl.request.signal = (uintptr_t)info;
- if (!sna_wait_vblank(sna, &vbl, info->pipe))
- return;
-
- info->queued = false;
}
done:
@@ -2619,9 +2651,10 @@ sna_dri2_immediate_blit(struct sna *sna,
DBG(("%s: emitting immediate blit, throttling client, synced? %d, chained? %d, pipe %d\n",
__FUNCTION__, sync, chain != info, info->pipe));
- info->type = SWAP_THROTTLE;
+ info->type = SWAP_COMPLETE;
info->sync = sync;
info->keepalive = KEEPALIVE;
+
if (chain == info) {
union drm_wait_vblank vbl;
@@ -2636,86 +2669,66 @@ sna_dri2_immediate_blit(struct sna *sna,
} else
__sna_dri2_copy_event(info, sync | DRI2_BO);
- info->queued = true;
+ assert(info->signal);
+
VG_CLEAR(vbl);
vbl.request.type =
DRM_VBLANK_RELATIVE |
DRM_VBLANK_EVENT;
vbl.request.sequence = 1;
vbl.request.signal = (uintptr_t)info;
+
+ assert(!info->queued);
if ((!swap_limit(draw, 2 + !sync) && !sync) ||
sna_wait_vblank(sna, &vbl, info->pipe)) {
DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
frame_swap_complete(info, DRI2_BLIT_COMPLETE);
sna_dri2_event_free(info);
}
+ info->queued = true;
return;
}
- /* Takeover the placeholder vblank */
- if (chain->type == SWAP_ALIVE) {
+ if (chain->type == SWAP_COMPLETE) {
assert(chain->draw == info->draw);
- assert(chain->bo == NULL);
+ assert(chain->client == info->client);
+ assert(chain->event_complete == info->event_complete);
+ assert(chain->event_data == info->event_data);
assert(chain->queued);
- DBG(("%s: stealing placeholder\n", __FUNCTION__));
-
- _sna_dri2_destroy_buffer(chain->sna, chain->draw, chain->front);
- _sna_dri2_destroy_buffer(chain->sna, chain->draw, chain->back);
-
- _list_del(&info->link);
- info->link = chain->link;
+ if (!sync && chain->pending.bo) {
+ bool signal = chain->signal;
- *chain = *info;
- chain->queued = true;
+ DBG(("%s: swap elision, unblocking client\n", __FUNCTION__));
+ chain->signal = true;
+ frame_swap_complete(chain, DRI2_EXCHANGE_COMPLETE);
+ chain->signal = signal;
- free(info);
- return;
- }
+ assert(chain->pending.bo->active_scanout > 0);
+ chain->pending.bo->active_scanout--;
- if (get_private(info->back)->copy.bo != get_private(info->back)->bo) {
- DBG(("%s: adding active marker [%d] to handle=%d, removing [%d] from handle=%d\n",
- __FUNCTION__,
- get_private(info->back)->bo->active_scanout,
- get_private(info->back)->bo->handle,
- get_private(info->back)->copy.bo ? get_private(info->back)->copy.bo->active_scanout : 0,
- get_private(info->back)->copy.bo ? get_private(info->back)->copy.bo->handle : 0));
- assert(get_private(info->back)->bo->active_scanout == 0 ||
- get_private(info->back)->bo->scanout);
- if (get_private(info->back)->copy.bo) {
- assert(get_private(info->back)->copy.bo->active_scanout);
- get_private(info->back)->copy.bo->active_scanout--;
- kgem_bo_destroy(&sna->kgem,
- get_private(info->back)->copy.bo);
+ sna_dri2_cache_bo(chain->sna, chain->draw,
+ chain->pending.bo,
+ chain->pending.name,
+ chain->pending.size,
+ chain->pending.flags);
+ chain->pending.bo = NULL;
}
- get_private(info->back)->copy.bo = ref(get_private(info->back)->bo);
- get_private(info->back)->copy.name = info->back->name;
- get_private(info->back)->copy.size = get_private(info->back)->size;
- get_private(info->back)->copy.flags = info->back->flags;
- get_private(info->back)->bo->active_scanout++;
- }
-
- if (chain->type == SWAP_THROTTLE &&
- chain->chain != info &&
- chain->chain->type == SWAP_THROTTLE) {
- struct sna_dri2_event *tmp = chain->chain;
- DBG(("%s: replacing next swap\n", __FUNCTION__));
+ if (chain->pending.bo == NULL) {
+ DBG(("%s: setting as pending blit\n", __FUNCTION__));
+ chain->pending.bo = ref(get_private(info->back)->bo);
+ chain->pending.size = get_private(info->back)->size;
+ chain->pending.name = info->back->name;
+ chain->pending.flags = info->back->flags;
+ info->signal = false; /* transfer signal to pending */
- assert(!tmp->queued);
-
- assert(info->chain == NULL);
- info->chain = tmp->chain;
- if (info->chain == info)
- info->chain = NULL;
- chain->chain = info;
- tmp->chain = NULL;
+ /* Prevent us from handing it back on next GetBuffers */
+ chain->pending.bo->active_scanout++;
- DBG(("%s: swap elision, unblocking client\n", __FUNCTION__));
- frame_swap_complete(tmp, DRI2_EXCHANGE_COMPLETE);
-
- tmp->draw = NULL;
- sna_dri2_event_free(tmp);
+ sna_dri2_event_free(info);
+ return;
+ }
}
DBG(("%s: pending blit, chained\n", __FUNCTION__));
@@ -2740,10 +2753,11 @@ sna_dri2_flip_continue(struct sna_dri2_event *info)
if (!sna_page_flip(info->sna, bo, sna_dri2_flip_handler, info))
return false;
+ DBG(("%s: queued flip=%p\n", __FUNCTION__, info));
assert(info->sna->dri2.flip_pending == NULL ||
info->sna->dri2.flip_pending == info);
info->sna->dri2.flip_pending = info;
- assert(info->queued);
+ info->queued = true;
return true;
}
@@ -2780,7 +2794,6 @@ static void chain_flip(struct sna *sna)
assert(chain == dri2_chain(chain->draw));
assert(!chain->queued);
- chain->queued = true;
if (can_flip(sna, chain->draw, chain->front, chain->back, chain->crtc) &&
sna_dri2_flip(chain)) {
@@ -2794,16 +2807,20 @@ static void chain_flip(struct sna *sna)
VG_CLEAR(vbl);
- chain->type = SWAP_WAIT;
+ chain->type = SWAP_COMPLETE;
+ assert(chain->signal);
+
vbl.request.type =
DRM_VBLANK_RELATIVE |
DRM_VBLANK_EVENT;
vbl.request.sequence = 1;
vbl.request.signal = (uintptr_t)chain;
- assert(chain->queued);
- if (!sna_wait_vblank(sna, &vbl, chain->pipe))
+ assert(!chain->queued);
+ if (!sna_wait_vblank(sna, &vbl, chain->pipe)) {
+ chain->queued = true;
return;
+ }
}
DBG(("%s: fake triple buffering (or vblank wait failed), unblocking client\n", __FUNCTION__));
@@ -2816,8 +2833,9 @@ static void sna_dri2_flip_event(struct sna_dri2_event *flip)
{
struct sna *sna = flip->sna;
- DBG(("%s(pipe=%d, event=%d)\n", __FUNCTION__, flip->pipe, flip->type));
+ DBG(("%s flip=%p (pipe=%d, event=%d)\n", __FUNCTION__, flip, flip->pipe, flip->type));
assert(flip->queued);
+ flip->queued = false;
if (sna->dri2.flip_pending == flip)
sna->dri2.flip_pending = NULL;
@@ -2834,9 +2852,12 @@ static void sna_dri2_flip_event(struct sna_dri2_event *flip)
break;
case FLIP_THROTTLE:
- DBG(("%s: triple buffer swap complete, unblocking client\n", __FUNCTION__));
- frame_swap_complete(flip, DRI2_FLIP_COMPLETE);
+ if (flip->signal) {
+ DBG(("%s: triple buffer swap complete, unblocking client\n", __FUNCTION__));
+ frame_swap_complete(flip, DRI2_FLIP_COMPLETE);
+ }
case FLIP_COMPLETE:
+ assert(!flip->signal);
if (sna->dri2.flip_pending) {
DBG(("%s: pending flip\n", __FUNCTION__));
sna_dri2_event_free(flip);
@@ -2845,10 +2866,8 @@ static void sna_dri2_flip_event(struct sna_dri2_event *flip)
DBG(("%s: flip chain complete\n", __FUNCTION__));
if (!sna_dri2_flip_keepalive(flip)) {
if (flip->chain) {
- sna_dri2_remove_event((WindowPtr)flip->draw,
- flip);
+ sna_dri2_remove_event(flip);
chain_swap(flip->chain);
- flip->draw = NULL;
}
sna_dri2_event_free(flip);
@@ -2857,7 +2876,7 @@ static void sna_dri2_flip_event(struct sna_dri2_event *flip)
DBG(("%s: no longer able to flip\n", __FUNCTION__));
if (flip->draw != NULL)
__sna_dri2_copy_event(flip, 0);
- if (flip->flip_continue == FLIP_COMPLETE) {
+ if (flip->signal) {
DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
frame_swap_complete(flip, DRI2_BLIT_COMPLETE);
}
@@ -2999,6 +3018,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
struct sna_dri2_event *info;
if (immediate) {
+ bool signal = false;
info = sna->dri2.flip_pending;
DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d, continuation? %d\n",
__FUNCTION__, sna_crtc_pipe(crtc),
@@ -3016,7 +3036,9 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
DBG(("%s: executing xchg of pending flip: flip_continue=%d, keepalive=%d, chain?=%d\n", __FUNCTION__, info->flip_continue, info->keepalive, current_msc < *target_msc));
sna_dri2_xchg(draw, front, back);
info->flip_continue = FLIP_COMPLETE;
- info->keepalive++;
+ info->keepalive = KEEPALIVE;
+ signal = info->signal;
+ info->signal = true;
if (xorg_can_triple_buffer() &&
current_msc < *target_msc) {
DBG(("%s: chaining flip\n", __FUNCTION__));
@@ -3033,6 +3055,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
assert(info->crtc == crtc);
info->event_complete = func;
info->event_data = data;
+ info->signal = true;
info->front = sna_dri2_reference_buffer(front);
info->back = sna_dri2_reference_buffer(back);
@@ -3046,7 +3069,6 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
__FUNCTION__));
info->type = FLIP;
sna->dri2.flip_pending = info;
- assert(!info->queued);
current_msc++;
} else if (sna->mode.flip_active) {
DBG(("%s: %d outstanding flips from old client, queueing\n",
@@ -3069,6 +3091,7 @@ new_back:
sna_dri2_get_back(sna, draw, back);
DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
frame_swap_complete(info, DRI2_EXCHANGE_COMPLETE);
+ info->signal = signal;
if (info->type == FLIP_ASYNC)
sna_dri2_event_free(info);
}
@@ -3091,6 +3114,7 @@ queue:
assert(info->crtc == crtc);
info->event_complete = func;
info->event_data = data;
+ info->signal = true;
info->type = FLIP;
info->front = sna_dri2_reference_buffer(front);
@@ -3111,11 +3135,13 @@ queue:
vbl.reply.sequence = draw_target_seq(draw, *target_msc - 1);
vbl.request.signal = (uintptr_t)info;
- info->queued = true;
+ assert(!info->queued);
if (sna_wait_vblank(sna, &vbl, info->pipe)) {
sna_dri2_event_free(info);
return false;
}
+
+ info->queued = true;
}
DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc));
@@ -3260,6 +3286,7 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
assert(info->crtc == crtc);
info->event_complete = func;
info->event_data = data;
+ info->signal = true;
info->front = sna_dri2_reference_buffer(front);
info->back = sna_dri2_reference_buffer(back);
@@ -3279,13 +3306,11 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
vbl.request.signal = (uintptr_t)info;
info->type = SWAP;
- info->queued = true;
vbl.request.sequence = draw_target_seq(draw, *target_msc - 1);
if (*target_msc <= current_msc + 1) {
DBG(("%s: performing blit before queueing\n", __FUNCTION__));
- assert(info->queued);
__sna_dri2_copy_event(info, DRI2_SYNC);
- info->type = SWAP_WAIT;
+ info->type = SWAP_COMPLETE;
vbl.request.type =
DRM_VBLANK_RELATIVE |
@@ -3294,12 +3319,13 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
*target_msc = current_msc + 1;
}
- assert(info->queued);
+ assert(!info->queued);
if (sna_wait_vblank(sna, &vbl, info->pipe))
goto blit;
+ info->queued = true;
DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc));
- swap_limit(draw, 1 + (info->type == SWAP_WAIT));
+ swap_limit(draw, 1 + (info->type == SWAP_COMPLETE));
return TRUE;
blit:
@@ -3315,16 +3341,21 @@ skip:
DBG(("%s: unable to show frame, unblocking client\n", __FUNCTION__));
if (crtc == NULL)
crtc = sna_primary_crtc(sna);
- if (crtc) {
- if (!info)
+ if (crtc && (sna->flags & SNA_NO_WAIT) == 0) {
+ if (info == NULL)
info = sna_dri2_add_event(sna, draw, client, crtc);
if (info != dri2_chain(draw))
goto fake;
- info->type = SWAP_WAIT;
+ assert(info->crtc == crtc);
+
+ info->type = SWAP_COMPLETE;
info->event_complete = func;
info->event_data = data;
- info->queued = true;
+ info->signal = true;
+
+ info->front = sna_dri2_reference_buffer(front);
+ info->back = sna_dri2_reference_buffer(back);
VG_CLEAR(vbl);
vbl.request.type =
@@ -3333,20 +3364,22 @@ skip:
vbl.request.signal = (uintptr_t)info;
vbl.request.sequence = 1;
+ assert(!info->queued);
if (sna_wait_vblank(sna, &vbl, info->pipe))
goto fake;
- *target_msc = current_msc + 1;
- swap_limit(draw, 1);
+ info->queued = true;
+ swap_limit(draw, 2);
} else {
fake:
/* XXX Use a Timer to throttle the client? */
fake_swap_complete(sna, client, draw, crtc, type, func, data);
- *target_msc = 0; /* offscreen, so zero out target vblank */
- if (info)
+ if (info) {
+ info->signal = false;
sna_dri2_event_free(info);
+ }
}
- DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc));
+ DBG(("%s: reported target_msc=%llu (in)\n", __FUNCTION__, *target_msc));
return TRUE;
}
@@ -3400,7 +3433,6 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc
CARD64 current_msc;
union drm_wait_vblank vbl;
const struct ust_msc *swap;
- int pipe;
crtc = sna_dri2_get_crtc(draw);
DBG(("%s(pipe=%d, target_msc=%llu, divisor=%llu, rem=%llu)\n",
@@ -3415,17 +3447,7 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc
if (crtc == NULL)
goto out_complete;
- pipe = sna_crtc_pipe(crtc);
-
- VG_CLEAR(vbl);
-
- /* Get current count */
- vbl.request.type = _DRM_VBLANK_RELATIVE;
- vbl.request.sequence = 0;
- if (sna_wait_vblank(sna, &vbl, pipe))
- goto out_complete;
-
- current_msc = draw_current_msc(draw, crtc, sna_crtc_record_vblank(crtc, &vbl));
+ current_msc = get_current_msc(sna, draw, crtc);
/* If target_msc already reached or passed, set it to
* current_msc to ensure we return a reasonable value back
@@ -3463,10 +3485,11 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc
}
vbl.request.sequence = draw_target_seq(draw, target_msc);
- info->queued = true;
- if (sna_wait_vblank(sna, &vbl, pipe))
+ assert(!info->queued);
+ if (sna_wait_vblank(sna, &vbl, info->pipe))
goto out_free_info;
+ info->queued = true;
DRI2BlockClient(client, draw);
return TRUE;