e06f8cd277cc992c23dfd99c5be1780bf80d11d9
[mesa.git] / src / mesa / main / texobj.c
1 /* $Id: texobj.c,v 1.26 2000/08/03 14:03:17 brianp Exp $ */
2
3 /*
4 * Mesa 3-D graphics library
5 * Version: 3.5
6 *
7 * Copyright (C) 1999-2000 Brian Paul All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27
28 #ifdef PC_HEADER
29 #include "all.h"
30 #else
31 #include "glheader.h"
32 #include "colortab.h"
33 #include "context.h"
34 #include "enums.h"
35 #include "hash.h"
36 #include "mem.h"
37 #include "teximage.h"
38 #include "texstate.h"
39 #include "texobj.h"
40 #include "types.h"
41 #endif
42
43
44
45 /*
46 * Allocate a new texture object and add it to the linked list of texture
47 * objects. If name>0 then also insert the new texture object into the hash
48 * table.
49 * Input: shared - the shared GL state structure to contain the texture object
50 * name - integer name for the texture object
51 * dimensions - either 1, 2, 3 or 6 (cube map)
52 * Return: pointer to new texture object
53 */
54 struct gl_texture_object *
55 gl_alloc_texture_object( struct gl_shared_state *shared, GLuint name,
56 GLuint dimensions)
57 {
58 struct gl_texture_object *obj;
59
60 ASSERT(dimensions <= 3 || dimensions == 6);
61
62 obj = CALLOC_STRUCT(gl_texture_object);
63
64 if (obj) {
65 /* init the non-zero fields */
66 obj->RefCount = 1;
67 obj->Name = name;
68 obj->Dimensions = dimensions;
69 obj->Priority = 1.0F;
70 obj->WrapS = GL_REPEAT;
71 obj->WrapT = GL_REPEAT;
72 obj->MinFilter = GL_NEAREST_MIPMAP_LINEAR;
73 obj->MagFilter = GL_LINEAR;
74 obj->MinLod = -1000.0;
75 obj->MaxLod = 1000.0;
76 obj->BaseLevel = 0;
77 obj->MaxLevel = 1000;
78 obj->MinMagThresh = 0.0F;
79 _mesa_init_colortable(&obj->Palette);
80
81 /* insert into linked list */
82 if (shared) {
83 _glthread_LOCK_MUTEX(shared->Mutex);
84 obj->Next = shared->TexObjectList;
85 shared->TexObjectList = obj;
86 _glthread_UNLOCK_MUTEX(shared->Mutex);
87 }
88
89 if (name > 0) {
90 /* insert into hash table */
91 _mesa_HashInsert(shared->TexObjects, name, obj);
92 }
93 }
94 return obj;
95 }
96
97
98 /*
99 * Deallocate a texture object struct and remove it from the given
100 * shared GL state.
101 * Input: shared - the shared GL state to which the object belongs
102 * t - the texture object to delete
103 */
104 void gl_free_texture_object( struct gl_shared_state *shared,
105 struct gl_texture_object *t )
106 {
107 struct gl_texture_object *tprev, *tcurr;
108
109 assert(t);
110
111 /* Remove t from dirty list so we don't touch free'd memory later.
112 * Test for shared since Proxy texture aren't in global linked list.
113 */
114 if (shared)
115 gl_remove_texobj_from_dirty_list( shared, t );
116
117 /* unlink t from the linked list */
118 if (shared) {
119 _glthread_LOCK_MUTEX(shared->Mutex);
120 tprev = NULL;
121 tcurr = shared->TexObjectList;
122 while (tcurr) {
123 if (tcurr==t) {
124 if (tprev) {
125 tprev->Next = t->Next;
126 }
127 else {
128 shared->TexObjectList = t->Next;
129 }
130 break;
131 }
132 tprev = tcurr;
133 tcurr = tcurr->Next;
134 }
135 _glthread_UNLOCK_MUTEX(shared->Mutex);
136 }
137
138 if (t->Name) {
139 /* remove from hash table */
140 _mesa_HashRemove(shared->TexObjects, t->Name);
141 }
142
143 _mesa_free_colortable_data(&t->Palette);
144
145 /* free texture images */
146 {
147 GLuint i;
148 for (i=0;i<MAX_TEXTURE_LEVELS;i++) {
149 if (t->Image[i]) {
150 _mesa_free_texture_image( t->Image[i] );
151 }
152 }
153 }
154 /* free this object */
155 FREE( t );
156 }
157
158
159
160 /*
161 * Examine a texture object to determine if it is complete or not.
162 * The t->Complete flag will be set to GL_TRUE or GL_FALSE accordingly.
163 */
164 void
165 _mesa_test_texobj_completeness( const GLcontext *ctx,
166 struct gl_texture_object *t )
167 {
168 const GLint baseLevel = t->BaseLevel;
169
170 t->Complete = GL_TRUE; /* be optimistic */
171
172 /* Always need level zero image */
173 if (!t->Image[baseLevel]) {
174 t->Complete = GL_FALSE;
175 return;
176 }
177
178 /* Compute number of mipmap levels */
179 if (t->Dimensions == 1) {
180 t->P = t->Image[baseLevel]->WidthLog2;
181 }
182 else if (t->Dimensions == 2 || t->Dimensions == 6) {
183 t->P = MAX2(t->Image[baseLevel]->WidthLog2,
184 t->Image[baseLevel]->HeightLog2);
185 }
186 else if (t->Dimensions == 3) {
187 GLint max = MAX2(t->Image[baseLevel]->WidthLog2,
188 t->Image[baseLevel]->HeightLog2);
189 max = MAX2(max, (GLint)(t->Image[baseLevel]->DepthLog2));
190 t->P = max;
191 }
192
193 /* Compute M (see the 1.2 spec) used during mipmapping */
194 t->M = (GLfloat) (MIN2(t->MaxLevel, t->P) - t->BaseLevel);
195
196
197 if (t->Dimensions == 6) {
198 /* make sure all six level 0 images are same size */
199 const GLint w = t->Image[baseLevel]->Width2;
200 const GLint h = t->Image[baseLevel]->Height2;
201 if (!t->NegX[baseLevel] ||
202 t->NegX[baseLevel]->Width2 != w ||
203 t->NegX[baseLevel]->Height2 != h ||
204 !t->PosY[baseLevel] ||
205 t->PosY[baseLevel]->Width2 != w ||
206 t->PosY[baseLevel]->Height2 != h ||
207 !t->NegY[baseLevel] ||
208 t->NegY[baseLevel]->Width2 != w ||
209 t->NegY[baseLevel]->Height2 != h ||
210 !t->PosZ[baseLevel] ||
211 t->PosZ[baseLevel]->Width2 != w ||
212 t->PosZ[baseLevel]->Height2 != h ||
213 !t->NegZ[baseLevel] ||
214 t->NegZ[baseLevel]->Width2 != w ||
215 t->NegZ[baseLevel]->Height2 != h) {
216 t->Complete = GL_FALSE;
217 return;
218 }
219 }
220
221 if (t->MinFilter != GL_NEAREST && t->MinFilter != GL_LINEAR) {
222 /*
223 * Mipmapping: determine if we have a complete set of mipmaps
224 */
225 GLint i;
226 GLint minLevel = baseLevel;
227 GLint maxLevel = MIN2(t->P, ctx->Const.MaxTextureLevels-1);
228 maxLevel = MIN2(maxLevel, t->MaxLevel);
229
230 if (minLevel > maxLevel) {
231 t->Complete = GL_FALSE;
232 return;
233 }
234
235 /* Test dimension-independent attributes */
236 for (i = minLevel; i <= maxLevel; i++) {
237 if (t->Image[i]) {
238 if (t->Image[i]->Format != t->Image[baseLevel]->Format) {
239 t->Complete = GL_FALSE;
240 return;
241 }
242 if (t->Image[i]->Border != t->Image[baseLevel]->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[baseLevel]->Width2;
253 for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) {
254 if (width > 1) {
255 width /= 2;
256 }
257 if (i >= minLevel && i <= maxLevel) {
258 if (!t->Image[i]) {
259 t->Complete = GL_FALSE;
260 return;
261 }
262 if (t->Image[i]->Width2 != width ) {
263 t->Complete = GL_FALSE;
264 return;
265 }
266 }
267 if (width == 1) {
268 return; /* found smallest needed mipmap, all done! */
269 }
270 }
271 }
272 else if (t->Dimensions == 2) {
273 /* Test 2-D mipmaps */
274 GLuint width = t->Image[baseLevel]->Width2;
275 GLuint height = t->Image[baseLevel]->Height2;
276 for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) {
277 if (width > 1) {
278 width /= 2;
279 }
280 if (height > 1) {
281 height /= 2;
282 }
283 if (i >= minLevel && i <= maxLevel) {
284 if (!t->Image[i]) {
285 t->Complete = GL_FALSE;
286 return;
287 }
288 if (t->Image[i]->Width2 != width) {
289 t->Complete = GL_FALSE;
290 return;
291 }
292 if (t->Image[i]->Height2 != height) {
293 t->Complete = GL_FALSE;
294 return;
295 }
296 if (width==1 && height==1) {
297 return; /* found smallest needed mipmap, all done! */
298 }
299 }
300 }
301 }
302 else if (t->Dimensions == 3) {
303 /* Test 3-D mipmaps */
304 GLuint width = t->Image[baseLevel]->Width2;
305 GLuint height = t->Image[baseLevel]->Height2;
306 GLuint depth = t->Image[baseLevel]->Depth2;
307 for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) {
308 if (width > 1) {
309 width /= 2;
310 }
311 if (height > 1) {
312 height /= 2;
313 }
314 if (depth > 1) {
315 depth /= 2;
316 }
317 if (i >= minLevel && i <= maxLevel) {
318 if (!t->Image[i]) {
319 t->Complete = GL_FALSE;
320 return;
321 }
322 if (t->Image[i]->Width2 != width) {
323 t->Complete = GL_FALSE;
324 return;
325 }
326 if (t->Image[i]->Height2 != height) {
327 t->Complete = GL_FALSE;
328 return;
329 }
330 if (t->Image[i]->Depth2 != depth) {
331 t->Complete = GL_FALSE;
332 return;
333 }
334 }
335 if (width == 1 && height == 1 && depth == 1) {
336 return; /* found smallest needed mipmap, all done! */
337 }
338 }
339 }
340 else if (t->Dimensions == 6) {
341 /* make sure 6 cube faces are consistant */
342 GLuint width = t->Image[baseLevel]->Width2;
343 GLuint height = t->Image[baseLevel]->Height2;
344 for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) {
345 if (width > 1) {
346 width /= 2;
347 }
348 if (height > 1) {
349 height /= 2;
350 }
351 if (i >= minLevel && i <= maxLevel) {
352 /* check that we have images defined */
353 if (!t->Image[i] || !t->NegX[i] ||
354 !t->PosY[i] || !t->NegY[i] ||
355 !t->PosZ[i] || !t->NegZ[i]) {
356 t->Complete = GL_FALSE;
357 return;
358 }
359 /* check that all six images have same size */
360 if (t->NegX[i]->Width2!=width || t->NegX[i]->Height2!=height ||
361 t->PosY[i]->Width2!=width || t->PosY[i]->Height2!=height ||
362 t->NegY[i]->Width2!=width || t->NegY[i]->Height2!=height ||
363 t->PosZ[i]->Width2!=width || t->PosZ[i]->Height2!=height ||
364 t->NegZ[i]->Width2!=width || t->NegZ[i]->Height2!=height) {
365 t->Complete = GL_FALSE;
366 return;
367 }
368 }
369 if (width == 1 && height == 1) {
370 return; /* found smallest needed mipmap, all done! */
371 }
372 }
373 }
374 else {
375 /* Dimensions = ??? */
376 gl_problem(NULL, "Bug in gl_test_texture_object_completeness\n");
377 }
378 }
379 }
380
381
382 _glthread_DECLARE_STATIC_MUTEX(GenTexturesLock);
383
384
385 /*
386 * Execute glGenTextures
387 */
388 void
389 _mesa_GenTextures( GLsizei n, GLuint *texName )
390 {
391 GET_CURRENT_CONTEXT(ctx);
392 GLuint first;
393 GLint i;
394
395 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glGenTextures");
396 if (n < 0) {
397 gl_error( ctx, GL_INVALID_VALUE, "glGenTextures" );
398 return;
399 }
400
401 if (!texName)
402 return;
403
404 /*
405 * This must be atomic (generation and allocation of texture IDs)
406 */
407 _glthread_LOCK_MUTEX(GenTexturesLock);
408
409 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n);
410
411 /* Return the texture names */
412 for (i=0;i<n;i++) {
413 texName[i] = first + i;
414 }
415
416 /* Allocate new, empty texture objects */
417 for (i=0;i<n;i++) {
418 GLuint name = first + i;
419 GLuint dims = 0;
420 (void) gl_alloc_texture_object(ctx->Shared, name, dims);
421 }
422
423 _glthread_UNLOCK_MUTEX(GenTexturesLock);
424 }
425
426
427
428 /*
429 * Execute glDeleteTextures
430 */
431 void
432 _mesa_DeleteTextures( GLsizei n, const GLuint *texName)
433 {
434 GET_CURRENT_CONTEXT(ctx);
435 GLint i;
436
437 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glDeleteTextures");
438
439 if (!texName)
440 return;
441
442 for (i=0;i<n;i++) {
443 struct gl_texture_object *t;
444 if (texName[i]>0) {
445 t = (struct gl_texture_object *)
446 _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]);
447 if (t) {
448 /* First check if this texture is currently bound.
449 * If so, unbind it and decrement the reference count.
450 */
451 GLuint u;
452 for (u = 0; u < MAX_TEXTURE_UNITS; u++) {
453 struct gl_texture_unit *unit = &ctx->Texture.Unit[u];
454 GLuint d;
455 for (d = 1 ; d <= 3 ; d++) {
456 if (unit->CurrentD[d] == t) {
457 unit->CurrentD[d] = ctx->Shared->DefaultD[d];
458 ctx->Shared->DefaultD[d]->RefCount++;
459 t->RefCount--;
460 ASSERT( t->RefCount >= 0 );
461 }
462 }
463 }
464
465 /* Decrement reference count and delete if zero */
466 t->RefCount--;
467 ASSERT( t->RefCount >= 0 );
468 if (t->RefCount == 0) {
469 if (ctx->Driver.DeleteTexture)
470 (*ctx->Driver.DeleteTexture)( ctx, t );
471 gl_free_texture_object(ctx->Shared, t);
472 }
473 }
474 }
475 }
476 }
477
478
479
480 /*
481 * Execute glBindTexture
482 */
483 void
484 _mesa_BindTexture( GLenum target, GLuint texName )
485 {
486 GET_CURRENT_CONTEXT(ctx);
487 GLuint unit = ctx->Texture.CurrentUnit;
488 struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
489 struct gl_texture_object *oldTexObj;
490 struct gl_texture_object *newTexObj;
491 GLuint dim;
492
493 if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
494 fprintf(stderr, "glBindTexture %s %d\n",
495 gl_lookup_enum_by_nr(target), (GLint) texName);
496
497 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glBindTexture");
498
499 switch (target) {
500 case GL_TEXTURE_1D:
501 dim = 1;
502 oldTexObj = texUnit->CurrentD[1];
503 break;
504 case GL_TEXTURE_2D:
505 dim = 2;
506 oldTexObj = texUnit->CurrentD[2];
507 break;
508 case GL_TEXTURE_3D:
509 dim = 3;
510 oldTexObj = texUnit->CurrentD[3];
511 break;
512 case GL_TEXTURE_CUBE_MAP_ARB:
513 if (ctx->Extensions.HaveTextureCubeMap) {
514 dim = 6;
515 oldTexObj = texUnit->CurrentCubeMap;
516 break;
517 }
518 /* fallthrough */
519 default:
520 gl_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" );
521 return;
522 }
523
524 if (oldTexObj->Name == texName)
525 return;
526
527 if (texName == 0) {
528 if (target == GL_TEXTURE_CUBE_MAP_ARB)
529 newTexObj = ctx->Shared->DefaultCubeMap;
530 else
531 newTexObj = ctx->Shared->DefaultD[dim];
532 }
533 else {
534 struct _mesa_HashTable *hash = ctx->Shared->TexObjects;
535 newTexObj = (struct gl_texture_object *) _mesa_HashLookup(hash, texName);
536
537 if (!newTexObj)
538 newTexObj = gl_alloc_texture_object(ctx->Shared, texName, dim);
539
540 if (newTexObj->Dimensions != dim) {
541 if (newTexObj->Dimensions) {
542 /* the named texture object's dimensions don't match the target */
543 gl_error( ctx, GL_INVALID_OPERATION, "glBindTexture" );
544 return;
545 }
546 newTexObj->Dimensions = dim;
547 }
548 }
549
550 newTexObj->RefCount++;
551
552 switch (target) {
553 case GL_TEXTURE_1D:
554 texUnit->CurrentD[1] = newTexObj;
555 break;
556 case GL_TEXTURE_2D:
557 texUnit->CurrentD[2] = newTexObj;
558 break;
559 case GL_TEXTURE_3D:
560 texUnit->CurrentD[3] = newTexObj;
561 break;
562 case GL_TEXTURE_CUBE_MAP_ARB:
563 texUnit->CurrentCubeMap = newTexObj;
564 break;
565 default:
566 gl_problem(ctx, "bad target in BindTexture");
567 }
568
569 /* If we've changed the CurrentD[123] texture object then update the
570 * ctx->Texture.Current pointer to point to the new texture object.
571 */
572 texUnit->Current = texUnit->CurrentD[texUnit->CurrentDimension];
573
574 /* Check if we may have to use a new triangle rasterizer */
575 if ((ctx->IndirectTriangles & DD_SW_RASTERIZE) &&
576 ( oldTexObj->WrapS != newTexObj->WrapS
577 || oldTexObj->WrapT != newTexObj->WrapT
578 || oldTexObj->WrapR != newTexObj->WrapR
579 || oldTexObj->MinFilter != newTexObj->MinFilter
580 || oldTexObj->MagFilter != newTexObj->MagFilter
581 || (oldTexObj->Image[0] && newTexObj->Image[0] &&
582 (oldTexObj->Image[0]->Format!=newTexObj->Image[0]->Format))))
583 {
584 ctx->NewState |= (NEW_RASTER_OPS | NEW_TEXTURING);
585 }
586
587 if (oldTexObj->Complete != newTexObj->Complete)
588 ctx->NewState |= NEW_TEXTURING;
589
590 /* Pass BindTexture call to device driver */
591 if (ctx->Driver.BindTexture) {
592 (*ctx->Driver.BindTexture)( ctx, target, newTexObj );
593 }
594
595 if (oldTexObj->Name > 0) {
596 /* never delete default (id=0) texture objects */
597 oldTexObj->RefCount--;
598 if (oldTexObj->RefCount <= 0) {
599 if (ctx->Driver.DeleteTexture) {
600 (*ctx->Driver.DeleteTexture)( ctx, oldTexObj );
601 }
602 gl_free_texture_object(ctx->Shared, oldTexObj);
603 }
604 }
605 }
606
607
608
609 /*
610 * Execute glPrioritizeTextures
611 */
612 void
613 _mesa_PrioritizeTextures( GLsizei n, const GLuint *texName,
614 const GLclampf *priorities )
615 {
616 GET_CURRENT_CONTEXT(ctx);
617 GLint i;
618
619 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPrioritizeTextures");
620 if (n < 0) {
621 gl_error( ctx, GL_INVALID_VALUE, "glPrioritizeTextures" );
622 return;
623 }
624
625 if (!priorities)
626 return;
627
628 for (i = 0; i < n; i++) {
629 if (texName[i] > 0) {
630 struct gl_texture_object *t = (struct gl_texture_object *)
631 _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]);
632 if (t) {
633 t->Priority = CLAMP( priorities[i], 0.0F, 1.0F );
634 if (ctx->Driver.PrioritizeTexture)
635 ctx->Driver.PrioritizeTexture( ctx, t, t->Priority );
636 }
637 }
638 }
639 }
640
641
642
643 /*
644 * Execute glAreTexturesResident
645 */
646 GLboolean
647 _mesa_AreTexturesResident(GLsizei n, const GLuint *texName,
648 GLboolean *residences)
649 {
650 GET_CURRENT_CONTEXT(ctx);
651 GLboolean allResident = GL_TRUE;
652 GLint i;
653
654 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx,
655 "glAreTexturesResident", GL_FALSE);
656 if (n < 0) {
657 gl_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)");
658 return GL_FALSE;
659 }
660
661 if (!texName || !residences)
662 return GL_FALSE;
663
664 for (i = 0; i < n; i++) {
665 struct gl_texture_object *t;
666 if (texName[i] == 0) {
667 gl_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)");
668 return GL_FALSE;
669 }
670 t = (struct gl_texture_object *)
671 _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]);
672 if (t) {
673 if (ctx->Driver.IsTextureResident) {
674 residences[i] = ctx->Driver.IsTextureResident(ctx, t);
675 if (!residences[i])
676 allResident = GL_FALSE;
677 }
678 else {
679 residences[i] = GL_TRUE;
680 }
681 }
682 else {
683 gl_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)");
684 return GL_FALSE;
685 }
686 }
687 return allResident;
688 }
689
690
691
692 /*
693 * Execute glIsTexture
694 */
695 GLboolean
696 _mesa_IsTexture( GLuint texture )
697 {
698 GET_CURRENT_CONTEXT(ctx);
699 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, "glIsTextures",
700 GL_FALSE);
701 if (texture > 0 && _mesa_HashLookup(ctx->Shared->TexObjects, texture)) {
702 return GL_TRUE;
703 }
704 else {
705 return GL_FALSE;
706 }
707 }
708