Ejemplo n.º 1
0
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, &reg, (&(BoxRec){ 0, 0, draw->width, draw->height }), 0);
	REGION_TRANSLATE(0, &reg, 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;
}