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