1 
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5 #include <schroedinger/schro.h>
6 #include <schroedinger/schroasync.h>
7 #include <schroedinger/opengl/schroopengl.h>
8 #include <schroedinger/opengl/schroopenglcanvas.h>
9 #include <schroedinger/opengl/schroopenglshader.h>
10 #include <limits.h>
11 #include <GL/glew.h>
12 #include <GL/glxew.h>
13 
14 #define REQUIRED_TEXTURE_UNITS 9
15 
16 struct _SchroOpenGL {
17   int is_usable;
18   int is_visible;
19   int lock_count;
20   SchroMutex *mutex;
21   Display *display;
22   Window root;
23   int screen;
24   XVisualInfo *visual_info;
25   GLXContext context;
26   Window window;
27   void *tmp;
28   int tmp_size;
29   SchroOpenGLShaderLibrary *shader_library;
30   SchroOpenGLCanvasPool* canvas_pool;
31   SchroOpenGLCanvas *obmc_weight_canvas;
32 };
33 
34 static int
schro_opengl_x_error_handler(Display * display,XErrorEvent * event)35 schro_opengl_x_error_handler (Display *display, XErrorEvent *event)
36 {
37   char errormsg[512];
38 
39   XGetErrorText (display, event->error_code, errormsg, sizeof (errormsg));
40   SCHRO_ERROR ("Xlib error: %s", errormsg);
41 
42   return 0;
43 }
44 
45 static int
schro_opengl_open_display(SchroOpenGL * opengl,const char * display_name)46 schro_opengl_open_display (SchroOpenGL *opengl, const char *display_name)
47 {
48   SCHRO_ASSERT (opengl->display == NULL);
49 
50   opengl->display = XOpenDisplay (display_name);
51 
52   if (opengl->display == NULL) {
53     SCHRO_ERROR ("failed to open display %s", display_name);
54     return FALSE;
55   }
56 
57   XSynchronize (opengl->display, False);
58   XSetErrorHandler (schro_opengl_x_error_handler);
59 
60   opengl->root = DefaultRootWindow (opengl->display);
61   opengl->screen = DefaultScreen (opengl->display);
62 
63   return TRUE;
64 }
65 
66 static void
schro_opengl_close_display(SchroOpenGL * opengl)67 schro_opengl_close_display (SchroOpenGL *opengl)
68 {
69   if (opengl->display) {
70     XCloseDisplay (opengl->display);
71     opengl->display = NULL;
72   }
73 }
74 
75 static int
schro_opengl_create_window(SchroOpenGL * opengl)76 schro_opengl_create_window (SchroOpenGL *opengl)
77 {
78   int error_base;
79   int event_base;
80   int result;
81   int visual_attr[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8,
82       GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, None };
83   int mask;
84   XSetWindowAttributes window_attr;
85 
86   SCHRO_ASSERT (opengl->display != NULL);
87   SCHRO_ASSERT (opengl->root != None);
88 
89   result = glXQueryExtension (opengl->display, &error_base, &event_base);
90 
91   if (!result) {
92     SCHRO_ERROR ("missing GLX extension");
93     return FALSE;
94   }
95 
96   opengl->visual_info = glXChooseVisual (opengl->display, opengl->screen,
97       visual_attr);
98 
99   if (opengl->visual_info == NULL) {
100     SCHRO_ERROR ("no usable visual");
101     return FALSE;
102   }
103 
104   opengl->context = glXCreateContext (opengl->display, opengl->visual_info,
105       NULL, True);
106 
107   if (opengl->context == NULL) {
108     SCHRO_ERROR ("failed to create direct GLX context");
109 
110     XFree (opengl->visual_info);
111     opengl->visual_info = NULL;
112 
113     return FALSE;
114   }
115 
116   mask = CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect;
117 
118   window_attr.background_pixel = 0;
119   window_attr.border_pixel = 0;
120   window_attr.colormap = XCreateColormap (opengl->display, opengl->root,
121       opengl->visual_info->visual, AllocNone);
122   window_attr.override_redirect = False;
123 
124   opengl->window = XCreateWindow (opengl->display, opengl->root, 0, 0,
125       100, 100, 0, opengl->visual_info->depth, InputOutput,
126       opengl->visual_info->visual, mask, &window_attr);
127 
128   if (opengl->window == None) {
129     SCHRO_ERROR ("failed to create window with visual %ld",
130         opengl->visual_info->visualid);
131 
132     glXDestroyContext (opengl->display, opengl->context);
133     opengl->context = NULL;
134 
135     XFree (opengl->visual_info);
136     opengl->visual_info = NULL;
137 
138     return FALSE;
139   }
140 
141   XSync (opengl->display, FALSE);
142 
143   return TRUE;
144 }
145 
146 static void
schro_opengl_destroy_window(SchroOpenGL * opengl)147 schro_opengl_destroy_window (SchroOpenGL *opengl)
148 {
149   if (opengl->window != None) {
150     XDestroyWindow (opengl->display, opengl->window);
151     opengl->window = None;
152   }
153 
154   if (opengl->context) {
155     glXDestroyContext (opengl->display, opengl->context);
156     opengl->context = NULL;
157   }
158 
159   if (opengl->visual_info) {
160     XFree (opengl->visual_info);
161     opengl->visual_info = NULL;
162   }
163 }
164 
165 static int
schro_opengl_init_glew(SchroOpenGL * opengl)166 schro_opengl_init_glew (SchroOpenGL *opengl)
167 {
168   int ok = TRUE;
169   int major, minor, micro;
170   GLenum error;
171 
172   schro_opengl_lock (opengl);
173 
174   error = glewInit ();
175 
176   if (error != GLEW_OK) {
177     SCHRO_ERROR ("GLEW error: %s", glewGetErrorString (error));
178     ok = FALSE;
179   }
180 
181   major = atoi ((const char*) glewGetString (GLEW_VERSION_MAJOR));
182   minor = atoi ((const char*) glewGetString (GLEW_VERSION_MINOR));
183   micro = atoi ((const char*) glewGetString (GLEW_VERSION_MICRO));
184 
185   if (major < 1) {
186     SCHRO_ERROR ("missing GLEW >= 1.5.0");
187     ok = FALSE;
188   } else if (major == 1 && minor < 5) {
189     SCHRO_ERROR ("missing GLEW >= 1.5.0");
190     ok = FALSE;
191   } else if (major == 1 && minor == 5 && micro < 0) {
192     SCHRO_ERROR ("missing GLEW >= 1.5.0");
193     ok = FALSE;
194   }
195 
196   schro_opengl_unlock (opengl);
197 
198   return ok;
199 }
200 
201 static int
schro_opengl_check_essential_extensions(SchroOpenGL * opengl)202 schro_opengl_check_essential_extensions (SchroOpenGL *opengl)
203 {
204   int ok = TRUE;
205   //GLint texture_units;
206 
207   schro_opengl_lock (opengl);
208 
209   #define CHECK_EXTENSION(_name) \
210     if (!GLEW_##_name) { \
211       SCHRO_ERROR ("missing essential extension GL_" #_name); \
212       ok = FALSE; \
213     }
214 
215   #define CHECK_EXTENSION_GROUPS(_group1, _group2, _name) \
216     if (!GLEW_##_group1##_##_name && !GLEW_##_group2##_##_name) { \
217       SCHRO_ERROR ("missing essential extension GL_{" #_group1 "|" #_group2 "}_" #_name); \
218       ok = FALSE; \
219     }
220 
221   CHECK_EXTENSION (EXT_framebuffer_object)
222   CHECK_EXTENSION_GROUPS (ARB, NV, texture_rectangle)
223   CHECK_EXTENSION (ARB_multitexture)
224   CHECK_EXTENSION (ARB_shader_objects)
225   CHECK_EXTENSION (ARB_shading_language_100)
226   CHECK_EXTENSION (ARB_fragment_shader)
227 
228   #undef CHECK_EXTENSION
229   #undef CHECK_EXTENSION_GROUPS
230 
231   if (ok) {
232     /*glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &texture_units);
233 
234     if (texture_units < REQUIRED_TEXTURE_UNITS) {
235       SCHRO_ERROR ("GL_MAX_TEXTURE_UNITS_ARB >= %i required, have %i",
236           REQUIRED_TEXTURE_UNITS, texture_units);
237 
238       ok = FALSE;
239     }*/
240   }
241 
242   schro_opengl_unlock (opengl);
243 
244   return ok;
245 }
246 
schro_opengl_init(void)247 void schro_opengl_init (void)
248 {
249   XInitThreads ();
250 }
251 
252 SchroOpenGL *
schro_opengl_new(void)253 schro_opengl_new (void)
254 {
255   SchroOpenGL *opengl = schro_malloc0 (sizeof (SchroOpenGL));
256 
257   opengl->is_usable = TRUE;
258   opengl->is_visible = FALSE;
259   opengl->lock_count = 0;
260   opengl->display = NULL;
261   opengl->root = None;
262   opengl->screen = 0;
263   opengl->visual_info = NULL;
264   opengl->context = NULL;
265   opengl->window = None;
266   opengl->tmp = NULL;
267   opengl->tmp_size = 0;
268   opengl->shader_library = NULL;
269   opengl->canvas_pool = NULL;
270   opengl->obmc_weight_canvas = NULL;
271 
272   opengl->mutex = schro_mutex_new ();
273 
274   if (!schro_opengl_open_display (opengl, NULL)) {
275     opengl->is_usable = FALSE;
276     return opengl;
277   }
278 
279   if (!schro_opengl_create_window (opengl)) {
280     opengl->is_usable = FALSE;
281     return opengl;
282   }
283 
284   if (!schro_opengl_init_glew (opengl)) {
285     opengl->is_usable = FALSE;
286     return opengl;
287   }
288 
289   if (!schro_opengl_check_essential_extensions (opengl)) {
290     opengl->is_usable = FALSE;
291     return opengl;
292   }
293 
294   opengl->shader_library = schro_opengl_shader_library_new (opengl);
295 
296   opengl->canvas_pool = schro_opengl_canvas_pool_new (opengl);
297 
298   schro_opengl_canvas_check_flags ();
299 
300   opengl->obmc_weight_canvas = schro_opengl_canvas_new (opengl,
301       SCHRO_FRAME_FORMAT_S16_444, 32, 32);
302 
303   schro_opengl_lock (opengl);
304 
305   glMatrixMode (GL_MODELVIEW);
306   glLoadIdentity ();
307 
308   glMatrixMode (GL_PROJECTION);
309   glLoadIdentity ();
310 
311   glEnable (GL_TEXTURE_RECTANGLE_ARB);
312 
313   schro_opengl_unlock (opengl);
314 
315   //schro_opengl_set_visible (opengl, TRUE);
316 
317   return opengl;
318 }
319 
320 void
schro_opengl_free(SchroOpenGL * opengl)321 schro_opengl_free (SchroOpenGL *opengl)
322 {
323   SCHRO_ASSERT (opengl->lock_count == 0);
324 
325   if (opengl->shader_library) {
326     schro_opengl_shader_library_free (opengl->shader_library);
327     opengl->shader_library = NULL;
328   }
329 
330   if (opengl->obmc_weight_canvas) {
331     schro_opengl_canvas_free (opengl->obmc_weight_canvas);
332     opengl->obmc_weight_canvas = NULL;
333   }
334 
335   SCHRO_ASSERT (opengl->lock_count == 0);
336 
337   if (opengl->canvas_pool) {
338     schro_opengl_canvas_pool_free (opengl->canvas_pool);
339     opengl->canvas_pool = NULL;
340   }
341 
342   schro_opengl_destroy_window (opengl);
343   schro_opengl_close_display (opengl);
344 
345   if (opengl->tmp) {
346     schro_free (opengl->tmp);
347     opengl->tmp = NULL;
348   }
349 
350   schro_mutex_free (opengl->mutex);
351 
352   schro_free (opengl);
353 }
354 
355 int
schro_opengl_is_usable(SchroOpenGL * opengl)356 schro_opengl_is_usable (SchroOpenGL *opengl) {
357   return opengl->is_usable;
358 }
359 
360 void
schro_opengl_lock(SchroOpenGL * opengl)361 schro_opengl_lock (SchroOpenGL *opengl)
362 {
363   SCHRO_ASSERT (opengl->display != NULL);
364   SCHRO_ASSERT (opengl->window != None);
365   SCHRO_ASSERT (opengl->context != NULL);
366   SCHRO_ASSERT (opengl->lock_count < (INT_MAX - 1));
367 
368   schro_mutex_lock (opengl->mutex);
369 
370   if (opengl->lock_count == 0) {
371     XLockDisplay (opengl->display);
372 
373     if (!glXMakeCurrent (opengl->display, opengl->window, opengl->context)) {
374       SCHRO_ERROR ("glXMakeCurrent failed");
375     }
376 
377     XUnlockDisplay (opengl->display);
378   }
379 
380   ++opengl->lock_count;
381 
382   SCHRO_OPENGL_CHECK_ERROR
383 }
384 
385 void
schro_opengl_unlock(SchroOpenGL * opengl)386 schro_opengl_unlock (SchroOpenGL *opengl)
387 {
388 #if SCHRO_OPENGL_UNBIND_TEXTURES
389   int i;
390   GLint texture;
391 #endif
392   GLint framebuffer;
393 
394   SCHRO_ASSERT (opengl->display != NULL);
395   SCHRO_ASSERT (opengl->lock_count > 0);
396 
397   SCHRO_OPENGL_CHECK_ERROR
398 
399   --opengl->lock_count;
400 
401   if (opengl->lock_count == 0) {
402 #if SCHRO_OPENGL_UNBIND_TEXTURES
403     for (i = 0; i < REQUIRED_TEXTURE_UNITS; ++i) {
404       glActiveTextureARB (GL_TEXTURE0_ARB + i);
405       glGetIntegerv (GL_TEXTURE_BINDING_RECTANGLE_ARB, &texture);
406 
407       SCHRO_ASSERT (!glIsTexture (texture));
408       SCHRO_ASSERT (texture == 0);
409     }
410 
411     glActiveTextureARB (GL_TEXTURE0_ARB);
412 #endif
413 
414     glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &framebuffer);
415 
416     SCHRO_ASSERT (!glIsFramebufferEXT (framebuffer));
417     SCHRO_ASSERT (framebuffer == 0);
418 
419     if (GLEW_EXT_framebuffer_blit) {
420       glGetIntegerv (GL_READ_FRAMEBUFFER_BINDING_EXT, &framebuffer);
421 
422       SCHRO_ASSERT (!glIsFramebufferEXT (framebuffer));
423       SCHRO_ASSERT (framebuffer == 0);
424 
425       glGetIntegerv (GL_DRAW_FRAMEBUFFER_BINDING_EXT, &framebuffer);
426 
427       SCHRO_ASSERT (!glIsFramebufferEXT (framebuffer));
428       SCHRO_ASSERT (framebuffer == 0);
429     }
430 
431     XLockDisplay (opengl->display);
432 
433     if (!glXMakeCurrent (opengl->display, None, NULL)) {
434       SCHRO_ERROR ("glXMakeCurrent failed");
435     }
436 
437     XUnlockDisplay (opengl->display);
438   }
439 
440   schro_mutex_unlock (opengl->mutex);
441 }
442 
443 void
schro_opengl_check_error(const char * file,int line,const char * func)444 schro_opengl_check_error (const char* file, int line, const char* func)
445 {
446   GLenum error = glGetError ();
447 
448   if (error) {
449     SCHRO_ERROR ("GL Error 0x%04x in %s(%d) %s", (int) error, file, line, func);
450     //SCHRO_ASSERT (0);
451   }
452 }
453 
454 void
schro_opengl_check_framebuffer(const char * file,int line,const char * func)455 schro_opengl_check_framebuffer (const char *file, int line, const char *func)
456 {
457   switch (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT)) {
458     case GL_FRAMEBUFFER_COMPLETE_EXT:
459       break;
460     case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
461       SCHRO_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT in %s(%d) %s",
462           file, line, func);
463       break;
464     case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
465       SCHRO_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT in "
466           "%s(%d) %s", file, line, func);
467       break;
468     case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
469       SCHRO_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT in %s(%d) %s",
470           file, line, func);
471       break;
472     case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
473       SCHRO_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT in %s(%d) %s",
474           file, line, func);
475       break;
476     case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
477       SCHRO_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT in %s(%d) %s",
478           file, line, func);
479       break;
480     case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
481       SCHRO_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT in %s(%d) %s",
482           file, line, func);
483       break;
484     case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
485       SCHRO_ERROR ("GL_FRAMEBUFFER_UNSUPPORTED_EXT in %s(%d)", file, line,
486          func);
487       break;
488     default:
489       SCHRO_ERROR ("unknown error from glCheckFramebufferStatusEXT in "
490           "%s(%d) %s", file, line, func);
491       break;
492   }
493 }
494 
495 void
schro_opengl_set_visible(SchroOpenGL * opengl,int visible)496 schro_opengl_set_visible (SchroOpenGL *opengl, int visible)
497 {
498   if (opengl->is_visible == visible) {
499     return;
500   }
501 
502   opengl->is_visible = visible;
503 
504   if (opengl->is_visible) {
505     XMapWindow (opengl->display, opengl->window);
506   } else {
507     XUnmapWindow (opengl->display, opengl->window);
508   }
509 
510   XSync (opengl->display, FALSE);
511 }
512 
513 void
schro_opengl_setup_viewport(int width,int height)514 schro_opengl_setup_viewport (int width, int height)
515 {
516   glViewport (0, 0, width, height);
517 
518   glLoadIdentity ();
519   glOrtho (0, width, 0, height, -1, 1);
520 }
521 
522 void
schro_opengl_render_quad(int x,int y,int width,int height)523 schro_opengl_render_quad (int x, int y, int width, int height)
524 {
525   glBegin (GL_QUADS);
526   glTexCoord2f (x,         y);          glVertex3f (x,         y,          0);
527   glTexCoord2f (x + width, y);          glVertex3f (x + width, y,          0);
528   glTexCoord2f (x + width, y + height); glVertex3f (x + width, y + height, 0);
529   glTexCoord2f (x,         y + height); glVertex3f (x,         y + height, 0);
530   glEnd ();
531 }
532 
533 SchroOpenGLShaderLibrary *
schro_opengl_get_shader_library(SchroOpenGL * opengl)534 schro_opengl_get_shader_library (SchroOpenGL *opengl)
535 {
536   return opengl->shader_library;
537 }
538 
539 void *
schro_opengl_get_tmp(SchroOpenGL * opengl,int size)540 schro_opengl_get_tmp (SchroOpenGL *opengl, int size)
541 {
542   SCHRO_ASSERT (size > 0);
543 
544   if (opengl->tmp_size < size || !opengl->tmp) {
545     opengl->tmp_size = size;
546 
547     if (!opengl->tmp) {
548       opengl->tmp = schro_malloc (opengl->tmp_size);
549     } else {
550       opengl->tmp = schro_realloc (opengl->tmp, opengl->tmp_size);
551     }
552   }
553 
554   return opengl->tmp;
555 }
556 
557 SchroOpenGLCanvas *
schro_opengl_get_obmc_weight_canvas(SchroOpenGL * opengl,int width,int height)558 schro_opengl_get_obmc_weight_canvas (SchroOpenGL *opengl, int width,
559     int height)
560 {
561   if (width > opengl->obmc_weight_canvas->width ||
562       height > opengl->obmc_weight_canvas->height) {
563     schro_opengl_canvas_free (opengl->obmc_weight_canvas);
564 
565     opengl->obmc_weight_canvas = schro_opengl_canvas_new (opengl,
566         SCHRO_FRAME_FORMAT_S16_444, MAX (width, 64), MAX (height, 64));
567   }
568 
569   return opengl->obmc_weight_canvas;
570 }
571 
572 SchroOpenGLCanvasPool *
schro_opengl_get_canvas_pool(SchroOpenGL * opengl)573 schro_opengl_get_canvas_pool (SchroOpenGL *opengl)
574 {
575   return opengl->canvas_pool;
576 }
577 
578 static void *
schro_opengl_domain_alloc(int size)579 schro_opengl_domain_alloc (int size)
580 {
581   return schro_malloc0 (size);
582 }
583 
584 static void
schro_opengl_domain_free(void * ptr,int size)585 schro_opengl_domain_free (void *ptr, int size)
586 {
587   schro_free (ptr);
588 }
589 
590 SchroMemoryDomain *
schro_memory_domain_new_opengl(void)591 schro_memory_domain_new_opengl (void)
592 {
593   SchroMemoryDomain *domain;
594 
595   domain = schro_memory_domain_new ();
596   domain->flags = SCHRO_MEMORY_DOMAIN_OPENGL;
597   domain->alloc = schro_opengl_domain_alloc;
598   domain->free = schro_opengl_domain_free;
599 
600   return domain;
601 }
602 
603