/* * Queue an event to report back to the Present extension when the specified * MSC has past */ static int intel_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) { xf86CrtcPtr xf86_crtc = crtc->devPrivate; ScreenPtr screen = crtc->pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); intel_screen_private *intel = intel_get_screen_private(scrn); int pipe = intel_present_crtc_pipe(screen, crtc); struct intel_present_vblank_event *event; drmVBlank vbl; int ret; uint32_t seq; event = calloc(sizeof(struct intel_present_vblank_event), 1); if (!event) return BadAlloc; event->event_id = event_id; seq = intel_drm_queue_alloc(scrn, xf86_crtc, event, intel_present_vblank_handler, intel_present_vblank_abort); if (!seq) { free(event); return BadAlloc; } vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe); vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, xf86_crtc, msc); vbl.request.signal = seq; for (;;) { ret = drmWaitVBlank(intel->drmSubFD, &vbl); if (!ret) break; if (errno != EBUSY || !intel_present_flush_drm_events(screen)) return BadAlloc; } DebugPresent(("\t\tiq %lld seq %u msc %llu (hw msc %u)\n", (long long) event_id, seq, (long long) msc, vbl.request.sequence)); return Success; }
static int do_wait( drmVBlank * vbl, GLuint * vbl_seq, int fd ) { int ret; ret = drmWaitVBlank( fd, vbl ); if ( ret != 0 ) { static GLboolean first_time = GL_TRUE; if ( first_time ) { fprintf(stderr, "%s: drmWaitVBlank returned %d, IRQs don't seem to be" " working correctly.\nTry adjusting the vblank_mode" " configuration parameter.\n", __FUNCTION__, ret); first_time = GL_FALSE; } return -1; } *vbl_seq = vbl->reply.sequence; return 0; }
static int nouveau_present_ust_msc(RRCrtcPtr rrcrtc, uint64_t *ust, uint64_t *msc) { xf86CrtcPtr crtc = rrcrtc->devPrivate; NVPtr pNv = NVPTR(crtc->scrn); drmVBlank args; int ret; args.request.type = DRM_VBLANK_RELATIVE; args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT; args.request.sequence = 0, args.request.signal = 0, ret = drmWaitVBlank(pNv->dev->fd, &args); if (ret) { *ust = *msc = 0; return BadMatch; } *ust = (CARD64)args.reply.tval_sec * 1000000 + args.reply.tval_usec; *msc = args.reply.sequence; return Success; }
static DFBResult drmkmsPlaneUpdateFlipRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreSurface *surface, DFBSurfaceFlipFlags flags, const DFBRegion *left_update, CoreSurfaceBufferLock *left_lock, const DFBRegion *right_update, CoreSurfaceBufferLock *right_lock, bool flip ) { int ret; DRMKMSData *drmkms = driver_data; DRMKMSLayerData *data = layer_data; D_DEBUG_AT( DRMKMS_Layer, "%s()\n", __FUNCTION__ ); direct_mutex_lock( &data->lock ); while (data->flip_pending) { D_DEBUG_AT( DRMKMS_Layer, " -> waiting for pending flip (previous)\n" ); if (direct_waitqueue_wait_timeout( &data->wq_event, &data->lock, 30000 ) == DR_TIMEOUT) break; } dfb_surface_ref( surface ); data->surface = surface; data->surfacebuffer_index = left_lock->buffer->index; /* Task */ data->pending_task = left_lock->task; if (!data->muted) { ret = drmModeSetPlane(drmkms->fd, data->plane->plane_id, drmkms->encoder[0]->crtc_id, (u32)(long)left_lock->handle, /* plane_flags */ 0, data->config->dest.x, data->config->dest.y, data->config->dest.w, data->config->dest.h, data->config->source.x << 16, data->config->source.y <<16, data->config->source.w << 16, data->config->source.h << 16); if (ret) { D_PERROR( "DRMKMS/Layer/FlipRegion: Failed setting plane configuration!\n" ); direct_mutex_unlock( &data->lock ); return ret; } } if (flip) dfb_surface_flip( surface, false ); data->flip_pending = true; drmVBlank vbl; vbl.request.type = DRM_VBLANK_EVENT | DRM_VBLANK_RELATIVE; vbl.request.signal = (unsigned long)data; vbl.request.sequence = 1; drmWaitVBlank( drmkms->fd, &vbl ); if ((flags & DSFLIP_WAITFORSYNC) == DSFLIP_WAITFORSYNC) { while (data->flip_pending) { D_DEBUG_AT( DRMKMS_Layer, " -> waiting for pending flip (WAITFORSYNC)\n" ); if (direct_waitqueue_wait_timeout( &data->wq_event, &data->lock, 30000 ) == DR_TIMEOUT) break; } } direct_mutex_unlock( &data->lock ); return DFB_OK; }
int driWaitForMSC32( __DRIdrawablePrivate *priv, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t * msc ) { drmVBlank vbl; if ( divisor != 0 ) { unsigned int target = (unsigned int)target_msc; unsigned int next = target; unsigned int r; int dont_wait = (target_msc == 0); do { /* dont_wait means we're using the glXWaitVideoSyncSGI() behavior. * The first time around, just get the current count and proceed * to the test for (MSC % divisor) == remainder. */ vbl.request.type = dont_wait ? DRM_VBLANK_RELATIVE : DRM_VBLANK_ABSOLUTE; vbl.request.sequence = next ? msc_to_vblank(priv, next) : 0; if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) vbl.request.type |= DRM_VBLANK_SECONDARY; if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) { /* FIXME: This doesn't seem like the right thing to return here. */ return GLX_BAD_CONTEXT; } *msc = vblank_to_msc(priv, vbl.reply.sequence); dont_wait = 0; if (target_msc != 0 && *msc == target) break; /* Assuming the wait-done test fails, the next refresh to wait for * will be one that satisfies (MSC % divisor) == remainder. The * value (MSC - (MSC % divisor) + remainder) is the refresh value * closest to the current value that would satisfy the equation. * If this refresh has already happened, we add divisor to obtain * the next refresh after the current one that will satisfy it. */ r = (*msc % (unsigned int)divisor); next = (*msc - r + (unsigned int)remainder); if (next <= *msc) next += (unsigned int)divisor; } while ( r != (unsigned int)remainder ); } else { /* If the \c divisor is zero, just wait until the MSC is greater * than or equal to \c target_msc. */ vbl.request.type = DRM_VBLANK_ABSOLUTE; vbl.request.sequence = target_msc ? msc_to_vblank(priv, target_msc) : 0; if ( priv->vblFlags & VBLANK_FLAG_SECONDARY ) vbl.request.type |= DRM_VBLANK_SECONDARY; if ( drmWaitVBlank( priv->driScreenPriv->fd, &vbl ) != 0 ) { /* FIXME: This doesn't seem like the right thing to return here. */ return GLX_BAD_CONTEXT; } } *msc = vblank_to_msc(priv, vbl.reply.sequence); if ( *msc < target_msc ) { *msc += 0x0000000100000000LL; } return 0; }
/* * Flush the DRM event queue when full; makes space for new events. * * Returns a negative value on error, 0 if there was nothing to process, * or 1 if we handled any events. */ static int ms_flush_drm_events(ScreenPtr screen) { ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); struct pollfd p = { .fd = ms->fd, .events = POLLIN }; int r; do { r = poll(&p, 1, 0); } while (r == -1 && (errno == EINTR || errno == EAGAIN)); /* If there was an error, r will be < 0. Return that. If there was * nothing to process, r == 0. Return that. */ if (r <= 0) return r; /* Try to handle the event. If there was an error, return it. */ r = drmHandleEvent(ms->fd, &ms->event_context); if (r < 0) return r; /* Otherwise return 1 to indicate that we handled an event. */ return 1; } /* * Called when the queued vblank event has occurred */ static void ms_present_vblank_handler(uint64_t msc, uint64_t usec, void *data) { struct ms_present_vblank_event *event = data; DebugPresent(("\t\tmh %lld msc %llu\n", (long long) event->event_id, (long long) msc)); present_event_notify(event->event_id, usec, msc); free(event); } /* * Called when the queued vblank is aborted */ static void ms_present_vblank_abort(void *data) { struct ms_present_vblank_event *event = data; DebugPresent(("\t\tma %lld\n", (long long) event->event_id)); free(event); } /* * Queue an event to report back to the Present extension when the specified * MSC has past */ static int ms_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) { xf86CrtcPtr xf86_crtc = crtc->devPrivate; ScreenPtr screen = crtc->pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; struct ms_present_vblank_event *event; drmVBlank vbl; int ret; uint32_t seq; event = calloc(sizeof(struct ms_present_vblank_event), 1); if (!event) return BadAlloc; event->event_id = event_id; seq = ms_drm_queue_alloc(xf86_crtc, event, ms_present_vblank_handler, ms_present_vblank_abort); if (!seq) { free(event); return BadAlloc; } vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe; vbl.request.sequence = ms_crtc_msc_to_kernel_msc(xf86_crtc, msc); vbl.request.signal = seq; for (;;) { ret = drmWaitVBlank(ms->fd, &vbl); if (!ret) break; /* If we hit EBUSY, then try to flush events. If we can't, then * this is an error. */ if (errno != EBUSY || ms_flush_drm_events(screen) < 0) { ms_drm_abort_seq(scrn, seq); return BadAlloc; } } DebugPresent(("\t\tmq %lld seq %u msc %llu (hw msc %u)\n", (long long) event_id, seq, (long long) msc, vbl.request.sequence)); return Success; }
int main(int argc, char **argv) { const char *device = NULL, *module = NULL; int c, fd, ret; drmVBlank vbl; drmEventContext evctx; struct vbl_info handler_info; opterr = 0; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'D': device = optarg; break; case 'M': module = optarg; break; case 's': secondary = 1; break; default: usage(argv[0]); break; } } fd = util_open(module, device); if (fd < 0) return 1; /* Get current count first */ vbl.request.type = DRM_VBLANK_RELATIVE; if (secondary) vbl.request.type |= DRM_VBLANK_SECONDARY; vbl.request.sequence = 0; ret = drmWaitVBlank(fd, &vbl); if (ret != 0) { printf("drmWaitVBlank (relative) failed ret: %i\n", ret); return -1; } printf("starting count: %d\n", vbl.request.sequence); handler_info.vbl_count = 0; gettimeofday(&handler_info.start, NULL); /* Queue an event for frame + 1 */ vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT; if (secondary) vbl.request.type |= DRM_VBLANK_SECONDARY; vbl.request.sequence = 1; vbl.request.signal = (unsigned long)&handler_info; ret = drmWaitVBlank(fd, &vbl); if (ret != 0) { printf("drmWaitVBlank (relative, event) failed ret: %i\n", ret); return -1; } /* Set up our event handler */ memset(&evctx, 0, sizeof evctx); evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.vblank_handler = vblank_handler; evctx.page_flip_handler = NULL; /* Poll for events */ while (1) { struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; fd_set fds; FD_ZERO(&fds); FD_SET(0, &fds); FD_SET(fd, &fds); ret = select(fd + 1, &fds, NULL, NULL, &timeout); if (ret <= 0) { fprintf(stderr, "select timed out or error (ret %d)\n", ret); continue; } else if (FD_ISSET(0, &fds)) { break; } ret = drmHandleEvent(fd, &evctx); if (ret != 0) { printf("drmHandleEvent failed: %i\n", ret); return -1; } } return 0; }
int main(int argc, char **argv) { unsigned i; int c, fd, ret; const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "exynos", "omapdrm", "tilcdc", "msm", "tegra", "imx-drm", "rockchip", "mediatek" }; drmVBlank vbl; drmEventContext evctx; struct vbl_info handler_info; opterr = 0; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 's': secondary = 1; break; default: usage(argv[0]); break; } } for (i = 0; i < ARRAY_SIZE(modules); i++) { printf("trying to load module %s...", modules[i]); fd = drmOpen(modules[i], NULL); if (fd < 0) { printf("failed.\n"); } else { printf("success.\n"); break; } } if (i == ARRAY_SIZE(modules)) { fprintf(stderr, "failed to load any modules, aborting.\n"); return -1; } /* Get current count first */ vbl.request.type = DRM_VBLANK_RELATIVE; if (secondary) vbl.request.type |= DRM_VBLANK_SECONDARY; vbl.request.sequence = 0; ret = drmWaitVBlank(fd, &vbl); if (ret != 0) { printf("drmWaitVBlank (relative) failed ret: %i\n", ret); return -1; } printf("starting count: %d\n", vbl.request.sequence); handler_info.vbl_count = 0; gettimeofday(&handler_info.start, NULL); /* Queue an event for frame + 1 */ vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT; if (secondary) vbl.request.type |= DRM_VBLANK_SECONDARY; vbl.request.sequence = 1; vbl.request.signal = (unsigned long)&handler_info; ret = drmWaitVBlank(fd, &vbl); if (ret != 0) { printf("drmWaitVBlank (relative, event) failed ret: %i\n", ret); return -1; } /* Set up our event handler */ memset(&evctx, 0, sizeof evctx); evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.vblank_handler = vblank_handler; evctx.page_flip_handler = NULL; /* Poll for events */ while (1) { struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 }; fd_set fds; int ret; FD_ZERO(&fds); FD_SET(0, &fds); FD_SET(fd, &fds); ret = select(fd + 1, &fds, NULL, NULL, &timeout); if (ret <= 0) { fprintf(stderr, "select timed out or error (ret %d)\n", ret); continue; } else if (FD_ISSET(0, &fds)) { break; } ret = drmHandleEvent(fd, &evctx); if (ret != 0) { printf("drmHandleEvent failed: %i\n", ret); return -1; } } return 0; }
/* * Correct a drawablePrivate's set of vblank flags WRT the current context. * When considering multiple crtcs. */ GLuint intelFixupVblank(struct intel_context *intel, __DRIdrawablePrivate *dPriv) { if (!intel->intelScreen->driScrnPriv->dri2.enabled && intel->intelScreen->driScrnPriv->ddx_version.minor >= 7) { volatile drm_i915_sarea_t *sarea = intel->sarea; drm_clip_rect_t drw_rect = { .x1 = dPriv->x, .x2 = dPriv->x + dPriv->w, .y1 = dPriv->y, .y2 = dPriv->y + dPriv->h }; drm_clip_rect_t planeA_rect = { .x1 = sarea->planeA_x, .y1 = sarea->planeA_y, .x2 = sarea->planeA_x + sarea->planeA_w, .y2 = sarea->planeA_y + sarea->planeA_h }; drm_clip_rect_t planeB_rect = { .x1 = sarea->planeB_x, .y1 = sarea->planeB_y, .x2 = sarea->planeB_x + sarea->planeB_w, .y2 = sarea->planeB_y + sarea->planeB_h }; GLint areaA = driIntersectArea( drw_rect, planeA_rect ); GLint areaB = driIntersectArea( drw_rect, planeB_rect ); GLuint flags = dPriv->vblFlags; /* Update vblank info */ if (areaB > areaA || (areaA == areaB && areaB > 0)) { flags = dPriv->vblFlags | VBLANK_FLAG_SECONDARY; } else { flags = dPriv->vblFlags & ~VBLANK_FLAG_SECONDARY; } /* Do the stupid test: Is one of them actually disabled? */ if (sarea->planeA_w == 0 || sarea->planeA_h == 0) { flags = dPriv->vblFlags | VBLANK_FLAG_SECONDARY; } else if (sarea->planeB_w == 0 || sarea->planeB_h == 0) { flags = dPriv->vblFlags & ~VBLANK_FLAG_SECONDARY; } return flags; } else { return dPriv->vblFlags & ~VBLANK_FLAG_SECONDARY; } } /** * Called from driSwapBuffers() */ void intelSwapBuffers(__DRIdrawablePrivate * dPriv) { __DRIscreenPrivate *psp = dPriv->driScreenPriv; if (dPriv->driContextPriv && dPriv->driContextPriv->driverPrivate) { GET_CURRENT_CONTEXT(ctx); struct intel_context *intel; if (ctx == NULL) return; intel = intel_context(ctx); if (ctx->Visual.doubleBufferMode) { GLboolean missed_target; struct intel_framebuffer *intel_fb = dPriv->driverPrivate; int64_t ust; _mesa_notifySwapBuffers(ctx); /* flush pending rendering comands */ /* * The old swapping ioctl was incredibly racy, just wait for vblank * and do the swap ourselves. */ driWaitForVBlank(dPriv, &missed_target); /* * Update each buffer's vbl_pending so we don't get too out of * sync */ intel_get_renderbuffer(&intel_fb->Base, BUFFER_BACK_LEFT)->vbl_pending = dPriv->vblSeq; intel_get_renderbuffer(&intel_fb->Base, BUFFER_FRONT_LEFT)->vbl_pending = dPriv->vblSeq; intelCopyBuffer(dPriv, NULL); intel_fb->swap_count++; (*psp->systemTime->getUST) (&ust); if (missed_target) { intel_fb->swap_missed_count++; intel_fb->swap_missed_ust = ust - intel_fb->swap_ust; } intel_fb->swap_ust = ust; } drmCommandNone(intel->driFd, DRM_I915_GEM_THROTTLE); } else { /* XXX this shouldn't be an error but we can't handle it for now */ fprintf(stderr, "%s: drawable has no context!\n", __FUNCTION__); } } /** * Called from driCopySubBuffer() */ void intelCopySubBuffer(__DRIdrawablePrivate * dPriv, int x, int y, int w, int h) { if (dPriv->driContextPriv && dPriv->driContextPriv->driverPrivate) { struct intel_context *intel = (struct intel_context *) dPriv->driContextPriv->driverPrivate; GLcontext *ctx = &intel->ctx; if (ctx->Visual.doubleBufferMode) { drm_clip_rect_t rect; rect.x1 = x + dPriv->x; rect.y1 = (dPriv->h - y - h) + dPriv->y; rect.x2 = rect.x1 + w; rect.y2 = rect.y1 + h; _mesa_notifySwapBuffers(ctx); /* flush pending rendering comands */ intelCopyBuffer(dPriv, &rect); } } else { /* XXX this shouldn't be an error but we can't handle it for now */ fprintf(stderr, "%s: drawable has no context!\n", __FUNCTION__); } } /** * This will be called whenever the currently bound window is moved/resized. * XXX: actually, it seems to NOT be called when the window is only moved (BP). */ void intelWindowMoved(struct intel_context *intel) { GLcontext *ctx = &intel->ctx; __DRIdrawablePrivate *dPriv = intel->driDrawable; struct intel_framebuffer *intel_fb = dPriv->driverPrivate; if (!intel->intelScreen->driScrnPriv->dri2.enabled && intel->intelScreen->driScrnPriv->ddx_version.minor >= 7) { GLuint flags = intelFixupVblank(intel, dPriv); /* Check to see if we changed pipes */ if (flags != dPriv->vblFlags && dPriv->vblFlags && !(dPriv->vblFlags & VBLANK_FLAG_NO_IRQ)) { int64_t count; drmVBlank vbl; int i; /* * Deal with page flipping */ vbl.request.type = DRM_VBLANK_ABSOLUTE; if ( dPriv->vblFlags & VBLANK_FLAG_SECONDARY ) { vbl.request.type |= DRM_VBLANK_SECONDARY; } for (i = 0; i < 2; i++) { if (!intel_fb->color_rb[i] || (intel_fb->vbl_waited - intel_fb->color_rb[i]->vbl_pending) <= (1<<23)) continue; vbl.request.sequence = intel_fb->color_rb[i]->vbl_pending; drmWaitVBlank(intel->driFd, &vbl); } /* * Update msc_base from old pipe */ driDrawableGetMSC32(dPriv->driScreenPriv, dPriv, &count); dPriv->msc_base = count; /* * Then get new vblank_base and vblSeq values */ dPriv->vblFlags = flags; driGetCurrentVBlank(dPriv); dPriv->vblank_base = dPriv->vblSeq; intel_fb->vbl_waited = dPriv->vblSeq; for (i = 0; i < 2; i++) { if (intel_fb->color_rb[i]) intel_fb->color_rb[i]->vbl_pending = intel_fb->vbl_waited; } } } else { dPriv->vblFlags &= ~VBLANK_FLAG_SECONDARY; } /* Update Mesa's notion of window size */ driUpdateFramebufferSize(ctx, dPriv); intel_fb->Base.Initialized = GL_TRUE; /* XXX remove someday */ /* Update hardware scissor */ if (ctx->Driver.Scissor != NULL) { ctx->Driver.Scissor(ctx, ctx->Scissor.X, ctx->Scissor.Y, ctx->Scissor.Width, ctx->Scissor.Height); } /* Re-calculate viewport related state */ if (ctx->Driver.DepthRange != NULL) ctx->Driver.DepthRange( ctx, ctx->Viewport.Near, ctx->Viewport.Far ); }
static gboolean gst_kms_sink_sync (GstKMSSink * self) { gint ret; gboolean waiting; drmEventContext evctxt = { .version = DRM_EVENT_CONTEXT_VERSION, .page_flip_handler = sync_handler, .vblank_handler = sync_handler, }; drmVBlank vbl = { .request = { .type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT, .sequence = 1, .signal = (gulong) & waiting, }, }; if (self->pipe == 1) vbl.request.type |= DRM_VBLANK_SECONDARY; else if (self->pipe > 1) vbl.request.type |= self->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT; waiting = TRUE; if (!self->has_async_page_flip) { ret = drmWaitVBlank (self->fd, &vbl); if (ret) goto vblank_failed; } else { ret = drmModePageFlip (self->fd, self->crtc_id, self->buffer_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting); if (ret) goto pageflip_failed; } while (waiting) { do { ret = gst_poll_wait (self->poll, 3 * GST_SECOND); } while (ret == -1 && (errno == EAGAIN || errno == EINTR)); ret = drmHandleEvent (self->fd, &evctxt); if (ret) goto event_failed; } return TRUE; /* ERRORS */ vblank_failed: { GST_WARNING_OBJECT (self, "drmWaitVBlank failed: %s (%d)", strerror (-ret), ret); return FALSE; } pageflip_failed: { GST_WARNING_OBJECT (self, "drmModePageFlip failed: %s (%d)", strerror (-ret), ret); return FALSE; } event_failed: { GST_ERROR_OBJECT (self, "drmHandleEvent failed: %s (%d)", strerror (-ret), ret); return FALSE; } }
static int vivante_dri2_ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc, CARD64 divisor, CARD64 remainder) { struct vivante *vivante = vivante_get_screen_priv(draw->pScreen); struct vivante_dri_wait *wait; drmVBlank vbl; int ret, crtc; CARD64 cur_msc; target_msc &= 0xffffffff; divisor &= 0xffffffff; remainder &= 0xffffffff; crtc = vivante_dri2_drawable_crtc(draw); /* Drawable not displayed, just complete */ if (crtc < 0) goto out; wait = new_wait_info(client, draw, DRI2_WAITMSC); if (!wait) goto out; /* Get current count */ ret = vivante_dri2_waitvblank(vivante, &vbl, crtc, __FUNCTION__); if (ret) goto out_free; cur_msc = vbl.reply.sequence; /* * If the divisor is zero, or cur_msc is smaller than target_msc, we * just need to make sure target_msc passes before waking up the client. */ if (divisor == 0 || cur_msc < target_msc) { if (cur_msc >= target_msc) target_msc = cur_msc; vbl.request.sequence = target_msc; } else { /* * If we get here, target_msc has already passed or we * don't have one, so queue an event that will satisfy * the divisor/remainder equation. */ vbl.request.sequence = cur_msc - (cur_msc % divisor) + remainder; /* * If calculated remainder is larger than requested * remainder, it means we've passed the point where * seq % divisor == remainder, so we need to wait for * the next time that will happen. */ if ((cur_msc & divisor) >= remainder) vbl.request.sequence += divisor; } vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drm_req_crtc(crtc); vbl.request.signal = (unsigned long)wait; ret = drmWaitVBlank(vivante->drm_fd, &vbl); if (ret) { xf86DrvMsg(vivante->scrnIndex, X_WARNING, "%s: get vblank counter failed: %s\n", __FUNCTION__, strerror(errno)); goto out_free; } wait->frame = vbl.reply.sequence; DRI2BlockClient(client, draw); return TRUE; out_free: del_wait_info(wait); out: DRI2WaitMSCComplete(client, draw, target_msc, 0, 0); return TRUE; }
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; }
int main(int argc, char *argv[]) { struct opt_data opts; optproc(argc, argv, &opts); if(audio_init(&opts) < 0) exit(1); if(opts.w < 0 && opts.h < 0) opts.w = opts.h = 512; else if(opts.w < 0) opts.w = opts.h; else if(opts.h < 0) opts.h = opts.w; int ret; ret = init_drm(&opts, 512); if (ret) { printf("failed to initialize DRM\n"); return ret; } ret = init_gbm(); if (ret) { printf("failed to initialize GBM\n"); return ret; } ret = init_egl(); if (ret) { printf("failed to initialize EGL\n"); return ret; } // call init_gl //init_gl(&opts, drm.mode->hdisplay, drm.mode->vdisplay); init_gl(&opts, opts.w, opts.h); //TODO: better dealing with our great big screen eglSwapBuffers(gl.display, gl.surface); struct gbm_bo *bo = gbm_surface_lock_front_buffer(gbm.surface); fb = drm_fb_get_from_bo(bo); /* set mode: */ ret = drmModeSetCrtc(drm.fd, drm.crtc_id, fb->fb_id, 0, 0, &drm.connector_id, 1, drm.mode); if (ret) { printf("failed to set mode: %s\n", strerror(errno)); return ret; } // turn off line buffering on input and echoing of characters struct termios term; tcgetattr(STDIN_FILENO, &save_term); tcgetattr(STDIN_FILENO, &term); term.c_lflag &= ~(ICANON|ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &term); atexit(shutdown_cleanup); drmVBlank vbl; /* Get current count first hopefully also sync up with blank too*/ vbl.request.type = DRM_VBLANK_RELATIVE; vbl.request.sequence = 1; ret = drmWaitVBlank(drm.fd, &vbl); if (ret != 0) { printf("drmWaitVBlank (relative, event) failed ret: %i\n", ret); return -1; } printf("starting msc: %d\n", vbl.request.sequence); /* Queue an event for frame + 1 */ vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT; vbl.request.sequence = 1; vbl.request.signal = NULL; ret = drmWaitVBlank(drm.fd, &vbl); if (ret != 0) { printf("drmWaitVBlank (relative, event) failed ret: %i\n", ret); return -1; } struct pollfd pfds[] = { {drm.fd, POLLIN | POLLPRI, 0 }, {STDIN_FILENO, POLLIN, 0 }, }; int debug_maxsrc = 0, debug_pal = 0, show_mandel = 0, show_fps_hist = 0; bool done = false; while (!done) { render_frame(debug_maxsrc, debug_pal, show_mandel, show_fps_hist); while (waiting_for_flip) { // TODO: input handling ret = poll(pfds, 2, -1); if (ret < 0) { printf("poll err: %s\n", strerror(errno)); return ret; } else if (ret == 0) { printf("poll timeout!\n"); done = true; break; } if (pfds[1].revents) { char buf[128]; int cnt = read(STDIN_FILENO, buf, sizeof(buf)); if(buf[0] == 27) done = true; else if(buf[0] == '1') debug_maxsrc = !debug_maxsrc; else if(buf[0] == '2') debug_pal = !debug_pal; else if(buf[0] == '3') show_mandel = !show_mandel; else if(buf[0] == '4') show_fps_hist = !show_fps_hist; //continue; } if(pfds[0].revents) drmHandleEvent(drm.fd, &evctx); } /* release last buffer to render on again: */ gbm_surface_release_buffer(gbm.surface, bo); bo = next_bo; } audio_shutdown(); return ret; }
void CVideoSyncDRM::Run(volatile bool& stop) { drmVBlank vbl; VblInfo info; int ret; int crtc = g_Windowing.GetCrtc(); vbl.request.type = DRM_VBLANK_RELATIVE; if (crtc == 1) { vbl.request.type = (drmVBlankSeqType)(vbl.request.type | DRM_VBLANK_SECONDARY); } else if (crtc > 1) { vbl.request.type = (drmVBlankSeqType)(vbl.request.type | ((crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) & DRM_VBLANK_HIGH_CRTC_MASK)); } vbl.request.sequence = 0; ret = drmWaitVBlank(m_fd, &vbl); if (ret != 0) { CLog::Log(LOGERROR, "CVideoSyncDRM::%s - drmWaitVBlank returned error", __FUNCTION__); return; } info.start = CurrentHostCounter(); info.videoSync = this; vbl.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT); if (crtc == 1) { vbl.request.type = (drmVBlankSeqType)(vbl.request.type | DRM_VBLANK_SECONDARY); } else if (crtc > 1) { vbl.request.type = (drmVBlankSeqType)(vbl.request.type | ((crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) & DRM_VBLANK_HIGH_CRTC_MASK)); } vbl.request.sequence = 1; vbl.request.signal = (unsigned long)&info; ret = drmWaitVBlank(m_fd, &vbl); if (ret != 0) { CLog::Log(LOGERROR, "CVideoSyncDRM::%s - drmWaitVBlank returned error", __FUNCTION__); return; } drmEventContext evctx; memset(&evctx, 0, sizeof evctx); evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.vblank_handler = EventHandler; evctx.page_flip_handler = NULL; timeval timeout; fd_set fds; FD_ZERO(&fds); FD_SET(m_fd, &fds); while (!stop && !m_abort) { timeout.tv_sec = 1; timeout.tv_usec = 0; ret = select(m_fd + 1, &fds, NULL, NULL, &timeout); if (ret <= 0) { continue; } ret = drmHandleEvent(m_fd, &evctx); if (ret != 0) { CLog::Log(LOGERROR, "CVideoSyncDRM::%s - drmHandleEvent returned error", __FUNCTION__); break; } } }
static int nouveau_wait_vblank(DrawablePtr draw, int type, CARD64 msc, CARD64 *pmsc, CARD64 *pust, void *data) { ScrnInfoPtr scrn = xf86ScreenToScrn(draw->pScreen); NVPtr pNv = NVPTR(scrn); xf86CrtcPtr crtc; drmVBlank vbl; struct dri2_vblank *event = NULL; void *token = NULL; int ret; int head; /* Select crtc which shows the largest part of the drawable */ crtc = nouveau_pick_best_crtc(scrn, FALSE, draw->x, draw->y, draw->width, draw->height); if (!crtc) { xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Wait for VBlank failed: No valid crtc for drawable.\n"); return -EINVAL; } if (type & DRM_VBLANK_EVENT) { event = drmmode_event_queue(scrn, ++dri2_sequence, sizeof(*event), nouveau_dri2_vblank_handler, &token); if (!event) return -ENOMEM; event->s = data; } /* Map xf86CrtcPtr to drmWaitVBlank compatible display head index. */ head = drmmode_head(crtc); if (head == 1) type |= DRM_VBLANK_SECONDARY; else if (head > 1) #ifdef DRM_VBLANK_HIGH_CRTC_SHIFT type |= (head << DRM_VBLANK_HIGH_CRTC_SHIFT) & DRM_VBLANK_HIGH_CRTC_MASK; #else xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Wait for VBlank failed: Called for CRTC %d > 1, but " "DRM_VBLANK_HIGH_CRTC_SHIFT not defined at build time.\n", head); #endif vbl.request.type = type; vbl.request.sequence = msc; vbl.request.signal = (unsigned long)token; ret = drmWaitVBlank(pNv->dev->fd, &vbl); if (ret) { xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Wait for VBlank failed: %s\n", strerror(errno)); if (event) drmmode_event_abort(scrn, dri2_sequence--, false); return ret; } if (pmsc) *pmsc = vbl.reply.sequence; if (pust) *pust = (CARD64)vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec; return 0; }