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 \class SoGLCacheContextElement Inventor/elements/SoGLCacheContextElement.h
35 \brief The SoGLCacheContextElement class handles the OpenGL cache for a context.
36
37 \ingroup elements
38 */
39
40 // *************************************************************************
41
42 /*! \file SoGLCacheContextElement.h */
43 #include "coindefs.h"
44 #include <Inventor/elements/SoGLCacheContextElement.h>
45
46 #include <cstdlib>
47 #include <cstring>
48
49 #include <Inventor/SbName.h>
50 #include <Inventor/elements/SoGLDisplayList.h>
51 #include <Inventor/lists/SbList.h>
52 #include <Inventor/misc/SoState.h>
53 #include <Inventor/system/gl.h>
54 #include <Inventor/misc/SoContextHandler.h>
55 #include <Inventor/misc/SoGLDriverDatabase.h>
56
57 #include "rendering/SoGL.h"
58 #include "threads/threadsutilp.h"
59 #include "tidbitsp.h"
60
61 // *************************************************************************
62
63 static int biggest_cache_context_id = 0;
64
65 // *************************************************************************
66
67 SO_ELEMENT_SOURCE(SoGLCacheContextElement);
68
69 // *************************************************************************
70
71 typedef struct {
72 SbName extname;
73 SbList <int> context;
74 SbList <SbBool> supported;
75 } so_glext_info;
76
77 typedef struct {
78 int context;
79 int handle;
80 } so_gltexhandle_info;
81
82 typedef struct {
83 uint32_t contextid;
84 SoScheduleDeleteCB * cb;
85 void * closure;
86 } so_scheduledeletecb_info;
87
88 static SbList <so_glext_info *> * extsupportlist;
89 static SbList <SoGLDisplayList*> * scheduledeletelist;
90 static SbList <so_scheduledeletecb_info*> * scheduledeletecblist;
91 static void * glcache_mutex;
92
93 // needed to be able to remove the callback in the cleanup function
94 // (SoGLCacheContextElement::cleanupContext() is private)
95 static SoContextHandler::ContextDestructionCB * soglcache_contextdestructioncb;
96
soglcachecontext_cleanup(void)97 static void soglcachecontext_cleanup(void)
98 {
99 int i,n = extsupportlist->getLength();
100 for (i = 0; i < n; i++) {
101 delete (*extsupportlist)[i];
102 }
103 n = scheduledeletecblist->getLength();
104 for (i = 0; i < n; i++) {
105 // just delete callbacks, don't call them
106 delete (*scheduledeletecblist)[i];
107 }
108
109 delete extsupportlist;
110 delete scheduledeletelist;
111 delete scheduledeletecblist;
112 CC_MUTEX_DESTRUCT(glcache_mutex);
113
114 if (soglcache_contextdestructioncb) {
115 SoContextHandler::removeContextDestructionCallback(soglcache_contextdestructioncb, NULL);
116 soglcache_contextdestructioncb = NULL;
117 }
118
119 biggest_cache_context_id = 0;
120 }
121
122 //
123 // Used both as a callback from SoContextHandler and called directly
124 // from inside this class every time ::set() is called.
125 //
126 void
cleanupContext(uint32_t contextid,void * COIN_UNUSED_ARG (userdata))127 SoGLCacheContextElement::cleanupContext(uint32_t contextid, void * COIN_UNUSED_ARG(userdata))
128 {
129 int context = (int) contextid;
130
131 CC_MUTEX_LOCK(glcache_mutex);
132
133 int i = 0;
134 int n = scheduledeletelist->getLength();
135
136 while (i < n) {
137 SoGLDisplayList * dl = (*scheduledeletelist)[i];
138 if (dl->getContext() == context) {
139 scheduledeletelist->removeFast(i);
140 n--;
141 delete dl;
142 }
143 else i++;
144 }
145
146 i = 0;
147 n = scheduledeletecblist->getLength();
148 while (i < n) {
149 so_scheduledeletecb_info * info = (*scheduledeletecblist)[i];
150 if (info->contextid == contextid) {
151 info->cb(info->closure, info->contextid);
152 scheduledeletecblist->removeFast(i);
153 n--;
154 delete info;
155 }
156 else i++;
157 }
158
159 CC_MUTEX_UNLOCK(glcache_mutex);
160
161 }
162
163 // *************************************************************************
164
165 // doc from parent
166 void
initClass(void)167 SoGLCacheContextElement::initClass(void)
168 {
169 SO_ELEMENT_INIT_CLASS(SoGLCacheContextElement, inherited);
170
171 extsupportlist = new SbList <so_glext_info *>;
172 scheduledeletelist = new SbList <SoGLDisplayList*>;
173 scheduledeletecblist = new SbList <so_scheduledeletecb_info*>;
174 CC_MUTEX_CONSTRUCT(glcache_mutex);
175 coin_atexit((coin_atexit_f *)soglcachecontext_cleanup, CC_ATEXIT_NORMAL);
176
177 // add a callback which is called every time a GL-context is
178 // destructed. it's important that this callback is added in
179 // initClass() to make it work properly when destructing a
180 // context. See comments in SoContextHandler.cpp for more
181 // information.
182 SoContextHandler::addContextDestructionCallback(cleanupContext, NULL);
183 soglcache_contextdestructioncb = cleanupContext;
184 }
185
186 /*!
187 Destructor.
188 */
~SoGLCacheContextElement()189 SoGLCacheContextElement::~SoGLCacheContextElement()
190 {
191 }
192
193 // doc from parent
194 void
init(SoState * COIN_UNUSED_ARG (state))195 SoGLCacheContextElement::init(SoState * COIN_UNUSED_ARG(state))
196 {
197 // these values will be set up in set(), but initialize them anyway
198 this->context = 0;
199 this->twopass = FALSE;
200 this->rendering = RENDERING_UNSET;
201 this->autocachebits = 0;
202 this->numshapes = 0;
203 this->numseparators = 0;
204 }
205
206 // doc from parent
207 SbBool
matches(const SoElement * elt) const208 SoGLCacheContextElement::matches(const SoElement * elt) const
209 {
210 const SoGLCacheContextElement * elem = (SoGLCacheContextElement*) elt;
211
212 return
213 this->context == elem->context &&
214 this->twopass == elem->twopass &&
215 this->rendering == elem->rendering;
216 }
217
218 // doc from parent
219 SoElement *
copyMatchInfo(void) const220 SoGLCacheContextElement::copyMatchInfo(void) const
221 {
222 SoGLCacheContextElement * elem = (SoGLCacheContextElement*)
223 this->getTypeId().createInstance();
224 elem->context = this->context;
225 elem->twopass = this->twopass;
226 elem->rendering = this->rendering;
227 return elem;
228 }
229
230 /*!
231 Sets data for context.
232 */
233 void
set(SoState * state,int context,SbBool twopasstransparency,SbBool remoterendering)234 SoGLCacheContextElement::set(SoState * state, int context,
235 SbBool twopasstransparency,
236 SbBool remoterendering)
237 {
238 SoGLCacheContextElement * elem = (SoGLCacheContextElement *)
239 state->getElementNoPush(classStackIndex);
240 elem->twopass = twopasstransparency;
241 elem->rendering = remoterendering ? RENDERING_SET_INDIRECT : RENDERING_SET_DIRECT;
242 elem->autocachebits = 0;
243 elem->context = context;
244 if (context > biggest_cache_context_id) {
245 biggest_cache_context_id = context;
246 }
247
248 if (remoterendering) elem->autocachebits = DO_AUTO_CACHE;
249
250 // really delete GL resources scheduled for destruction
251 SoGLCacheContextElement::cleanupContext((uint32_t) context, NULL);
252 }
253
254 /*!
255 Returns context id.
256
257 Note that the signature on this function is slightly wrong: the
258 function should really return an \c uint32_t, like
259 SoGLRenderAction::getCacheContext() does. It is kept like this for
260 compatibility reasons.
261
262 The value returned will always be a positive integer.
263 */
264 int
get(SoState * state)265 SoGLCacheContextElement::get(SoState * state)
266 {
267 const SoGLCacheContextElement * elem = (const SoGLCacheContextElement *)
268 SoElement::getConstElement(state, classStackIndex);
269 return elem->context;
270 }
271
272 /*!
273 Returns an extension id based on the GL extension string.
274 The extension id can be used to quickly test for the availability
275 of an extension later, using extSupported().
276 */
277 int
getExtID(const char * str)278 SoGLCacheContextElement::getExtID(const char * str)
279 {
280 CC_MUTEX_LOCK(glcache_mutex);
281
282 SbName extname(str);
283 int i, n = extsupportlist->getLength();
284 for (i = 0; i < n; i++) {
285 if ((*extsupportlist)[i]->extname == extname) break;
286 }
287 if (i == n) { // not found
288 so_glext_info * info = new so_glext_info;
289 info->extname = extname;
290 extsupportlist->append(info);
291 }
292
293 CC_MUTEX_UNLOCK(glcache_mutex);
294
295 return i;
296 }
297
298 /*!
299 Returns TRUE if the extension is supported for the current context.
300 \a extid must be an id returned from getExtId(). The test result
301 is cached so this method is pretty fast and can be used run-time.
302 */
303 SbBool
extSupported(SoState * state,int extid)304 SoGLCacheContextElement::extSupported(SoState * state, int extid)
305 {
306 CC_MUTEX_LOCK(glcache_mutex);
307
308 assert(extid >= 0 && extid < extsupportlist->getLength());
309
310 so_glext_info * info = (*extsupportlist)[extid];
311
312 int currcontext = SoGLCacheContextElement::get(state);
313 int n = info->context.getLength();
314 for (int i = 0; i < n; i++) {
315 if (info->context[i] == currcontext) {
316 CC_MUTEX_UNLOCK(glcache_mutex);
317 return info->supported[i];
318 }
319 }
320 const cc_glglue * w = sogl_glue_instance(state);
321 SbBool supported = SoGLDriverDatabase::isSupported(w, info->extname.getString());
322 info->context.append(currcontext);
323 info->supported.append(supported);
324
325 CC_MUTEX_UNLOCK(glcache_mutex);
326
327 return supported;
328 }
329
330 /*!
331 Returns the OpenGL version for the current context. This method is
332 an extension versus the Open Inventor API.
333 */
334 void
getOpenGLVersion(SoState * state,int & major,int & minor)335 SoGLCacheContextElement::getOpenGLVersion(SoState * state,
336 int & major, int & minor)
337 {
338 int currcontext = SoGLCacheContextElement::get(state);
339 const cc_glglue * w = cc_glglue_instance(currcontext);
340 unsigned int majoru, minoru, dummy;
341 cc_glglue_glversion(w, &majoru, &minoru, &dummy);
342 major = (int)majoru;
343 minor = (int)minoru;
344 }
345
346 /*!
347 Returns if mipmapped textures are fast for the current context.
348 In Coin, we just return TRUE for the moment.
349 */
350 SbBool
areMipMapsFast(SoState * COIN_UNUSED_ARG (state))351 SoGLCacheContextElement::areMipMapsFast(SoState * COIN_UNUSED_ARG(state))
352 {
353 return TRUE; // FIXME: how do we test this? pederb 20001003
354 }
355
356 /*!
357 Update auto cache bits.
358 */
359 void
shouldAutoCache(SoState * state,int bits)360 SoGLCacheContextElement::shouldAutoCache(SoState * state, int bits)
361 {
362 SoGLCacheContextElement * elem = (SoGLCacheContextElement*)
363 state->getElementNoPush(classStackIndex);
364 elem->autocachebits |= bits;
365 }
366
367 /*!
368 Increment the number of shapes in a open cache.
369
370 \since Coin 3.0
371 */
372 void
incNumShapes(SoState * state)373 SoGLCacheContextElement::incNumShapes(SoState * state)
374 {
375 SoGLCacheContextElement * elem = (SoGLCacheContextElement*)
376 state->getElementNoPush(classStackIndex);
377
378 elem->numshapes++;
379 }
380
381 /*!
382 Returns the number of shapes in an open cache.
383
384 \since Coin 3.0
385 */
386 int
getNumShapes(SoState * state)387 SoGLCacheContextElement::getNumShapes(SoState * state)
388 {
389 SoGLCacheContextElement * elem = (SoGLCacheContextElement*)
390 state->getElementNoPush(classStackIndex);
391
392 return elem->numshapes;
393 }
394
395 /*!
396 Increment the number of separators in an open cache.
397
398 \since Coin 3.0
399 */
400 void
incNumSeparators(SoState * state)401 SoGLCacheContextElement::incNumSeparators(SoState * state)
402 {
403 SoGLCacheContextElement * elem = (SoGLCacheContextElement*)
404 state->getElementNoPush(classStackIndex);
405
406 elem->numseparators++;
407 }
408
409 /*!
410 Returns the number of separators in an open cache.
411
412 \since Coin 3.0
413 */
414 int
getNumSeparators(SoState * state)415 SoGLCacheContextElement::getNumSeparators(SoState * state)
416 {
417 SoGLCacheContextElement * elem = (SoGLCacheContextElement*)
418 state->getElementNoPush(classStackIndex);
419
420 return elem->numseparators;
421 }
422
423 /*!
424 Sets the auto cache bits.
425 */
426 void
setAutoCacheBits(SoState * state,int bits)427 SoGLCacheContextElement::setAutoCacheBits(SoState * state, int bits)
428 {
429 SoGLCacheContextElement * elem = (SoGLCacheContextElement*)
430 state->getElementNoPush(classStackIndex);
431
432 elem->autocachebits = bits;
433 }
434
435 // Private function which "unwinds" the real value of the "rendering"
436 // variable.
437 SbBool
isDirectRendering(SoState * state) const438 SoGLCacheContextElement::isDirectRendering(SoState * state) const
439 {
440 SbBool isdirect;
441 if (this->rendering == RENDERING_UNSET) {
442 const cc_glglue * w = sogl_glue_instance(state);
443 isdirect = cc_glglue_isdirect(w);
444 }
445 else {
446 isdirect = this->rendering == RENDERING_SET_DIRECT;
447 }
448 return isdirect;
449 }
450
451 /*!
452 Not properly supported yet.
453 */
454 int
resetAutoCacheBits(SoState * state)455 SoGLCacheContextElement::resetAutoCacheBits(SoState * state)
456 {
457 SoGLCacheContextElement *elem = (SoGLCacheContextElement *)
458 state->getElementNoPush(classStackIndex);
459 int ret = elem->autocachebits;
460
461 elem->autocachebits = elem->isDirectRendering(state) ? 0 : DO_AUTO_CACHE;
462 elem->numshapes = 0;
463 elem->numseparators = 0;
464
465 return ret;
466 }
467
468 /*!
469 Returns \c TRUE if rendering is indirect / remote.
470 */
471 SbBool
getIsRemoteRendering(SoState * state)472 SoGLCacheContextElement::getIsRemoteRendering(SoState * state)
473 {
474 const SoGLCacheContextElement *elem = (const SoGLCacheContextElement *)
475 state->getConstElement(classStackIndex);
476 return !elem->isDirectRendering(state);
477 }
478
479 //
480 // internal method used by SoGLDisplayList to delete list as soon as
481 // the display list context is current again.
482 //
483 void
scheduleDelete(SoState * state,class SoGLDisplayList * dl)484 SoGLCacheContextElement::scheduleDelete(SoState * state, class SoGLDisplayList * dl)
485 {
486 if (state && dl->getContext() == SoGLCacheContextElement::get(state)) {
487 delete dl;
488 }
489 else {
490 CC_MUTEX_LOCK(glcache_mutex);
491 scheduledeletelist->append(dl);
492 CC_MUTEX_UNLOCK(glcache_mutex);
493 }
494 }
495
496 /*!
497 Can be used to receive a callback the next time Coin knows that the
498 context (specified by \a contextid) is the current OpenGL context.
499
500 This function can be used to free OpenGL resources for a context.
501
502 Note that the callback will be invoked only once, and then removed
503 from the internal list of scheduled callbacks.
504
505 \since Coin 2.3
506 */
507 void
scheduleDeleteCallback(const uint32_t contextid,SoScheduleDeleteCB * cb,void * closure)508 SoGLCacheContextElement::scheduleDeleteCallback(const uint32_t contextid,
509 SoScheduleDeleteCB * cb,
510 void * closure)
511 {
512 so_scheduledeletecb_info * info = new so_scheduledeletecb_info;
513 info->contextid = contextid;
514 info->cb = cb;
515 info->closure = closure;
516
517 CC_MUTEX_LOCK(glcache_mutex);
518 scheduledeletecblist->append(info);
519 CC_MUTEX_UNLOCK(glcache_mutex);
520 }
521
522 /*!
523 Returns an unique cache context id, in the range [1, ->.
524
525 If you render the same scene graph into two or different cache
526 contexts, and you've not using display list and texture object
527 sharing among contexts, the cache context id need to be unique for
528 rendering to work.
529
530 \COIN_FUNCTION_EXTENSION
531
532 \sa SoGLRenderAction::setCacheContext()
533 */
534 uint32_t
getUniqueCacheContext(void)535 SoGLCacheContextElement::getUniqueCacheContext(void)
536 {
537 CC_MUTEX_LOCK(glcache_mutex);
538 uint32_t id = ++biggest_cache_context_id;
539 CC_MUTEX_UNLOCK(glcache_mutex);
540 return id;
541 }
542