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 it's 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 */
56 static Display *dpy;
57 static XVisualInfo *visual_info;
58 static GLXFBConfig fb_config;
59 Bool ephyr_glamor_gles2;
60 Bool ephyr_glamor_skip_present;
61 /** @} */
62
63 /**
64 * Per-screen state for Xephyr with glamor.
65 */
66 struct 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
83 static GLint
ephyr_glamor_compile_glsl_prog(GLenum type,const char * source)84 ephyr_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
114 static GLuint
ephyr_glamor_build_glsl_prog(GLuint vs,GLuint fs)115 ephyr_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
141 static void
ephyr_glamor_setup_texturing_shader(struct ephyr_glamor * glamor)142 ephyr_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 #ifdef GLES_USE_HIGHP
158 "precision highp float;\n"
159 #else
160 "precision mediump float;\n"
161 #endif
162 "#endif\n"
163 "\n"
164 "varying vec2 t;\n"
165 "uniform sampler2D s; /* initially 0 */\n"
166 "\n"
167 "void main()\n"
168 "{\n"
169 " gl_FragColor = texture2D(s, t);\n"
170 "}\n";
171
172 GLuint fs, vs, prog;
173
174 vs = ephyr_glamor_compile_glsl_prog(GL_VERTEX_SHADER, vs_source);
175 fs = ephyr_glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, fs_source);
176 prog = ephyr_glamor_build_glsl_prog(vs, fs);
177
178 glamor->texture_shader = prog;
179 glamor->texture_shader_position_loc = glGetAttribLocation(prog, "position");
180 assert(glamor->texture_shader_position_loc != -1);
181 glamor->texture_shader_texcoord_loc = glGetAttribLocation(prog, "texcoord");
182 assert(glamor->texture_shader_texcoord_loc != -1);
183 }
184
185 xcb_connection_t *
ephyr_glamor_connect(void)186 ephyr_glamor_connect(void)
187 {
188 dpy = XOpenDisplay(NULL);
189 if (!dpy)
190 return NULL;
191
192 XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
193
194 return XGetXCBConnection(dpy);
195 }
196
197 void
ephyr_glamor_set_texture(struct ephyr_glamor * glamor,uint32_t tex)198 ephyr_glamor_set_texture(struct ephyr_glamor *glamor, uint32_t tex)
199 {
200 glamor->tex = tex;
201 }
202
203 static void
ephyr_glamor_set_vertices(struct ephyr_glamor * glamor)204 ephyr_glamor_set_vertices(struct ephyr_glamor *glamor)
205 {
206 glVertexAttribPointer(glamor->texture_shader_position_loc,
207 2, GL_FLOAT, FALSE, 0, (void *) 0);
208 glVertexAttribPointer(glamor->texture_shader_texcoord_loc,
209 2, GL_FLOAT, FALSE, 0, (void *) (sizeof (float) * 8));
210
211 glEnableVertexAttribArray(glamor->texture_shader_position_loc);
212 glEnableVertexAttribArray(glamor->texture_shader_texcoord_loc);
213 }
214
215 void
ephyr_glamor_damage_redisplay(struct ephyr_glamor * glamor,struct pixman_region16 * damage)216 ephyr_glamor_damage_redisplay(struct ephyr_glamor *glamor,
217 struct pixman_region16 *damage)
218 {
219 GLint old_vao;
220
221 /* Skip presenting the output in this mode. Presentation is
222 * expensive, and if we're just running the X Test suite headless,
223 * nobody's watching.
224 */
225 if (ephyr_glamor_skip_present)
226 return;
227
228 glXMakeCurrent(dpy, glamor->glx_win, glamor->ctx);
229
230 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
231 glBindVertexArray(glamor->vao);
232
233 glBindFramebuffer(GL_FRAMEBUFFER, 0);
234 glUseProgram(glamor->texture_shader);
235 glViewport(0, 0, glamor->width, glamor->height);
236 if (!ephyr_glamor_gles2)
237 glDisable(GL_COLOR_LOGIC_OP);
238
239 glActiveTexture(GL_TEXTURE0);
240 glBindTexture(GL_TEXTURE_2D, glamor->tex);
241 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
242
243 glBindVertexArray(old_vao);
244
245 glXSwapBuffers(dpy, glamor->glx_win);
246 }
247
248 /**
249 * Xlib-based handling of xcb events for glamor.
250 *
251 * We need to let the Xlib event filtering run on the event so that
252 * Mesa's dri2_glx.c userspace event mangling gets run, and we
253 * correctly get our invalidate events propagated into the driver.
254 */
255 void
ephyr_glamor_process_event(xcb_generic_event_t * xev)256 ephyr_glamor_process_event(xcb_generic_event_t *xev)
257 {
258
259 uint32_t response_type = xev->response_type & 0x7f;
260 /* Note the types on wire_to_event: there's an Xlib XEvent (with
261 * the broken types) that it returns, and a protocol xEvent that
262 * it inspects.
263 */
264 Bool (*wire_to_event)(Display *dpy, XEvent *ret, xEvent *event);
265
266 XLockDisplay(dpy);
267 /* Set the event handler to NULL to get access to the current one. */
268 wire_to_event = XESetWireToEvent(dpy, response_type, NULL);
269 if (wire_to_event) {
270 XEvent processed_event;
271
272 /* OK they had an event handler. Plug it back in, and call
273 * through to it.
274 */
275 XESetWireToEvent(dpy, response_type, wire_to_event);
276 xev->sequence = LastKnownRequestProcessed(dpy);
277 wire_to_event(dpy, &processed_event, (xEvent *)xev);
278 }
279 XUnlockDisplay(dpy);
280 }
281
282 static int
ephyr_glx_error_handler(Display * _dpy,XErrorEvent * ev)283 ephyr_glx_error_handler(Display * _dpy, XErrorEvent * ev)
284 {
285 return 0;
286 }
287
288 struct ephyr_glamor *
ephyr_glamor_glx_screen_init(xcb_window_t win)289 ephyr_glamor_glx_screen_init(xcb_window_t win)
290 {
291 int (*oldErrorHandler) (Display *, XErrorEvent *);
292 static const float position[] = {
293 -1, -1,
294 1, -1,
295 1, 1,
296 -1, 1,
297 0, 1,
298 1, 1,
299 1, 0,
300 0, 0,
301 };
302 GLint old_vao;
303
304 GLXContext ctx;
305 struct ephyr_glamor *glamor;
306 GLXWindow glx_win;
307
308 glamor = calloc(1, sizeof(struct ephyr_glamor));
309 if (!glamor) {
310 FatalError("malloc");
311 return NULL;
312 }
313
314 glx_win = glXCreateWindow(dpy, fb_config, win, NULL);
315
316 if (ephyr_glamor_gles2) {
317 static const int context_attribs[] = {
318 GLX_CONTEXT_MAJOR_VERSION_ARB, 2,
319 GLX_CONTEXT_MINOR_VERSION_ARB, 0,
320 GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES_PROFILE_BIT_EXT,
321 0,
322 };
323 if (epoxy_has_glx_extension(dpy, DefaultScreen(dpy),
324 "GLX_EXT_create_context_es2_profile")) {
325 ctx = glXCreateContextAttribsARB(dpy, fb_config, NULL, True,
326 context_attribs);
327 } else {
328 FatalError("Xephyr -glamor_gles2 rquires "
329 "GLX_EXT_create_context_es2_profile\n");
330 }
331 } else {
332 if (epoxy_has_glx_extension(dpy, DefaultScreen(dpy),
333 "GLX_ARB_create_context")) {
334 static const int context_attribs[] = {
335 GLX_CONTEXT_PROFILE_MASK_ARB,
336 GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
337 GLX_CONTEXT_MAJOR_VERSION_ARB,
338 GLAMOR_GL_CORE_VER_MAJOR,
339 GLX_CONTEXT_MINOR_VERSION_ARB,
340 GLAMOR_GL_CORE_VER_MINOR,
341 0,
342 };
343 oldErrorHandler = XSetErrorHandler(ephyr_glx_error_handler);
344 ctx = glXCreateContextAttribsARB(dpy, fb_config, NULL, True,
345 context_attribs);
346 XSync(dpy, False);
347 XSetErrorHandler(oldErrorHandler);
348 } else {
349 ctx = NULL;
350 }
351
352 if (!ctx)
353 ctx = glXCreateContext(dpy, visual_info, NULL, True);
354 }
355 if (ctx == NULL)
356 FatalError("glXCreateContext failed\n");
357
358 if (!glXMakeCurrent(dpy, glx_win, ctx))
359 FatalError("glXMakeCurrent failed\n");
360
361 glamor->ctx = ctx;
362 glamor->win = win;
363 glamor->glx_win = glx_win;
364 ephyr_glamor_setup_texturing_shader(glamor);
365
366 glGenVertexArrays(1, &glamor->vao);
367 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
368 glBindVertexArray(glamor->vao);
369
370 glGenBuffers(1, &glamor->vbo);
371
372 glBindBuffer(GL_ARRAY_BUFFER, glamor->vbo);
373 glBufferData(GL_ARRAY_BUFFER, sizeof (position), position, GL_STATIC_DRAW);
374
375 ephyr_glamor_set_vertices(glamor);
376 glBindVertexArray(old_vao);
377
378 return glamor;
379 }
380
381 void
ephyr_glamor_glx_screen_fini(struct ephyr_glamor * glamor)382 ephyr_glamor_glx_screen_fini(struct ephyr_glamor *glamor)
383 {
384 glXMakeCurrent(dpy, None, NULL);
385 glXDestroyContext(dpy, glamor->ctx);
386 glXDestroyWindow(dpy, glamor->glx_win);
387
388 free(glamor);
389 }
390
391 xcb_visualtype_t *
ephyr_glamor_get_visual(void)392 ephyr_glamor_get_visual(void)
393 {
394 xcb_screen_t *xscreen =
395 xcb_aux_get_screen(XGetXCBConnection(dpy), DefaultScreen(dpy));
396 int attribs[] = {
397 GLX_RENDER_TYPE, GLX_RGBA_BIT,
398 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
399 GLX_RED_SIZE, 1,
400 GLX_GREEN_SIZE, 1,
401 GLX_BLUE_SIZE, 1,
402 GLX_DOUBLEBUFFER, 1,
403 None
404 };
405 int event_base = 0, error_base = 0, nelements;
406 GLXFBConfig *fbconfigs;
407
408 if (!glXQueryExtension (dpy, &error_base, &event_base))
409 FatalError("Couldn't find GLX extension\n");
410
411 fbconfigs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs, &nelements);
412 if (!nelements)
413 FatalError("Couldn't choose an FBConfig\n");
414 fb_config = fbconfigs[0];
415 free(fbconfigs);
416
417 visual_info = glXGetVisualFromFBConfig(dpy, fb_config);
418 if (visual_info == NULL)
419 FatalError("Couldn't get RGB visual\n");
420
421 return xcb_aux_find_visual_by_id(xscreen, visual_info->visualid);
422 }
423
424 void
ephyr_glamor_set_window_size(struct ephyr_glamor * glamor,unsigned width,unsigned height)425 ephyr_glamor_set_window_size(struct ephyr_glamor *glamor,
426 unsigned width, unsigned height)
427 {
428 if (!glamor)
429 return;
430
431 glamor->width = width;
432 glamor->height = height;
433 }
434