1/* 2 * Copyright © 2013 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24/** @file ephyr_glamor_glx.c 25 * 26 * Separate file for hiding Xlib and GLX-using parts of xephyr from 27 * the rest of the server-struct-aware build. 28 */ 29 30#include <stdlib.h> 31#include <X11/Xlib.h> 32#include <X11/Xlibint.h> 33#undef Xcalloc 34#undef Xrealloc 35#undef Xfree 36#include <X11/Xlib-xcb.h> 37#include <xcb/xcb_aux.h> 38#include <pixman.h> 39#include <epoxy/glx.h> 40#include "ephyr_glamor_glx.h" 41#include "os.h" 42#include <X11/Xproto.h> 43 44/* until we need geometry shaders GL3.1 should suffice. */ 45/* Xephyr has its own copy of this for build reasons */ 46#define GLAMOR_GL_CORE_VER_MAJOR 3 47#define GLAMOR_GL_CORE_VER_MINOR 1 48/** @{ 49 * 50 * global state for Xephyr with glamor. 51 * 52 * Xephyr can render with multiple windows, but all the windows have 53 * to be on the same X connection and all have to have the same 54 * visual. 55 */ 56static Display *dpy; 57static XVisualInfo *visual_info; 58static GLXFBConfig fb_config; 59Bool ephyr_glamor_gles2; 60Bool ephyr_glamor_skip_present; 61/** @} */ 62 63/** 64 * Per-screen state for Xephyr with glamor. 65 */ 66struct ephyr_glamor { 67 GLXContext ctx; 68 Window win; 69 GLXWindow glx_win; 70 71 GLuint tex; 72 73 GLuint texture_shader; 74 GLuint texture_shader_position_loc; 75 GLuint texture_shader_texcoord_loc; 76 77 /* Size of the window that we're rendering to. */ 78 unsigned width, height; 79 80 GLuint vao, vbo; 81}; 82 83static GLint 84ephyr_glamor_compile_glsl_prog(GLenum type, const char *source) 85{ 86 GLint ok; 87 GLint prog; 88 89 prog = glCreateShader(type); 90 glShaderSource(prog, 1, (const GLchar **) &source, NULL); 91 glCompileShader(prog); 92 glGetShaderiv(prog, GL_COMPILE_STATUS, &ok); 93 if (!ok) { 94 GLchar *info; 95 GLint size; 96 97 glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size); 98 info = malloc(size); 99 if (info) { 100 glGetShaderInfoLog(prog, size, NULL, info); 101 ErrorF("Failed to compile %s: %s\n", 102 type == GL_FRAGMENT_SHADER ? "FS" : "VS", info); 103 ErrorF("Program source:\n%s", source); 104 free(info); 105 } 106 else 107 ErrorF("Failed to get shader compilation info.\n"); 108 FatalError("GLSL compile failure\n"); 109 } 110 111 return prog; 112} 113 114static GLuint 115ephyr_glamor_build_glsl_prog(GLuint vs, GLuint fs) 116{ 117 GLint ok; 118 GLuint prog; 119 120 prog = glCreateProgram(); 121 glAttachShader(prog, vs); 122 glAttachShader(prog, fs); 123 124 glLinkProgram(prog); 125 glGetProgramiv(prog, GL_LINK_STATUS, &ok); 126 if (!ok) { 127 GLchar *info; 128 GLint size; 129 130 glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); 131 info = malloc(size); 132 133 glGetProgramInfoLog(prog, size, NULL, info); 134 ErrorF("Failed to link: %s\n", info); 135 FatalError("GLSL link failure\n"); 136 } 137 138 return prog; 139} 140 141static void 142ephyr_glamor_setup_texturing_shader(struct ephyr_glamor *glamor) 143{ 144 const char *vs_source = 145 "attribute vec2 texcoord;\n" 146 "attribute vec2 position;\n" 147 "varying vec2 t;\n" 148 "\n" 149 "void main()\n" 150 "{\n" 151 " t = texcoord;\n" 152 " gl_Position = vec4(position, 0, 1);\n" 153 "}\n"; 154 155 const char *fs_source = 156 "#ifdef GL_ES\n" 157 "precision mediump float;\n" 158 "#endif\n" 159 "\n" 160 "varying vec2 t;\n" 161 "uniform sampler2D s; /* initially 0 */\n" 162 "\n" 163 "void main()\n" 164 "{\n" 165 " gl_FragColor = texture2D(s, t);\n" 166 "}\n"; 167 168 GLuint fs, vs, prog; 169 170 vs = ephyr_glamor_compile_glsl_prog(GL_VERTEX_SHADER, vs_source); 171 fs = ephyr_glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, fs_source); 172 prog = ephyr_glamor_build_glsl_prog(vs, fs); 173 174 glamor->texture_shader = prog; 175 glamor->texture_shader_position_loc = glGetAttribLocation(prog, "position"); 176 assert(glamor->texture_shader_position_loc != -1); 177 glamor->texture_shader_texcoord_loc = glGetAttribLocation(prog, "texcoord"); 178 assert(glamor->texture_shader_texcoord_loc != -1); 179} 180 181xcb_connection_t * 182ephyr_glamor_connect(void) 183{ 184 dpy = XOpenDisplay(NULL); 185 if (!dpy) 186 return NULL; 187 188 XSetEventQueueOwner(dpy, XCBOwnsEventQueue); 189 190 return XGetXCBConnection(dpy); 191} 192 193void 194ephyr_glamor_set_texture(struct ephyr_glamor *glamor, uint32_t tex) 195{ 196 glamor->tex = tex; 197} 198 199static void 200ephyr_glamor_set_vertices(struct ephyr_glamor *glamor) 201{ 202 glVertexAttribPointer(glamor->texture_shader_position_loc, 203 2, GL_FLOAT, FALSE, 0, (void *) 0); 204 glVertexAttribPointer(glamor->texture_shader_texcoord_loc, 205 2, GL_FLOAT, FALSE, 0, (void *) (sizeof (float) * 8)); 206 207 glEnableVertexAttribArray(glamor->texture_shader_position_loc); 208 glEnableVertexAttribArray(glamor->texture_shader_texcoord_loc); 209} 210 211void 212ephyr_glamor_damage_redisplay(struct ephyr_glamor *glamor, 213 struct pixman_region16 *damage) 214{ 215 GLint old_vao; 216 217 /* Skip presenting the output in this mode. Presentation is 218 * expensive, and if we're just running the X Test suite headless, 219 * nobody's watching. 220 */ 221 if (ephyr_glamor_skip_present) 222 return; 223 224 glXMakeCurrent(dpy, glamor->glx_win, glamor->ctx); 225 226 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao); 227 glBindVertexArray(glamor->vao); 228 229 glBindFramebuffer(GL_FRAMEBUFFER, 0); 230 glUseProgram(glamor->texture_shader); 231 glViewport(0, 0, glamor->width, glamor->height); 232 if (!ephyr_glamor_gles2) 233 glDisable(GL_COLOR_LOGIC_OP); 234 235 glActiveTexture(GL_TEXTURE0); 236 glBindTexture(GL_TEXTURE_2D, glamor->tex); 237 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 238 239 glBindVertexArray(old_vao); 240 241 glXSwapBuffers(dpy, glamor->glx_win); 242} 243 244/** 245 * Xlib-based handling of xcb events for glamor. 246 * 247 * We need to let the Xlib event filtering run on the event so that 248 * Mesa's dri2_glx.c userspace event mangling gets run, and we 249 * correctly get our invalidate events propagated into the driver. 250 */ 251void 252ephyr_glamor_process_event(xcb_generic_event_t *xev) 253{ 254 255 uint32_t response_type = xev->response_type & 0x7f; 256 /* Note the types on wire_to_event: there's an Xlib XEvent (with 257 * the broken types) that it returns, and a protocol xEvent that 258 * it inspects. 259 */ 260 Bool (*wire_to_event)(Display *dpy, XEvent *ret, xEvent *event); 261 262 XLockDisplay(dpy); 263 /* Set the event handler to NULL to get access to the current one. */ 264 wire_to_event = XESetWireToEvent(dpy, response_type, NULL); 265 if (wire_to_event) { 266 XEvent processed_event; 267 268 /* OK they had an event handler. Plug it back in, and call 269 * through to it. 270 */ 271 XESetWireToEvent(dpy, response_type, wire_to_event); 272 xev->sequence = LastKnownRequestProcessed(dpy); 273 wire_to_event(dpy, &processed_event, (xEvent *)xev); 274 } 275 XUnlockDisplay(dpy); 276} 277 278static int 279ephyr_glx_error_handler(Display * _dpy, XErrorEvent * ev) 280{ 281 return 0; 282} 283 284struct ephyr_glamor * 285ephyr_glamor_glx_screen_init(xcb_window_t win) 286{ 287 int (*oldErrorHandler) (Display *, XErrorEvent *); 288 static const float position[] = { 289 -1, -1, 290 1, -1, 291 1, 1, 292 -1, 1, 293 0, 1, 294 1, 1, 295 1, 0, 296 0, 0, 297 }; 298 GLint old_vao; 299 300 GLXContext ctx; 301 struct ephyr_glamor *glamor; 302 GLXWindow glx_win; 303 304 glamor = calloc(1, sizeof(struct ephyr_glamor)); 305 if (!glamor) { 306 FatalError("malloc"); 307 return NULL; 308 } 309 310 glx_win = glXCreateWindow(dpy, fb_config, win, NULL); 311 312 if (ephyr_glamor_gles2) { 313 static const int context_attribs[] = { 314 GLX_CONTEXT_MAJOR_VERSION_ARB, 2, 315 GLX_CONTEXT_MINOR_VERSION_ARB, 0, 316 GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES_PROFILE_BIT_EXT, 317 0, 318 }; 319 if (epoxy_has_glx_extension(dpy, DefaultScreen(dpy), 320 "GLX_EXT_create_context_es2_profile")) { 321 ctx = glXCreateContextAttribsARB(dpy, fb_config, NULL, True, 322 context_attribs); 323 } else { 324 FatalError("Xephyr -glamor_gles2 requires " 325 "GLX_EXT_create_context_es2_profile\n"); 326 } 327 } else { 328 if (epoxy_has_glx_extension(dpy, DefaultScreen(dpy), 329 "GLX_ARB_create_context")) { 330 static const int context_attribs[] = { 331 GLX_CONTEXT_PROFILE_MASK_ARB, 332 GLX_CONTEXT_CORE_PROFILE_BIT_ARB, 333 GLX_CONTEXT_MAJOR_VERSION_ARB, 334 GLAMOR_GL_CORE_VER_MAJOR, 335 GLX_CONTEXT_MINOR_VERSION_ARB, 336 GLAMOR_GL_CORE_VER_MINOR, 337 0, 338 }; 339 oldErrorHandler = XSetErrorHandler(ephyr_glx_error_handler); 340 ctx = glXCreateContextAttribsARB(dpy, fb_config, NULL, True, 341 context_attribs); 342 XSync(dpy, False); 343 XSetErrorHandler(oldErrorHandler); 344 } else { 345 ctx = NULL; 346 } 347 348 if (!ctx) 349 ctx = glXCreateContext(dpy, visual_info, NULL, True); 350 } 351 if (ctx == NULL) 352 FatalError("glXCreateContext failed\n"); 353 354 if (!glXMakeCurrent(dpy, glx_win, ctx)) 355 FatalError("glXMakeCurrent failed\n"); 356 357 glamor->ctx = ctx; 358 glamor->win = win; 359 glamor->glx_win = glx_win; 360 ephyr_glamor_setup_texturing_shader(glamor); 361 362 glGenVertexArrays(1, &glamor->vao); 363 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao); 364 glBindVertexArray(glamor->vao); 365 366 glGenBuffers(1, &glamor->vbo); 367 368 glBindBuffer(GL_ARRAY_BUFFER, glamor->vbo); 369 glBufferData(GL_ARRAY_BUFFER, sizeof (position), position, GL_STATIC_DRAW); 370 371 ephyr_glamor_set_vertices(glamor); 372 glBindVertexArray(old_vao); 373 374 return glamor; 375} 376 377void 378ephyr_glamor_glx_screen_fini(struct ephyr_glamor *glamor) 379{ 380 glXMakeCurrent(dpy, None, NULL); 381 glXDestroyContext(dpy, glamor->ctx); 382 glXDestroyWindow(dpy, glamor->glx_win); 383 384 free(glamor); 385} 386 387xcb_visualtype_t * 388ephyr_glamor_get_visual(void) 389{ 390 xcb_screen_t *xscreen = 391 xcb_aux_get_screen(XGetXCBConnection(dpy), DefaultScreen(dpy)); 392 int attribs[] = { 393 GLX_RENDER_TYPE, GLX_RGBA_BIT, 394 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 395 GLX_RED_SIZE, 1, 396 GLX_GREEN_SIZE, 1, 397 GLX_BLUE_SIZE, 1, 398 GLX_DOUBLEBUFFER, 1, 399 None 400 }; 401 int event_base = 0, error_base = 0, nelements; 402 GLXFBConfig *fbconfigs; 403 404 if (!glXQueryExtension (dpy, &error_base, &event_base)) 405 FatalError("Couldn't find GLX extension\n"); 406 407 fbconfigs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs, &nelements); 408 if (!nelements) 409 FatalError("Couldn't choose an FBConfig\n"); 410 fb_config = fbconfigs[0]; 411 free(fbconfigs); 412 413 visual_info = glXGetVisualFromFBConfig(dpy, fb_config); 414 if (visual_info == NULL) 415 FatalError("Couldn't get RGB visual\n"); 416 417 return xcb_aux_find_visual_by_id(xscreen, visual_info->visualid); 418} 419 420void 421ephyr_glamor_set_window_size(struct ephyr_glamor *glamor, 422 unsigned width, unsigned height) 423{ 424 if (!glamor) 425 return; 426 427 glamor->width = width; 428 glamor->height = height; 429} 430