Create a simple buffer sharing example application.

BUG=none
TEST=none

Change-Id: Iab7b42893f413458ca087583473ef8e787b2ddb2
diff --git a/src/buffer-share.c b/src/buffer-share.c
new file mode 100644
index 0000000..44557ef
--- /dev/null
+++ b/src/buffer-share.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ * Copyright © 2011 Benjamin Franzke
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE /* Needed for O_CLOEXEC */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <gbm.h>
+#include <GL/gl.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+struct kms {
+   drmModeConnector *connector;
+   drmModeEncoder *encoder;
+   drmModeModeInfo mode;
+};
+
+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
+render_to_texture(int width, int height)
+{
+   GLfloat rsize = 100;
+   GLfloat x = (width - rsize) / 2.0 ;
+   GLfloat y = (height - rsize) / 2.0;
+
+   glViewport(0, 0, (GLint) width, (GLint) height);
+
+   glMatrixMode(GL_PROJECTION);
+   glLoadIdentity();
+
+   glOrtho(0, width, 0, height, 1.0, -1.0);
+
+   glMatrixMode(GL_MODELVIEW);
+   glLoadIdentity();
+
+   glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
+   glClear(GL_COLOR_BUFFER_BIT);
+   glColor3f(1.0f, 0.0f, 0.0f);
+
+   glRectf(x, y, x + rsize, y + rsize);
+
+   glFinish();
+}
+
+static void
+render_from_texture(int width, int height)
+{
+   GLfloat x = width / 2.0;
+   GLfloat y = height / 2.0;
+
+   GLfloat Vertices[] =
+   {
+      0.0f, y, 0.0f,
+      0.0f, 0.0f, 0.0f,
+      x, 0.0f, 0.0f,
+      x, y, 0.0f
+   };
+   GLfloat TexCoords[] =
+   {
+      0.0f, 1.0f,
+      0.0f, 0.0f,
+      1.0f, 0.0f,
+      1.0f, 1.0f
+   };
+
+   glViewport(0, 0, (GLint) width, (GLint) height);
+
+   glMatrixMode(GL_PROJECTION);
+   glLoadIdentity();
+
+   glOrtho(0, width, 0, height, 1.0, -1.0);
+
+   glMatrixMode(GL_MODELVIEW);
+   glLoadIdentity();
+
+   glClear(GL_COLOR_BUFFER_BIT);
+
+   glEnableClientState(GL_VERTEX_ARRAY);
+   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+   glVertexPointer(3, GL_FLOAT, 0, Vertices);
+   glTexCoordPointer(2, GL_FLOAT, 0, TexCoords);
+   glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+   glDisableClientState(GL_VERTEX_ARRAY);
+   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+   glFinish();
+}
+
+static const char device_name[] = "/dev/dri/card0";
+
+static void
+page_flip_handler(int fd, unsigned int frame,
+                  unsigned int sec, unsigned int usec, void *data)
+{
+  ;
+}
+
+void quit_handler(int signum)
+{
+  printf("Quitting!\n");
+}
+
+int main(int argc, char *argv[])
+{
+   EGLDisplay dpy;
+   EGLContext ctx;
+   EGLImageKHR image, out_image;
+   EGLint major, minor;
+   const char *extensions;
+   GLuint fb, texture, out_texture;
+   uint32_t handle, stride, fb_id, name;
+   struct kms kms;
+   int ret, fd;
+   struct gbm_device *gbm;
+   struct gbm_bo *bo;
+   drmModeCrtcPtr saved_crtc;
+   time_t start, end;
+   int is_producer = (argc == 1);
+
+   signal (SIGINT, quit_handler);
+
+   fd = open(device_name, O_RDWR | O_CLOEXEC);
+   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;
+   }
+
+   extensions = eglQueryString(dpy, EGL_EXTENSIONS);
+   if (!strstr(extensions, "EGL_KHR_surfaceless_opengl")) {
+      printf("No support for EGL_KHR_surfaceless_opengl\n");
+      ret = -1;
+      goto egl_terminate;
+   }
+
+   if (!setup_kms(fd, &kms)) {
+      ret = -1;
+      goto egl_terminate;
+   }
+
+   eglBindAPI(EGL_OPENGL_API);
+   ctx = eglCreateContext(dpy, NULL, EGL_NO_CONTEXT, NULL);
+   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;
+   }
+
+   glGenFramebuffers(1, &fb);
+   glBindFramebuffer(GL_FRAMEBUFFER, fb);
+
+   glGenTextures(1, &out_texture);
+   glBindTexture(GL_TEXTURE_2D, out_texture);
+   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+   bo  = gbm_bo_create(gbm, kms.mode.hdisplay, kms.mode.vdisplay,
+                       GBM_BO_FORMAT_XRGB8888,
+                       GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+   if (bo == NULL) {
+      fprintf(stderr, "failed to create gbm bo\n");
+      ret = -1;
+      goto unmake_current;
+   }
+
+   out_image = eglCreateImageKHR(dpy, NULL, EGL_NATIVE_PIXMAP_KHR, bo, NULL);
+   if (out_image == EGL_NO_IMAGE_KHR) {
+      fprintf(stderr, "failed to create egl image\n");
+      ret = -1;
+      goto destroy_gbm_bo;
+   }
+
+   glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, out_image);
+
+   glFramebufferTexture2D(GL_FRAMEBUFFER,
+                          GL_COLOR_ATTACHMENT0,
+                          GL_TEXTURE_2D,
+                          out_texture, 0);
+
+   if ((ret = glCheckFramebufferStatus(GL_FRAMEBUFFER)) !=
+       GL_FRAMEBUFFER_COMPLETE) {
+      fprintf(stderr, "framebuffer not complete: %x\n", ret);
+      ret = 1;
+      goto destroy_gbm_bo;
+   }
+
+   if (is_producer) {
+      char name_str[sizeof(long) + 1] = { 0 };
+      char stride_str[sizeof(long) + 1] = { 0 };
+
+      eglExportDRMImageMESA(dpy, out_image, &name, NULL, &stride);
+
+      snprintf(name_str, sizeof(name_str), "%d", (int)name);
+      snprintf(stride_str, sizeof(stride_str), "%d", (int)stride);
+
+      render_to_texture(kms.mode.hdisplay, kms.mode.vdisplay);
+
+      drmDropMaster(fd);
+
+      if (!fork())
+         execl(argv[0], argv[0], name_str, stride_str, NULL);
+       else
+         wait(NULL);
+
+      goto destroy_gbm_bo;
+   }
+
+   sscanf(argv[1], "%d", &name);
+   sscanf(argv[2], "%d", &stride);
+
+   EGLint attribs[] = {
+      EGL_WIDTH, kms.mode.hdisplay,
+      EGL_HEIGHT, kms.mode.vdisplay,
+      EGL_DRM_BUFFER_STRIDE_MESA, stride / 4,
+      EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
+      EGL_NONE
+   };
+
+   image = eglCreateImageKHR(dpy,
+                             EGL_NO_CONTEXT,
+                             EGL_DRM_BUFFER_MESA,
+                             (void *)(intptr_t)(name),
+                             attribs);
+
+   if (image == EGL_NO_IMAGE_KHR) {
+      fprintf(stderr, "Failed to import image\n");
+      return;
+   }
+
+   glEnable(GL_TEXTURE_2D);
+
+   glGenTextures(1, &texture);
+   glBindTexture(GL_TEXTURE_2D, texture);
+   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+   eglExportDRMImageMESA(dpy, out_image, NULL, &handle, &stride);
+
+   render_from_texture(kms.mode.hdisplay, kms.mode.vdisplay);
+
+   ret = drmModeAddFB(fd,
+                      kms.mode.hdisplay, kms.mode.vdisplay,
+                      24, 32, stride, handle, &fb_id);
+   if (ret) {
+      fprintf(stderr, "failed to create fb\n");
+      goto destroy_gbm_bo;
+   }
+
+   saved_crtc = drmModeGetCrtc(fd, kms.encoder->crtc_id);
+   if (saved_crtc == NULL)
+      goto rm_fb;
+
+   drmEventContext evctx;
+   fd_set rfds;
+
+   ret = drmModePageFlip(fd, kms.encoder->crtc_id,
+                         fb_id, DRM_MODE_PAGE_FLIP_EVENT, 0);
+   if (ret) {
+      fprintf(stderr, "failed to page flip: %m\n");
+      goto free_saved_crtc;
+   }
+
+   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);
+
+   sleep(60);
+
+   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, fb_id);
+   eglDestroyImageKHR(dpy, image);
+destroy_gbm_bo:
+   gbm_bo_destroy(bo);
+unmake_current:
+   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;
+}