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; }
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 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; }
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(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; }
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 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; }
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; }
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; }
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; }