441 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			441 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright © 2013 Intel Corporation
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice (including the next
 | |
|  * paragraph) shall be included in all copies or substantial portions of the
 | |
|  * Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | |
|  * IN THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| /** @file ephyr_glamor.c
 | |
|  *
 | |
|  * Glamor support and EGL setup.
 | |
|  */
 | |
| #define MESA_EGL_NO_X11_HEADERS
 | |
| #define EGL_NO_X11
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <stdint.h>
 | |
| #include <xcb/xcb.h>
 | |
| #include <xcb/xcb_aux.h>
 | |
| #include <pixman.h>
 | |
| #include "glamor_context.h"
 | |
| #include "glamor_egl.h"
 | |
| #include "glamor_priv.h"
 | |
| #include "ephyr.h"
 | |
| #include "ephyr_glamor.h"
 | |
| #include "os.h"
 | |
| 
 | |
| /* until we need geometry shaders GL3.1 should suffice. */
 | |
| /* Xephyr has its own copy of this for build reasons */
 | |
| #define GLAMOR_GL_CORE_VER_MAJOR 3
 | |
| #define GLAMOR_GL_CORE_VER_MINOR 1
 | |
| /** @{
 | |
|  *
 | |
|  * global state for Xephyr with glamor, all of which is arguably a bug.
 | |
|  */
 | |
| Bool ephyr_glamor_gles2;
 | |
| Bool ephyr_glamor_skip_present;
 | |
| /** @} */
 | |
| 
 | |
| /**
 | |
|  * Per-screen state for Xephyr with glamor.
 | |
|  */
 | |
| struct ephyr_glamor {
 | |
|     EGLDisplay dpy;
 | |
|     EGLContext ctx;
 | |
|     xcb_window_t win;
 | |
|     EGLSurface egl_win;
 | |
| 
 | |
|     GLuint tex;
 | |
| 
 | |
|     GLuint texture_shader;
 | |
|     GLuint texture_shader_position_loc;
 | |
|     GLuint texture_shader_texcoord_loc;
 | |
| 
 | |
|     /* Size of the window that we're rendering to. */
 | |
|     unsigned width, height;
 | |
| 
 | |
|     GLuint vao, vbo;
 | |
| };
 | |
| 
 | |
| static void
 | |
| glamor_egl_make_current(struct glamor_context *glamor_ctx)
 | |
| {
 | |
|     /* There's only a single global dispatch table in Mesa.  EGL, GLX,
 | |
|      * and AIGLX's direct dispatch table manipulation don't talk to
 | |
|      * each other.  We need to set the context to NULL first to avoid
 | |
|      * EGL's no-op context change fast path when switching back to
 | |
|      * EGL.
 | |
|      */
 | |
|     eglMakeCurrent(glamor_ctx->display, EGL_NO_SURFACE,
 | |
|                    EGL_NO_SURFACE, EGL_NO_CONTEXT);
 | |
| 
 | |
|     if (!eglMakeCurrent(glamor_ctx->display,
 | |
|                         glamor_ctx->surface, glamor_ctx->surface,
 | |
|                         glamor_ctx->ctx)) {
 | |
|         FatalError("Failed to make EGL context current\n");
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
 | |
| {
 | |
|     KdScreenPriv(screen);
 | |
|     KdScreenInfo *kd_screen = pScreenPriv->screen;
 | |
|     EphyrScrPriv *scrpriv = kd_screen->driver;
 | |
|     struct ephyr_glamor *ephyr_glamor = scrpriv->glamor;
 | |
| 
 | |
|     glamor_enable_dri3(screen);
 | |
|     glamor_ctx->display = ephyr_glamor->dpy;
 | |
|     glamor_ctx->ctx = ephyr_glamor->ctx;
 | |
|     glamor_ctx->surface = ephyr_glamor->egl_win;
 | |
|     glamor_ctx->make_current = glamor_egl_make_current;
 | |
| }
 | |
| 
 | |
| int
 | |
| glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
 | |
|                                PixmapPtr pixmap,
 | |
|                                CARD16 *stride, CARD32 *size)
 | |
| {
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
 | |
|                            uint32_t *offsets, uint32_t *strides,
 | |
|                            uint64_t *modifier)
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
 | |
|                           CARD16 *stride, CARD32 *size)
 | |
| {
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| static GLuint
 | |
| ephyr_glamor_build_glsl_prog(GLuint vs, GLuint fs)
 | |
| {
 | |
|     GLint ok;
 | |
|     GLuint prog;
 | |
| 
 | |
|     prog = glCreateProgram();
 | |
|     glAttachShader(prog, vs);
 | |
|     glAttachShader(prog, fs);
 | |
| 
 | |
|     glLinkProgram(prog);
 | |
|     glGetProgramiv(prog, GL_LINK_STATUS, &ok);
 | |
|     if (!ok) {
 | |
|         GLchar *info;
 | |
|         GLint size;
 | |
| 
 | |
|         glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
 | |
|         info = malloc(size);
 | |
| 
 | |
|         glGetProgramInfoLog(prog, size, NULL, info);
 | |
|         ErrorF("Failed to link: %s\n", info);
 | |
|         FatalError("GLSL link failure\n");
 | |
|     }
 | |
| 
 | |
|     return prog;
 | |
| }
 | |
| 
 | |
| static void
 | |
| ephyr_glamor_setup_texturing_shader(struct ephyr_glamor *glamor)
 | |
| {
 | |
|     const char *vs_source =
 | |
|         "attribute vec2 texcoord;\n"
 | |
|         "attribute vec2 position;\n"
 | |
|         "varying vec2 t;\n"
 | |
|         "\n"
 | |
|         "void main()\n"
 | |
|         "{\n"
 | |
|         "    t = texcoord;\n"
 | |
|         "    gl_Position = vec4(position, 0, 1);\n"
 | |
|         "}\n";
 | |
| 
 | |
|     const char *fs_source =
 | |
|         "#ifdef GL_ES\n"
 | |
|         "precision mediump float;\n"
 | |
|         "#endif\n"
 | |
|         "\n"
 | |
|         "varying vec2 t;\n"
 | |
|         "uniform sampler2D s; /* initially 0 */\n"
 | |
|         "\n"
 | |
|         "void main()\n"
 | |
|         "{\n"
 | |
|         "    gl_FragColor = texture2D(s, t);\n"
 | |
|         "}\n";
 | |
| 
 | |
|     GLuint fs, vs, prog;
 | |
| 
 | |
|     vs = glamor_compile_glsl_prog(GL_VERTEX_SHADER, vs_source);
 | |
|     fs = glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, fs_source);
 | |
|     prog = ephyr_glamor_build_glsl_prog(vs, fs);
 | |
| 
 | |
|     glamor->texture_shader = prog;
 | |
|     glamor->texture_shader_position_loc = glGetAttribLocation(prog, "position");
 | |
|     assert(glamor->texture_shader_position_loc != -1);
 | |
|     glamor->texture_shader_texcoord_loc = glGetAttribLocation(prog, "texcoord");
 | |
|     assert(glamor->texture_shader_texcoord_loc != -1);
 | |
| }
 | |
| 
 | |
| #ifndef EGL_PLATFORM_XCB_EXT
 | |
| #define EGL_PLATFORM_XCB_EXT 0x31DC
 | |
| #endif
 | |
| 
 | |
| #include <dlfcn.h>
 | |
| #ifndef RTLD_DEFAULT
 | |
| #define RTLD_DEFAULT NULL
 | |
| #endif
 | |
| 
 | |
| /* (loud booing)
 | |
|  *
 | |
|  * keeping this as a static variable is bad form, we _could_ have zaphod heads
 | |
|  * on different displays (for example). but other bits of Xephyr are already
 | |
|  * broken for that case, and fixing that would entail fixing the rest of the
 | |
|  * contortions with hostx.c anyway, so this works for now.
 | |
|  */
 | |
| static EGLDisplay edpy = EGL_NO_DISPLAY;
 | |
| 
 | |
| xcb_connection_t *
 | |
| ephyr_glamor_connect(void)
 | |
| {
 | |
|     int major = 0, minor = 0;
 | |
| 
 | |
|     /*
 | |
|      * Try pure xcb first. If that doesn't work but we can find XOpenDisplay,
 | |
|      * fall back to xlib. This lets us potentially not load libX11 at all, if
 | |
|      * the EGL is also pure xcb.
 | |
|      */
 | |
| 
 | |
|     if (epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_EXT_platform_xcb")) {
 | |
|         xcb_connection_t *conn = xcb_connect(NULL, NULL);
 | |
|         EGLDisplay dpy = glamor_egl_get_display(EGL_PLATFORM_XCB_EXT, conn);
 | |
| 
 | |
|         if (dpy == EGL_NO_DISPLAY) {
 | |
|             xcb_disconnect(conn);
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         edpy = dpy;
 | |
|         eglInitialize(dpy, &major, &minor);
 | |
|         return conn;
 | |
|     }
 | |
| 
 | |
|     if (epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_EXT_platform_x11") ||
 | |
|         epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_KHR_platform_x11)")) {
 | |
|         void *lib = NULL;
 | |
|         xcb_connection_t *ret = NULL;
 | |
|         void *(*x_open_display)(void *) =
 | |
|             (void *) dlsym(RTLD_DEFAULT, "XOpenDisplay");
 | |
|         xcb_connection_t *(*x_get_xcb_connection)(void *) =
 | |
|             (void *) dlsym(RTLD_DEFAULT, "XGetXCBConnection");
 | |
| 
 | |
|         if (x_open_display == NULL)
 | |
|             return NULL;
 | |
| 
 | |
|         if (x_get_xcb_connection == NULL) {
 | |
|             lib = dlopen("libX11-xcb.so.1", RTLD_LOCAL | RTLD_LAZY);
 | |
|             x_get_xcb_connection =
 | |
|                 (void *) dlsym(lib, "XGetXCBConnection");
 | |
|         }
 | |
| 
 | |
|         if (x_get_xcb_connection == NULL)
 | |
|             goto out;
 | |
| 
 | |
|         void *xdpy = x_open_display(NULL);
 | |
|         EGLDisplay dpy = glamor_egl_get_display(EGL_PLATFORM_X11_KHR, xdpy);
 | |
|         if (dpy == EGL_NO_DISPLAY)
 | |
|             goto out;
 | |
| 
 | |
|         edpy = dpy;
 | |
|         eglInitialize(dpy, &major, &minor);
 | |
|         ret = x_get_xcb_connection(xdpy);
 | |
| out:
 | |
|         if (lib)
 | |
|             dlclose(lib);
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| ephyr_glamor_set_texture(struct ephyr_glamor *glamor, uint32_t tex)
 | |
| {
 | |
|     glamor->tex = tex;
 | |
| }
 | |
| 
 | |
| static void
 | |
| ephyr_glamor_set_vertices(struct ephyr_glamor *glamor)
 | |
| {
 | |
|     glVertexAttribPointer(glamor->texture_shader_position_loc,
 | |
|                           2, GL_FLOAT, FALSE, 0, (void *) 0);
 | |
|     glVertexAttribPointer(glamor->texture_shader_texcoord_loc,
 | |
|                           2, GL_FLOAT, FALSE, 0, (void *) (sizeof (float) * 8));
 | |
| 
 | |
|     glEnableVertexAttribArray(glamor->texture_shader_position_loc);
 | |
|     glEnableVertexAttribArray(glamor->texture_shader_texcoord_loc);
 | |
| }
 | |
| 
 | |
| void
 | |
| ephyr_glamor_damage_redisplay(struct ephyr_glamor *glamor,
 | |
|                               struct pixman_region16 *damage)
 | |
| {
 | |
|     GLint old_vao;
 | |
| 
 | |
|     /* Skip presenting the output in this mode.  Presentation is
 | |
|      * expensive, and if we're just running the X Test suite headless,
 | |
|      * nobody's watching.
 | |
|      */
 | |
|     if (ephyr_glamor_skip_present)
 | |
|         return;
 | |
| 
 | |
|     eglMakeCurrent(glamor->dpy, glamor->egl_win, glamor->egl_win, glamor->ctx);
 | |
| 
 | |
|     glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
 | |
|     glBindVertexArray(glamor->vao);
 | |
| 
 | |
|     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 | |
|     glUseProgram(glamor->texture_shader);
 | |
|     glViewport(0, 0, glamor->width, glamor->height);
 | |
|     if (!ephyr_glamor_gles2)
 | |
|         glDisable(GL_COLOR_LOGIC_OP);
 | |
| 
 | |
|     glActiveTexture(GL_TEXTURE0);
 | |
|     glBindTexture(GL_TEXTURE_2D, glamor->tex);
 | |
|     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 | |
| 
 | |
|     glBindVertexArray(old_vao);
 | |
| 
 | |
|     eglSwapBuffers(glamor->dpy, glamor->egl_win);
 | |
| }
 | |
| 
 | |
| struct ephyr_glamor *
 | |
| ephyr_glamor_screen_init(xcb_window_t win, xcb_visualid_t vid)
 | |
| {
 | |
|     static const float position[] = {
 | |
|         -1, -1,
 | |
|          1, -1,
 | |
|          1,  1,
 | |
|         -1,  1,
 | |
|         0, 1,
 | |
|         1, 1,
 | |
|         1, 0,
 | |
|         0, 0,
 | |
|     };
 | |
|     GLint old_vao;
 | |
| 
 | |
|     EGLContext ctx;
 | |
|     struct ephyr_glamor *glamor;
 | |
|     EGLSurface egl_win;
 | |
| 
 | |
|     glamor = calloc(1, sizeof(struct ephyr_glamor));
 | |
|     if (!glamor) {
 | |
|         FatalError("malloc");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     const EGLint config_attribs[] = {
 | |
|         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
 | |
|         EGL_NATIVE_VISUAL_ID, vid,
 | |
|         EGL_NONE,
 | |
|     };
 | |
|     EGLConfig config = EGL_NO_CONFIG_KHR;
 | |
|     int num_configs = 0;
 | |
| 
 | |
|     /* (loud booing (see above)) */
 | |
|     glamor->dpy = edpy;
 | |
| 
 | |
|     eglChooseConfig(glamor->dpy, config_attribs, &config, 1, &num_configs);
 | |
|     if (num_configs != 1)
 | |
|         FatalError("Unable to find an EGLConfig for vid %#x\n", vid);
 | |
| 
 | |
|     egl_win = eglCreatePlatformWindowSurfaceEXT(glamor->dpy, config,
 | |
|                                                 &win, NULL);
 | |
| 
 | |
|     if (ephyr_glamor_gles2)
 | |
|         eglBindAPI(EGL_OPENGL_ES_API);
 | |
|     else
 | |
|         eglBindAPI(EGL_OPENGL_API);
 | |
| 
 | |
|     EGLint context_attribs[5];
 | |
|     int i = 0;
 | |
|     context_attribs[i++] = EGL_CONTEXT_MAJOR_VERSION;
 | |
|     context_attribs[i++] = ephyr_glamor_gles2 ? 2 : 3;
 | |
|     context_attribs[i++] = EGL_CONTEXT_MINOR_VERSION;
 | |
|     context_attribs[i++] = ephyr_glamor_gles2 ? 0 : 1;
 | |
|     context_attribs[i++] = EGL_NONE;
 | |
| 
 | |
|     ctx = eglCreateContext(glamor->dpy, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT,
 | |
|                            context_attribs);
 | |
| 
 | |
|     if (ctx == NULL)
 | |
|         FatalError("eglCreateContext failed\n");
 | |
| 
 | |
|     if (!eglMakeCurrent(glamor->dpy, egl_win, egl_win, ctx))
 | |
|         FatalError("eglMakeCurrent failed\n");
 | |
| 
 | |
|     glamor->ctx = ctx;
 | |
|     glamor->win = win;
 | |
|     glamor->egl_win = egl_win;
 | |
|     ephyr_glamor_setup_texturing_shader(glamor);
 | |
| 
 | |
|     glGenVertexArrays(1, &glamor->vao);
 | |
|     glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
 | |
|     glBindVertexArray(glamor->vao);
 | |
| 
 | |
|     glGenBuffers(1, &glamor->vbo);
 | |
| 
 | |
|     glBindBuffer(GL_ARRAY_BUFFER, glamor->vbo);
 | |
|     glBufferData(GL_ARRAY_BUFFER, sizeof (position), position, GL_STATIC_DRAW);
 | |
| 
 | |
|     ephyr_glamor_set_vertices(glamor);
 | |
|     glBindVertexArray(old_vao);
 | |
| 
 | |
|     return glamor;
 | |
| }
 | |
| 
 | |
| void
 | |
| ephyr_glamor_screen_fini(struct ephyr_glamor *glamor)
 | |
| {
 | |
|     eglMakeCurrent(glamor->dpy,
 | |
|                    EGL_NO_SURFACE, EGL_NO_SURFACE,
 | |
|                    EGL_NO_CONTEXT);
 | |
|     eglDestroyContext(glamor->dpy, glamor->ctx);
 | |
|     eglDestroySurface(glamor->dpy, glamor->egl_win);
 | |
| 
 | |
|     free(glamor);
 | |
| }
 | |
| 
 | |
| void
 | |
| ephyr_glamor_set_window_size(struct ephyr_glamor *glamor,
 | |
|                              unsigned width, unsigned height)
 | |
| {
 | |
|     if (!glamor)
 | |
|         return;
 | |
| 
 | |
|     glamor->width = width;
 | |
|     glamor->height = height;
 | |
| }
 |