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