void NativeStateDRM::flip() { gbm_bo* next = gbm_surface_lock_front_buffer(surface_); fb_ = fb_get_from_bo(next); unsigned int waiting(1); if (!crtc_set_) { int status = drmModeSetCrtc(fd_, encoder_->crtc_id, fb_->fb_id, 0, 0, &connector_->connector_id, 1, mode_); if (status >= 0) { crtc_set_ = true; bo_ = next; } else { Log::error("Failed to set crtc: %d\n", status); } return; } int status = drmModePageFlip(fd_, encoder_->crtc_id, fb_->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting); if (status < 0) { Log::error("Failed to enqueue page flip: %d\n", status); return; } fd_set fds; FD_ZERO(&fds); FD_SET(fd_, &fds); drmEventContext evCtx; evCtx.version = DRM_EVENT_CONTEXT_VERSION; evCtx.page_flip_handler = page_flip_handler; while (waiting) { status = select(fd_ + 1, &fds, 0, 0, 0); if (status < 0) { // Most of the time, select() will return an error because the // user pressed Ctrl-C. So, only print out a message in debug // mode, and just check for the likely condition and release // the current buffer object before getting out. Log::debug("Error in select\n"); if (should_quit()) { gbm_surface_release_buffer(surface_, bo_); bo_ = next; } return; } drmHandleEvent(fd_, &evCtx); } gbm_surface_release_buffer(surface_, bo_); bo_ = next; }
static bool gfx_ctx_drm_wait_flip(bool block) { int timeout = 0; if (!waiting_for_flip) return false; if (block) timeout = -1; while (waiting_for_flip) { if (!drm_wait_flip(timeout)) break; } if (waiting_for_flip) return true; /* Page flip has taken place. */ /* This buffer is not on-screen anymore. Release it to GBM. */ gbm_surface_release_buffer(g_gbm_surface, g_bo); /* This buffer is being shown now. */ g_bo = g_next_bo; return false; }
static void wait_flip(bool block) { struct pollfd fds = {0}; fds.fd = g_drm_fd; fds.events = POLLIN; drmEventContext evctx = {0}; evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.page_flip_handler = page_flip_handler; int timeout = block ? -1 : 0; while (waiting_for_flip) { fds.revents = 0; if (poll(&fds, 1, timeout) < 0) break; if (fds.revents & (POLLHUP | POLLERR)) break; if (fds.revents & POLLIN) drmHandleEvent(g_drm_fd, &evctx); else break; } if (!waiting_for_flip) // Page flip has taken place. { gbm_surface_release_buffer(g_gbm_surface, g_bo); // This buffer is not on-screen anymore. Release it to GBM. g_bo = g_next_bo; // This buffer is being shown now. } }
void QEglFSKmsGbmScreen::flip() { if (!m_gbm_surface) { qWarning("Cannot sync before platform init!"); return; } m_gbm_bo_next = gbm_surface_lock_front_buffer(m_gbm_surface); if (!m_gbm_bo_next) { qWarning("Could not lock GBM surface front buffer!"); return; } FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next); QKmsOutput &op(output()); const int fd = device()->fd(); const uint32_t w = op.modes[op.mode].hdisplay; const uint32_t h = op.modes[op.mode].vdisplay; if (!op.mode_set) { int ret = drmModeSetCrtc(fd, op.crtc_id, fb->fb, 0, 0, &op.connector_id, 1, &op.modes[op.mode]); if (ret == -1) { qErrnoWarning(errno, "Could not set DRM mode!"); } else { op.mode_set = true; setPowerState(PowerStateOn); if (!op.plane_set) { op.plane_set = true; if (op.wants_plane) { int ret = drmModeSetPlane(fd, op.plane_id, op.crtc_id, uint32_t(-1), 0, 0, 0, w, h, 0 << 16, 0 << 16, w << 16, h << 16); if (ret == -1) qErrnoWarning(errno, "drmModeSetPlane failed"); } } } } int ret = drmModePageFlip(fd, op.crtc_id, fb->fb, DRM_MODE_PAGE_FLIP_EVENT, this); if (ret) { qErrnoWarning("Could not queue DRM page flip!"); gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); m_gbm_bo_next = Q_NULLPTR; } }
void QKmsScreen::handlePageFlipped() { if (m_current_bo) gbm_surface_release_buffer(m_gbmSurface, m_current_bo); m_current_bo = m_next_bo; m_next_bo = 0; }
void QEglFSKmsGbmScreen::flipFinished() { if (m_gbm_bo_current) gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_current); m_gbm_bo_current = m_gbm_bo_next; m_gbm_bo_next = Q_NULLPTR; }
void RenderingBackendGBM::Surface::releaseBuffer(uint32_t handle) { auto it = m_lockedBuffers.find(handle); assert(it != m_lockedBuffers.end()); struct gbm_bo* bo = it->second; if (bo) gbm_surface_release_buffer(m_surface, bo); }
static void free_current_bo(struct gbm_context *context) { if (context->current_fb_id) { drmModeRmFB(context->dev->fd, context->current_fb_id); context->current_fb_id = 0; } if (context->current_bo) { gbm_surface_release_buffer(context->gbm_surface, context->current_bo); context->current_bo = NULL; } }
static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { if (current_fb_id) drmModeRmFB(fd, current_fb_id); current_fb_id = next_fb_id; next_fb_id = 0; if (current_bo) gbm_surface_release_buffer(gs, current_bo); current_bo = next_bo; next_bo = NULL; }
void QKmsScreen::performPageFlip() { if (!m_next_bo) return; uint32_t width = gbm_bo_get_width(m_next_bo); uint32_t height = gbm_bo_get_height(m_next_bo); uint32_t stride = gbm_bo_get_stride(m_next_bo); uint32_t handle = gbm_bo_get_handle(m_next_bo).u32; uint32_t fb_id; int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, stride, handle, &fb_id); if (ret) { qFatal("kms: Failed to create fb: fd %d, w %d, h %d, stride %d, handle %d, ret %d", m_device->fd(), width, height, stride, handle, ret); } if (!m_modeSet) { //Set the Mode of the screen. int ret = drmModeSetCrtc(m_device->fd(), m_crtcId, fb_id, 0, 0, &m_connectorId, 1, &m_mode); if (ret) qFatal("failed to set mode"); m_modeSet = true; // Initialize cursor static int hideCursor = qgetenv("QT_QPA_KMS_HIDECURSOR").toInt(); if (!hideCursor) { QCursor cursor(Qt::ArrowCursor); m_cursor->changeCursor(&cursor, 0); } } int pageFlipStatus = drmModePageFlip(m_device->fd(), m_crtcId, fb_id, DRM_MODE_PAGE_FLIP_EVENT, this); if (pageFlipStatus) { qWarning("Pageflip status: %d", pageFlipStatus); gbm_surface_release_buffer(m_gbmSurface, m_next_bo); m_next_bo = 0; } }
static void wait_flip(bool block) { int timeout = 0; struct pollfd fds = {0}; drmEventContext evctx = {0}; driver_t *driver = driver_get_ptr(); gfx_ctx_drm_egl_data_t *drm = (gfx_ctx_drm_egl_data_t*) driver->video_context_data; fds.fd = drm->g_drm_fd; fds.events = POLLIN; evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.page_flip_handler = page_flip_handler; if (block) timeout = -1; while (waiting_for_flip) { fds.revents = 0; if (poll(&fds, 1, timeout) < 0) break; if (fds.revents & (POLLHUP | POLLERR)) break; if (fds.revents & POLLIN) drmHandleEvent(drm->g_drm_fd, &evctx); else break; } if (waiting_for_flip) return; /* Page flip has taken place. */ /* This buffer is not on-screen anymore. Release it to GBM. */ gbm_surface_release_buffer(drm->g_gbm_surface, drm->g_bo); /* This buffer is being shown now. */ drm->g_bo = drm->g_next_bo; }
bool CDRMLegacy::WaitingForFlip() { if(!flip_happening) { return false; } m_drm_fds.fd = m_drm->fd; m_drm_fds.events = POLLIN; m_drm_evctx.version = DRM_EVENT_CONTEXT_VERSION; m_drm_evctx.page_flip_handler = PageFlipHandler; m_drm_fds.revents = 0; while(flip_happening) { auto ret = poll(&m_drm_fds, 1, -1); if(ret < 0) { return true; } if(m_drm_fds.revents & (POLLHUP | POLLERR)) { return true; } if(m_drm_fds.revents & POLLIN) { drmHandleEvent(m_drm->fd, &m_drm_evctx); } } gbm_surface_release_buffer(m_gbm->surface, m_bo); m_bo = m_next_bo; return false; }
int DRM_PageFlip(void){ int ret; struct gbm_bo *next_bo; int waiting_for_flip = 1; next_bo = gbm_surface_lock_front_buffer(gbm.surface); fb = drm_fb_get_from_bo(next_bo); /* * Here you could also update drm plane layers if you want * hw composition */ ret = drmModePageFlip(drm.fd, drm.crtc_id, fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip); if (ret) { printf("failed to queue page flip: %s\n", strerror(errno)); return -1; } while (waiting_for_flip) { //Aquí realmente no nos interesa leer el teclado. Sólo //hacemos el select para esperar por drm.fd FD_ZERO (&fds); FD_SET (0, &fds); FD_SET (drm.fd, &fds); ret = select(drm.fd+1, &fds, NULL, NULL, NULL); drmHandleEvent(drm.fd, &evctx); } /* release last buffer to render on again: */ gbm_surface_release_buffer(gbm.surface, bo); bo = next_bo; return 0; }
bool CDRMLegacy::SetVideoMode(RESOLUTION_INFO res) { gbm_surface_release_buffer(m_gbm->surface, m_bo); m_bo = gbm_surface_lock_front_buffer(m_gbm->surface); m_drm_fb = CDRMUtils::DrmFbGetFromBo(m_bo); auto ret = drmModeSetCrtc(m_drm->fd, m_drm->crtc_id, m_drm_fb->fb_id, 0, 0, &m_drm->connector_id, 1, m_drm->mode); if(ret < 0) { CLog::Log(LOGERROR, "CDRMUtils::%s - failed to set crtc mode: %dx%d%s @ %d Hz", __FUNCTION__, m_drm->mode->hdisplay, m_drm->mode->vdisplay, m_drm->mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_drm->mode->vrefresh); return false; } CLog::Log(LOGDEBUG, "CDRMUtils::%s - set crtc mode: %dx%d%s @ %d Hz", __FUNCTION__, m_drm->mode->hdisplay, m_drm->mode->vdisplay, m_drm->mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_drm->mode->vrefresh); return true; }
static void free_current_bo (CoglOnscreen *onscreen) { CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglRendererKMS *kms_renderer = egl_renderer->platform; if (kms_onscreen->current_fb_id) { drmModeRmFB (kms_renderer->fd, kms_onscreen->current_fb_id); kms_onscreen->current_fb_id = 0; } if (kms_onscreen->current_bo) { gbm_surface_release_buffer (kms_onscreen->surface, kms_onscreen->current_bo); kms_onscreen->current_bo = NULL; } }
static void _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen) { CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglDisplayEGL *egl_display = context->display->winsys; CoglDisplayKMS *kms_display = egl_display->platform; CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglRendererKMS *kms_renderer = egl_renderer->platform; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform; uint32_t handle, stride; CoglFlipKMS *flip; GList *l; /* If we already have a pending swap then block until it completes */ while (kms_onscreen->next_fb_id != 0) handle_drm_event (kms_renderer); /* First chain-up. This will call eglSwapBuffers */ parent_vtable->onscreen_swap_buffers (onscreen); /* Now we need to set the CRTC to whatever is the front buffer */ kms_onscreen->next_bo = gbm_surface_lock_front_buffer (kms_onscreen->surface); #if (COGL_VERSION_ENCODE (COGL_GBM_MAJOR, COGL_GBM_MINOR, COGL_GBM_MICRO) >= \ COGL_VERSION_ENCODE (8, 1, 0)) stride = gbm_bo_get_stride (kms_onscreen->next_bo); #else stride = gbm_bo_get_pitch (kms_onscreen->next_bo); #endif handle = gbm_bo_get_handle (kms_onscreen->next_bo).u32; if (drmModeAddFB (kms_renderer->fd, kms_display->width, kms_display->height, 24, /* depth */ 32, /* bpp */ stride, handle, &kms_onscreen->next_fb_id)) { g_warning ("Failed to create new back buffer handle: %m"); gbm_surface_release_buffer (kms_onscreen->surface, kms_onscreen->next_bo); kms_onscreen->next_bo = NULL; kms_onscreen->next_fb_id = 0; return; } /* If this is the first framebuffer to be presented then we now setup the * crtc modes... */ if (kms_display->pending_set_crtc) { setup_crtc_modes (context->display, kms_onscreen->next_fb_id); kms_display->pending_set_crtc = FALSE; } flip = g_slice_new0 (CoglFlipKMS); flip->onscreen = onscreen; for (l = kms_display->outputs; l; l = l->next) { CoglOutputKMS *output = l->data; if (drmModePageFlip (kms_renderer->fd, output->encoder->crtc_id, kms_onscreen->next_fb_id, DRM_MODE_PAGE_FLIP_EVENT, flip)) { g_warning ("Failed to flip: %m"); continue; } flip->pending++; } if (flip->pending == 0) { drmModeRmFB (kms_renderer->fd, kms_onscreen->next_fb_id); gbm_surface_release_buffer (kms_onscreen->surface, kms_onscreen->next_bo); kms_onscreen->next_bo = NULL; kms_onscreen->next_fb_id = 0; g_slice_free (CoglFlipKMS, flip); flip = NULL; } else { /* Ensure the onscreen remains valid while it has any pending flips... */ cogl_object_ref (flip->onscreen); } }
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; }
static int atomic_run(const struct gbm *gbm, const struct egl *egl) { struct gbm_bo *bo = NULL; struct drm_fb *fb; uint32_t i = 0; uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK; struct timeval timeout; fd_set fds; int ret; if (egl_check(egl, eglDupNativeFenceFDANDROID) || egl_check(egl, eglCreateSyncKHR) || egl_check(egl, eglDestroySyncKHR) || egl_check(egl, eglWaitSyncKHR) || egl_check(egl, eglClientWaitSyncKHR)) return -1; /* Allow a modeset change for the first commit only. */ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; while (1) { struct gbm_bo *next_bo; EGLSyncKHR gpu_fence = NULL; /* out-fence from gpu, in-fence to kms */ EGLSyncKHR kms_fence = NULL; /* in-fence to gpu, out-fence from kms */ if (drm.kms_out_fence_fd != -1) { kms_fence = create_fence(egl, drm.kms_out_fence_fd); assert(kms_fence); /* driver now has ownership of the fence fd: */ drm.kms_out_fence_fd = -1; /* wait "on the gpu" (ie. this won't necessarily block, but * will block the rendering until fence is signaled), until * the previous pageflip completes so we don't render into * the buffer that is still on screen. */ egl->eglWaitSyncKHR(egl->display, kms_fence, 0); } egl->draw(i++); /* insert fence to be singled in cmdstream.. this fence will be * signaled when gpu rendering done */ gpu_fence = create_fence(egl, EGL_NO_NATIVE_FENCE_FD_ANDROID); assert(gpu_fence); eglSwapBuffers(egl->display, egl->surface); /* after swapbuffers, gpu_fence should be flushed, so safe * to get fd: */ drm.kms_in_fence_fd = egl->eglDupNativeFenceFDANDROID(egl->display, gpu_fence); egl->eglDestroySyncKHR(egl->display, gpu_fence); assert(drm.kms_in_fence_fd != -1); next_bo = gbm_surface_lock_front_buffer(gbm->surface); if (!next_bo) { printf("Failed to lock frontbuffer\n"); return -1; } fb = drm_fb_get_from_bo(next_bo); if (!fb) { printf("Failed to get a new framebuffer BO\n"); return -1; } if (kms_fence) { EGLint status; /* Wait on the CPU side for the _previous_ commit to * complete before we post the flip through KMS, as * atomic will reject the commit if we post a new one * whilst the previous one is still pending. */ do { status = egl->eglClientWaitSyncKHR(egl->display, kms_fence, 0, EGL_FOREVER_KHR); } while (status != EGL_CONDITION_SATISFIED_KHR); egl->eglDestroySyncKHR(egl->display, kms_fence); } /* * Here you could also update drm plane layers if you want * hw composition */ ret = drm_atomic_commit(fb->fb_id, flags); if (ret) { printf("failed to commit: %s\n", strerror(errno)); return -1; } /* release last buffer to render on again: */ if (bo) gbm_surface_release_buffer(gbm->surface, bo); bo = next_bo; /* Allow a modeset change for the first commit only. */ flags &= ~(DRM_MODE_ATOMIC_ALLOW_MODESET); /* watch for user interruption */ FD_ZERO(&fds); FD_SET(0, &fds); memset(&timeout, 0, sizeof(timeout)); ret = select(1, &fds, NULL, NULL, &timeout); if (ret < 0) { printf("select() failed: %s\n", strerror(errno)); break; } /* * select() will immediately timeout if there was no user * interrupt because of the 0 timeout. However, that's an * expected situation, not an error, so we just ignore it * here. */ if (FD_ISSET(0, &fds)) { printf("user interrupted!\n"); break; } } return ret; }
int init_egl_drm() { fd_set fds; drmEventContext evctx = { .version = DRM_EVENT_CONTEXT_VERSION, .vblank_handler = 0, .page_flip_handler = page_flip_handler, }; struct gbm_bo *bo; struct drm_fb *fb; ret = init_drm(); if (ret) { printf("failed to initialize DRM\n"); return ret; } printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n", drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay); FD_ZERO(&fds); FD_SET(drm.fd, &fds); ret = init_gbm(); if (ret) { printf("failed to initialize GBM\n"); return ret; } //Initialise egl regularly here } void egl_drm_draw_flip() { /* set mode: */ if (all_display) { for (i=0; i<drm.ndisp; i++) { ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0, &drm.connector_id[i], 1, drm.mode[i]); if (ret) { printf("display %d failed to set mode: %s\n", i, strerror(errno)); return ret; } } } else { ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id, 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]); if (ret) { printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno)); return ret; } } //Draw call here //Swapping is involved next_bo = gbm_surface_lock_front_buffer(gbm.surface); fb = drm_fb_get_from_bo(next_bo); /* * Here you could also update drm plane layers if you want * hw composition */ ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip); if (ret) { printf("failed to queue page flip: %s\n", strerror(errno)); return -1; } while (waiting_for_flip) { ret = select(drm.fd + 1, &fds, NULL, NULL, NULL); if (ret < 0) { printf("select err: %s\n", strerror(errno)); return ret; } else if (ret == 0) { printf("select timeout!\n"); return -1; } else if (FD_ISSET(0, &fds)) { continue; } drmHandleEvent(drm.fd, &evctx); } /* release last buffer to render on again: */ gbm_surface_release_buffer(gbm.surface, bo); bo = next_bo; }