1 /* $Id: texobj.c,v 1.21 1998/01/16 01:09:44 brianp Exp $ */
2
3 /*
4 * Mesa 3-D graphics library
5 * Version: 2.6
6 * Copyright (C) 1995-1997 Brian Paul
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23
24 /*
25 * $Log: texobj.c,v $
26 * Revision 1.21 1998/01/16 01:09:44 brianp
27 * glGenTextures() didn't reserve the returned texture IDs
28 *
29 * Revision 1.20 1997/12/07 17:34:05 brianp
30 * added DavidB's v0.21 fxmesa driver patch
31 *
32 * Revision 1.19 1997/11/07 03:38:07 brianp
33 * added stdio.h include for SunOS 4.x
34 *
35 * Revision 1.18 1997/10/13 23:57:59 brianp
36 * added target parameter to Driver.BindTexture()
37 *
38 * Revision 1.17 1997/09/29 23:28:14 brianp
39 * updated for new device driver texture functions
40 *
41 * Revision 1.16 1997/09/27 00:14:39 brianp
42 * added GL_EXT_paletted_texture extension
43 *
44 * Revision 1.15 1997/09/23 00:58:15 brianp
45 * now using hash table for texture objects
46 *
47 * Revision 1.14 1997/08/23 18:41:40 brianp
48 * fixed bug in glBindTexture() when binding an incomplete texture image
49 *
50 * Revision 1.13 1997/08/23 17:14:44 brianp
51 * fixed bug: glBindTexture(target, 0) caused segfault
52 *
53 * Revision 1.12 1997/07/24 01:25:34 brianp
54 * changed precompiled header symbol from PCH to PC_HEADER
55 *
56 * Revision 1.11 1997/05/28 03:26:49 brianp
57 * added precompiled header (PCH) support
58 *
59 * Revision 1.10 1997/05/17 03:41:49 brianp
60 * added code to update ctx->Texture.Current in gl_BindTexture()
61 *
62 * Revision 1.9 1997/05/03 00:52:52 brianp
63 * removed a few unused variables
64 *
65 * Revision 1.8 1997/05/01 02:08:12 brianp
66 * new implementation of gl_BindTexture()
67 *
68 * Revision 1.7 1997/04/28 23:38:45 brianp
69 * added gl_test_texture_object_completeness()
70 *
71 * Revision 1.6 1997/04/14 02:03:05 brianp
72 * added MinMagThresh to texture object
73 *
74 * Revision 1.5 1997/02/09 18:52:15 brianp
75 * added GL_EXT_texture3D support
76 *
77 * Revision 1.4 1997/01/16 03:35:34 brianp
78 * added calls to device driver DeleteTexture() and BindTexture() functions
79 *
80 * Revision 1.3 1997/01/09 19:49:47 brianp
81 * added a check to switch rasterizers if needed in glBindTexture()
82 *
83 * Revision 1.2 1996/09/27 17:09:42 brianp
84 * removed a redundant return statement
85 *
86 * Revision 1.1 1996/09/13 01:38:16 brianp
87 * Initial revision
88 *
89 */
90
91
92 #ifdef PC_HEADER
93 #include "all.h"
94 #else
95 #include <assert.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include "context.h"
99 #include "hash.h"
100 #include "macros.h"
101 #include "teximage.h"
102 #include "texobj.h"
103 #include "types.h"
104 #endif
105
106
107
108 /*
109 * Allocate a new texture object and add it to the linked list of texture
110 * objects. If name>0 then also insert the new texture object into the hash
111 * table.
112 * Input: shared - the shared GL state structure to contain the texture object
113 * name - integer name for the texture object
114 * dimensions - either 1, 2 or 3
115 * Return: pointer to new texture object
116 */
117 struct gl_texture_object *
gl_alloc_texture_object(struct gl_shared_state * shared,GLuint name,GLuint dimensions)118 gl_alloc_texture_object( struct gl_shared_state *shared, GLuint name,
119 GLuint dimensions)
120 {
121 struct gl_texture_object *obj;
122
123 assert(dimensions >= 0 && dimensions <= 2);
124
125 obj = (struct gl_texture_object *)
126 calloc(1,sizeof(struct gl_texture_object));
127 if (obj) {
128 /* init the non-zero fields */
129 obj->Name = name;
130 obj->Dimensions = dimensions;
131 obj->WrapS = GL_REPEAT;
132 obj->WrapT = GL_REPEAT;
133 obj->MinFilter = GL_NEAREST_MIPMAP_LINEAR;
134 obj->MagFilter = GL_LINEAR;
135 obj->MinMagThresh = 0.0F;
136 obj->Palette[0] = 255;
137 obj->Palette[1] = 255;
138 obj->Palette[2] = 255;
139 obj->Palette[3] = 255;
140 obj->PaletteSize = 1;
141 obj->PaletteIntFormat = GL_RGBA;
142 obj->PaletteFormat = GL_RGBA;
143
144 /* insert into linked list */
145 if (shared) {
146 obj->Next = shared->TexObjectList;
147 shared->TexObjectList = obj;
148 }
149
150 if (name > 0) {
151 /* insert into hash table */
152 HashInsert(shared->TexObjects, name, obj);
153 }
154 }
155 return obj;
156 }
157
158
159 /*
160 * Deallocate a texture object struct and remove it from the given
161 * shared GL state.
162 * Input: shared - the shared GL state to which the object belongs
163 * t - the texture object to delete
164 */
gl_free_texture_object(struct gl_shared_state * shared,struct gl_texture_object * t)165 void gl_free_texture_object( struct gl_shared_state *shared,
166 struct gl_texture_object *t )
167 {
168 struct gl_texture_object *tprev, *tcurr;
169
170 assert(t);
171
172 /* unlink t from the linked list */
173 if (shared) {
174 tprev = NULL;
175 tcurr = shared->TexObjectList;
176 while (tcurr) {
177 if (tcurr==t) {
178 if (tprev) {
179 tprev->Next = t->Next;
180 }
181 else {
182 shared->TexObjectList = t->Next;
183 }
184 break;
185 }
186 tprev = tcurr;
187 tcurr = tcurr->Next;
188 }
189 }
190
191 if (t->Name) {
192 /* remove from hash table */
193 HashRemove(shared->TexObjects, t->Name);
194 }
195
196 /* free texture image */
197 {
198 GLuint i;
199 for (i=0;i<MAX_TEXTURE_LEVELS;i++) {
200 if (t->Image[i]) {
201 gl_free_texture_image( t->Image[i] );
202 }
203 }
204 }
205 /* free this object */
206 free( t );
207 }
208
209
210
211 /*
212 * Examine a texture object to determine if it is complete or not.
213 * The t->Complete flag will be set to GL_TRUE or GL_FALSE accordingly.
214 */
gl_test_texture_object_completeness(struct gl_texture_object * t)215 void gl_test_texture_object_completeness( struct gl_texture_object *t )
216 {
217 t->Complete = GL_TRUE; /* be optimistic */
218
219 /* Always need level zero image */
220 if (!t->Image[0] || !t->Image[0]->Data) {
221 t->Complete = GL_FALSE;
222 return;
223 }
224
225 if (t->MinFilter!=GL_NEAREST && t->MinFilter!=GL_LINEAR) {
226 /*
227 * Mipmapping: determine if we have a complete set of mipmaps
228 */
229 int i;
230
231 /* Test dimension-independent attributes */
232 for (i=1; i<MAX_TEXTURE_LEVELS; i++) {
233 if (t->Image[i]) {
234 if (!t->Image[i]->Data) {
235 t->Complete = GL_FALSE;
236 return;
237 }
238 if (t->Image[i]->Format != t->Image[0]->Format) {
239 t->Complete = GL_FALSE;
240 return;
241 }
242 if (t->Image[i]->Border != t->Image[0]->Border) {
243 t->Complete = GL_FALSE;
244 return;
245 }
246 }
247 }
248
249 /* Test things which depend on number of texture image dimensions */
250 if (t->Dimensions==1) {
251 /* Test 1-D mipmaps */
252 GLuint width = t->Image[0]->Width2;
253 for (i=1; i<MAX_TEXTURE_LEVELS; i++) {
254 if (width>1) {
255 width /= 2;
256 }
257 if (!t->Image[i]) {
258 t->Complete = GL_FALSE;
259 return;
260 }
261 if (!t->Image[i]->Data) {
262 t->Complete = GL_FALSE;
263 return;
264 }
265 if (t->Image[i]->Format != t->Image[0]->Format) {
266 t->Complete = GL_FALSE;
267 return;
268 }
269 if (t->Image[i]->Border != t->Image[0]->Border) {
270 t->Complete = GL_FALSE;
271 return;
272 }
273 if (t->Image[i]->Width2 != width ) {
274 t->Complete = GL_FALSE;
275 return;
276 }
277 if (width==1) {
278 return; /* found smallest needed mipmap, all done! */
279 }
280 }
281 }
282 else if (t->Dimensions==2) {
283 /* Test 2-D mipmaps */
284 GLuint width = t->Image[0]->Width2;
285 GLuint height = t->Image[0]->Height2;
286 for (i=1; i<MAX_TEXTURE_LEVELS; i++) {
287 if (width>1) {
288 width /= 2;
289 }
290 if (height>1) {
291 height /= 2;
292 }
293 if (!t->Image[i]) {
294 t->Complete = GL_FALSE;
295 return;
296 }
297 if (t->Image[i]->Width2 != width) {
298 t->Complete = GL_FALSE;
299 return;
300 }
301 if (t->Image[i]->Height2 != height) {
302 t->Complete = GL_FALSE;
303 return;
304 }
305 if (width==1 && height==1) {
306 return; /* found smallest needed mipmap, all done! */
307 }
308 }
309 }
310 else {
311 /* Dimensions = ??? */
312 gl_problem(NULL, "Bug in gl_test_texture_object_completeness\n");
313 }
314 }
315 }
316
317
318
319 /*
320 * Execute glGenTextures
321 */
gl_GenTextures(GLcontext * ctx,GLsizei n,GLuint * texName)322 void gl_GenTextures( GLcontext *ctx, GLsizei n, GLuint *texName )
323 {
324 GLuint first, i;
325
326 if (INSIDE_BEGIN_END(ctx)) {
327 gl_error( ctx, GL_INVALID_OPERATION, "glGenTextures" );
328 return;
329 }
330 if (n<0) {
331 gl_error( ctx, GL_INVALID_VALUE, "glGenTextures" );
332 return;
333 }
334
335 first = HashFindFreeKeyBlock(ctx->Shared->TexObjects, n);
336
337 /* Return the texture names */
338 for (i=0;i<n;i++) {
339 texName[i] = first + i;
340 }
341
342 /* Allocate new, empty texture objects */
343 for (i=0;i<n;i++) {
344 GLuint name = first + i;
345 GLuint dims = 0;
346 struct gl_texture_object *newTexObj = gl_alloc_texture_object(ctx->Shared, name, dims);
347 (void)newTexObj;
348 }
349 }
350
351
352
353 /*
354 * Execute glDeleteTextures
355 */
gl_DeleteTextures(GLcontext * ctx,GLsizei n,const GLuint * texName)356 void gl_DeleteTextures( GLcontext *ctx, GLsizei n, const GLuint *texName)
357 {
358 GLuint i;
359
360 if (INSIDE_BEGIN_END(ctx)) {
361 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" );
362 return;
363 }
364
365 for (i=0;i<n;i++) {
366 struct gl_texture_object *t;
367 if (texName[i]>0) {
368 t = (struct gl_texture_object *)
369 HashLookup(ctx->Shared->TexObjects, texName[i]);
370 if (t) {
371 if (ctx->Texture.Current1D==t) {
372 /* revert to default 1-D texture */
373 ctx->Texture.Current1D = ctx->Shared->Default1D;
374 t->RefCount--;
375 assert( t->RefCount >= 0 );
376 }
377 else if (ctx->Texture.Current2D==t) {
378 /* revert to default 2-D texture */
379 ctx->Texture.Current2D = ctx->Shared->Default2D;
380 t->RefCount--;
381 assert( t->RefCount >= 0 );
382 }
383
384 /* tell device driver to delete texture */
385 if (ctx->Driver.DeleteTexture) {
386 (*ctx->Driver.DeleteTexture)( ctx, t );
387 }
388
389 if (t->RefCount==0) {
390 gl_free_texture_object(ctx->Shared, t);
391 }
392 }
393 }
394 }
395 }
396
397
398
399 /*
400 * Execute glBindTexture
401 */
gl_BindTexture(GLcontext * ctx,GLenum target,GLuint texName)402 void gl_BindTexture( GLcontext *ctx, GLenum target, GLuint texName )
403 {
404 struct gl_texture_object *oldTexObj;
405 struct gl_texture_object *newTexObj;
406 struct gl_texture_object **targetPointer;
407 GLuint targetDimensions;
408
409 if (INSIDE_BEGIN_END(ctx)) {
410 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" );
411 return;
412 }
413 switch (target) {
414 case GL_TEXTURE_1D:
415 oldTexObj = ctx->Texture.Current1D;
416 targetPointer = &ctx->Texture.Current1D;
417 targetDimensions = 1;
418 break;
419 case GL_TEXTURE_2D:
420 oldTexObj = ctx->Texture.Current2D;
421 targetPointer = &ctx->Texture.Current2D;
422 targetDimensions = 2;
423 break;
424 default:
425 gl_error( ctx, GL_INVALID_ENUM, "glBindTexture" );
426 return;
427 }
428
429 if (texName==0) {
430 /* use default n-D texture */
431 switch (target) {
432 case GL_TEXTURE_1D:
433 newTexObj = ctx->Shared->Default1D;
434 break;
435 case GL_TEXTURE_2D:
436 newTexObj = ctx->Shared->Default2D;
437 break;
438 default:
439 gl_problem(ctx, "Bad target in gl_BindTexture");
440 return;
441 }
442 }
443 else {
444 newTexObj = (struct gl_texture_object *)
445 HashLookup(ctx->Shared->TexObjects, texName);
446 if (newTexObj) {
447 if (newTexObj->Dimensions == 0) {
448 /* first time bound */
449 newTexObj->Dimensions = targetDimensions;
450 }
451 else if (newTexObj->Dimensions != targetDimensions) {
452 /* wrong dimensionality */
453 gl_error( ctx, GL_INVALID_OPERATION, "glBindTextureEXT" );
454 return;
455 }
456 }
457 else {
458 /* create new texture object */
459 newTexObj = gl_alloc_texture_object(ctx->Shared, texName,
460 targetDimensions);
461 }
462 }
463
464 /* Update the Texture.Current[123]D pointer */
465 *targetPointer = newTexObj;
466
467 /* Tidy up reference counting */
468 if (*targetPointer != oldTexObj && oldTexObj->Name>0) {
469 /* decrement reference count of the prev texture object */
470 oldTexObj->RefCount--;
471 assert( oldTexObj->RefCount >= 0 );
472 }
473
474 if (newTexObj->Name>0) {
475 newTexObj->RefCount++;
476 }
477
478 /* Check if we may have to use a new triangle rasterizer */
479 if ( oldTexObj->WrapS != newTexObj->WrapS
480 || oldTexObj->WrapT != newTexObj->WrapT
481 || oldTexObj->WrapR != newTexObj->WrapR
482 || oldTexObj->MinFilter != newTexObj->MinFilter
483 || oldTexObj->MagFilter != newTexObj->MagFilter
484 || (oldTexObj->Image[0] && newTexObj->Image[0] &&
485 (oldTexObj->Image[0]->Format!=newTexObj->Image[0]->Format))
486 || !newTexObj->Complete) {
487 ctx->NewState |= NEW_RASTER_OPS;
488 }
489
490 /* If we've changed the Current[123]D texture object then update the
491 * ctx->Texture.Current pointer to point to the new texture object.
492 */
493 if (oldTexObj==ctx->Texture.Current) {
494 ctx->Texture.Current = newTexObj;
495 }
496
497 /* The current n-D texture object can never be NULL! */
498 assert(*targetPointer);
499
500 /* Pass BindTexture call to device driver */
501 if (ctx->Driver.BindTexture) {
502 (*ctx->Driver.BindTexture)( ctx, target, newTexObj );
503 }
504 }
505
506
507
508 /*
509 * Execute glPrioritizeTextures
510 */
gl_PrioritizeTextures(GLcontext * ctx,GLsizei n,const GLuint * texName,const GLclampf * priorities)511 void gl_PrioritizeTextures( GLcontext *ctx,
512 GLsizei n, const GLuint *texName,
513 const GLclampf *priorities )
514 {
515 GLuint i;
516
517 if (INSIDE_BEGIN_END(ctx)) {
518 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" );
519 return;
520 }
521 if (n<0) {
522 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)" );
523 return;
524 }
525
526 for (i=0;i<n;i++) {
527 struct gl_texture_object *t;
528 if (texName[i]>0) {
529 t = (struct gl_texture_object *)
530 HashLookup(ctx->Shared->TexObjects, texName[i]);
531 if (t) {
532 t->Priority = CLAMP( priorities[i], 0.0F, 1.0F );
533 }
534 }
535 }
536 }
537
538
539
540 /*
541 * Execute glAreTexturesResident
542 */
gl_AreTexturesResident(GLcontext * ctx,GLsizei n,const GLuint * texName,GLboolean * residences)543 GLboolean gl_AreTexturesResident( GLcontext *ctx, GLsizei n,
544 const GLuint *texName,
545 GLboolean *residences )
546 {
547 GLboolean resident = GL_TRUE;
548 GLuint i;
549
550 if (INSIDE_BEGIN_END(ctx)) {
551 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" );
552 return GL_FALSE;
553 }
554 if (n<0) {
555 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)" );
556 return GL_FALSE;
557 }
558
559 for (i=0;i<n;i++) {
560 struct gl_texture_object *t;
561 if (texName[i]==0) {
562 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)" );
563 return GL_FALSE;
564 }
565 t = (struct gl_texture_object *)
566 HashLookup(ctx->Shared->TexObjects, texName[i]);
567 if (t) {
568 /* we consider all valid texture objects to be resident */
569 residences[i] = GL_TRUE;
570 }
571 else {
572 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)" );
573 return GL_FALSE;
574 }
575 }
576 return resident;
577 }
578
579
580
581 /*
582 * Execute glIsTexture
583 */
gl_IsTexture(GLcontext * ctx,GLuint texture)584 GLboolean gl_IsTexture( GLcontext *ctx, GLuint texture )
585 {
586 if (INSIDE_BEGIN_END(ctx)) {
587 gl_error( ctx, GL_INVALID_OPERATION, "glIsTextures" );
588 return GL_FALSE;
589 }
590 if (texture>0 && HashLookup(ctx->Shared->TexObjects, texture)) {
591 return GL_TRUE;
592 }
593 else {
594 return GL_FALSE;
595 }
596 }
597
598