From b27ecf3059bc066ef59f2a71c1d8d8f0ffec7191 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 12 Nov 2012 14:06:06 +0000 Subject: [PATCH] sna/dri: Prevent scheduling a swap on stale buffers If the screen has been reconfigured and the DRI client tries to swap the old buffers (having not processed the invalidate event and retrieved the current names), quietly drop the request. If we try to obey the request, we will end up attaching a back buffer that is the wrong size to the scanout... Signed-off-by: Chris Wilson --- src/sna/sna_dri.c | 96 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 21 deletions(-) diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c index 23d9572a..17e3db17 100644 --- a/src/sna/sna_dri.c +++ b/src/sna/sna_dri.c @@ -98,9 +98,10 @@ struct sna_dri_frame_event { }; struct sna_dri_private { - int refcnt; PixmapPtr pixmap; struct kgem_bo *bo; + unsigned long serial; + int refcnt; }; static inline struct sna_dri_frame_event * @@ -259,6 +260,8 @@ sna_dri_create_buffer(DrawablePtr draw, assert(private->pixmap == pixmap); assert(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)); private->refcnt++; return buffer; @@ -353,6 +356,7 @@ sna_dri_create_buffer(DrawablePtr draw, private->refcnt = 1; private->bo = bo; private->pixmap = pixmap; + private->serial = get_drawable_pixmap(draw)->drawable.serialNumber; if (buffer->name == 0) goto err; @@ -1064,6 +1068,15 @@ can_flip(struct sna * sna, } assert(get_private(front)->pixmap == sna->front); + assert(get_private(front)->serial == pixmap->drawable.serialNumber); + + if (get_private(back)->serial != pixmap->drawable.serialNumber) { + DBG(("%s: no, DRI2 drawable has a stale reference to the pixmap (DRI2 pixmap=%ld, X pixmap=%ld)\n", + __FUNCTION__, + get_private(back)->serial, + pixmap->drawable.serialNumber)); + return false; + } DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d\n", __FUNCTION__, @@ -1154,9 +1167,34 @@ can_exchange(struct sna * sna, return false; } + assert(get_private(front)->serial == pixmap->drawable.serialNumber); + if (get_private(back)->serial != pixmap->drawable.serialNumber) { + DBG(("%s: no, DRI2 drawable has a stale reference to the pixmap (DRI2 pixmap=%ld, X pixmap=%ld)\n", + __FUNCTION__, + get_private(back)->serial, + pixmap->drawable.serialNumber)); + return false; + } + return true; } +static bool +can_blit(struct sna * sna, + DrawablePtr draw, + DRI2BufferPtr front, + DRI2BufferPtr back) +{ + PixmapPtr pixmap; + + if (draw->type == DRAWABLE_PIXMAP) + return true; + + pixmap = get_drawable_pixmap(draw); + return (get_private(front)->serial == pixmap->drawable.serialNumber && + get_private(back)->serial == pixmap->drawable.serialNumber); +} + inline static uint32_t pipe_select(int pipe) { /* The third pipe was introduced with IvyBridge long after @@ -1236,7 +1274,7 @@ static void chain_swap(struct sna *sna, DBG(("%s: performing chained exchange\n", __FUNCTION__)); sna_dri_exchange_buffers(draw, chain->front, chain->back); type = DRI2_EXCHANGE_COMPLETE; - } else { + } else if (can_blit(sna, draw, chain->front, chain->back)) { DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); chain->bo = sna_dri_copy_to_front(sna, draw, NULL, @@ -1245,6 +1283,12 @@ static void chain_swap(struct sna *sna, true); type = DRI2_BLIT_COMPLETE; + } else { + DRI2SwapComplete(chain->client, draw, + 0, 0, 0, DRI2_BLIT_COMPLETE, + chain->client ? chain->event_complete : NULL, chain->event_data); + sna_dri_frame_event_info_free(sna, draw, chain); + return; } DRI2SwapComplete(chain->client, draw, @@ -1309,10 +1353,11 @@ void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event) } /* else fall through to blit */ case DRI2_SWAP: - info->bo = sna_dri_copy_to_front(sna, draw, NULL, - get_private(info->front)->bo, - get_private(info->back)->bo, - true); + if (can_blit(sna, draw, info->front, info->back)) + info->bo = sna_dri_copy_to_front(sna, draw, NULL, + get_private(info->front)->bo, + get_private(info->back)->bo, + true); info->type = DRI2_SWAP_WAIT; /* fall through to SwapComplete */ case DRI2_SWAP_WAIT: @@ -1411,12 +1456,14 @@ static void chain_flip(struct sna *sna) } else sna->dri.flip_pending = chain; } else { - DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); + if (can_blit(sna, chain->draw, chain->front, chain->back)) { + DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); - chain->bo = sna_dri_copy_to_front(sna, chain->draw, NULL, - get_private(chain->front)->bo, - get_private(chain->back)->bo, - true); + chain->bo = sna_dri_copy_to_front(sna, chain->draw, NULL, + get_private(chain->front)->bo, + get_private(chain->back)->bo, + true); + } DRI2SwapComplete(chain->client, chain->draw, 0, 0, 0, DRI2_BLIT_COMPLETE, chain->client ? chain->event_complete : NULL, chain->event_data); sna_dri_frame_event_info_free(sna, chain->draw, chain); @@ -1494,10 +1541,12 @@ static void sna_dri_flip_event(struct sna *sna, DBG(("%s: no longer able to flip\n", __FUNCTION__)); if (flip->draw) { - flip->bo = sna_dri_copy_to_front(sna, flip->draw, NULL, - get_private(flip->front)->bo, - get_private(flip->back)->bo, - false); + if (can_blit(sna, flip->draw, flip->front, flip->back)) { + flip->bo = sna_dri_copy_to_front(sna, flip->draw, NULL, + get_private(flip->front)->bo, + get_private(flip->back)->bo, + false); + } DRI2SwapComplete(flip->client, flip->draw, 0, 0, 0, DRI2_BLIT_COMPLETE, @@ -1966,10 +2015,15 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, info->type = swap_type; if (divisor == 0) { - if (can_exchange(sna, draw, front, back)) + if (can_exchange(sna, draw, front, back)) { sna_dri_immediate_xchg(sna, draw, info); - else + } else if (can_blit(sna, draw, front, back)) { sna_dri_immediate_blit(sna, draw, info); + } else { + DRI2SwapComplete(client, draw, 0, 0, 0, + DRI2_BLIT_COMPLETE, func, data); + sna_dri_frame_event_info_free(sna, draw, info); + } return TRUE; } @@ -2044,17 +2098,17 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, return TRUE; blit_fallback: + pipe = DRI2_BLIT_COMPLETE; if (can_exchange(sna, draw, front, back)) { DBG(("%s -- xchg\n", __FUNCTION__)); sna_dri_exchange_buffers(draw, front, back); pipe = DRI2_EXCHANGE_COMPLETE; - } else { + } else if (can_blit(sna, draw, front, back)) { DBG(("%s -- blit\n", __FUNCTION__)); sna_dri_copy_to_front(sna, draw, NULL, get_private(front)->bo, get_private(back)->bo, false); - pipe = DRI2_BLIT_COMPLETE; } if (info) sna_dri_frame_event_info_free(sna, draw, info); @@ -2078,17 +2132,17 @@ sna_dri_async_swap(ClientPtr client, DrawablePtr draw, if (!can_flip(sna, draw, front, back)) { blit: + name = DRI2_BLIT_COMPLETE; if (can_exchange(sna, draw, front, back)) { DBG(("%s: unable to flip, so xchg\n", __FUNCTION__)); sna_dri_exchange_buffers(draw, front, back); name = DRI2_EXCHANGE_COMPLETE; - } else { + } else if (can_blit(sna, draw, front, back)) { DBG(("%s: unable to flip, so blit\n", __FUNCTION__)); sna_dri_copy_to_front(sna, draw, NULL, get_private(front)->bo, get_private(back)->bo, false); - name = DRI2_BLIT_COMPLETE; } DRI2SwapComplete(client, draw, 0, 0, 0, name, func, data);