]> git.cworth.org Git - apitrace/blob - retrace/glws_egl_xlib.cpp
retrace: Implement glxCopySubBufferMESA
[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, bool pbuffer) :
96         Drawable(vis, w, h, pbuffer),
97         api(EGL_OPENGL_ES_API)
98     {
99         XVisualInfo *visinfo = static_cast<const EglVisual *>(visual)->visinfo;
100
101         Window root = RootWindow(display, screen);
102
103         /* window attributes */
104         XSetWindowAttributes attr;
105         attr.background_pixel = 0;
106         attr.border_pixel = 0;
107         attr.colormap = XCreateColormap(display, root, visinfo->visual, AllocNone);
108         attr.event_mask = StructureNotifyMask;
109
110         unsigned long mask;
111         mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
112
113         int x = 0, y = 0;
114
115         window = XCreateWindow(
116             display, root,
117             x, y, width, height,
118             0,
119             visinfo->depth,
120             InputOutput,
121             visinfo->visual,
122             mask,
123             &attr);
124
125         XSizeHints sizehints;
126         sizehints.x = x;
127         sizehints.y = y;
128         sizehints.width  = width;
129         sizehints.height = height;
130         sizehints.flags = USSize | USPosition;
131         XSetNormalHints(display, window, &sizehints);
132
133         const char *name = "glretrace";
134         XSetStandardProperties(
135             display, window, name, name,
136             None, (char **)NULL, 0, &sizehints);
137
138         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
139
140         EGLConfig config = static_cast<const EglVisual *>(visual)->config;
141         surface = eglCreateWindowSurface(eglDisplay, config, (EGLNativeWindowType)window, NULL);
142     }
143
144     void waitForEvent(int type) {
145         XEvent event;
146         do {
147             XWindowEvent(display, window, StructureNotifyMask, &event);
148             describeEvent(event);
149         } while (event.type != type);
150     }
151
152     ~EglDrawable() {
153         eglDestroySurface(eglDisplay, surface);
154         eglWaitClient();
155         XDestroyWindow(display, window);
156         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
157     }
158
159     void
160     recreate(void) {
161         EGLContext currentContext = eglGetCurrentContext();
162         EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
163         EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ);
164         bool rebindDrawSurface = currentDrawSurface == surface;
165         bool rebindReadSurface = currentReadSurface == surface;
166
167         if (rebindDrawSurface || rebindReadSurface) {
168             eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
169         }
170
171         eglDestroySurface(eglDisplay, surface);
172
173         EGLConfig config = static_cast<const EglVisual *>(visual)->config;
174         surface = eglCreateWindowSurface(eglDisplay, config, (EGLNativeWindowType)window, NULL);
175
176         if (rebindDrawSurface || rebindReadSurface) {
177             eglMakeCurrent(eglDisplay, surface, surface, currentContext);
178         }
179     }
180
181     void
182     resize(int w, int h) {
183         if (w == width && h == height) {
184             return;
185         }
186
187         eglWaitClient();
188
189         // We need to ensure that pending events are processed here, and XSync
190         // with discard = True guarantees that, but it appears the limited
191         // event processing we do so far is sufficient
192         //XSync(display, True);
193
194         Drawable::resize(w, h);
195
196         // Tell the window manager to respect the requested size
197         XSizeHints size_hints;
198         size_hints.max_width  = size_hints.min_width  = w;
199         size_hints.max_height = size_hints.min_height = h;
200         size_hints.flags = PMinSize | PMaxSize;
201         XSetWMNormalHints(display, window, &size_hints);
202
203         XResizeWindow(display, window, w, h);
204
205         waitForEvent(ConfigureNotify);
206
207         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
208
209         /*
210          * Some implementations won't update the backbuffer unless we recreate
211          * the EGL surface.
212          */
213
214         int eglWidth;
215         int eglHeight;
216
217         eglQuerySurface(eglDisplay, surface, EGL_WIDTH, &eglWidth);
218         eglQuerySurface(eglDisplay, surface, EGL_HEIGHT, &eglHeight);
219
220         if (eglWidth != width || eglHeight != height) {
221             recreate();
222
223             eglQuerySurface(eglDisplay, surface, EGL_WIDTH, &eglWidth);
224             eglQuerySurface(eglDisplay, surface, EGL_HEIGHT, &eglHeight);
225         }
226
227         assert(eglWidth == width);
228         assert(eglHeight == height);
229     }
230
231     void show(void) {
232         if (visible) {
233             return;
234         }
235
236         eglWaitClient();
237
238         XMapWindow(display, window);
239
240         waitForEvent(MapNotify);
241
242         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
243
244         Drawable::show();
245     }
246
247     void copySubBuffer(int x, int y, int width, int height) {
248         printf("glws_egl_xlib:copySubBuffer: Not yet implemented\n");
249         exit(1);
250     }
251
252     void swapBuffers(void) {
253         eglBindAPI(api);
254         eglSwapBuffers(eglDisplay, surface);
255     }
256 };
257
258
259 class EglContext : public Context
260 {
261 public:
262     EGLContext context;
263
264     EglContext(const Visual *vis, Profile prof, EGLContext ctx) :
265         Context(vis, prof),
266         context(ctx)
267     {}
268
269     ~EglContext() {
270         eglDestroyContext(eglDisplay, context);
271     }
272 };
273
274 /**
275  * Load the symbols from the specified shared object into global namespace, so
276  * that they can be later found by dlsym(RTLD_NEXT, ...);
277  */
278 static void
279 load(const char *filename)
280 {
281     if (!dlopen(filename, RTLD_GLOBAL | RTLD_LAZY)) {
282         std::cerr << "error: unable to open " << filename << "\n";
283         exit(1);
284     }
285 }
286
287 void
288 init(void) {
289     load("libEGL.so.1");
290
291     display = XOpenDisplay(NULL);
292     if (!display) {
293         std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n";
294         exit(1);
295     }
296
297     screen = DefaultScreen(display);
298
299     eglDisplay = eglGetDisplay((EGLNativeDisplayType)display);
300     if (eglDisplay == EGL_NO_DISPLAY) {
301         std::cerr << "error: unable to get EGL display\n";
302         XCloseDisplay(display);
303         exit(1);
304     }
305
306     EGLint major, minor;
307     if (!eglInitialize(eglDisplay, &major, &minor)) {
308         std::cerr << "error: unable to initialize EGL display\n";
309         XCloseDisplay(display);
310         exit(1);
311     }
312 }
313
314 void
315 cleanup(void) {
316     if (display) {
317         eglTerminate(eglDisplay);
318         XCloseDisplay(display);
319         display = NULL;
320     }
321 }
322
323 Visual *
324 createVisual(bool doubleBuffer, Profile profile) {
325     EglVisual *visual = new EglVisual();
326     // possible combinations
327     const EGLint api_bits_gl[7] = {
328         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
329         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT,
330         EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT,
331         EGL_OPENGL_BIT,
332         EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
333         EGL_OPENGL_ES2_BIT,
334         EGL_OPENGL_ES_BIT,
335     };
336     const EGLint api_bits_gles1[7] = {
337         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
338         EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
339         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT,
340         EGL_OPENGL_ES_BIT,
341         EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT,
342         EGL_OPENGL_BIT,
343         EGL_OPENGL_ES2_BIT,
344     };
345     const EGLint api_bits_gles2[7] = {
346         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
347         EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT,
348         EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT,
349         EGL_OPENGL_ES2_BIT,
350         EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT,
351         EGL_OPENGL_BIT,
352         EGL_OPENGL_ES_BIT,
353     };
354     const EGLint *api_bits;
355
356     switch(profile) {
357     case PROFILE_COMPAT:
358         api_bits = api_bits_gl;
359         break;
360     case PROFILE_ES1:
361         api_bits = api_bits_gles1;
362         break;
363     case PROFILE_ES2:
364         api_bits = api_bits_gles2;
365         break;
366     default:
367         return NULL;
368     };
369
370     for (int i = 0; i < 7; i++) {
371         Attributes<EGLint> attribs;
372
373         attribs.add(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
374         attribs.add(EGL_RED_SIZE, 1);
375         attribs.add(EGL_GREEN_SIZE, 1);
376         attribs.add(EGL_BLUE_SIZE, 1);
377         attribs.add(EGL_ALPHA_SIZE, 1);
378         attribs.add(EGL_DEPTH_SIZE, 1);
379         attribs.add(EGL_STENCIL_SIZE, 1);
380         attribs.add(EGL_RENDERABLE_TYPE, api_bits[i]);
381         attribs.end(EGL_NONE);
382
383         EGLint num_configs, vid;
384         if (eglChooseConfig(eglDisplay, attribs, &visual->config, 1, &num_configs) &&
385             num_configs == 1 &&
386             eglGetConfigAttrib(eglDisplay, visual->config, EGL_NATIVE_VISUAL_ID, &vid)) {
387             XVisualInfo templ;
388             int num_visuals;
389
390             templ.visualid = vid;
391             visual->visinfo = XGetVisualInfo(display, VisualIDMask, &templ, &num_visuals);
392             break;
393         }
394     }
395
396     assert(visual->visinfo);
397
398     return visual;
399 }
400
401 Drawable *
402 createDrawable(const Visual *visual, int width, int height, bool pbuffer)
403 {
404     return new EglDrawable(visual, width, height, pbuffer);
405 }
406
407 Context *
408 createContext(const Visual *_visual, Context *shareContext, Profile profile, bool debug)
409 {
410     const EglVisual *visual = static_cast<const EglVisual *>(_visual);
411     EGLContext share_context = EGL_NO_CONTEXT;
412     EGLContext context;
413     Attributes<EGLint> attribs;
414
415     if (shareContext) {
416         share_context = static_cast<EglContext*>(shareContext)->context;
417     }
418
419     EGLint api = eglQueryAPI();
420
421     switch (profile) {
422     case PROFILE_COMPAT:
423         load("libGL.so.1");
424         eglBindAPI(EGL_OPENGL_API);
425         break;
426     case PROFILE_CORE:
427         assert(0);
428         return NULL;
429     case PROFILE_ES1:
430         load("libGLESv1_CM.so.1");
431         eglBindAPI(EGL_OPENGL_ES_API);
432         break;
433     case PROFILE_ES2:
434         load("libGLESv2.so.2");
435         eglBindAPI(EGL_OPENGL_ES_API);
436         attribs.add(EGL_CONTEXT_CLIENT_VERSION, 2);
437         break;
438     default:
439         return NULL;
440     }
441
442     attribs.end(EGL_NONE);
443
444     context = eglCreateContext(eglDisplay, visual->config, share_context, attribs);
445     if (!context)
446         return NULL;
447
448     eglBindAPI(api);
449
450     return new EglContext(visual, profile, context);
451 }
452
453 bool
454 makeCurrent(Drawable *drawable, Context *context)
455 {
456     if (!drawable || !context) {
457         return eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
458     } else {
459         EglDrawable *eglDrawable = static_cast<EglDrawable *>(drawable);
460         EglContext *eglContext = static_cast<EglContext *>(context);
461         EGLBoolean ok;
462
463         ok = eglMakeCurrent(eglDisplay, eglDrawable->surface,
464                             eglDrawable->surface, eglContext->context);
465
466         if (ok) {
467             EGLint api;
468
469             eglQueryContext(eglDisplay, eglContext->context,
470                             EGL_CONTEXT_CLIENT_TYPE, &api);
471
472             eglDrawable->api = api;
473         }
474
475         return ok;
476     }
477 }
478
479 bool
480 processEvents(void) {
481     while (XPending(display) > 0) {
482         XEvent event;
483         XNextEvent(display, &event);
484         describeEvent(event);
485     }
486     return true;
487 }
488
489
490 } /* namespace glws */