1 /*
2 * Copyright (C) 2007-2020 Kim Woelders
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 #include "config.h"
24
25 #include <stdlib.h>
26 #define GLX_GLXEXT_PROTOTYPES 1
27 #include <GL/gl.h>
28 #include <GL/glu.h>
29 #include <GL/glx.h>
30 #include <X11/extensions/Xcomposite.h>
31 #include <X11/extensions/Xrender.h>
32
33 #include "E.h"
34 #include "eglx.h"
35 #include "eimage.h"
36 #include "xwin.h"
37
38 #define ENABLE_DEBUG 1
39 #if ENABLE_DEBUG
40 #define Dprintf(fmt...) do { if(EDebug(EDBUG_TYPE_GLX))Eprintf(fmt); } while(0)
41 #define D2printf(fmt...) do { if(EDebug(EDBUG_TYPE_GLX)>1)Eprintf(fmt); } while(0)
42 #else
43 #define Dprintf(fmt...)
44 #define D2printf(fmt...)
45 #endif /* ENABLE_DEBUG */
46
47 #define ETEX_TYPE_IMAGE 1
48 #define ETEX_TYPE_PIXMAP 2
49
50 #if 1
51 #define TEXTURE_TARGET GL_TEXTURE_2D
52 #else
53 #define TEXTURE_TARGET GLX_TEXTURE_RECTANGLE_EXT
54 #endif
55
56 #ifdef HAVE_GLX_glXBindTexImageEXT
57
58 #define _glXBindTexImageEXT glXBindTexImageEXT
59 #define _glXReleaseTexImageEXT glXReleaseTexImageEXT
60
61 #else
62
63 #include <dlfcn.h>
64
65 /* GL functions and helper */
66 typedef void (*glXBindTexImageEXT_func)(Display * dpy,
67 GLXDrawable drawable,
68 int buffer,
69 const int *attrib_list);
70 typedef void (*glXReleaseTexImageEXT_func)(Display * dpy,
71 GLXDrawable drawable,
72 int buffer);
73 typedef void (*glXFuncPtr)(void);
74 typedef glXFuncPtr(*glXGetProcAddress_func) (const GLubyte *);
75
76 static glXBindTexImageEXT_func _glXBindTexImageEXT;
77 static glXReleaseTexImageEXT_func _glXReleaseTexImageEXT;
78 static glXGetProcAddress_func glx_get_proc_address;
79
80 static glXFuncPtr
get_func_addr(const char * name)81 get_func_addr(const char *name)
82 {
83 glXFuncPtr ret = NULL;
84
85 if (glx_get_proc_address)
86 ret = glx_get_proc_address((const GLubyte *)name);
87 if (!ret)
88 ret = (glXFuncPtr) dlsym(RTLD_DEFAULT, name);
89
90 return ret;
91 }
92
93 static int
glx_funcs_init(void)94 glx_funcs_init(void)
95 {
96 glx_get_proc_address = (glXGetProcAddress_func)
97 get_func_addr("glXGetProcAddress");
98 if (!glx_get_proc_address)
99 glx_get_proc_address = (glXGetProcAddress_func)
100 get_func_addr("glXGetProcAddressARB");
101
102 _glXBindTexImageEXT = (glXBindTexImageEXT_func)
103 get_func_addr("glXBindTexImageEXT");
104
105 _glXReleaseTexImageEXT = (glXReleaseTexImageEXT_func)
106 get_func_addr("glXReleaseTexImageEXT");
107
108 return !_glXBindTexImageEXT || !_glXReleaseTexImageEXT;
109 }
110
111 #endif /* HAVE_GLX_glXBindTexImageEXT */
112
113 static void EobjTexturesFree(void);
114
115 typedef struct {
116 XVisualInfo *vi;
117 GLXContext ctx;
118 GLXFBConfig fbc;
119 unsigned ctx_initialised:1;
120 } EGlContext;
121
122 static EGlContext egl;
123
124 #define FBCATTR(fbc, attr, want) _EGlFbcAttrib(fbc, #attr, attr, want)
125 static int
_EGlFbcAttrib(GLXFBConfig fbc,const char * name,int attr,int want)126 _EGlFbcAttrib(GLXFBConfig fbc, const char *name, int attr, int want)
127 {
128 int err, value;
129
130 value = 0xabbabeef;
131 err = glXGetFBConfigAttrib(disp, fbc, attr, &value);
132 if (err)
133 Eprintf(" %s *** Error %d ***\n", name, err);
134 else if (want > 0)
135 D2printf(" %s=%#x (want %#x)\n", name, value, want);
136 else
137 D2printf(" %s=%#x\n", name, value);
138
139 return value;
140 }
141
142 int
EGlInit(void)143 EGlInit(void)
144 {
145 /* From NV's README.txt (AddARGBGLXVisuals) */
146 static const int attrs[] = {
147 GLX_VISUAL_CAVEAT_EXT, GLX_NONE_EXT,
148 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
149 GLX_RENDER_TYPE, GLX_RGBA_BIT,
150 GLX_RED_SIZE, 1,
151 GLX_GREEN_SIZE, 1,
152 GLX_BLUE_SIZE, 1,
153 GLX_ALPHA_SIZE, 1,
154 GLX_DOUBLEBUFFER, True,
155 GLX_DEPTH_SIZE, 1,
156 0
157 };
158 XVisualInfo *vi;
159 GLXFBConfig *fbc;
160 int i, ix, num;
161 int value;
162 char *s;
163 XID vid = NoXID;
164 XRenderPictFormat *pictFormat;
165
166 Dprintf("EGlInit\n");
167
168 memset(&egl, 0, sizeof(EGlContext));
169
170 s = getenv("EVISUAL");
171 if (s)
172 {
173 vid = strtoul(s, NULL, 0);
174 Eprintf("Want Visual Id=%#lx\n", vid);
175 }
176
177 /* Create a GLX context */
178 fbc = glXChooseFBConfig(disp, DefaultScreen(disp), attrs, &num);
179 if (!fbc)
180 {
181 Eprintf("No FB configs\n");
182 return -1;
183 }
184
185 D2printf("Visuals found: %d\n", num);
186 ix = -1;
187 for (i = 0; i < num; i++)
188 {
189 vi = glXGetVisualFromFBConfig(disp, fbc[i]);
190 if (!vi)
191 continue;
192
193 D2printf("Checking Visual ID=%#lx depth=%d\n", vi->visualid, vi->depth);
194 if (vid && vi->visualid != vid)
195 continue;
196
197 #if 1
198 value = FBCATTR(fbc[i], GLX_FBCONFIG_ID, -1);
199 value = FBCATTR(fbc[i], GLX_CONFIG_CAVEAT, GLX_NONE);
200 value = FBCATTR(fbc[i], GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT);
201 value = FBCATTR(fbc[i], GLX_RENDER_TYPE, -1);
202 value = FBCATTR(fbc[i], GLX_X_VISUAL_TYPE, -1);
203 value = FBCATTR(fbc[i], GLX_X_RENDERABLE, -1);
204 value = FBCATTR(fbc[i], GLX_BUFFER_SIZE, -1);
205 value = FBCATTR(fbc[i], GLX_LEVEL, -1);
206 value = FBCATTR(fbc[i], GLX_TRANSPARENT_TYPE, -1);
207 #endif
208
209 #if 1
210 value = FBCATTR(fbc[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, 1);
211 value = FBCATTR(fbc[i], GLX_BIND_TO_TEXTURE_RGB_EXT, 1);
212 if (!value)
213 continue;
214 value = FBCATTR(fbc[i], GLX_BIND_TO_MIPMAP_TEXTURE_EXT, -1);
215 value = FBCATTR(fbc[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT,
216 GLX_TEXTURE_2D_BIT_EXT);
217 #if 0
218 if (!(value & GLX_TEXTURE_2D_BIT_EXT))
219 continue;
220 #endif
221 value = FBCATTR(fbc[i], GLX_Y_INVERTED_EXT, -1);
222 #endif
223
224 #if 1
225 /* We want an ARGB visual */
226 pictFormat = XRenderFindVisualFormat(disp, vi->visual);
227 if (!pictFormat)
228 continue;
229 if (pictFormat->direct.alphaMask == 0)
230 continue;
231 #endif
232
233 D2printf(" - passed\n");
234 if (ix < 0)
235 ix = i;
236
237 XFree(vi);
238 }
239
240 if (ix >= 0)
241 egl.fbc = fbc[ix];
242 XFree(fbc);
243
244 if (ix < 0)
245 {
246 Eprintf("No FB config match\n");
247 return -1;
248 }
249
250 #ifndef HAVE_GLX_glXBindTexImageEXT
251 if (glx_funcs_init())
252 {
253 Eprintf("glXBindTexImageEXT or glXReleaseTexImageEXT not available\n");
254 return -1;
255 }
256 #endif
257
258 egl.vi = glXGetVisualFromFBConfig(disp, egl.fbc);
259
260 egl.ctx = glXCreateNewContext(disp, egl.fbc, GLX_RGBA_TYPE, NULL, True);
261
262 Dprintf("Direct Rendering %s\n",
263 glXIsDirect(disp, egl.ctx) ? "enabled" : "not available");
264 Dprintf("Visual ID=%#lx depth %d\n", egl.vi->visualid, egl.vi->depth);
265
266 return 0;
267 }
268
269 void
EGlExit(void)270 EGlExit(void)
271 {
272 Dprintf("EGlExit\n");
273
274 EobjTexturesFree();
275
276 if (egl.vi)
277 {
278 XFree(egl.vi);
279 egl.vi = NULL;
280 }
281
282 if (egl.ctx)
283 {
284 EGlWindowDisconnect();
285 glXDestroyContext(disp, egl.ctx);
286 egl.ctx = NULL;
287 }
288 }
289
290 Visual *
EGlGetVisual(void)291 EGlGetVisual(void)
292 {
293 if (!egl.vi)
294 EGlInit();
295 return (egl.vi) ? egl.vi->visual : NULL;
296 }
297
298 unsigned int
EGlGetDepth(void)299 EGlGetDepth(void)
300 {
301 if (!egl.vi)
302 EGlInit();
303 return (egl.vi) ? egl.vi->depth : 0;
304 }
305
306 void
EGlWindowConnect(EX_Window xwin)307 EGlWindowConnect(EX_Window xwin)
308 {
309 glXMakeContextCurrent(disp, xwin, xwin, egl.ctx);
310
311 if (egl.ctx_initialised)
312 return;
313
314 /* First time */
315 glEnable(TEXTURE_TARGET);
316
317 glShadeModel(GL_SMOOTH);
318 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
319 glClearDepth(1.0f);
320 glEnable(GL_DEPTH_TEST);
321 glDepthFunc(GL_LEQUAL);
322 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
323
324 egl.ctx_initialised = 1;
325 }
326
327 void
EGlWindowDisconnect(void)328 EGlWindowDisconnect(void)
329 {
330 if (!glXMakeContextCurrent(disp, NoXID, NoXID, NULL))
331 {
332 Eprintf("Failed to release GL context.\n");
333 }
334 }
335
336 ETexture *
EGlTextureFromImage(EImage * im,int mode)337 EGlTextureFromImage(EImage * im, int mode)
338 {
339 ETexture *et;
340 int w, h;
341 unsigned char *data;
342
343 if (!im)
344 return NULL;
345
346 et = ECALLOC(ETexture, 1);
347 if (!et)
348 return NULL;
349
350 et->type = ETEX_TYPE_IMAGE;
351 et->target = TEXTURE_TARGET;
352 glGenTextures(1, &et->texture);
353 glBindTexture(et->target, et->texture);
354
355 EImageGetSize(im, &w, &h);
356 data = (unsigned char *)EImageGetData(im);
357
358 switch (mode)
359 {
360 case 0: /* No filtering */
361 glTexImage2D(et->target, 0, GL_RGB8, w, h, 0, GL_BGRA,
362 GL_UNSIGNED_BYTE, data);
363 glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
364 glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
365 break;
366 case 1: /* Linear filtering */
367 glTexImage2D(et->target, 0, GL_RGB8, w, h, 0, GL_BGRA,
368 GL_UNSIGNED_BYTE, data);
369 glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
370 glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
371 break;
372 case 2: /* Mipmapping */
373 gluBuild2DMipmaps(et->target, GL_RGB8, w, h, GL_BGRA,
374 GL_UNSIGNED_BYTE, data);
375 glTexParameteri(et->target, GL_TEXTURE_MIN_FILTER,
376 GL_LINEAR_MIPMAP_NEAREST);
377 glTexParameteri(et->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
378 break;
379 }
380
381 return et;
382 }
383
384 static GLXPixmap
GetGlPixmap(EX_Window xwin,EX_Drawable draw)385 GetGlPixmap(EX_Window xwin, EX_Drawable draw)
386 {
387 static const int attrs[] = {
388 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
389 GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
390 0
391 };
392 EX_Pixmap pixmap;
393 GLXPixmap glxpixmap;
394
395 if (xwin == NoXID && draw == NoXID)
396 return 0;
397
398 pixmap = (draw) ? draw : XCompositeNameWindowPixmap(disp, xwin);
399 glxpixmap = glXCreatePixmap(disp, egl.fbc, pixmap, attrs);
400 Dprintf("GetGlPixmap: Window=%#x Drawable=%#x glxpixmap=%#lx\n",
401 xwin, draw, glxpixmap);
402
403 return glxpixmap;
404 }
405
406 static void
_EGlTextureFromDrawable(ETexture * et,EX_Drawable draw,int mode)407 _EGlTextureFromDrawable(ETexture * et, EX_Drawable draw, int mode)
408 {
409 if (!et || draw == NoXID)
410 return;
411
412 glBindTexture(et->target, et->texture);
413 et->glxpmap = GetGlPixmap(draw, (mode & 0x100) ? NoXID : draw);
414 if (et->glxpmap == NoXID)
415 return;
416
417 _glXBindTexImageEXT(disp, et->glxpmap, GLX_FRONT_LEFT_EXT, NULL);
418 #if 0 /* No! */
419 glXDestroyPixmap(disp, et->glxpmap);
420 #endif
421 }
422
423 ETexture *
EGlTextureFromDrawable(EX_Drawable draw,int mode)424 EGlTextureFromDrawable(EX_Drawable draw, int mode)
425 {
426 ETexture *et;
427
428 if (draw == NoXID)
429 return NULL;
430
431 et = ECALLOC(ETexture, 1);
432 if (!et)
433 return NULL;
434
435 et->type = ETEX_TYPE_PIXMAP;
436 et->target = TEXTURE_TARGET;
437 glGenTextures(1, &et->texture);
438 glBindTexture(et->target, et->texture);
439
440 _EGlTextureFromDrawable(et, draw, mode);
441
442 return et;
443 }
444
445 void
EGlTextureDestroy(ETexture * et)446 EGlTextureDestroy(ETexture * et)
447 {
448 if (!et)
449 return;
450
451 Dprintf("EGlTextureDestroy %d type=%u pmap=%#x\n", et->texture, et->type,
452 et->glxpmap);
453
454 EGlTextureInvalidate(et);
455 glDeleteTextures(1, &et->texture);
456 Efree(et);
457 }
458
459 void
EGlTextureInvalidate(ETexture * et)460 EGlTextureInvalidate(ETexture * et)
461 {
462 if (!et)
463 return;
464
465 Dprintf("EGlTextureInvalidate %d type=%u pmap=%#x\n", et->texture, et->type,
466 et->glxpmap);
467
468 switch (et->type)
469 {
470 case ETEX_TYPE_IMAGE:
471 break;
472 case ETEX_TYPE_PIXMAP:
473 if (!et->glxpmap)
474 break;
475 _glXReleaseTexImageEXT(disp, et->glxpmap, GLX_FRONT_LEFT_EXT);
476 glXDestroyPixmap(disp, et->glxpmap);
477 et->glxpmap = NoXID;
478 break;
479 }
480 }
481
482 #include "eobj.h"
483
484 static void
EobjTexturesFree(void)485 EobjTexturesFree(void)
486 {
487 int i, num;
488 EObj *const *eol;
489
490 eol = EobjListStackGet(&num);
491 for (i = 0; i < num; i++)
492 EobjTextureDestroy(eol[i]);
493 }
494
495 ETexture *
EobjGetTexture(EObj * eo)496 EobjGetTexture(EObj * eo)
497 {
498 if (eo->glhook)
499 {
500 if (eo->glhook->glxpmap == NoXID)
501 _EGlTextureFromDrawable(eo->glhook, EobjGetPixmap(eo), 0);
502 }
503 else
504 {
505 EobjTextureCreate(eo);
506 }
507
508 return eo->glhook;
509 }
510
511 void
EobjTextureCreate(EObj * eo)512 EobjTextureCreate(EObj * eo)
513 {
514 EX_Pixmap pmap;
515
516 pmap = EobjGetPixmap(eo);
517 if (pmap == NoXID)
518 return;
519
520 eo->glhook = EGlTextureFromDrawable(pmap, 0);
521 }
522
523 void
EobjTextureDestroy(EObj * eo)524 EobjTextureDestroy(EObj * eo)
525 {
526 EGlTextureDestroy(eo->glhook);
527 eo->glhook = NULL;
528 }
529
530 void
EobjTextureInvalidate(EObj * eo)531 EobjTextureInvalidate(EObj * eo)
532 {
533 EGlTextureInvalidate(eo->glhook);
534 }
535