Andreas, Adam, could you review? Br, DS
On Fri, Apr 29, 2016 at 7:54 AM, Dongseong Hwang <dongseong.hw...@intel.com> wrote: > 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.hw...@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 > >
_______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev