]> git.cworth.org Git - apitrace/blob - retrace/glws_egl_xlib.cpp
Merge remote-tracking branch 'github/master' into d2d
[apitrace] / retrace / glws_egl_xlib.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 LunarG, Inc.
4  * Copyright 2011 Jose Fonseca
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 #include <assert.h>
28 #include <stdlib.h>
29
30 #include <iostream>
31
32 #include <dlfcn.h>
33
34 #include "glproc.hpp"
35 #include "glws.hpp"
36
37
38 namespace glws {
39
40
41 static Display *display = NULL;
42 static EGLDisplay eglDisplay = EGL_NO_DISPLAY;
43 static int screen = 0;
44
45
46 class EglVisual : public Visual
47 {
48 public:
49     EGLConfig config;
50     XVisualInfo *visinfo;
51
52     EglVisual() :
53         config(0),
54         visinfo(0)
55     {}
56
57     ~EglVisual() {
58         XFree(visinfo);
59     }
60 };
61
62
63 static void describeEvent(const XEvent &event) {
64     if (0) {
65         switch (event.type) {
66         case ConfigureNotify:
67             std::cerr << "ConfigureNotify";
68             break;
69         case Expose:
70             std::cerr << "Expose";
71             break;
72         case KeyPress:
73             std::cerr << "KeyPress";
74             break;
75         case MapNotify:
76             std::cerr << "MapNotify";
77             break;
78         case ReparentNotify:
79             std::cerr << "ReparentNotify";
80             break;
81         default:
82             std::cerr << "Event " << event.type;
83         }
84         std::cerr << " " << event.xany.window << "\n";
85     }
86 }
87
88 class EglDrawable : public Drawable
89 {
90 public:
91     Window window;
92     EGLSurface surface;
93     EGLint api;
94
95     EglDrawable(const Visual *vis, int w, int h) :
96         Drawable(vis, w, h), api(EGL_OPENGL_ES_API)
97     {
98         XVisualInfo *visinfo = static_cast<const EglVisual *>(visual)->visinfo;
99
100         Window root = RootWindow(display, screen);
101
102         /* window attributes */
103         XSetWindowAttributes attr;
104         attr.background_pixel = 0;
105         attr.border_pixel = 0;
106         attr.colormap = XCreateColormap(display, root, visinfo->visual, AllocNone);
107         attr.event_mask = StructureNotifyMask;
108
109         unsigned long mask;
110         mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
111
112         int x = 0, y = 0;
113
114         window = XCreateWindow(
115             display, root,
116             x, y, width, height,
117             0,
118             visinfo->depth,
119             InputOutput,
120             visinfo->visual,
121             mask,
122             &attr);
123
124         XSizeHints sizehints;
125         sizehints.x = x;
126         sizehints.y = y;
127         sizehints.width  = width;
128         sizehints.height = height;
129         sizehints.flags = USSize | USPosition;
130         XSetNormalHints(display, window, &sizehints);
131
132         const char *name = "glretrace";
133         XSetStandardProperties(
134             display, window, name, name,
135             None, (char **)NULL, 0, &sizehints);
136
137         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
138
139         EGLConfig config = static_cast<const EglVisual *>(visual)->config;
140         surface = eglCreateWindowSurface(eglDisplay, config, (EGLNativeWindowType)window, NULL);
141     }
142
143     void waitForEvent(int type) {
144         XEvent event;
145         do {
146             XWindowEvent(display, window, StructureNotifyMask, &event);
147             describeEvent(event);
148         } while (event.type != type);
149     }
150
151     ~EglDrawable() {
152         eglDestroySurface(eglDisplay, surface);
153         eglWaitClient();
154         XDestroyWindow(display, window);
155         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
156     }
157
158     void
159     recreate(void) {
160         EGLContext currentContext = eglGetCurrentContext();
161         EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
162         EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_DRAW);
163         bool rebindDrawSurface = currentDrawSurface == surface;
164         bool rebindReadSurface = currentReadSurface == surface;
165
166         if (rebindDrawSurface || rebindReadSurface) {
167             eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
168         }
169
170         eglDestroySurface(eglDisplay, surface);
171
172         EGLConfig config = static_cast<const EglVisual *>(visual)->config;
173         surface = eglCreateWindowSurface(eglDisplay, config, (EGLNativeWindowType)window, NULL);
174
175         if (rebindDrawSurface || rebindReadSurface) {
176             eglMakeCurrent(eglDisplay, surface, surface, currentContext);
177         }
178     }
179
180     void
181     resize(int w, int h) {
182         if (w == width && h == height) {
183             return;
184         }
185
186         eglWaitClient();
187
188         // We need to ensure that pending events are processed here, and XSync
189         // with discard = True guarantees that, but it appears the limited
190         // event processing we do so far is sufficient
191         //XSync(display, True);
192
193         Drawable::resize(w, h);
194
195         XResizeWindow(display, window, w, h);
196
197         // Tell the window manager to respect the requested size
198         XSizeHints size_hints;
199         size_hints.max_width  = size_hints.min_width  = w;
200         size_hints.max_height = size_hints.min_height = h;
201         size_hints.flags = PMinSize | PMaxSize;
202         XSetWMNormalHints(display, window, &size_hints);
203
204         waitForEvent(ConfigureNotify);
205
206         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
207
208         /*
209          * Some implementations won't update the backbuffer unless we recreate
210          * the EGL surface.
211          */
212
213         int eglWidth;
214         int eglHeight;
215
216         eglQuerySurface(eglDisplay, surface, EGL_WIDTH, &eglWidth);
217         eglQuerySurface(eglDisplay, surface, EGL_HEIGHT, &eglHeight);
218
219         if (eglWidth != width || eglHeight != height) {
220             recreate();
221
222             eglQuerySurface(eglDisplay, surface, EGL_WIDTH, &eglWidth);
223             eglQuerySurface(eglDisplay, surface, EGL_HEIGHT, &eglHeight);
224         }
225
226         assert(eglWidth == width);
227         assert(eglHeight == height);
228     }
229
230     void show(void) {
231         if (visible) {
232             return;
233         }
234
235         eglWaitClient();
236
237         XMapWindow(display, window);
238
239         waitForEvent(MapNotify);
240
241         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
242
243         Drawable::show();
244     }
245
246     void swapBuffers(void) {
247         eglBindAPI(api);
248         eglSwapBuffers(eglDisplay, surface);
249     }
250 };
251
252
253 class EglContext : public Context
254 {
255 public:
256     EGLContext context;
257
258     EglContext(const Visual *vis, Profile prof, EGLContext ctx) :
259         Context(vis, prof),
260         context(ctx)
261     {}
262
263     ~EglContext() {
264         eglDestroyContext(eglDisplay, context);
265     }
266 };
267
268 /**
269  * Load the symbols from the specified shared object into global namespace, so
270  * that they can be later found by dlsym(RTLD_NEXT, ...);
271  */
272 static void
273 load(const char *filename)
274 {
275     if (!dlopen(filename, RTLD_GLOBAL | RTLD_LAZY)) {
276         std::cerr << "error: unable to open " << filename << "\n";
277         exit(1);
278     }
279 }
280
281 void
282 init(void) {
283     load("libEGL.so.1");
284
285     display = XOpenDisplay(NULL);
286     if (!display) {
287         std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n";
288         exit(1);
289     }
290
291     screen = DefaultScreen(display);
292
293     eglDisplay = eglGetDisplay((EGLNativeDisplayType)display);
294     if (eglDisplay == EGL_NO_DISPLAY) {
295         std::cerr << "error: unable to get EGL display\n";
296         XCloseDisplay(display);
297         exit(1);
298     }
299
300     EGLint major, minor;
301     if (!eglInitialize(eglDisplay, &major, &minor)) {
302         std::cerr << "error: unable to initialize EGL display\n";
303         XCloseDisplay(display);
304         exit(1);
305     }
306 }
307
308 void
309 cleanup(void) {
310     if (display) {
311         eglTerminate(eglDisplay);
312         XCloseDisplay(display);
313         display = NULL;
314     }
315 }
316
317 Visual *
318 createVisual(bool doubleBuffer, Profile profile) {
319     EglVisual *visual = new EglVisual();
320     // possible combinations
321     const EGLint api_bits_gl[7] = {
322         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
323         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT,
324         EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT,
325         EGL_OPENGL_BIT,
326         EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
327         EGL_OPENGL_ES2_BIT,
328         EGL_OPENGL_ES_BIT,
329     };
330     const EGLint api_bits_gles1[7] = {
331         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
332         EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
333         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT,
334         EGL_OPENGL_ES_BIT,
335         EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT,
336         EGL_OPENGL_BIT,
337         EGL_OPENGL_ES2_BIT,
338     };
339     const EGLint api_bits_gles2[7] = {
340         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
341         EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
342         EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT,
343         EGL_OPENGL_ES2_BIT,
344         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT,
345         EGL_OPENGL_BIT,
346         EGL_OPENGL_ES_BIT,
347     };
348     const EGLint *api_bits;
349
350     switch(profile) {
351     case PROFILE_COMPAT:
352         api_bits = api_bits_gl;
353         break;
354     case PROFILE_ES1:
355         api_bits = api_bits_gles1;
356         break;
357     case PROFILE_ES2:
358         api_bits = api_bits_gles2;
359         break;
360     default:
361         return NULL;
362     };
363
364     for (int i = 0; i < 7; i++) {
365         Attributes<EGLint> attribs;
366
367         attribs.add(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
368         attribs.add(EGL_RED_SIZE, 1);
369         attribs.add(EGL_GREEN_SIZE, 1);
370         attribs.add(EGL_BLUE_SIZE, 1);
371         attribs.add(EGL_ALPHA_SIZE, 1);
372         attribs.add(EGL_DEPTH_SIZE, 1);
373         attribs.add(EGL_STENCIL_SIZE, 1);
374         attribs.add(EGL_RENDERABLE_TYPE, api_bits[i]);
375         attribs.end(EGL_NONE);
376
377         EGLint num_configs, vid;
378         if (eglChooseConfig(eglDisplay, attribs, &visual->config, 1, &num_configs) &&
379             num_configs == 1 &&
380             eglGetConfigAttrib(eglDisplay, visual->config, EGL_NATIVE_VISUAL_ID, &vid)) {
381             XVisualInfo templ;
382             int num_visuals;
383
384             templ.visualid = vid;
385             visual->visinfo = XGetVisualInfo(display, VisualIDMask, &templ, &num_visuals);
386             break;
387         }
388     }
389
390     assert(visual->visinfo);
391
392     return visual;
393 }
394
395 Drawable *
396 createDrawable(const Visual *visual, int width, int height)
397 {
398     return new EglDrawable(visual, width, height);
399 }
400
401 Context *
402 createContext(const Visual *_visual, Context *shareContext, Profile profile, bool debug)
403 {
404     const EglVisual *visual = static_cast<const EglVisual *>(_visual);
405     EGLContext share_context = EGL_NO_CONTEXT;
406     EGLContext context;
407     Attributes<EGLint> attribs;
408
409     if (shareContext) {
410         share_context = static_cast<EglContext*>(shareContext)->context;
411     }
412
413     EGLint api = eglQueryAPI();
414
415     switch (profile) {
416     case PROFILE_COMPAT:
417         load("libGL.so.1");
418         eglBindAPI(EGL_OPENGL_API);
419         break;
420     case PROFILE_CORE:
421         assert(0);
422         return NULL;
423     case PROFILE_ES1:
424         load("libGLESv1_CM.so.1");
425         eglBindAPI(EGL_OPENGL_ES_API);
426         break;
427     case PROFILE_ES2:
428         load("libGLESv2.so.2");
429         eglBindAPI(EGL_OPENGL_ES_API);
430         attribs.add(EGL_CONTEXT_CLIENT_VERSION, 2);
431         break;
432     default:
433         return NULL;
434     }
435
436     attribs.end(EGL_NONE);
437
438     context = eglCreateContext(eglDisplay, visual->config, share_context, attribs);
439     if (!context)
440         return NULL;
441
442     eglBindAPI(api);
443
444     return new EglContext(visual, profile, context);
445 }
446
447 bool
448 makeCurrent(Drawable *drawable, Context *context)
449 {
450     if (!drawable || !context) {
451         return eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
452     } else {
453         EglDrawable *eglDrawable = static_cast<EglDrawable *>(drawable);
454         EglContext *eglContext = static_cast<EglContext *>(context);
455         EGLBoolean ok;
456
457         ok = eglMakeCurrent(eglDisplay, eglDrawable->surface,
458                             eglDrawable->surface, eglContext->context);
459
460         if (ok) {
461             EGLint api;
462
463             eglQueryContext(eglDisplay, eglContext->context,
464                             EGL_CONTEXT_CLIENT_TYPE, &api);
465
466             eglDrawable->api = api;
467         }
468
469         return ok;
470     }
471 }
472
473 bool
474 processEvents(void) {
475     while (XPending(display) > 0) {
476         XEvent event;
477         XNextEvent(display, &event);
478         describeEvent(event);
479     }
480     return true;
481 }
482
483
484 } /* namespace glws */