[Mesa-dev] [PATCH demos] egl: Add es2eglkms demo

Dongseong Hwang dongseong.hwang at intel.com
Fri Apr 29 04:54:29 UTC 2016


This demo shows how ChromeOS renders OpenGL ES2 via kms, drm and gbm.
This demo consists of
1. kms modesetting and vsync
2. EGL and GLES2 context creation
3. gbm bo creation and EGL Image binding

drm connection code is copied from eglkms.
ES2 rendering code is copied from es2tri.

Signed-off-by: Dongseong Hwang <dongseong.hwang at intel.com>
---
 src/egl/opengles2/Makefile.am |   9 +
 src/egl/opengles2/es2eglkms.c | 608 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 617 insertions(+)
 create mode 100644 src/egl/opengles2/es2eglkms.c

diff --git a/src/egl/opengles2/Makefile.am b/src/egl/opengles2/Makefile.am
index b80ba50..2564a74 100644
--- a/src/egl/opengles2/Makefile.am
+++ b/src/egl/opengles2/Makefile.am
@@ -45,6 +45,11 @@ endif
 if HAVE_WAYLAND
 bin_PROGRAMS += es2gears_wayland
 endif
+if HAVE_DRM
+if HAVE_GBM
+bin_PROGRAMS += es2eglkms
+endif
+endif
 endif
 endif
 
@@ -57,3 +62,7 @@ es2gears_x11_LDADD = ../eglut/libeglut_x11.la
 
 es2gears_wayland_SOURCES = es2gears.c
 es2gears_wayland_LDADD = ../eglut/libeglut_wayland.la
+
+es2eglkms_SOURCES = es2eglkms.c
+es2eglkms_CFLAGS = $(AM_CFLAGS) $(DRM_CFLAGS) $(GBM_CFLAGS)
+es2eglkms_LDADD = $(AM_LDFLAGS) $(DRM_LIBS) $(GBM_LIBS)
\ No newline at end of file
diff --git a/src/egl/opengles2/es2eglkms.c b/src/egl/opengles2/es2eglkms.c
new file mode 100644
index 0000000..95b3ade
--- /dev/null
+++ b/src/egl/opengles2/es2eglkms.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright © 2016 Dongseong Hwang
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+
+#include <gbm.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+// double buffering
+#define NUM_BUFFERS 2
+
+struct framebuffer {
+   struct gbm_bo *bo;
+   int fd;
+   uint32_t fb_id;
+   EGLImageKHR image;
+   GLuint gl_tex;
+   GLuint gl_fb;
+};
+
+struct kms {
+   drmModeConnector *connector;
+   drmModeEncoder *encoder;
+   drmModeModeInfo mode;
+};
+
+struct gl {
+   GLuint program;
+   GLfloat view_rotz;
+   GLint u_matrix;
+   GLint attr_pos;
+   GLint attr_color;
+
+   PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
+   PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
+};
+
+static void make_z_rot_matrix(GLfloat angle, GLfloat *m)
+{
+   float c = cos(angle * M_PI / 180.0);
+   float s = sin(angle * M_PI / 180.0);
+   int i;
+   for (i = 0; i < 16; i++)
+      m[i] = 0.0;
+   m[0] = m[5] = m[10] = m[15] = 1.0;
+
+   m[0] = c;
+   m[1] = s;
+   m[4] = -s;
+   m[5] = c;
+}
+
+static void make_scale_matrix(GLfloat xs, GLfloat ys, GLfloat zs, GLfloat *m)
+{
+   int i;
+   for (i = 0; i < 16; i++)
+      m[i] = 0.0;
+   m[0] = xs;
+   m[5] = ys;
+   m[10] = zs;
+   m[15] = 1.0;
+}
+
+static void mul_matrix(GLfloat *prod, const GLfloat *a, const GLfloat *b)
+{
+#define A(row, col) a[(col << 2) + row]
+#define B(row, col) b[(col << 2) + row]
+#define P(row, col) p[(col << 2) + row]
+   GLfloat p[16];
+   GLint i;
+   for (i = 0; i < 4; i++) {
+      const GLfloat ai0 = A(i, 0), ai1 = A(i, 1), ai2 = A(i, 2), ai3 = A(i, 3);
+      P(i, 0) = ai0 * B(0, 0) + ai1 * B(1, 0) + ai2 * B(2, 0) + ai3 * B(3, 0);
+      P(i, 1) = ai0 * B(0, 1) + ai1 * B(1, 1) + ai2 * B(2, 1) + ai3 * B(3, 1);
+      P(i, 2) = ai0 * B(0, 2) + ai1 * B(1, 2) + ai2 * B(2, 2) + ai3 * B(3, 2);
+      P(i, 3) = ai0 * B(0, 3) + ai1 * B(1, 3) + ai2 * B(2, 3) + ai3 * B(3, 3);
+   }
+   memcpy(prod, p, sizeof(p));
+#undef A
+#undef B
+#undef PROD
+}
+
+static EGLBoolean setup_kms(int fd, struct kms *kms)
+{
+   drmModeRes *resources;
+   drmModeConnector *connector;
+   drmModeEncoder *encoder;
+   int i;
+
+   resources = drmModeGetResources(fd);
+   if (!resources) {
+      fprintf(stderr, "drmModeGetResources failed\n");
+      return EGL_FALSE;
+   }
+
+   for (i = 0; i < resources->count_connectors; i++) {
+      connector = drmModeGetConnector(fd, resources->connectors[i]);
+      if (connector == NULL)
+         continue;
+
+      if (connector->connection == DRM_MODE_CONNECTED &&
+          connector->count_modes > 0)
+         break;
+
+      drmModeFreeConnector(connector);
+   }
+
+   if (i == resources->count_connectors) {
+      fprintf(stderr, "No currently active connector found.\n");
+      return EGL_FALSE;
+   }
+
+   for (i = 0; i < resources->count_encoders; i++) {
+      encoder = drmModeGetEncoder(fd, resources->encoders[i]);
+
+      if (encoder == NULL)
+         continue;
+
+      if (encoder->encoder_id == connector->encoder_id)
+         break;
+
+      drmModeFreeEncoder(encoder);
+   }
+
+   kms->connector = connector;
+   kms->encoder = encoder;
+   kms->mode = connector->modes[0];
+
+   return EGL_TRUE;
+}
+
+static void gl_init(struct gl *gl, int width, int height)
+{
+   glClearColor(0.4, 0.4, 0.4, 0.0);
+
+   static const char *fragShaderText =
+       "precision mediump float;\n"
+       "varying vec4 v_color;\n"
+       "void main() {\n"
+       "   gl_FragColor = v_color;\n"
+       "}\n";
+   static const char *vertShaderText =
+       "uniform mat4 modelviewProjection;\n"
+       "attribute vec4 pos;\n"
+       "attribute vec4 color;\n"
+       "varying vec4 v_color;\n"
+       "void main() {\n"
+       "   gl_Position = modelviewProjection * pos;\n"
+       "   v_color = color;\n"
+       "}\n";
+
+   GLuint fragShader, vertShader;
+   GLint stat;
+
+   fragShader = glCreateShader(GL_FRAGMENT_SHADER);
+   glShaderSource(fragShader, 1, (const char **)&fragShaderText, NULL);
+   glCompileShader(fragShader);
+   glGetShaderiv(fragShader, GL_COMPILE_STATUS, &stat);
+   if (!stat) {
+      printf("Error: fragment shader did not compile!\n");
+      exit(1);
+   }
+
+   vertShader = glCreateShader(GL_VERTEX_SHADER);
+   glShaderSource(vertShader, 1, (const char **)&vertShaderText, NULL);
+   glCompileShader(vertShader);
+   glGetShaderiv(vertShader, GL_COMPILE_STATUS, &stat);
+   if (!stat) {
+      printf("Error: vertex shader did not compile!\n");
+      exit(1);
+   }
+
+   gl->program = glCreateProgram();
+   glAttachShader(gl->program, fragShader);
+   glAttachShader(gl->program, vertShader);
+
+   gl->attr_pos = 0;
+   gl->attr_color = 1;
+   glBindAttribLocation(gl->program, gl->attr_pos, "pos");
+   glBindAttribLocation(gl->program, gl->attr_color, "color");
+   glLinkProgram(gl->program);
+   glGetProgramiv(gl->program, GL_LINK_STATUS, &stat);
+   if (!stat) {
+      char log[1000];
+      GLsizei len;
+      glGetProgramInfoLog(gl->program, 1000, &len, log);
+      printf("Error: linking:\n%s\n", log);
+      exit(1);
+   }
+
+   glUseProgram(gl->program);
+
+   gl->u_matrix = glGetUniformLocation(gl->program, "modelviewProjection");
+   printf("Uniform modelviewProjection at %d\n", gl->u_matrix);
+   printf("Attrib pos at %d\n", gl->attr_pos);
+   printf("Attrib color at %d\n", gl->attr_color);
+
+   gl->view_rotz = 0;
+
+   glViewport(0, 0, (GLint)width, (GLint)height);
+}
+
+static void render_stuff(struct gl *gl)
+{
+   static const GLfloat verts[3][2] = { { -1, -1 }, { 1, -1 }, { 0, 1 } };
+   static const GLfloat colors[3][3] = {
+      { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 }
+   };
+   GLfloat mat[16], rot[16], scale[16];
+
+   gl->view_rotz += 1.0;
+
+   glClear(GL_COLOR_BUFFER_BIT);
+
+   /* Set modelview/projection matrix */
+   make_z_rot_matrix(gl->view_rotz, rot);
+   make_scale_matrix(0.5, 0.5, 0.5, scale);
+   mul_matrix(mat, rot, scale);
+   glUniformMatrix4fv(gl->u_matrix, 1, GL_FALSE, mat);
+
+   glVertexAttribPointer(gl->attr_pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
+   glVertexAttribPointer(gl->attr_color, 3, GL_FLOAT, GL_FALSE, 0, colors);
+   glEnableVertexAttribArray(gl->attr_pos);
+   glEnableVertexAttribArray(gl->attr_color);
+
+   glDrawArrays(GL_TRIANGLES, 0, 3);
+}
+
+static void egl_sync_fence(EGLDisplay dpy, struct gl *gl)
+{
+   EGLSyncKHR sync = gl->eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+   glFlush();
+   gl->eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+}
+
+static int has_extension(const char *extension, const char *extensions)
+{
+   const char *start, *where, *terminator;
+   start = extensions;
+   for (;;) {
+      where = (char *)strstr((const char *)start, extension);
+      if (!where)
+         break;
+      terminator = where + strlen(extension);
+      if (where == start || *(where - 1) == ' ')
+         if (*terminator == ' ' || *terminator == '\0')
+            return 0;
+      start = terminator;
+   }
+   return -1;
+}
+
+static void page_flip_handler(int fd, unsigned int frame, unsigned int sec,
+                              unsigned int usec, void *data)
+{
+   int *waiting_for_flip = data;
+   *waiting_for_flip = 0;
+}
+
+static const char device_name[] = "/dev/dri/card0";
+
+int main(int argc, char *argv[])
+{
+   EGLDisplay dpy;
+   EGLContext ctx;
+   EGLConfig config;
+   EGLint major, minor, n;
+   // Names are the original gl/egl function names with the prefix chopped off.
+   PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
+   PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
+   PFNGLEGLIMAGETARGETTEXTURE2DOESPROC eglImageTargetTexture2DOES;
+   int egl_sync_supported = 0;
+   struct gl gl;
+   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 framebuffer fbs[NUM_BUFFERS];
+   int front_buffer;
+   drmEventContext evctx = {
+      .version = DRM_EVENT_CONTEXT_VERSION,
+      .page_flip_handler = page_flip_handler,
+   };
+   fd_set fds;
+
+   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;
+   }
+
+   eglCreateImageKHR =
+       (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
+   eglDestroyImageKHR =
+       (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
+   eglImageTargetTexture2DOES =
+       (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress(
+           "glEGLImageTargetTexture2DOES");
+   gl.eglCreateSyncKHR =
+       (PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR");
+   gl.eglClientWaitSyncKHR =
+       (PFNEGLCLIENTWAITSYNCKHRPROC)eglGetProcAddress("eglClientWaitSyncKHR");
+   if (!eglCreateImageKHR || !eglDestroyImageKHR ||
+       !eglImageTargetTexture2DOES) {
+      fprintf(stderr,
+              "eglGetProcAddress returned NULL for a required extension entry "
+              "point.\n");
+      ret = -1;
+      goto destroy_gbm_device;
+   }
+   if (gl.eglCreateSyncKHR && gl.eglClientWaitSyncKHR) {
+      egl_sync_supported = 1;
+   }
+
+   dpy = eglGetDisplay(gbm);
+   if (dpy == EGL_NO_DISPLAY) {
+      fprintf(stderr, "eglGetDisplay() failed\n");
+      ret = -1;
+      goto destroy_gbm_device;
+   }
+
+   if (!eglInitialize(dpy, &major, &minor)) {
+      fprintf(stderr, "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;
+   }
+
+   if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+      fprintf(stderr, "failed to bind api EGL_OPENGL_ES_API\n");
+      ret = -1;
+      goto egl_terminate;
+   }
+
+   static const EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_DONT_CARE,
+                                     EGL_NONE };
+
+   if (!eglChooseConfig(dpy, attribs, &config, 1, &n) || n != 1) {
+      fprintf(stderr, "failed to choose argb config\n");
+      goto egl_terminate;
+   }
+
+   static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2,
+                                             EGL_NONE };
+
+   ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, context_attribs);
+   if (ctx == NULL) {
+      fprintf(stderr, "failed to create context\n");
+      ret = -1;
+      goto egl_terminate;
+   }
+
+   if (!eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx)) {
+      fprintf(stderr, "failed to make context current\n");
+      ret = -1;
+      goto destroy_context;
+   }
+
+   const char *egl_extensions = eglQueryString(dpy, EGL_EXTENSIONS);
+   printf("EGL Extensions \"%s\"\n", egl_extensions);
+   if (has_extension("EGL_KHR_image_base", egl_extensions)) {
+      fprintf(stderr, "EGL_KHR_image_base extension not supported\n");
+      ret = -1;
+      goto destroy_context;
+   }
+   if (has_extension("EGL_EXT_image_dma_buf_import", egl_extensions)) {
+      fprintf(stderr, "EGL_EXT_image_dma_buf_import extension not supported\n");
+      ret = -1;
+      goto destroy_context;
+   }
+
+   const char *gl_extensions = (const char *)glGetString(GL_EXTENSIONS);
+   if (has_extension("GL_OES_EGL_image", gl_extensions)) {
+      fprintf(stderr, "GL_OES_EGL_image extension not supported\n");
+      ret = -1;
+      goto destroy_context;
+   }
+
+   uint32_t width = kms.mode.hdisplay;
+   uint32_t height = kms.mode.vdisplay;
+
+   for (size_t i = 0; i < NUM_BUFFERS; i++) {
+      fbs[i].bo = gbm_bo_create(gbm, width, height, GBM_FORMAT_XRGB8888,
+                                GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+      if (!fbs[i].bo) {
+         fprintf(stderr, "failed to create a gbm buffer.\n");
+         ret = -1;
+         goto rm_fb;
+      }
+
+      fbs[i].fd = gbm_bo_get_fd(fbs[i].bo);
+      if (fbs[i].fd < 0) {
+         fprintf(stderr, "failed to get fb for bo: %d", fbs[i].fd);
+         ret = -1;
+         goto rm_fb;
+      }
+
+      uint32_t handle = gbm_bo_get_handle(fbs[i].bo).u32;
+      uint32_t stride = gbm_bo_get_stride(fbs[i].bo);
+      uint32_t offset = 0;
+      drmModeAddFB2(fd, width, height, GBM_FORMAT_XRGB8888, &handle, &stride,
+                    &offset, &fbs[i].fb_id, 0);
+      if (!fbs[i].fb_id) {
+         fprintf(stderr, "failed to create framebuffer from buffer object.\n");
+         ret = -1;
+         goto rm_fb;
+      }
+
+      const EGLint khr_image_attrs[] = { EGL_DMA_BUF_PLANE0_FD_EXT,
+                                         fbs[i].fd,
+                                         EGL_WIDTH,
+                                         width,
+                                         EGL_HEIGHT,
+                                         height,
+                                         EGL_LINUX_DRM_FOURCC_EXT,
+                                         GBM_FORMAT_XRGB8888,
+                                         EGL_DMA_BUF_PLANE0_PITCH_EXT,
+                                         stride,
+                                         EGL_DMA_BUF_PLANE0_OFFSET_EXT,
+                                         offset,
+                                         EGL_NONE };
+
+      fbs[i].image =
+          eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
+                            NULL /* no client buffer */, khr_image_attrs);
+      if (fbs[i].image == EGL_NO_IMAGE_KHR) {
+         fprintf(stderr, "failed to make image from buffer object: %x\n",
+                 eglGetError());
+         ret = -1;
+         goto rm_fb;
+      }
+
+      glGenTextures(1, &fbs[i].gl_tex);
+      glBindTexture(GL_TEXTURE_2D, fbs[i].gl_tex);
+      eglImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)fbs[i].image);
+      glBindTexture(GL_TEXTURE_2D, 0);
+
+      glGenFramebuffers(1, &fbs[i].gl_fb);
+      glBindFramebuffer(GL_FRAMEBUFFER, fbs[i].gl_fb);
+      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                             GL_TEXTURE_2D, fbs[i].gl_tex, 0);
+
+      if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+         fprintf(stderr,
+                 "failed framebuffer check for created target buffer: %x\n",
+                 glCheckFramebufferStatus(GL_FRAMEBUFFER));
+         ret = -1;
+         goto rm_gl_fb;
+      }
+   }
+
+   gl_init(&gl, width, height);
+   glClear(GL_COLOR_BUFFER_BIT);
+   if (egl_sync_supported) {
+      egl_sync_fence(dpy, &gl);
+   } else {
+      glFinish();
+   }
+
+   front_buffer = 0;
+   const struct framebuffer *back_fb = &fbs[front_buffer ^ 1];
+
+   saved_crtc = drmModeGetCrtc(fd, kms.encoder->crtc_id);
+   if (saved_crtc == NULL)
+      goto rm_gl_fb;
+
+   ret = drmModeSetCrtc(fd, kms.encoder->crtc_id, back_fb->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;
+   }
+
+   front_buffer ^= 1;
+   int got_user_input = 0;
+   FD_ZERO(&fds);
+   FD_SET(0, &fds);
+   FD_SET(fd, &fds);
+   while (1) {
+      int waiting_for_flip = 1;
+
+      const struct framebuffer *back_fb = &fbs[front_buffer ^ 1];
+      glBindFramebuffer(GL_FRAMEBUFFER, back_fb->gl_fb);
+      render_stuff(&gl);
+      if (egl_sync_supported) {
+         egl_sync_fence(dpy, &gl);
+      } else {
+         glFinish();
+      }
+
+      ret = drmModePageFlip(fd, kms.encoder->crtc_id, back_fb->fb_id,
+                            DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
+      if (ret) {
+         fprintf(stderr, "failed to queue page flip: %s\n", strerror(errno));
+         goto free_saved_crtc;
+      }
+
+      while (waiting_for_flip) {
+         ret = select(fd + 1, &fds, NULL, NULL, NULL);
+         if (ret < 0) {
+            fprintf(stderr, "select err: %s\n", strerror(errno));
+            goto free_saved_crtc;
+         } else if (ret == 0) {
+            fprintf(stderr, "select timeout!\n");
+            goto free_saved_crtc;
+         } else if (FD_ISSET(0, &fds)) {
+            printf("exit due to user-input\n");
+            got_user_input = 1;
+            break;
+         } else if (FD_ISSET(fd, &fds)) {
+            drmHandleEvent(fd, &evctx);
+         }
+      }
+
+      if (got_user_input) {
+         break;
+      }
+      front_buffer ^= 1;
+   }
+
+   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_gl_fb:
+   for (size_t i = 0; i < NUM_BUFFERS; i++) {
+      glDeleteFramebuffers(1, &fbs[i].gl_fb);
+      glDeleteTextures(1, &fbs[i].gl_tex);
+   }
+rm_fb:
+   for (size_t i = 0; i < NUM_BUFFERS; i++) {
+      eglDestroyImageKHR(dpy, fbs[i].image);
+      drmModeRmFB(fd, fbs[i].fb_id);
+      close(fbs[i].fd);
+      gbm_bo_destroy(fbs[i].bo);
+   }
+   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;
+}
-- 
2.5.0



More information about the mesa-dev mailing list