2 * uterm - Linux User-Space Terminal
4 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #define EGL_EGLEXT_PROTOTYPES
31 #define GL_GLEXT_PROTOTYPES
34 #include <EGL/eglext.h>
38 #include <GLES2/gl2.h>
39 #include <GLES2/gl2ext.h>
46 #include <xf86drmMode.h>
50 #include "static_gl.h"
51 #include "uterm_drm_shared_internal.h"
52 #include "uterm_drm3d_internal.h"
53 #include "uterm_video.h"
54 #include "uterm_video_internal.h"
56 #define LOG_SUBSYSTEM "uterm_drm3d_video"
58 static int display_init(struct uterm_display *disp)
60 struct uterm_drm3d_display *d3d;
63 d3d = malloc(sizeof(*d3d));
66 memset(d3d, 0, sizeof(*d3d));
68 ret = uterm_drm_display_init(disp, d3d);
77 static void display_destroy(struct uterm_display *disp)
79 free(uterm_drm_display_get_data(disp));
80 uterm_drm_display_destroy(disp);
83 static void bo_destroy_event(struct gbm_bo *bo, void *data)
85 struct uterm_drm3d_rb *rb = data;
86 struct uterm_drm_video *vdrm;
91 vdrm = rb->disp->video->data;
92 drmModeRmFB(vdrm->fd, rb->fb);
96 static struct uterm_drm3d_rb *bo_to_rb(struct uterm_display *disp,
99 struct uterm_drm3d_rb *rb = gbm_bo_get_user_data(bo);
100 struct uterm_video *video = disp->video;
101 struct uterm_drm_video *vdrm = video->data;
103 unsigned int stride, handle, width, height;;
108 rb = malloc(sizeof(*rb));
110 log_error("cannot allocate memory for render buffer (%d): %m",
117 #ifdef BUILD_HAVE_GBM_BO_GET_PITCH
118 stride = gbm_bo_get_pitch(rb->bo);
120 stride = gbm_bo_get_stride(rb->bo);
122 handle = gbm_bo_get_handle(rb->bo).u32;
123 width = gbm_bo_get_width(rb->bo);
124 height = gbm_bo_get_height(rb->bo);
126 ret = drmModeAddFB(vdrm->fd, width, height, 24, 32, stride,
129 log_err("cannot add drm-fb (%d): %m", errno);
134 gbm_bo_set_user_data(bo, rb, bo_destroy_event);
138 static int display_activate(struct uterm_display *disp,
139 struct uterm_mode *mode)
141 struct uterm_video *video = disp->video;
142 struct uterm_drm_video *vdrm;
143 struct uterm_drm3d_video *v3d;
144 struct uterm_drm_display *ddrm = disp->data;
145 struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
148 drmModeModeInfo *minfo;
154 v3d = uterm_drm_video_get_data(video);
155 minfo = uterm_drm_mode_get_info(mode);
156 log_info("activating display %p to %ux%u", disp,
157 minfo->hdisplay, minfo->vdisplay);
159 ret = uterm_drm_display_activate(disp, vdrm->fd);
165 disp->current_mode = mode;
167 d3d->gbm = gbm_surface_create(v3d->gbm, minfo->hdisplay,
168 minfo->vdisplay, GBM_FORMAT_XRGB8888,
170 GBM_BO_USE_RENDERING);
172 log_error("cannot create gbm surface (%d): %m", errno);
177 d3d->surface = eglCreateWindowSurface(v3d->disp, v3d->conf,
178 (EGLNativeWindowType)d3d->gbm,
180 if (d3d->surface == EGL_NO_SURFACE) {
181 log_error("cannot create EGL window surface");
186 if (!eglMakeCurrent(v3d->disp, d3d->surface, d3d->surface,
188 log_error("cannot activate EGL context");
193 glClearColor(0, 0, 0, 0);
194 glClear(GL_COLOR_BUFFER_BIT);
195 if (!eglSwapBuffers(v3d->disp, d3d->surface)) {
196 log_error("cannot swap buffers");
201 bo = gbm_surface_lock_front_buffer(d3d->gbm);
203 log_error("cannot lock front buffer during creation");
208 d3d->current = bo_to_rb(disp, bo);
210 log_error("cannot lock front buffer");
215 ret = drmModeSetCrtc(vdrm->fd, ddrm->crtc_id, d3d->current->fb,
216 0, 0, &ddrm->conn_id, 1, minfo);
218 log_err("cannot set drm-crtc");
223 disp->flags |= DISPLAY_ONLINE;
227 gbm_surface_release_buffer(d3d->gbm, bo);
229 eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
232 eglDestroySurface(v3d->disp, d3d->surface);
234 gbm_surface_destroy(d3d->gbm);
236 disp->current_mode = NULL;
237 uterm_drm_display_deactivate(disp, vdrm->fd);
241 static void display_deactivate(struct uterm_display *disp)
243 struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
244 struct uterm_video *video = disp->video;
245 struct uterm_drm_video *vdrm;
246 struct uterm_drm3d_video *v3d;
248 log_info("deactivating display %p", disp);
251 v3d = uterm_drm_video_get_data(video);
252 uterm_drm_display_deactivate(disp, vdrm->fd);
254 eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
256 eglDestroySurface(v3d->disp, d3d->surface);
259 gbm_surface_release_buffer(d3d->gbm,
264 gbm_surface_release_buffer(d3d->gbm,
269 gbm_surface_destroy(d3d->gbm);
270 disp->current_mode = NULL;
273 int uterm_drm3d_display_use(struct uterm_display *disp, bool *opengl)
275 struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
276 struct uterm_drm3d_video *v3d;
278 v3d = uterm_drm_video_get_data(disp->video);
279 if (!eglMakeCurrent(v3d->disp, d3d->surface,
280 d3d->surface, v3d->ctx)) {
281 log_error("cannot activate EGL context");
288 /* TODO: lets find a way how to retrieve the current front buffer */
292 static int display_swap(struct uterm_display *disp, bool immediate)
296 struct uterm_drm3d_rb *rb;
297 struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
298 struct uterm_video *video = disp->video;
299 struct uterm_drm3d_video *v3d = uterm_drm_video_get_data(video);
301 if (!gbm_surface_has_free_buffers(d3d->gbm))
304 if (!eglSwapBuffers(v3d->disp, d3d->surface)) {
305 log_error("cannot swap EGL buffers (%d): %m", errno);
309 bo = gbm_surface_lock_front_buffer(d3d->gbm);
311 log_error("cannot lock front buffer");
315 rb = bo_to_rb(disp, bo);
317 log_error("cannot lock front gbm buffer (%d): %m", errno);
318 gbm_surface_release_buffer(d3d->gbm, bo);
322 ret = uterm_drm_display_swap(disp, rb->fb, immediate);
324 gbm_surface_release_buffer(d3d->gbm, bo);
329 gbm_surface_release_buffer(d3d->gbm, d3d->next->bo);
335 gbm_surface_release_buffer(d3d->gbm, d3d->current->bo);
344 static const struct display_ops drm_display_ops = {
345 .init = display_init,
346 .destroy = display_destroy,
347 .activate = display_activate,
348 .deactivate = display_deactivate,
349 .set_dpms = uterm_drm_display_set_dpms,
350 .use = uterm_drm3d_display_use,
352 .swap = display_swap,
353 .blit = uterm_drm3d_display_blit,
354 .fake_blendv = uterm_drm3d_display_fake_blendv,
355 .fill = uterm_drm3d_display_fill,
358 static void show_displays(struct uterm_video *video)
361 struct uterm_display *iter;
364 if (!video_is_awake(video))
367 shl_dlist_for_each(i, &video->displays) {
368 iter = shl_dlist_entry(i, struct uterm_display, list);
370 if (!display_is_online(iter))
372 if (iter->dpms != UTERM_DPMS_ON)
375 ret = uterm_drm3d_display_use(iter, NULL);
379 glClearColor(0, 0, 0, 1);
380 glClear(GL_COLOR_BUFFER_BIT);
381 display_swap(iter, true);
385 static void page_flip_handler(struct uterm_display *disp)
387 struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
391 gbm_surface_release_buffer(d3d->gbm,
393 d3d->current = d3d->next;
398 static int video_init(struct uterm_video *video, const char *node)
400 static const EGLint conf_att[] = {
401 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
402 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
409 static const EGLint ctx_att[] = {
410 EGL_CONTEXT_CLIENT_VERSION, 2,
415 EGLint major, minor, n;
418 struct uterm_drm_video *vdrm;
419 struct uterm_drm3d_video *v3d;
421 v3d = malloc(sizeof(*v3d));
424 memset(v3d, 0, sizeof(*v3d));
426 ret = uterm_drm_video_init(video, node, &drm_display_ops,
427 page_flip_handler, v3d);
432 log_debug("initialize 3D layer on %p", video);
434 v3d->gbm = gbm_create_device(vdrm->fd);
436 log_err("cannot create gbm device for %s (permission denied)",
442 v3d->disp = eglGetDisplay((EGLNativeDisplayType) v3d->gbm);
443 if (v3d->disp == EGL_NO_DISPLAY) {
444 log_err("cannot retrieve egl display for %s", node);
449 b = eglInitialize(v3d->disp, &major, &minor);
451 log_err("cannot init egl display for %s", node);
456 log_debug("EGL Init %d.%d", major, minor);
457 log_debug("EGL Version %s", eglQueryString(v3d->disp, EGL_VERSION));
458 log_debug("EGL Vendor %s", eglQueryString(v3d->disp, EGL_VENDOR));
459 ext = eglQueryString(v3d->disp, EGL_EXTENSIONS);
460 log_debug("EGL Extensions %s", ext);
462 if (!ext || !strstr(ext, "EGL_KHR_surfaceless_context")) {
463 log_err("surfaceless opengl not supported");
468 api = EGL_OPENGL_ES_API;
469 if (!eglBindAPI(api)) {
470 log_err("cannot bind opengl-es api");
475 b = eglChooseConfig(v3d->disp, conf_att, &v3d->conf, 1, &n);
477 log_error("cannot find a proper EGL framebuffer configuration");
482 v3d->ctx = eglCreateContext(v3d->disp, v3d->conf, EGL_NO_CONTEXT,
484 if (v3d->ctx == EGL_NO_CONTEXT) {
485 log_error("cannot create egl context");
490 if (!eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
492 log_error("cannot activate surfaceless EGL context");
497 ext = (const char*)glGetString(GL_EXTENSIONS);
498 if (ext && strstr((const char*)ext, "GL_EXT_unpack_subimage"))
499 v3d->supports_rowlen = true;
501 log_warning("your GL implementation does not support GL_EXT_unpack_subimage, rendering may be slower than usual");
506 eglDestroyContext(v3d->disp, v3d->ctx);
508 eglTerminate(v3d->disp);
510 gbm_device_destroy(v3d->gbm);
512 uterm_drm_video_destroy(video);
518 static void video_destroy(struct uterm_video *video)
520 struct uterm_drm3d_video *v3d = uterm_drm_video_get_data(video);
522 log_info("free drm video device %p", video);
524 if (!eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
526 log_error("cannot activate GL context during destruction");
527 uterm_drm3d_deinit_shaders(video);
529 eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
531 eglDestroyContext(v3d->disp, v3d->ctx);
532 eglTerminate(v3d->disp);
533 gbm_device_destroy(v3d->gbm);
535 uterm_drm_video_destroy(video);
538 static int video_poll(struct uterm_video *video)
540 return uterm_drm_video_poll(video);
543 static void video_sleep(struct uterm_video *video)
545 show_displays(video);
546 uterm_drm_video_sleep(video);
549 static int video_wake_up(struct uterm_video *video)
553 ret = uterm_drm_video_wake_up(video);
555 uterm_drm_video_arm_vt_timer(video);
559 show_displays(video);
563 static const struct video_ops drm_video_ops = {
565 .destroy = video_destroy,
566 .segfault = NULL, /* TODO: reset all saved CRTCs on segfault */
568 .sleep = video_sleep,
569 .wake_up = video_wake_up,
572 static const struct uterm_video_module drm3d_module = {
573 .ops = &drm_video_ops,
577 const struct uterm_video_module *UTERM_VIDEO_DRM3D = &drm3d_module;