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 "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
181 xcb_connection_t *
ephyr_glamor_connect(void)182 ephyr_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
193 void
ephyr_glamor_set_texture(struct ephyr_glamor * glamor,uint32_t tex)194 ephyr_glamor_set_texture(struct ephyr_glamor *glamor, uint32_t tex)
195 {
196 glamor->tex = tex;
197 }
198
199 static void
ephyr_glamor_set_vertices(struct ephyr_glamor * glamor)200 ephyr_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
211 void
ephyr_glamor_damage_redisplay(struct ephyr_glamor * glamor,struct pixman_region16 * damage)212 ephyr_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 */
251 void
ephyr_glamor_process_event(xcb_generic_event_t * xev)252 ephyr_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
278 static int
ephyr_glx_error_handler(Display * _dpy,XErrorEvent * ev)279 ephyr_glx_error_handler(Display * _dpy, XErrorEvent * ev)
280 {
281 return 0;
282 }
283
284 struct ephyr_glamor *
ephyr_glamor_glx_screen_init(xcb_window_t win)285 ephyr_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 rquires "
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
377 void
ephyr_glamor_glx_screen_fini(struct ephyr_glamor * glamor)378 ephyr_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
387 xcb_visualtype_t *
ephyr_glamor_get_visual(void)388 ephyr_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
420 void
ephyr_glamor_set_window_size(struct ephyr_glamor * glamor,unsigned width,unsigned height)421 ephyr_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