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