/* * 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. */ #include #include #include #include #include #include "glamor_egl.h" #include "glamor_priv.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 GLint ephyr_glamor_compile_glsl_prog(GLenum type, const char *source) { GLint ok; GLint prog; prog = glCreateShader(type); glShaderSource(prog, 1, (const GLchar **) &source, NULL); glCompileShader(prog); glGetShaderiv(prog, GL_COMPILE_STATUS, &ok); if (!ok) { GLchar *info; GLint size; glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size); info = malloc(size); if (info) { glGetShaderInfoLog(prog, size, NULL, info); ErrorF("Failed to compile %s: %s\n", type == GL_FRAGMENT_SHADER ? "FS" : "VS", info); ErrorF("Program source:\n%s", source); free(info); } else ErrorF("Failed to get shader compilation info.\n"); FatalError("GLSL compile failure\n"); } return prog; } 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 = ephyr_glamor_compile_glsl_prog(GL_VERTEX_SHADER, vs_source); fs = ephyr_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 #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, config, 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; }