/* * 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_glx.c * * Separate file for hiding Xlib and GLX-using parts of xephyr from * the rest of the server-struct-aware build. */ #include #include #include #undef Xcalloc #undef Xrealloc #undef Xfree #include #include #include #include #include "ephyr_glamor_glx.h" #include "os.h" #include /* until we need geometry shaders GL3.1 should suffice. */ /* Xephyr has it's 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. * * Xephyr can render with multiple windows, but all the windows have * to be on the same X connection and all have to have the same * visual. */ static Display *dpy; static XVisualInfo *visual_info; static GLXFBConfig fb_config; Bool ephyr_glamor_gles2; Bool ephyr_glamor_skip_present; /** @} */ /** * Per-screen state for Xephyr with glamor. */ struct ephyr_glamor { GLXContext ctx; Window win; GLXWindow glx_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); } xcb_connection_t * ephyr_glamor_connect(void) { dpy = XOpenDisplay(NULL); if (!dpy) return NULL; XSetEventQueueOwner(dpy, XCBOwnsEventQueue); return XGetXCBConnection(dpy); } 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); } static void ephyr_glamor_clear_vertices(struct ephyr_glamor *glamor) { glDisableVertexAttribArray(glamor->texture_shader_position_loc); glDisableVertexAttribArray(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; glXMakeCurrent(dpy, glamor->glx_win, glamor->ctx); if (glamor->vao) { glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao); glBindVertexArray(glamor->vao); } else { glBindBuffer(GL_ARRAY_BUFFER, glamor->vbo); ephyr_glamor_set_vertices(glamor); } 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); if (glamor->vao) glBindVertexArray(old_vao); else ephyr_glamor_clear_vertices(glamor); glXSwapBuffers(dpy, glamor->glx_win); } /** * Xlib-based handling of xcb events for glamor. * * We need to let the Xlib event filtering run on the event so that * Mesa's dri2_glx.c userspace event mangling gets run, and we * correctly get our invalidate events propagated into the driver. */ void ephyr_glamor_process_event(xcb_generic_event_t *xev) { uint32_t response_type = xev->response_type & 0x7f; /* Note the types on wire_to_event: there's an Xlib XEvent (with * the broken types) that it returns, and a protocol xEvent that * it inspects. */ Bool (*wire_to_event)(Display *dpy, XEvent *ret, xEvent *event); XLockDisplay(dpy); /* Set the event handler to NULL to get access to the current one. */ wire_to_event = XESetWireToEvent(dpy, response_type, NULL); if (wire_to_event) { XEvent processed_event; /* OK they had an event handler. Plug it back in, and call * through to it. */ XESetWireToEvent(dpy, response_type, wire_to_event); xev->sequence = LastKnownRequestProcessed(dpy); wire_to_event(dpy, &processed_event, (xEvent *)xev); } XUnlockDisplay(dpy); } static int ephyr_glx_error_handler(Display * _dpy, XErrorEvent * ev) { return 0; } struct ephyr_glamor * ephyr_glamor_glx_screen_init(xcb_window_t win) { int (*oldErrorHandler) (Display *, XErrorEvent *); static const float position[] = { -1, -1, 1, -1, 1, 1, -1, 1, 0, 1, 1, 1, 1, 0, 0, 0, }; GLint old_vao; GLXContext ctx; struct ephyr_glamor *glamor; GLXWindow glx_win; glamor = calloc(1, sizeof(struct ephyr_glamor)); if (!glamor) { FatalError("malloc"); return NULL; } glx_win = glXCreateWindow(dpy, fb_config, win, NULL); if (ephyr_glamor_gles2) { static const int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 2, GLX_CONTEXT_MINOR_VERSION_ARB, 0, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES_PROFILE_BIT_EXT, 0, }; if (epoxy_has_glx_extension(dpy, DefaultScreen(dpy), "GLX_EXT_create_context_es2_profile")) { ctx = glXCreateContextAttribsARB(dpy, fb_config, NULL, True, context_attribs); } else { FatalError("Xephyr -glamor_gles2 rquires " "GLX_EXT_create_context_es2_profile\n"); } } else { if (epoxy_has_glx_extension(dpy, DefaultScreen(dpy), "GLX_ARB_create_context")) { static const int context_attribs[] = { GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_MAJOR_VERSION_ARB, GLAMOR_GL_CORE_VER_MAJOR, GLX_CONTEXT_MINOR_VERSION_ARB, GLAMOR_GL_CORE_VER_MINOR, 0, }; oldErrorHandler = XSetErrorHandler(ephyr_glx_error_handler); ctx = glXCreateContextAttribsARB(dpy, fb_config, NULL, True, context_attribs); XSync(dpy, False); XSetErrorHandler(oldErrorHandler); } else { ctx = NULL; } if (!ctx) ctx = glXCreateContext(dpy, visual_info, NULL, True); } if (ctx == NULL) FatalError("glXCreateContext failed\n"); if (!glXMakeCurrent(dpy, glx_win, ctx)) FatalError("glXMakeCurrent failed\n"); glamor->ctx = ctx; glamor->win = win; glamor->glx_win = glx_win; ephyr_glamor_setup_texturing_shader(glamor); if (epoxy_has_gl_extension("GL_ARB_vertex_array_object")) { glGenVertexArrays(1, &glamor->vao); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao); glBindVertexArray(glamor->vao); } else glamor->vao = 0; glGenBuffers(1, &glamor->vbo); glBindBuffer(GL_ARRAY_BUFFER, glamor->vbo); glBufferData(GL_ARRAY_BUFFER, sizeof (position), position, GL_STATIC_DRAW); if (glamor->vao) { ephyr_glamor_set_vertices(glamor); glBindVertexArray(old_vao); } else glBindBuffer(GL_ARRAY_BUFFER, 0); return glamor; } void ephyr_glamor_glx_screen_fini(struct ephyr_glamor *glamor) { glXMakeCurrent(dpy, None, NULL); glXDestroyContext(dpy, glamor->ctx); glXDestroyWindow(dpy, glamor->glx_win); free(glamor); } xcb_visualtype_t * ephyr_glamor_get_visual(void) { xcb_screen_t *xscreen = xcb_aux_get_screen(XGetXCBConnection(dpy), DefaultScreen(dpy)); int attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER, 1, None }; int event_base = 0, error_base = 0, nelements; GLXFBConfig *fbconfigs; if (!glXQueryExtension (dpy, &error_base, &event_base)) FatalError("Couldn't find GLX extension\n"); fbconfigs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs, &nelements); if (!nelements) FatalError("Couldn't choose an FBConfig\n"); fb_config = fbconfigs[0]; free(fbconfigs); visual_info = glXGetVisualFromFBConfig(dpy, fb_config); if (visual_info == NULL) FatalError("Couldn't get RGB visual\n"); return xcb_aux_find_visual_by_id(xscreen, visual_info->visualid); } void ephyr_glamor_set_window_size(struct ephyr_glamor *glamor, unsigned width, unsigned height) { if (!glamor) return; glamor->width = width; glamor->height = height; }