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