static void nouveau_dri2_finish_swap(DrawablePtr draw, unsigned int frame, unsigned int tv_sec, unsigned int tv_usec, struct nouveau_dri2_vblank_state *s) { ScrnInfoPtr scrn = xf86ScreenToScrn(draw->pScreen); NVPtr pNv = NVPTR(scrn); PixmapPtr dst_pix; PixmapPtr src_pix = nouveau_dri2_buffer(s->src)->ppix; struct nouveau_bo *dst_bo; struct nouveau_bo *src_bo = nouveau_pixmap_bo(src_pix); struct nouveau_pushbuf *push = pNv->pushbuf; RegionRec reg; int type, ret; Bool front_updated, will_exchange; xf86CrtcPtr ref_crtc; REGION_INIT(0, ®, (&(BoxRec){ 0, 0, draw->width, draw->height }), 0); REGION_TRANSLATE(0, ®, draw->x, draw->y); /* Main crtc for this drawable shall finally deliver pageflip event. */ ref_crtc = nouveau_pick_best_crtc(scrn, FALSE, draw->x, draw->y, draw->width, draw->height); /* Update frontbuffer pixmap and name: Could have changed due to * window (un)redirection as part of compositing. */ front_updated = update_front(draw, s->dst); /* Assign frontbuffer pixmap, after update in update_front() */ dst_pix = nouveau_dri2_buffer(s->dst)->ppix; dst_bo = nouveau_pixmap_bo(dst_pix); /* Throttle on the previous frame before swapping */ nouveau_bo_wait(dst_bo, NOUVEAU_BO_RD, push->client); /* Swap by buffer exchange possible? */ will_exchange = front_updated && can_exchange(draw, dst_pix, src_pix); /* Only emit a wait for vblank pushbuf here if this is a copy-swap, or * if it is a kms pageflip-swap on an old kernel. Pure exchange swaps * don't need sync to vblank. kms pageflip-swaps on Linux 3.13+ are * synced to vblank in the kms driver, so we must not sync here, or * framerate will be cut in half! */ if (can_sync_to_vblank(draw) && (!will_exchange || (!pNv->has_async_pageflip && nouveau_exa_pixmap_is_onscreen(dst_pix)))) { /* Reference the back buffer to sync it to vblank */ nouveau_pushbuf_refn(push, &(struct nouveau_pushbuf_refn) { src_bo, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD }, 1);
void vivante_dri2_vblank(int fd, unsigned frame, unsigned tv_sec, unsigned tv_usec, void *event) { struct vivante_dri_wait *wait = event; DrawablePtr draw; if (!wait->drawable_id) goto out; if (dixLookupDrawable(&draw, wait->drawable_id, serverClient, M_ANY, DixWriteAccess) != Success) goto out; switch (wait->type) { case DRI2_FLIP: if (can_exchange(draw, wait->front, wait->back) && vivante_dri2_ScheduleFlip(draw, wait)) return; /* FALLTHROUGH */ case DRI2_SWAP: vivante_dri2_blit(wait->client, draw, wait->front, wait->back, frame, tv_sec, tv_usec, wait->client ? wait->event_func : NULL, wait->event_data); break; case DRI2_WAITMSC: if (wait->client) DRI2WaitMSCComplete(wait->client, draw, frame, tv_sec, tv_usec); break; default: xf86DrvMsg(xf86ScreenToScrn(draw->pScreen)->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __FUNCTION__); break; } out: del_wait_info(wait); }
static int vivante_dri2_ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back, CARD64 *target_msc, CARD64 divisor, CARD64 remainder, DRI2SwapEventPtr func, void *data) { struct vivante *vivante = vivante_get_screen_priv(draw->pScreen); struct vivante_dri_wait *wait; drmVBlank vbl; CARD64 cur_msc; int ret, crtc; crtc = vivante_dri2_drawable_crtc(draw); /* Drawable not displayed... just complete */ if (crtc < 0) goto blit; *target_msc &= 0xffffffff; divisor &= 0xffffffff; remainder &= 0xffffffff; wait = new_wait_info(client, draw, DRI2_SWAP); if (!wait) goto blit; wait->crtc = crtc; wait->event_func = func; wait->event_data = data; wait->front = front; wait->back = back; vivante_dri2_buffer_reference(front); vivante_dri2_buffer_reference(back); ret = vivante_dri2_waitvblank(vivante, &vbl, crtc, __FUNCTION__); if (ret) goto blit_free; cur_msc = vbl.reply.sequence; /* Flips need to be submitted one frame before */ if (can_exchange(draw, front, back)) { wait->type = DRI2_FLIP; if (*target_msc > 0) *target_msc -= 1; } if (divisor == 0 || cur_msc < *target_msc) { if (wait->type == DRI2_FLIP && vivante_dri2_ScheduleFlip(draw, wait)) return TRUE; /* * If target_msc has been reached or passed, set it to cur_msc * to ensure we return a reasonable value back to the caller. * This makes the swap_interval logic more robust. */ if (cur_msc >= *target_msc) *target_msc = cur_msc; vbl.request.sequence = *target_msc; } else { vbl.request.sequence = cur_msc - (cur_msc % divisor) + remainder; /* * If the calculated deadline sequence is smaller than or equal * to cur_msc, it means we've passed the point when effective * onset frame seq could satisfy seq % divisor == remainder, * so we need to wait for the next time this will happen. * * This comparison takes the 1 frame swap delay in pageflipping * mode into account, as well as a potential * DRM_VBLANK_NEXTONMISS delay if we are blitting/exchanging * instead of flipping. */ if (vbl.request.sequence <= cur_msc) vbl.request.sequence += divisor; /* Account for 1 frame extra pageflip delay if flip > 0 */ if (wait->type == DRI2_FLIP) vbl.request.sequence -= 1; } vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drm_req_crtc(crtc); if (wait->type != DRI2_FLIP) vbl.request.type |= DRM_VBLANK_NEXTONMISS; vbl.request.signal = (unsigned long)wait; ret = drmWaitVBlank(vivante->drm_fd, &vbl); if (ret) { xf86DrvMsg(vivante->scrnIndex, X_WARNING, "get vblank counter failed: %s\n", strerror(errno)); goto blit_free; } *target_msc = vbl.reply.sequence + (wait->type == DRI2_FLIP); wait->frame = *target_msc; return TRUE; blit_free: del_wait_info(wait); blit: vivante_dri2_blit(client, draw, front, back, 0, 0, 0, func, data); *target_msc = 0; return TRUE; }