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