Line 0
Link Here
|
|
|
1 |
/**************************************************************************\ |
2 |
* Copyright (c) Kongsberg Oil & Gas Technologies AS |
3 |
* All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions are |
7 |
* met: |
8 |
* |
9 |
* Redistributions of source code must retain the above copyright notice, |
10 |
* this list of conditions and the following disclaimer. |
11 |
* |
12 |
* Redistributions in binary form must reproduce the above copyright |
13 |
* notice, this list of conditions and the following disclaimer in the |
14 |
* documentation and/or other materials provided with the distribution. |
15 |
* |
16 |
* Neither the name of the copyright holder nor the names of its |
17 |
* contributors may be used to endorse or promote products derived from |
18 |
* this software without specific prior written permission. |
19 |
* |
20 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
21 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
22 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
23 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
24 |
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
25 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
26 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
27 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
28 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
29 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
30 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 |
\**************************************************************************/ |
32 |
|
33 |
/* |
34 |
* Environment variable controls available: |
35 |
* |
36 |
* - COIN_EGLGLUE_NO_PBUFFERS: set to 1 to force software rendering of |
37 |
* offscreen contexts. |
38 |
*/ |
39 |
|
40 |
#include "glue/gl_egl.h" |
41 |
#include "coindefs.h" |
42 |
|
43 |
#ifdef HAVE_CONFIG_H |
44 |
#include "config.h" |
45 |
#endif /* HAVE_CONFIG_H */ |
46 |
|
47 |
#include <cstdlib> |
48 |
#include <cstring> |
49 |
#include <cassert> |
50 |
|
51 |
#include <Inventor/C/tidbits.h> |
52 |
#include <Inventor/C/glue/gl.h> |
53 |
#include <Inventor/C/errors/debugerror.h> |
54 |
#include <Inventor/C/glue/dl.h> |
55 |
|
56 |
#include "glue/glp.h" |
57 |
#include "glue/dlp.h" |
58 |
|
59 |
/* ********************************************************************** */ |
60 |
|
61 |
#ifndef HAVE_EGL |
62 |
|
63 |
void * eglglue_getprocaddress(const cc_glglue * glue_in, const char * fname) |
64 |
{ |
65 |
assert(FALSE); return NULL; |
66 |
} |
67 |
|
68 |
void * eglglue_context_create_offscreen(unsigned int COIN_UNUSED_ARG(width), |
69 |
unsigned int COIN_UNUSED_ARG(height)) { |
70 |
assert(FALSE); return NULL; |
71 |
} |
72 |
|
73 |
SbBool eglglue_context_make_current(void * COIN_UNUSED_ARG(ctx)) |
74 |
{ |
75 |
assert(FALSE); return FALSE; |
76 |
} |
77 |
|
78 |
void eglglue_context_reinstate_previous(void * COIN_UNUSED_ARG(ctx)) |
79 |
{ |
80 |
assert(FALSE); |
81 |
} |
82 |
|
83 |
void eglglue_context_destruct(void * COIN_UNUSED_ARG(ctx)) |
84 |
{ |
85 |
assert(FALSE); |
86 |
} |
87 |
|
88 |
SbBool eglglue_context_pbuffer_max(void * ctx, unsigned int * lims) |
89 |
{ |
90 |
assert(FALSE); return FALSE; |
91 |
} |
92 |
|
93 |
#else /* HAVE_EGL */ |
94 |
|
95 |
/* ********************************************************************** */ |
96 |
|
97 |
#include <EGL/egl.h> |
98 |
#include <EGL/eglext.h> |
99 |
|
100 |
EGLDisplay eglglue_display = EGL_NO_DISPLAY; |
101 |
struct eglglue_contextdata; |
102 |
|
103 |
#define CASE_STR( value ) case value: return #value; |
104 |
const char* eglErrorString( EGLint error ) |
105 |
{ |
106 |
switch( error ) |
107 |
{ |
108 |
CASE_STR( EGL_SUCCESS ) |
109 |
CASE_STR( EGL_NOT_INITIALIZED ) |
110 |
CASE_STR( EGL_BAD_ACCESS ) |
111 |
CASE_STR( EGL_BAD_ALLOC ) |
112 |
CASE_STR( EGL_BAD_ATTRIBUTE ) |
113 |
CASE_STR( EGL_BAD_CONTEXT ) |
114 |
CASE_STR( EGL_BAD_CONFIG ) |
115 |
CASE_STR( EGL_BAD_CURRENT_SURFACE ) |
116 |
CASE_STR( EGL_BAD_DISPLAY ) |
117 |
CASE_STR( EGL_BAD_SURFACE ) |
118 |
CASE_STR( EGL_BAD_MATCH ) |
119 |
CASE_STR( EGL_BAD_PARAMETER ) |
120 |
CASE_STR( EGL_BAD_NATIVE_PIXMAP ) |
121 |
CASE_STR( EGL_BAD_NATIVE_WINDOW ) |
122 |
CASE_STR( EGL_CONTEXT_LOST ) |
123 |
default: return "Unknown"; |
124 |
} |
125 |
} |
126 |
const char* eglAPIString( EGLenum api ) |
127 |
{ |
128 |
switch( api ) |
129 |
{ |
130 |
CASE_STR( EGL_OPENGL_API ) |
131 |
CASE_STR( EGL_OPENGL_ES_API ) |
132 |
CASE_STR( EGL_OPENVG_API ) |
133 |
default: return "Unknown"; |
134 |
} |
135 |
} |
136 |
#undef CASE_STR |
137 |
|
138 |
struct eglglue_contextdata { |
139 |
EGLContext context; |
140 |
EGLSurface surface; |
141 |
EGLContext storedcontext; |
142 |
EGLSurface storedsurface; |
143 |
unsigned int width; |
144 |
unsigned int height; |
145 |
}; |
146 |
|
147 |
static struct eglglue_contextdata * |
148 |
eglglue_contextdata_init(unsigned int width, unsigned int height) |
149 |
{ |
150 |
struct eglglue_contextdata * ctx; |
151 |
ctx = (struct eglglue_contextdata *)malloc(sizeof(struct eglglue_contextdata)); |
152 |
|
153 |
ctx->context = EGL_NO_CONTEXT; |
154 |
ctx->surface = EGL_NO_SURFACE; |
155 |
ctx->storedcontext = EGL_NO_CONTEXT; |
156 |
ctx->storedsurface = EGL_NO_SURFACE; |
157 |
ctx->width = width; |
158 |
ctx->height = height; |
159 |
return ctx; |
160 |
} |
161 |
|
162 |
static EGLDisplay |
163 |
eglglue_get_display(void) |
164 |
{ |
165 |
if (eglglue_display == EGL_NO_DISPLAY) { |
166 |
eglglue_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, EGL_DEFAULT_DISPLAY, NULL); |
167 |
if (eglglue_display == EGL_NO_DISPLAY) { |
168 |
cc_debugerror_post("eglglue_get_display", |
169 |
"Display not found."); |
170 |
return EGL_NO_DISPLAY; |
171 |
} |
172 |
if (coin_glglue_debug()) { |
173 |
cc_debugerror_postinfo("eglglue_get_display", |
174 |
"got EGLDisplay==%p", |
175 |
eglglue_display); |
176 |
} |
177 |
} |
178 |
return eglglue_display; |
179 |
} |
180 |
|
181 |
void |
182 |
eglglue_init(cc_glglue * w) |
183 |
{ |
184 |
w->glx.isdirect = 1; |
185 |
w->glx.serverversion = NULL; |
186 |
w->glx.servervendor = NULL; |
187 |
w->glx.serverextensions = NULL; |
188 |
w->glx.clientversion = NULL; |
189 |
w->glx.clientvendor = NULL; |
190 |
w->glx.clientextensions = NULL; |
191 |
w->glx.glxextensions = NULL; |
192 |
|
193 |
w->glx.glXGetCurrentDisplay = (COIN_PFNGLXGETCURRENTDISPLAYPROC)eglglue_getprocaddress(w, "eglglue_get_display"); |
194 |
|
195 |
if (eglInitialize(eglglue_get_display(), &w->glx.version.major, &w->glx.version.minor) == EGL_FALSE) { |
196 |
cc_debugerror_post("eglglue_init", |
197 |
"Couldn't initialize EGL. %s", |
198 |
eglErrorString(eglGetError())); |
199 |
return; |
200 |
} |
201 |
|
202 |
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { |
203 |
cc_debugerror_post("eglglue_init", |
204 |
"eglBindAPI(EGL_OPENGL_API) failed. %s", |
205 |
eglErrorString(eglGetError())); |
206 |
return; |
207 |
} |
208 |
|
209 |
if (coin_glglue_debug()) { |
210 |
cc_debugerror_postinfo("eglglue_init", |
211 |
"EGL version: %d.%d", |
212 |
w->glx.version.major, |
213 |
w->glx.version.minor); |
214 |
cc_debugerror_postinfo("eglglue_init", |
215 |
"eglQueryString(EGL_VERSION)=='%s'", |
216 |
eglQueryString(eglglue_get_display(), EGL_VERSION)); |
217 |
cc_debugerror_postinfo("eglglue_init", |
218 |
"eglQueryString(EGL_VENDOR)=='%s'", |
219 |
eglQueryString(eglglue_get_display(), EGL_VENDOR)); |
220 |
cc_debugerror_postinfo("eglglue_init", |
221 |
"eglQueryString(EGL_CLIENT_APIS)=='%s'", |
222 |
eglQueryString(eglglue_get_display(), EGL_CLIENT_APIS)); |
223 |
cc_debugerror_postinfo("eglglue_init", |
224 |
"eglQueryAPI()=='%s'", |
225 |
eglAPIString(eglQueryAPI())); |
226 |
cc_debugerror_postinfo("eglglue_init", |
227 |
"eglQueryString(EGL_EXTENSIONS)=='%s'", |
228 |
eglQueryString(eglglue_get_display(), EGL_EXTENSIONS)); |
229 |
} |
230 |
} |
231 |
|
232 |
static void |
233 |
eglglue_contextdata_cleanup(struct eglglue_contextdata * ctx) |
234 |
{ |
235 |
if (ctx == NULL) { return; } |
236 |
if (eglglue_get_display() != EGL_NO_DISPLAY && ctx->context != EGL_NO_CONTEXT) eglDestroyContext(eglglue_get_display(), ctx->context); |
237 |
if (eglglue_get_display() != EGL_NO_DISPLAY && ctx->surface != EGL_NO_SURFACE) eglDestroySurface(eglglue_get_display(), ctx->surface); |
238 |
if (eglglue_get_display() != EGL_NO_DISPLAY && ctx->storedcontext != EGL_NO_CONTEXT) eglDestroyContext(eglglue_get_display(), ctx->storedcontext); |
239 |
if (eglglue_get_display() != EGL_NO_DISPLAY && ctx->storedsurface != EGL_NO_SURFACE) eglDestroySurface(eglglue_get_display(), ctx->storedsurface); |
240 |
free(ctx); |
241 |
} |
242 |
|
243 |
void * |
244 |
eglglue_context_create_offscreen(unsigned int width, unsigned int height) |
245 |
{ |
246 |
struct eglglue_contextdata * ctx; |
247 |
EGLint format; |
248 |
EGLint numConfigs; |
249 |
EGLConfig config; |
250 |
EGLint attrib[] = { |
251 |
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, |
252 |
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, |
253 |
EGL_RED_SIZE, 8, |
254 |
EGL_GREEN_SIZE, 8, |
255 |
EGL_BLUE_SIZE, 8, |
256 |
EGL_ALPHA_SIZE, 8, |
257 |
EGL_DEPTH_SIZE, 24, |
258 |
EGL_STENCIL_SIZE, 1, |
259 |
EGL_NONE |
260 |
}; |
261 |
|
262 |
EGLAttrib surface_attrib[] = { |
263 |
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, |
264 |
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, |
265 |
EGL_WIDTH, (EGLint) ctx->width, |
266 |
EGL_HEIGHT, (EGLint) ctx->height, |
267 |
EGL_NONE |
268 |
}; |
269 |
|
270 |
ctx = eglglue_contextdata_init(width, height); |
271 |
if (!ctx) return NULL; |
272 |
|
273 |
const int v = coin_glglue_stencil_bits_hack(); |
274 |
if (v != -1) { |
275 |
attrib[15] = v; |
276 |
} |
277 |
|
278 |
if (coin_glglue_debug()) { |
279 |
cc_debugerror_postinfo("eglglue_context_create_offscreen", |
280 |
"Creating offscreen context."); |
281 |
} |
282 |
|
283 |
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { |
284 |
cc_debugerror_post("eglglue_context_create_offscreen", |
285 |
"eglBindAPI(EGL_OPENGL_API) failed. %s", |
286 |
eglErrorString(eglGetError())); |
287 |
return NULL; |
288 |
} |
289 |
|
290 |
const char * env = coin_getenv("COIN_EGLGLUE_NO_PBUFFERS"); |
291 |
if (env && atoi(env) > 0) { |
292 |
attrib[3] = EGL_PIXMAP_BIT; |
293 |
if (coin_glglue_debug()) { |
294 |
cc_debugerror_postinfo("eglglue_context_create_offscreen", |
295 |
"Force software rendering."); |
296 |
} |
297 |
} |
298 |
|
299 |
eglChooseConfig(eglglue_get_display(), attrib, &config, 1, &numConfigs); |
300 |
if (numConfigs == 0) { |
301 |
if (attrib[3] == EGL_PBUFFER_BIT) { |
302 |
if (coin_glglue_debug()) { |
303 |
cc_debugerror_postinfo("eglglue_context_create_offscreen", |
304 |
"PBuffer offscreen rendering is NOT supported " |
305 |
"by the OpenGL driver. Try software rendering."); |
306 |
} |
307 |
attrib[3] = EGL_PIXMAP_BIT; |
308 |
eglChooseConfig(eglglue_get_display(), attrib, &config, 1, &numConfigs); |
309 |
} |
310 |
} |
311 |
if (numConfigs == 0) { |
312 |
cc_debugerror_post("eglglue_context_create_offscreen", |
313 |
"No matching EGL config. %s", |
314 |
eglErrorString(eglGetError())); |
315 |
eglglue_contextdata_cleanup(ctx); |
316 |
return NULL; |
317 |
} |
318 |
|
319 |
if (attrib[3] == EGL_PBUFFER_BIT) { |
320 |
ctx->surface = eglCreatePlatformWindowSurface(eglglue_get_display(), config, 0, surface_attrib); |
321 |
} else { |
322 |
ctx->surface = eglCreatePlatformPixmapSurface(eglglue_get_display(), config, 0, surface_attrib); |
323 |
} |
324 |
if (ctx->surface == EGL_NO_SURFACE) { |
325 |
cc_debugerror_post("eglglue_context_create_offscreen", |
326 |
"Couldn't create EGL surface. %s", |
327 |
eglErrorString(eglGetError())); |
328 |
eglglue_contextdata_cleanup(ctx); |
329 |
return NULL; |
330 |
} |
331 |
|
332 |
ctx->context = eglCreateContext(eglglue_get_display(), config, EGL_NO_CONTEXT, NULL); |
333 |
|
334 |
if (ctx->context == EGL_NO_CONTEXT) { |
335 |
cc_debugerror_post("eglglue_context_create_offscreen", |
336 |
"Couldn't create EGL context. %s", |
337 |
eglErrorString(eglGetError())); |
338 |
eglglue_contextdata_cleanup(ctx); |
339 |
return NULL; |
340 |
} |
341 |
|
342 |
if (coin_glglue_debug()) { |
343 |
cc_debugerror_postinfo("eglglue_context_create_offscreen", |
344 |
"created new %s offscreen context == %p", |
345 |
attrib[3] == EGL_PBUFFER_BIT ? "pBuffer" : "software", |
346 |
ctx->context); |
347 |
} |
348 |
return ctx; |
349 |
} |
350 |
|
351 |
SbBool |
352 |
eglglue_context_make_current(void * ctx) |
353 |
{ |
354 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
355 |
|
356 |
context->storedcontext = eglGetCurrentContext(); |
357 |
context->storedsurface = eglGetCurrentSurface(EGL_DRAW); |
358 |
if (eglMakeCurrent(eglglue_get_display(), context->surface, context->surface, context->context) == EGL_FALSE) { |
359 |
cc_debugerror_post("eglglue_context_make_current", |
360 |
"eglMakeCurrent failed: %s", |
361 |
eglErrorString(eglGetError())); |
362 |
return FALSE; |
363 |
} |
364 |
|
365 |
if (coin_glglue_debug()) { |
366 |
cc_debugerror_postinfo("eglglue_context_make_current", |
367 |
"EGL Context (0x%X)\n", |
368 |
context->context); |
369 |
} |
370 |
return TRUE; |
371 |
} |
372 |
|
373 |
void |
374 |
eglglue_context_reinstate_previous(void * ctx) |
375 |
{ |
376 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
377 |
|
378 |
if (context->storedcontext != EGL_NO_CONTEXT && context->storedsurface != EGL_NO_SURFACE) { |
379 |
if (eglMakeCurrent(eglglue_get_display(), context->storedsurface, context->storedsurface, context->storedcontext) == EGL_TRUE) { |
380 |
if (coin_glglue_debug()) { |
381 |
cc_debugerror_postinfo("eglglue_context_make_current", |
382 |
"EGL Context (0x%X)\n", |
383 |
context->context); |
384 |
} |
385 |
} else { |
386 |
cc_debugerror_post("eglglue_context_make_current", |
387 |
"eglMakeCurrent failed: %s", |
388 |
eglErrorString(eglGetError())); |
389 |
} |
390 |
} |
391 |
} |
392 |
|
393 |
void |
394 |
eglglue_context_destruct(void * ctx) |
395 |
{ |
396 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
397 |
|
398 |
if (coin_glglue_debug()) { |
399 |
cc_debugerror_postinfo("eglglue_context_destruct", |
400 |
"Destroying context %p", context->context); |
401 |
} |
402 |
eglglue_contextdata_cleanup(context); |
403 |
} |
404 |
|
405 |
void |
406 |
eglglue_context_bind_pbuffer(void * ctx) |
407 |
{ |
408 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
409 |
|
410 |
if (eglBindTexImage(eglglue_get_display(), context->surface, EGL_BACK_BUFFER) == EGL_FALSE) { |
411 |
cc_debugerror_post("eglglue_context_bind_pbuffer()" |
412 |
"after binding pbuffer: %s", |
413 |
eglErrorString(eglGetError())); |
414 |
} |
415 |
} |
416 |
|
417 |
void |
418 |
eglglue_context_release_pbuffer(void * ctx) |
419 |
{ |
420 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
421 |
|
422 |
if (eglReleaseTexImage(eglglue_get_display(), context->surface, EGL_BACK_BUFFER) == EGL_FALSE) { |
423 |
cc_debugerror_post("eglglue_context_release_pbuffer()" |
424 |
"releasing pbuffer: %s", |
425 |
eglErrorString(eglGetError())); |
426 |
} |
427 |
} |
428 |
|
429 |
SbBool |
430 |
eglglue_context_pbuffer_is_bound(void * ctx) |
431 |
{ |
432 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
433 |
GLint buffer = EGL_NONE; |
434 |
|
435 |
if(eglQueryContext(eglglue_get_display(), context->context, EGL_RENDER_BUFFER, &buffer) == EGL_FALSE) { |
436 |
cc_debugerror_post("eglglue_context_pbuffer_is_bound()" |
437 |
"after query pbuffer: %s", |
438 |
eglErrorString(eglGetError())); |
439 |
} |
440 |
return buffer == EGL_BACK_BUFFER; |
441 |
} |
442 |
|
443 |
SbBool |
444 |
eglglue_context_can_render_to_texture(void * ctx) |
445 |
{ |
446 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
447 |
return context->surface != EGL_NO_SURFACE; |
448 |
} |
449 |
|
450 |
SbBool |
451 |
eglglue_context_pbuffer_max(void * ctx, unsigned int * lims) |
452 |
{ |
453 |
int returnval, attribval, i; |
454 |
const int attribs[] = { |
455 |
EGL_MAX_PBUFFER_WIDTH, EGL_MAX_PBUFFER_HEIGHT, EGL_MAX_PBUFFER_PIXELS |
456 |
}; |
457 |
struct eglglue_contextdata * context = (struct eglglue_contextdata *)ctx; |
458 |
|
459 |
if (context->surface == EGL_NO_SURFACE) { return FALSE; } |
460 |
|
461 |
for (i = 0; i < 3; i++) { |
462 |
if(eglQuerySurface(eglglue_get_display(), context->surface, attribs[i], &attribval) == EGL_FALSE) { |
463 |
cc_debugerror_post("eglglue_context_pbuffer_max", |
464 |
"eglQuerySurface() failed, " |
465 |
"returned error code %s", |
466 |
eglErrorString(eglGetError())); |
467 |
return FALSE; |
468 |
} |
469 |
assert(attribval >= 0); |
470 |
lims[i] = (unsigned int)attribval; |
471 |
} |
472 |
return TRUE; |
473 |
} |
474 |
|
475 |
void * |
476 |
eglglue_getprocaddress(const cc_glglue * glue_in, const char * fname) |
477 |
{ |
478 |
return (void *)eglGetProcAddress(fname); |
479 |
} |
480 |
|
481 |
void |
482 |
eglglue_cleanup(void) |
483 |
{ |
484 |
if (eglglue_display != EGL_NO_DISPLAY) eglTerminate(eglglue_display); |
485 |
eglglue_display = EGL_NO_DISPLAY; |
486 |
} |
487 |
|
488 |
#endif /* HAVE_EGL */ |