static int drm_egl_init(struct MPGLContext *ctx, int flags) { struct priv *p = ctx->priv; p->kms = NULL; p->old_crtc = NULL; p->gbm.surface = NULL; p->gbm.device = NULL; p->active = false; p->waiting_for_flip = false; p->ev.version = DRM_EVENT_CONTEXT_VERSION; p->ev.page_flip_handler = page_flipped; p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log); if (p->vt_switcher_active) { vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx); vt_switcher_release(&p->vt_switcher, release_vt, ctx); } else { MP_WARN(ctx->vo, "Failed to set up VT switcher. Terminal switching will be unavailable.\n"); } MP_VERBOSE(ctx->vo, "Initializing KMS\n"); p->kms = kms_create(ctx->vo->log); if (!p->kms) { MP_ERR(ctx->vo, "Failed to create KMS.\n"); return -1; } // TODO: arguments should be configurable if (!kms_setup(p->kms, "/dev/dri/card0", -1, 0)) { MP_ERR(ctx->vo, "Failed to configure KMS.\n"); return -1; } if (!init_gbm(ctx)) { MP_ERR(ctx->vo, "Failed to setup GBM.\n"); return -1; } if (!init_egl(ctx, flags & VOFLAG_GLES)) { MP_ERR(ctx->vo, "Failed to setup EGL.\n"); return -1; } if (!eglMakeCurrent(p->egl.display, p->egl.surface, p->egl.surface, p->egl.context)) { MP_ERR(ctx->vo, "Failed to make context current.\n"); return -1; } const char *egl_exts = eglQueryString(p->egl.display, EGL_EXTENSIONS); void *(*gpa)(const GLubyte*) = (void *(*)(const GLubyte*))eglGetProcAddress; mpgl_load_functions(ctx->gl, gpa, egl_exts, ctx->vo->log); // required by gbm_surface_lock_front_buffer eglSwapBuffers(p->egl.display, p->egl.surface); MP_VERBOSE(ctx->vo, "Preparing framebuffer\n"); p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface); if (!p->gbm.bo) { MP_ERR(ctx->vo, "Failed to lock GBM surface.\n"); return -1; } update_framebuffer_from_bo(ctx, p->gbm.bo); if (!p->fb.id) { MP_ERR(ctx->vo, "Failed to create framebuffer.\n"); return -1; } if (!crtc_setup(ctx)) { MP_ERR( ctx->vo, "Failed to set CRTC for connector %u: %s\n", p->kms->connector->connector_id, mp_strerror(errno)); return -1; } return 0; }
RenderingBackend::BufferExport RenderingBackendGBM::Surface::lockFrontBuffer() { struct gbm_bo* bo = gbm_surface_lock_front_buffer(m_surface); assert(bo); uint32_t handle = gbm_bo_get_handle(bo).u32; #ifndef NDEBUG auto result = #endif m_lockedBuffers.insert({ handle, bo }); assert(result.second); auto* data = static_cast<BufferDataGBM*>(gbm_bo_get_user_data(bo)); if (data) { std::pair<uint32_t, uint32_t> storedSize{ data->width, data->height }; if (m_size == storedSize) return RenderingBackend::BufferExport{ -1, reinterpret_cast<const uint8_t*>(data), sizeof(BufferDataGBM) }; delete data; } data = new BufferDataGBM{ handle, m_size.first, m_size.second, gbm_bo_get_stride(bo), gbm_bo_get_format(bo), BufferDataGBM::magicValue }; gbm_bo_set_user_data(bo, data, &destroyBOData); return RenderingBackend::BufferExport{ gbm_bo_get_fd(bo), reinterpret_cast<const uint8_t*>(data), sizeof(BufferDataGBM) }; }
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::swapBuffers() { eglSwapBuffers(m_device->eglDisplay(), m_eglWindowSurface); m_next_bo = gbm_surface_lock_front_buffer(m_gbmSurface); if (!m_next_bo) qFatal("kms: Failed to lock front buffer"); performPageFlip(); }
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_queue_flip(void) { struct drm_fb *fb = NULL; g_next_bo = gbm_surface_lock_front_buffer(g_gbm_surface); fb = (struct drm_fb*)drm_fb_get_from_bo(g_next_bo); if (drmModePageFlip(g_drm_fd, g_crtc_id, fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip) == 0) return true; /* Failed to queue page flip. */ return false; }
static void swap(struct gbm_winsys *winsys) { struct gbm_dev *dev = winsys->dev; struct gbm_context *context = winsys->context; struct gbm_bo *bo; uint32_t handle, stride; uint32_t width, height; uint32_t fb_id; eglSwapBuffers(context->edpy, context->egl_surface); bo = gbm_surface_lock_front_buffer(context->gbm_surface); width = gbm_bo_get_width(bo); height = gbm_bo_get_height(bo); stride = gbm_bo_get_stride(bo); handle = gbm_bo_get_handle(bo).u32; if (drmModeAddFB(dev->fd, width, height, 24, /* depth */ 32, /* bpp */ stride, handle, &fb_id)) { fprintf(stderr, "Failed to create new back buffer handle: %m\n"); } else { if (dev->saved_crtc == NULL && set_initial_crtc(dev, fb_id)) return; if (drmModePageFlip(dev->fd, dev->crtc, fb_id, DRM_MODE_PAGE_FLIP_EVENT, dev)) { fprintf(stderr, "Failed to page flip: %m\n"); return; } dev->pending_swap = 1; wait_swap(dev); free_current_bo(context); context->current_bo = bo; context->current_fb_id = fb_id; } }
void swap_buffers(void) { eglSwapBuffers(gl.display, gl.surface); next_bo = gbm_surface_lock_front_buffer(gbm.surface); fb = drm_fb_get_from_bo(next_bo); waiting_for_flip = 1; int 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)); exit(-1); } }
static void queue_flip(void) { g_next_bo = gbm_surface_lock_front_buffer(g_gbm_surface); struct drm_fb *fb = drm_fb_get_from_bo(g_next_bo); int ret = drmModePageFlip(g_drm_fd, g_crtc_id, fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip); if (ret < 0) { RARCH_ERR("[KMS/EGL]: Failed to queue page flip.\n"); return; } waiting_for_flip = true; }
static void drm_egl_swap_buffers(MPGLContext *ctx) { struct priv *p = ctx->priv; eglSwapBuffers(p->egl.display, p->egl.surface); p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface); p->waiting_for_flip = true; update_framebuffer_from_bo(ctx, p->gbm.next_bo); int ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb.id, DRM_MODE_PAGE_FLIP_EVENT, p); if (ret) { MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno)); } // poll page flip finish event const int timeout_ms = 3000; struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
bool CDRMLegacy::QueueFlip() { m_next_bo = gbm_surface_lock_front_buffer(m_gbm->surface); m_drm_fb = CDRMUtils::DrmFbGetFromBo(m_next_bo); auto ret = drmModePageFlip(m_drm->fd, m_drm->crtc_id, m_drm_fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &flip_happening); if(ret) { CLog::Log(LOGDEBUG, "CDRMLegacy::%s - failed to queue DRM page flip", __FUNCTION__); return false; } return true; }
static void queue_flip(void) { g_next_bo = gbm_surface_lock_front_buffer(g_gbm_surface); struct drm_fb *fb = drm_fb_get_from_bo(g_next_bo); int ret = drmModePageFlip(g_drm_fd, g_crtc_id, fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip); if (ret < 0) { RARCH_ERR("[KMS/EGL]: Failed to queue page flip.\n"); return; } struct timeval tv; gettimeofday(&tv, NULL); flip_request_usec = (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec; waiting_for_flip = true; }
static void queue_flip(void) { int ret; struct drm_fb *fb = NULL; gfx_ctx_drm_egl_data_t *drm = (gfx_ctx_drm_egl_data_t*) driver.video_context_data; drm->g_next_bo = gbm_surface_lock_front_buffer(drm->g_gbm_surface); fb = (struct drm_fb*)drm_fb_get_from_bo(drm, drm->g_next_bo); ret = drmModePageFlip(drm->g_drm_fd, drm->g_crtc_id, fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip); if (ret < 0) { RARCH_ERR("[KMS/EGL]: Failed to queue page flip.\n"); return; } waiting_for_flip = true; }
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 bool gfx_ctx_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { if (g_inited) return false; int i; int ret = 0; struct drm_fb *fb = NULL; struct sigaction sa = {{0}}; sa.sa_handler = sighandler; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); #define EGL_ATTRIBS_BASE \ EGL_SURFACE_TYPE, EGL_WINDOW_BIT, \ EGL_RED_SIZE, 1, \ EGL_GREEN_SIZE, 1, \ EGL_BLUE_SIZE, 1, \ EGL_ALPHA_SIZE, 0, \ EGL_DEPTH_SIZE, 0 static const EGLint egl_attribs_gl[] = { EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE, }; static const EGLint egl_attribs_gles[] = { EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE, }; #ifdef EGL_KHR_create_context static const EGLint egl_attribs_gles3[] = { EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, EGL_NONE, }; #endif static const EGLint egl_attribs_vg[] = { EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE, }; const EGLint *attrib_ptr; switch (g_api) { case GFX_CTX_OPENGL_API: attrib_ptr = egl_attribs_gl; break; case GFX_CTX_OPENGL_ES_API: #ifdef EGL_KHR_create_context if (g_major >= 3) attrib_ptr = egl_attribs_gles3; else #endif attrib_ptr = egl_attribs_gles; break; case GFX_CTX_OPENVG_API: attrib_ptr = egl_attribs_vg; break; default: attrib_ptr = NULL; } // Find desired video mode, and use that. // If not fullscreen, we get desired windowed size, which is not appropriate. if ((width == 0 && height == 0) || !fullscreen) g_drm_mode = &g_connector->modes[0]; else { // Try to match g_settings.video.refresh_rate as closely as possible. // Lower resolutions tend to have multiple supported refresh rates as well. float minimum_fps_diff = 0.0f; // Find best match. for (i = 0; i < g_connector->count_modes; i++) { if (width != g_connector->modes[i].hdisplay || height != g_connector->modes[i].vdisplay) continue; if (!g_drm_mode) { g_drm_mode = &g_connector->modes[i]; minimum_fps_diff = g_drm_mode->vrefresh - g_settings.video.refresh_rate; } else { float diff = g_connector->modes[i].vrefresh - g_settings.video.refresh_rate; if (diff < minimum_fps_diff) { g_drm_mode = &g_connector->modes[i]; minimum_fps_diff = diff; } } } } if (!g_drm_mode) { RARCH_ERR("[KMS/EGL]: Did not find suitable video mode for %u x %u.\n", width, height); goto error; } g_fb_width = g_drm_mode->hdisplay; g_fb_height = g_drm_mode->vdisplay; // Create GBM surface. g_gbm_surface = gbm_surface_create(g_gbm_dev, g_fb_width, g_fb_height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!g_gbm_surface) { RARCH_ERR("[KMS/EGL]: Couldn't create GBM surface.\n"); goto error; } g_egl_dpy = eglGetDisplay((EGLNativeDisplayType)g_gbm_dev); if (!g_egl_dpy) { RARCH_ERR("[KMS/EGL]: Couldn't get EGL display.\n"); goto error; } EGLint major, minor; if (!eglInitialize(g_egl_dpy, &major, &minor)) goto error; EGLint n; if (!eglChooseConfig(g_egl_dpy, attrib_ptr, &g_config, 1, &n) || n != 1) goto error; EGLint egl_attribs[16]; EGLint *attr = egl_attribs; attr = egl_fill_attribs(attr); g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, attr != egl_attribs ? egl_attribs : NULL); if (g_egl_ctx == EGL_NO_CONTEXT) goto error; if (g_use_hw_ctx) { g_egl_hw_ctx = eglCreateContext(g_egl_dpy, g_config, g_egl_ctx, attr != egl_attribs ? egl_attribs : NULL); RARCH_LOG("[KMS/EGL]: Created shared context: %p.\n", (void*)g_egl_hw_ctx); if (g_egl_hw_ctx == EGL_NO_CONTEXT) goto error; } g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, (EGLNativeWindowType)g_gbm_surface, NULL); if (!g_egl_surf) goto error; if (!eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx)) goto error; glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(g_egl_dpy, g_egl_surf); g_bo = gbm_surface_lock_front_buffer(g_gbm_surface); fb = drm_fb_get_from_bo(g_bo); ret = drmModeSetCrtc(g_drm_fd, g_crtc_id, fb->fb_id, 0, 0, &g_connector_id, 1, g_drm_mode); if (ret < 0) goto error; g_inited = true; return true; error: gfx_ctx_destroy(data); return false; }
int init_egl(void) { EGLint major, minor, n; static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; static const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; gl.display = eglGetDisplay(gbm.dev); if (!eglInitialize(gl.display, &major, &minor)) { printf("failed to initialize\n"); return -1; } printf("Using display %p with EGL version %d.%d\n", gl.display, major, minor); printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION)); printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR)); printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS)); if (!eglBindAPI(EGL_OPENGL_ES_API)) { printf("failed to bind api EGL_OPENGL_ES_API\n"); return -1; } if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) { printf("failed to choose config: %d\n", n); return -1; } gl.context = eglCreateContext(gl.display, gl.config, EGL_NO_CONTEXT, context_attribs); if (gl.context == NULL) { printf("failed to create context\n"); return -1; } gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL); if (gl.surface == EGL_NO_SURFACE) { printf("failed to create egl surface\n"); return -1; } /* connect the context to the surface */ eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context); //MAC Sacar esto a otra función int ret; eglSwapBuffers(gl.display, gl.surface); 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; } //MAC Fin bloque return 0; }
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 drm_egl_init(struct MPGLContext *ctx, int flags) { if (ctx->vo->probing) { MP_VERBOSE(ctx->vo, "DRM EGL backend can be activated only manually.\n"); return -1; } struct priv *p = ctx->priv; p->kms = NULL; p->old_crtc = NULL; p->gbm.surface = NULL; p->gbm.device = NULL; p->active = false; p->waiting_for_flip = false; p->ev.version = DRM_EVENT_CONTEXT_VERSION; p->ev.page_flip_handler = page_flipped; p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log); if (p->vt_switcher_active) { vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx); vt_switcher_release(&p->vt_switcher, release_vt, ctx); } else { MP_WARN(ctx->vo, "Failed to set up VT switcher. Terminal switching will be unavailable.\n"); } MP_VERBOSE(ctx->vo, "Initializing KMS\n"); p->kms = kms_create(ctx->vo->log, ctx->vo->opts->drm_connector_spec, ctx->vo->opts->drm_mode_id); if (!p->kms) { MP_ERR(ctx->vo, "Failed to create KMS.\n"); return -1; } if (!init_gbm(ctx)) { MP_ERR(ctx->vo, "Failed to setup GBM.\n"); return -1; } if (!init_egl(ctx, flags)) { MP_ERR(ctx->vo, "Failed to setup EGL.\n"); return -1; } if (!eglMakeCurrent(p->egl.display, p->egl.surface, p->egl.surface, p->egl.context)) { MP_ERR(ctx->vo, "Failed to make context current.\n"); return -1; } const char *egl_exts = eglQueryString(p->egl.display, EGL_EXTENSIONS); void *(*gpa)(const GLubyte*) = (void *(*)(const GLubyte*))eglGetProcAddress; mpgl_load_functions(ctx->gl, gpa, egl_exts, ctx->vo->log); ctx->native_display_type = "drm"; ctx->native_display = (void *)(intptr_t)p->kms->fd; // required by gbm_surface_lock_front_buffer eglSwapBuffers(p->egl.display, p->egl.surface); MP_VERBOSE(ctx->vo, "Preparing framebuffer\n"); p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface); if (!p->gbm.bo) { MP_ERR(ctx->vo, "Failed to lock GBM surface.\n"); return -1; } update_framebuffer_from_bo(ctx, p->gbm.bo); if (!p->fb.id) { MP_ERR(ctx->vo, "Failed to create framebuffer.\n"); return -1; } if (!crtc_setup(ctx)) { MP_ERR(ctx->vo, "Failed to set CRTC for connector %u: %s\n", p->kms->connector->connector_id, mp_strerror(errno)); return -1; } return 0; }
/*!*********************************************************************** @Function OsRenderComplete @Returns false when the app should quit @description Main message loop / render loop *************************************************************************/ void PVRShellInit::OsRenderComplete() { int ckb = 0; size_t bytes_read; int ret, i; struct gbm_bo *bo; struct SDrmFbWrapper *fb; int waiting_for_flip = 1; fd_set fds; drmEventContext evctx; evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.page_flip_handler = pfnCallbackDrmPageFlip; bo = gbm_surface_lock_front_buffer(m_psGbmSurface); fb = DrmFbGetFromBo(bo); if(!m_ui32CurrentFb) { ret = drmModeSetCrtc(m_i32DrmFile, m_ui32DrmCrtcId, fb->ui32FbId, 0, 0, &m_ui32DrmConnectorId, 1, m_psDrmMode); if (ret) { m_pShell->PVRShellOutputDebug("display failed to set mode: %s\n", strerror(errno)); return; } } else { ret = drmModePageFlip(m_i32DrmFile, m_ui32DrmCrtcId, fb->ui32FbId, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip); if (ret) { m_pShell->PVRShellOutputDebug("display failed to flip page: %s\n", strerror(errno)); return; } FD_ZERO(&fds); FD_SET(m_i32DrmFile, &fds); while (waiting_for_flip) { ret = select(m_i32DrmFile + 1, &fds, NULL, NULL, NULL); if (ret < 0) { m_pShell->PVRShellOutputDebug("Select Error: %s\n", strerror(errno)); return; } else if (ret == 0) { m_pShell->PVRShellOutputDebug("Select Timeout\n"); return; } else if (FD_ISSET(0, &fds)) { continue; } drmHandleEvent(m_i32DrmFile, &evctx); } } // Check keyboard and keypad // Keyboard. if(devfd > 0) { while ((bytes_read = read(devfd, &ckb, 1)) == 1); switch(ckb) { case '0': case 'q': case 'Q': nLastKeyPressed = PVRShellKeyNameQUIT; break; case 13: nLastKeyPressed = PVRShellKeyNameSELECT; break; case ' ': case '1': nLastKeyPressed = PVRShellKeyNameACTION1; break; case '2': nLastKeyPressed = PVRShellKeyNameACTION2; break; case 65: // Up Arrow nLastKeyPressed = m_eKeyMapUP; break; case 66: // Down Arrow nLastKeyPressed = m_eKeyMapDOWN; break; case 68: // Left Arrow nLastKeyPressed = m_eKeyMapLEFT; break; case 67: // Right Arrow nLastKeyPressed = m_eKeyMapRIGHT; break; default: break; } } }
static bool gfx_ctx_set_video_mode( unsigned width, unsigned height, bool fullscreen) { if (g_inited) return false; int ret = 0; struct drm_fb *fb = NULL; struct sigaction sa = {{0}}; sa.sa_handler = sighandler; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); #define EGL_ATTRIBS_BASE \ EGL_SURFACE_TYPE, EGL_WINDOW_BIT, \ EGL_RED_SIZE, 1, \ EGL_GREEN_SIZE, 1, \ EGL_BLUE_SIZE, 1, \ EGL_ALPHA_SIZE, 0, \ EGL_DEPTH_SIZE, 0 static const EGLint egl_attribs_gl[] = { EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE, }; static const EGLint egl_attribs_gles[] = { EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE, }; static const EGLint egl_attribs_vg[] = { EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE, }; // GLES 2.0. Don't use for any other API. static const EGLint gles_context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; const EGLint *attrib_ptr; switch (g_api) { case GFX_CTX_OPENGL_API: attrib_ptr = egl_attribs_gl; break; case GFX_CTX_OPENGL_ES_API: attrib_ptr = egl_attribs_gles; break; case GFX_CTX_OPENVG_API: attrib_ptr = egl_attribs_vg; break; default: attrib_ptr = NULL; } g_egl_dpy = eglGetDisplay((EGLNativeDisplayType)g_gbm_dev); if (!g_egl_dpy) { RARCH_ERR("[KMS/EGL]: Couldn't get EGL display.\n"); goto error; } EGLint major, minor; if (!eglInitialize(g_egl_dpy, &major, &minor)) goto error; EGLint n; if (!eglChooseConfig(g_egl_dpy, attrib_ptr, &g_config, 1, &n) || n != 1) goto error; g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, (g_api == GFX_CTX_OPENGL_ES_API) ? gles_context_attribs : NULL); if (!g_egl_ctx) goto error; g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, (EGLNativeWindowType)g_gbm_surface, NULL); if (!g_egl_surf) goto error; if (!eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx)) goto error; glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(g_egl_dpy, g_egl_surf); g_bo = gbm_surface_lock_front_buffer(g_gbm_surface); fb = drm_fb_get_from_bo(g_bo); ret = drmModeSetCrtc(g_drm_fd, g_crtc_id, fb->fb_id, 0, 0, &g_connector_id, 1, g_drm_mode); if (ret < 0) goto error; g_inited = true; return true; error: gfx_ctx_destroy(); return false; }
static bool gfx_ctx_drm_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { float refresh_mod; int i, ret = 0; struct drm_fb *fb = NULL; settings_t *settings = config_get_ptr(); gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; if (!drm) return false; frontend_driver_install_signal_handler(); /* If we use black frame insertion, * we fake a 60 Hz monitor for 120 Hz one, * etc, so try to match that. */ refresh_mod = settings->video.black_frame_insertion ? 0.5f : 1.0f; /* Find desired video mode, and use that. * If not fullscreen, we get desired windowed size, * which is not appropriate. */ if ((width == 0 && height == 0) || !fullscreen) g_drm_mode = &g_drm_connector->modes[0]; else { /* Try to match settings->video.refresh_rate * as closely as possible. * * Lower resolutions tend to have multiple supported * refresh rates as well. */ float minimum_fps_diff = 0.0f; /* Find best match. */ for (i = 0; i < g_drm_connector->count_modes; i++) { float diff; if (width != g_drm_connector->modes[i].hdisplay || height != g_drm_connector->modes[i].vdisplay) continue; diff = fabsf(refresh_mod * g_drm_connector->modes[i].vrefresh - settings->video.refresh_rate); if (!g_drm_mode || diff < minimum_fps_diff) { g_drm_mode = &g_drm_connector->modes[i]; minimum_fps_diff = diff; } } } if (!g_drm_mode) { RARCH_ERR("[KMS/EGL]: Did not find suitable video mode for %u x %u.\n", width, height); goto error; } drm->fb_width = g_drm_mode->hdisplay; drm->fb_height = g_drm_mode->vdisplay; /* Create GBM surface. */ g_gbm_surface = gbm_surface_create( g_gbm_dev, drm->fb_width, drm->fb_height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!g_gbm_surface) { RARCH_ERR("[KMS/EGL]: Couldn't create GBM surface.\n"); goto error; } switch (drm_api) { case GFX_CTX_OPENGL_API: case GFX_CTX_OPENGL_ES_API: case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL if (!gfx_ctx_drm_egl_set_video_mode(drm)) goto error; #endif break; case GFX_CTX_NONE: default: break; } g_bo = gbm_surface_lock_front_buffer(g_gbm_surface); fb = drm_fb_get_from_bo(g_bo); ret = drmModeSetCrtc(g_drm_fd, g_crtc_id, fb->fb_id, 0, 0, &g_connector_id, 1, g_drm_mode); if (ret < 0) goto error; return true; error: gfx_ctx_drm_destroy_resources(drm); if (drm) free(drm); return false; }
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; }
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); } }
static bool gfx_ctx_drm_egl_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { static const EGLint egl_attribs_gl[] = { DRM_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE, }; static const EGLint egl_attribs_gles[] = { DRM_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE, }; #ifdef EGL_KHR_create_context static const EGLint egl_attribs_gles3[] = { DRM_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, EGL_NONE, }; #endif static const EGLint egl_attribs_vg[] = { DRM_EGL_ATTRIBS_BASE, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE, }; const EGLint *attrib_ptr; EGLint major, minor, n, egl_attribs[16], *attr; float refresh_mod; int i, ret = 0; struct sigaction sa = {{0}}; struct drm_fb *fb = NULL; gfx_ctx_drm_egl_data_t *drm = (gfx_ctx_drm_egl_data_t*) driver.video_context_data; if (!drm) return false; sa.sa_handler = sighandler; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); switch (g_api) { case GFX_CTX_OPENGL_API: attrib_ptr = egl_attribs_gl; break; case GFX_CTX_OPENGL_ES_API: #ifdef EGL_KHR_create_context if (g_major >= 3) attrib_ptr = egl_attribs_gles3; else #endif attrib_ptr = egl_attribs_gles; break; case GFX_CTX_OPENVG_API: attrib_ptr = egl_attribs_vg; break; default: attrib_ptr = NULL; } /* If we use black frame insertion, * we fake a 60 Hz monitor for 120 Hz one, etc, so try to match that. */ refresh_mod = g_settings.video.black_frame_insertion ? 0.5f : 1.0f; /* Find desired video mode, and use that. * If not fullscreen, we get desired windowed size, * which is not appropriate. */ if ((width == 0 && height == 0) || !fullscreen) drm->g_drm_mode = &drm->g_connector->modes[0]; else { /* Try to match g_settings.video.refresh_rate as closely as possible. * Lower resolutions tend to have multiple supported * refresh rates as well. */ float minimum_fps_diff = 0.0f; /* Find best match. */ for (i = 0; i < drm->g_connector->count_modes; i++) { if (width != drm->g_connector->modes[i].hdisplay || height != drm->g_connector->modes[i].vdisplay) continue; float diff = fabsf(refresh_mod * drm->g_connector->modes[i].vrefresh - g_settings.video.refresh_rate); if (!drm->g_drm_mode || diff < minimum_fps_diff) { drm->g_drm_mode = &drm->g_connector->modes[i]; minimum_fps_diff = diff; } } } if (!drm->g_drm_mode) { RARCH_ERR("[KMS/EGL]: Did not find suitable video mode for %u x %u.\n", width, height); goto error; } drm->g_fb_width = drm->g_drm_mode->hdisplay; drm->g_fb_height = drm->g_drm_mode->vdisplay; /* Create GBM surface. */ drm->g_gbm_surface = gbm_surface_create( drm->g_gbm_dev, drm->g_fb_width, drm->g_fb_height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!drm->g_gbm_surface) { RARCH_ERR("[KMS/EGL]: Couldn't create GBM surface.\n"); goto error; } drm->g_egl_dpy = eglGetDisplay((EGLNativeDisplayType)drm->g_gbm_dev); if (!drm->g_egl_dpy) { RARCH_ERR("[KMS/EGL]: Couldn't get EGL display.\n"); goto error; } if (!eglInitialize(drm->g_egl_dpy, &major, &minor)) goto error; if (!eglChooseConfig(drm->g_egl_dpy, attrib_ptr, &drm->g_config, 1, &n) || n != 1) goto error; attr = egl_fill_attribs(egl_attribs); drm->g_egl_ctx = eglCreateContext(drm->g_egl_dpy, drm->g_config, EGL_NO_CONTEXT, attr != egl_attribs ? egl_attribs : NULL); if (drm->g_egl_ctx == EGL_NO_CONTEXT) goto error; if (drm->g_use_hw_ctx) { drm->g_egl_hw_ctx = eglCreateContext(drm->g_egl_dpy, drm->g_config, drm->g_egl_ctx, attr != egl_attribs ? egl_attribs : NULL); RARCH_LOG("[KMS/EGL]: Created shared context: %p.\n", (void*)drm->g_egl_hw_ctx); if (drm->g_egl_hw_ctx == EGL_NO_CONTEXT) goto error; } drm->g_egl_surf = eglCreateWindowSurface(drm->g_egl_dpy, drm->g_config, (EGLNativeWindowType)drm->g_gbm_surface, NULL); if (!drm->g_egl_surf) goto error; if (!eglMakeCurrent(drm->g_egl_dpy, drm->g_egl_surf, drm->g_egl_surf, drm->g_egl_ctx)) goto error; glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(drm->g_egl_dpy, drm->g_egl_surf); drm->g_bo = gbm_surface_lock_front_buffer(drm->g_gbm_surface); fb = drm_fb_get_from_bo(drm, drm->g_bo); ret = drmModeSetCrtc(drm->g_drm_fd, drm->g_crtc_id, fb->fb_id, 0, 0, &drm->g_connector_id, 1, drm->g_drm_mode); if (ret < 0) goto error; return true; error: gfx_ctx_drm_egl_destroy_resources(drm); if (drm) free(drm); return false; }
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; }
int main(int argc, char *argv[]) { EGLDisplay dpy; EGLContext ctx; EGLSurface surface; EGLConfig config; EGLint major, minor, n; const char *ver; uint32_t handle, stride; struct kms kms; int ret, fd; struct gbm_device *gbm; struct gbm_bo *bo; drmModeCrtcPtr saved_crtc; struct gbm_surface *gs; fd = open(device_name, O_RDWR); if (fd < 0) { /* Probably permissions error */ fprintf(stderr, "couldn't open %s, skipping\n", device_name); return -1; } gbm = gbm_create_device(fd); if (gbm == NULL) { fprintf(stderr, "couldn't create gbm device\n"); ret = -1; goto close_fd; } dpy = eglGetDisplay(gbm); if (dpy == EGL_NO_DISPLAY) { fprintf(stderr, "eglGetDisplay() failed\n"); ret = -1; goto destroy_gbm_device; } if (!eglInitialize(dpy, &major, &minor)) { printf("eglInitialize() failed\n"); ret = -1; goto egl_terminate; } ver = eglQueryString(dpy, EGL_VERSION); printf("EGL_VERSION = %s\n", ver); if (!setup_kms(fd, &kms)) { ret = -1; goto egl_terminate; } eglBindAPI(EGL_OPENGL_API); if (!eglChooseConfig(dpy, attribs, &config, 1, &n) || n != 1) { fprintf(stderr, "failed to choose argb config\n"); goto egl_terminate; } ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL); if (ctx == NULL) { fprintf(stderr, "failed to create context\n"); ret = -1; goto egl_terminate; } gs = gbm_surface_create(gbm, kms.mode.hdisplay, kms.mode.vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); surface = eglCreateWindowSurface(dpy, config, gs, NULL); if (!eglMakeCurrent(dpy, surface, surface, ctx)) { fprintf(stderr, "failed to make context current\n"); ret = -1; goto destroy_context; } render_stuff(kms.mode.hdisplay, kms.mode.vdisplay); eglSwapBuffers(dpy, surface); bo = gbm_surface_lock_front_buffer(gs); handle = gbm_bo_get_handle(bo).u32; stride = gbm_bo_get_stride(bo); printf("handle=%d, stride=%d\n", handle, stride); ret = drmModeAddFB(fd, kms.mode.hdisplay, kms.mode.vdisplay, 24, 32, stride, handle, &kms.fb_id); if (ret) { fprintf(stderr, "failed to create fb\n"); goto rm_fb; } saved_crtc = drmModeGetCrtc(fd, kms.encoder->crtc_id); if (saved_crtc == NULL) goto rm_fb; ret = drmModeSetCrtc(fd, kms.encoder->crtc_id, kms.fb_id, 0, 0, &kms.connector->connector_id, 1, &kms.mode); if (ret) { fprintf(stderr, "failed to set mode: %m\n"); goto free_saved_crtc; } getchar(); ret = drmModeSetCrtc(fd, saved_crtc->crtc_id, saved_crtc->buffer_id, saved_crtc->x, saved_crtc->y, &kms.connector->connector_id, 1, &saved_crtc->mode); if (ret) { fprintf(stderr, "failed to restore crtc: %m\n"); } free_saved_crtc: drmModeFreeCrtc(saved_crtc); rm_fb: drmModeRmFB(fd, kms.fb_id); eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); destroy_context: eglDestroyContext(dpy, ctx); egl_terminate: eglTerminate(dpy); destroy_gbm_device: gbm_device_destroy(gbm); close_fd: close(fd); return ret; }
int main(int argc, char *argv[]) { EGLDisplay dpy; EGLContext ctx; EGLConfig config; EGLSurface surface; EGLint major, minor, n; const char *ver; uint32_t handle, stride; int ret, fd, frames = 0; struct gbm_device *gbm; drmModeCrtcPtr saved_crtc; time_t start, end; char *data; char j; int i; int once; once = 0; signal (SIGINT, quit_handler); fd = open(device_name, O_RDWR); if (fd < 0) { /* Probably permissions error */ fprintf(stderr, "couldn't open %s, skipping\n", device_name); return -1; } gbm = gbm_create_device(fd); if (gbm == NULL) { fprintf(stderr, "couldn't create gbm device\n"); ret = -1; goto close_fd; } dpy = eglGetDisplay(gbm); if (dpy == EGL_NO_DISPLAY) { fprintf(stderr, "eglGetDisplay() failed\n"); ret = -1; goto destroy_gbm_device; } if (!eglInitialize(dpy, &major, &minor)) { printf("eglInitialize() failed\n"); ret = -1; goto egl_terminate; } ver = eglQueryString(dpy, EGL_VERSION); printf("EGL_VERSION = %s\n", ver); if (!setup_kms(fd, &kms)) { ret = -1; goto egl_terminate; } eglBindAPI(EGL_OPENGL_API); if (!eglChooseConfig(dpy, attribs, &config, 1, &n) || n != 1) { fprintf(stderr, "failed to choose argb config\n"); goto egl_terminate; } ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL); if (ctx == NULL) { fprintf(stderr, "failed to create context\n"); ret = -1; goto egl_terminate; } gs = gbm_surface_create(gbm, kms.mode.hdisplay, kms.mode.vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (gs == NULL) { fprintf(stderr, "unable to create gbm surface\n"); ret = -1; goto egl_terminate; } surface = eglCreateWindowSurface(dpy, config, gs, NULL); if (surface == EGL_NO_SURFACE) { fprintf(stderr, "failed to create surface\n"); ret = -1; goto destroy_gbm_surface; } if (!eglMakeCurrent(dpy, surface, surface, ctx)) { fprintf(stderr, "failed to make context current\n"); ret = -1; goto destroy_surface; } saved_crtc = drmModeGetCrtc(fd, kms.crtc_id); if (saved_crtc == NULL) { fprintf(stderr, "no valid graphic configuration active (VT ?)\n"); } time(&start); do { drmEventContext evctx; fd_set rfds; render_stuff(kms.mode.hdisplay, kms.mode.vdisplay); eglSwapBuffers(dpy, surface); if (!gbm_surface_has_free_buffers(gs)) fprintf(stderr, "out of free buffers\n"); next_bo = gbm_surface_lock_front_buffer(gs); if (!next_bo) fprintf(stderr, "failed to lock front buffer: %m\n"); handle = gbm_bo_get_handle(next_bo).u32; stride = gbm_bo_get_stride(next_bo); ret = drmModeAddFB(fd, kms.mode.hdisplay, kms.mode.vdisplay, 24, 32, stride, handle, &next_fb_id); if (ret) { fprintf(stderr, "failed to create fb\n"); goto out; } /* make sure to setup crtc once (fix for broken drivers) */ if(once == 0){ once = 1; drmModeSetCrtc(fd, kms.crtc_id, next_fb_id, 0, 0, &kms.connector->connector_id, 1, &kms.mode); } ret = drmModePageFlip(fd, kms.crtc_id, next_fb_id, DRM_MODE_PAGE_FLIP_EVENT, 0); if (ret) { fprintf(stderr, "failed to page flip: %m\n"); goto out; } FD_ZERO(&rfds); FD_SET(fd, &rfds); while (select(fd + 1, &rfds, NULL, NULL, NULL) == -1) NULL; memset(&evctx, 0, sizeof evctx); evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.page_flip_handler = page_flip_handler; drmHandleEvent(fd, &evctx); frames++; } while (!quit); time(&end); printf("Frames per second: %.2lf\n", frames / difftime(end, start)); out: if(saved_crtc){ drmModeSetCrtc(fd, saved_crtc->crtc_id, saved_crtc->buffer_id, saved_crtc->x, saved_crtc->y, &kms.connector->connector_id, 1, &saved_crtc->mode); } drmModeFreeCrtc(saved_crtc); if (current_fb_id) drmModeRmFB(fd, current_fb_id); if (next_fb_id) drmModeRmFB(fd, next_fb_id); eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); destroy_context: eglDestroyContext(dpy, ctx); destroy_surface: eglDestroySurface(dpy, surface); destroy_gbm_surface: gbm_surface_destroy(gs); egl_terminate: eglTerminate(dpy); destroy_gbm_device: gbm_device_destroy(gbm); close_fd: close(fd); return ret; }