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 SoGLDisplayList Inventor/elements/SoGLCacheContextElement.h
35   \brief The SoGLDisplayList class stores and manages OpenGL display lists.
36 
37   \ingroup elements
38 
39   The TEXTURE_OBJECT type is not directly supported in Coin. We handle
40   textures differently in a more flexible class called SoGLImage,
41   which also stores some information about the texture used when
42   rendering. Old code which use this element should not stop
43   working though. The texture object extension will just not be used,
44   and the texture will be stored in a display list instead.
45 */
46 
47 // *************************************************************************
48 
49 #include <Inventor/elements/SoGLDisplayList.h>
50 
51 #include <cstring>
52 #include <cassert>
53 
54 #include <Inventor/C/glue/gl.h>
55 #include <Inventor/caches/SoGLRenderCache.h>
56 #include <Inventor/elements/SoCacheElement.h>
57 #include <Inventor/elements/SoGLCacheContextElement.h>
58 #include <Inventor/errors/SoDebugError.h>
59 #include <Inventor/misc/SoState.h>
60 #include <Inventor/misc/SoGLDriverDatabase.h>
61 
62 #include "glue/glp.h"
63 #include "rendering/SoGL.h"
64 #include "coindefs.h"
65 
66 #ifndef COIN_WORKAROUND_NO_USING_STD_FUNCS
67 using std::strcmp;
68 #endif // !COIN_WORKAROUND_NO_USING_STD_FUNCS
69 
70 class SoGLDisplayListP {
71  public:
72   SoGLDisplayList::Type type;
73   int numalloc;
74   unsigned int firstindex;
75   int context;
76   int refcount;
77   int openindex;
78   SbBool mipmap;
79   GLenum texturetarget;
80 };
81 
82 #define PRIVATE(obj) obj->pimpl
83 
84 // *************************************************************************
85 
86 /*!
87   Constructor.
88 */
SoGLDisplayList(SoState * state,Type type,int allocnum,SbBool mipmaptexobj)89 SoGLDisplayList::SoGLDisplayList(SoState * state, Type type, int allocnum,
90                                  SbBool mipmaptexobj)
91 {
92   PRIVATE(this) = new SoGLDisplayListP;
93   PRIVATE(this)->type = type;
94   PRIVATE(this)->numalloc = allocnum;
95   PRIVATE(this)->context = SoGLCacheContextElement::get(state);
96   PRIVATE(this)->refcount = 0;
97   PRIVATE(this)->mipmap = mipmaptexobj;
98   PRIVATE(this)->texturetarget = 0;
99 
100 #if COIN_DEBUG && 0 // debug
101   SoDebugError::postInfo("SoGLDisplayList::SoGLDisplayList", "%p", this);
102 #endif // debug
103 
104   // Check for known buggy OpenGL driver.
105   const char * versionstr = (const char *)glGetString(GL_VERSION);
106   assert(versionstr && "glGetString() returned 0 -- no valid GL context?");
107   if (strcmp(versionstr, "1.3.1 NVIDIA 28.02") == 0) {
108     // (From NVidia's changelog, it looks like the problem we've been
109     // seeing with the 28.02 driver and displaylists *might* have been
110     // fixed for the next version (28.80)).
111 
112     // Here's an Inventor file which can be used to reproduce the bug:
113     // -----8<----- [snip] ----------8<----- [snip] ----------8<------
114     // #Inventor V2.1 ascii
115     //
116     // # This dead simple scenegraph causes the examinerviewer to go blank
117     // # on a Linux box with GeForce2 card and version 1.3.1 28.02 of the
118     // # NVidia OpenGL drivers. The problem is gone for version 1.3.1 29.60
119     // # of the drivers, so this seems _very_ much like a driver bug with
120     // # OpenGL displaylists.
121     // #
122     // # The bug is also present for SGI Inventor.
123     // #
124     // # <mortene@sim.no>.
125     //
126     // Separator {
127     //    renderCaching ON
128     //    ShapeHints {
129     //       vertexOrdering COUNTERCLOCKWISE
130     //       faceType UNKNOWN_FACE_TYPE
131     //    }
132     //    Cube { }
133     // }
134     // -----8<----- [snip] ----------8<----- [snip] ----------8<------
135 
136 
137     // FIXME: should be more robust, and rather just disable the use
138     // of GL displaylists (but still issuing an
139     // SoDebugError::postWarning()). This should be straightforward to
140     // do when the FIXME below on better handling of the case where we
141     // are not able to allocate displaylist indices is taken care
142     // of. 20020911 mortene.
143 
144     static SbBool first = TRUE;
145     if (first) {
146       SoDebugError::post("SoGLDisplayList::SoGLDisplayList",
147                          "This OpenGL driver ('%s') is known to contain serious "
148                          "bugs in GL displaylist handling, and we strongly urge "
149                          "you to upgrade! As long as you are using this driver, "
150                          "GL rendering is likely to cause all sorts of nasty "
151                          "problems.",
152                          versionstr);
153       first = FALSE;
154     }
155   }
156 
157 
158   // Reserve displaylist IDs.
159 
160   if (PRIVATE(this)->type == TEXTURE_OBJECT) {
161     assert(allocnum == 1 && "it is only possible to create one texture object at a time");
162     const cc_glglue * glw = cc_glglue_instance(PRIVATE(this)->context);
163     if (SoGLDriverDatabase::isSupported(glw, SO_GL_TEXTURE_OBJECT)) {
164       // use temporary variable, in case GLuint is typedef'ed to
165       // something other than unsigned int
166       GLuint tmpindex;
167       cc_glglue_glGenTextures(glw, 1, &tmpindex);
168       PRIVATE(this)->firstindex = (unsigned int )tmpindex;
169     }
170     else { // Fall back to display list, allocation happens further down below.
171       PRIVATE(this)->type = DISPLAY_LIST;
172     }
173   }
174 
175   if (PRIVATE(this)->type == DISPLAY_LIST) {
176     PRIVATE(this)->firstindex = (unsigned int) glGenLists(allocnum);
177     if (PRIVATE(this)->firstindex == 0) {
178       SoDebugError::post("SoGLDisplayList::SoGLDisplayList",
179                          "Could not reserve %d displaylist%s. "
180                          "Expect flawed rendering.",
181                          allocnum, allocnum==1 ? "" : "s");
182       // FIXME: be more robust in handling this -- the rendering will
183       // gradually go bonkers after we hit this problem. 20020619 mortene.
184     }
185 #if COIN_DEBUG && 0 // debug
186     SoDebugError::postInfo("SoGLDisplayList::SoGLDisplayList",
187                            "firstindex==%d", PRIVATE(this)->firstindex);
188 #endif // debug
189   }
190 }
191 
192 // private destructor. Use ref()/unref()
~SoGLDisplayList()193 SoGLDisplayList::~SoGLDisplayList()
194 {
195 #if COIN_DEBUG && 0 // debug
196   SoDebugError::postInfo("SoGLDisplayList::~SoGLDisplayList", "%p", this);
197 #endif // debug
198 
199   if (PRIVATE(this)->type == DISPLAY_LIST) {
200     glDeleteLists((GLuint) PRIVATE(this)->firstindex, PRIVATE(this)->numalloc);
201   }
202   else {
203     assert(PRIVATE(this)->type == TEXTURE_OBJECT);
204 
205     const cc_glglue * glw = cc_glglue_instance(PRIVATE(this)->context);
206     assert(cc_glglue_has_texture_objects(glw));
207 
208     // Use temporary variable in case GLUint != unsigned int.
209     GLuint tmpindex = (GLuint) PRIVATE(this)->firstindex;
210     // It is only possible to create one texture object at a time, so
211     // there's only one index to delete.
212     cc_glglue_glDeleteTextures(glw, 1, &tmpindex);
213   }
214   delete PRIVATE(this);
215 }
216 
217 /*!
218   Increase reference count for this display list/texture object.
219 */
220 void
ref(void)221 SoGLDisplayList::ref(void)
222 {
223   PRIVATE(this)->refcount++;
224 }
225 
226 /*!
227   Decrease reference count for this instance. When reference count
228   reaches 0, the instence is deleted.
229 */
230 void
unref(SoState * state)231 SoGLDisplayList::unref(SoState * state)
232 {
233   assert(PRIVATE(this)->refcount > 0);
234   if (--PRIVATE(this)->refcount == 0) {
235     // Let SoGLCacheContext delete this instance the next time context is current.
236     SoGLCacheContextElement::scheduleDelete(state, this);
237   }
238 }
239 
240 /*!
241   Open this display list/texture object.
242 */
243 void
open(SoState * state,int index)244 SoGLDisplayList::open(SoState * state, int index)
245 {
246   if (PRIVATE(this)->type == DISPLAY_LIST) {
247     PRIVATE(this)->openindex = index;
248     // using GL_COMPILE here instead of GL_COMPILE_AND_EXECUTE will
249     // lead to much higher performance on nVidia cards, and doesn't
250     // hurt performance for other vendors.
251     glNewList((GLuint) (PRIVATE(this)->firstindex+PRIVATE(this)->openindex), GL_COMPILE);
252   }
253   else {
254     assert(PRIVATE(this)->type == TEXTURE_OBJECT);
255     assert(index == 0);
256     this->bindTexture(state);
257   }
258 }
259 
260 /*!
261   Close this display list/texture object.
262 */
263 void
close(SoState * COIN_UNUSED_ARG (state))264 SoGLDisplayList::close(SoState * COIN_UNUSED_ARG(state))
265 {
266   if (PRIVATE(this)->type == DISPLAY_LIST) {
267     glEndList();
268     GLenum err = sogl_glerror_debugging() ? glGetError() : GL_NO_ERROR;
269     if (err == GL_OUT_OF_MEMORY) {
270       SoDebugError::post("SoGLDisplayList::close",
271                          "Not enough memory resources available on system "
272                          "to store full display list. Expect flaws in "
273                          "rendering.");
274     }
275     glCallList((GLuint) (PRIVATE(this)->firstindex + PRIVATE(this)->openindex));
276   }
277   else {
278     const cc_glglue * glw = cc_glglue_instance(PRIVATE(this)->context);
279     assert(cc_glglue_has_texture_objects(glw));
280     GLenum target = PRIVATE(this)->texturetarget;
281     if (target == 0) {
282       // target is not set. Assume normal 2D texture.
283       target = GL_TEXTURE_2D;
284     }
285     // unbind current texture object
286     cc_glglue_glBindTexture(glw, target, (GLuint) 0);
287   }
288 }
289 
290 /*!
291   Execute this display list/texture object.
292 */
293 void
call(SoState * state,int index)294 SoGLDisplayList::call(SoState * state, int index)
295 {
296   if (PRIVATE(this)->type == DISPLAY_LIST) {
297     glCallList((GLuint) (PRIVATE(this)->firstindex + index));
298   }
299   else {
300     assert(PRIVATE(this)->type == TEXTURE_OBJECT);
301     assert(index == 0);
302     this->bindTexture(state);
303   }
304   this->addDependency(state);
305 }
306 
307 /*!
308   Create a dependency on the display list.
309 */
310 void
addDependency(SoState * state)311 SoGLDisplayList::addDependency(SoState * state)
312 {
313   if (state->isCacheOpen()) {
314     SoGLRenderCache * cache = (SoGLRenderCache*)
315       SoCacheElement::getCurrentCache(state);
316     if (cache) cache->addNestedCache(this);
317   }
318 }
319 
320 /*!
321   Returns whether the texture object stored in this instance
322   was created with mipmap data. This method is an extension
323   versus the Open Inventor API.
324 */
325 SbBool
isMipMapTextureObject(void) const326 SoGLDisplayList::isMipMapTextureObject(void) const
327 {
328   return PRIVATE(this)->mipmap;
329 }
330 
331 /*!
332   Return type. Display list or texture object.
333 */
334 SoGLDisplayList::Type
getType(void) const335 SoGLDisplayList::getType(void) const
336 {
337   return PRIVATE(this)->type;
338 }
339 
340 /*!
341   Return number of display lists/texture objects allocated.
342 */
343 int
getNumAllocated(void) const344 SoGLDisplayList::getNumAllocated(void) const
345 {
346   return PRIVATE(this)->numalloc;
347 }
348 
349 /*!
350   Return first GL index for this display list.
351 */
352 unsigned int
getFirstIndex(void) const353 SoGLDisplayList::getFirstIndex(void) const
354 {
355   return PRIVATE(this)->firstindex;
356 }
357 
358 /*!
359   Return an id for the current context.
360 */
361 int
getContext(void) const362 SoGLDisplayList::getContext(void) const
363 {
364   return PRIVATE(this)->context;
365 }
366 
367 /*!
368   Sets the texture object target
369   \since Coin 2.5
370 */
371 void
setTextureTarget(int target)372 SoGLDisplayList::setTextureTarget(int target)
373 {
374   PRIVATE(this)->texturetarget = (GLenum) target;
375 }
376 
377 /*!
378   Returns the texture target
379   \since Coin 2.5
380 */
381 int
getTextureTarget(void) const382 SoGLDisplayList::getTextureTarget(void) const
383 {
384   if (PRIVATE(this)->texturetarget)
385     return (int) PRIVATE(this)->texturetarget;
386   return GL_TEXTURE_2D;
387 }
388 
389 /*!
390   \COININTERNAL
391 
392   \COIN_FUNCTION_EXTENSION
393 
394   \since Coin 2.0
395 */
396 void
bindTexture(SoState * COIN_UNUSED_ARG (state))397 SoGLDisplayList::bindTexture(SoState * COIN_UNUSED_ARG(state))
398 {
399   const cc_glglue * glw = cc_glglue_instance(PRIVATE(this)->context);
400   assert(cc_glglue_has_texture_objects(glw));
401 
402   GLenum target = PRIVATE(this)->texturetarget;
403   if (target == 0) {
404     // target is not set. Assume normal 2D texture.
405     target = GL_TEXTURE_2D;
406   }
407   cc_glglue_glBindTexture(glw, target, (GLuint)PRIVATE(this)->firstindex);
408 }
409 
410 #undef PRIVATE
411