rearranged order of some functions
[mesa.git] / src / mesa / main / texobj.c
1 /* $Id: texobj.c,v 1.14 2000/02/12 01:59:19 brianp Exp $ */
2
3 /*
4 * Mesa 3-D graphics library
5 * Version: 3.3
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 "context.h"
33 #include "enums.h"
34 #include "hash.h"
35 #include "mem.h"
36 #include "teximage.h"
37 #include "texstate.h"
38 #include "texobj.h"
39 #include "types.h"
40 #endif
41
42
43
44 /*
45 * Allocate a new texture object and add it to the linked list of texture
46 * objects. If name>0 then also insert the new texture object into the hash
47 * table.
48 * Input: shared - the shared GL state structure to contain the texture object
49 * name - integer name for the texture object
50 * dimensions - either 1, 2 or 3
51 * Return: pointer to new texture object
52 */
53 struct gl_texture_object *
54 gl_alloc_texture_object( struct gl_shared_state *shared, GLuint name,
55 GLuint dimensions)
56 {
57 struct gl_texture_object *obj;
58
59 ASSERT(dimensions <= 3);
60
61 obj = CALLOC_STRUCT(gl_texture_object);
62
63 if (obj) {
64 /* init the non-zero fields */
65 obj->RefCount = 1;
66 obj->Name = name;
67 obj->Dimensions = dimensions;
68 obj->WrapS = GL_REPEAT;
69 obj->WrapT = GL_REPEAT;
70 obj->MinFilter = GL_NEAREST_MIPMAP_LINEAR;
71 obj->MagFilter = GL_LINEAR;
72 obj->MinLod = -1000.0;
73 obj->MaxLod = 1000.0;
74 obj->BaseLevel = 0;
75 obj->MaxLevel = 1000;
76 obj->MinMagThresh = 0.0F;
77 obj->Palette.Table[0] = 255;
78 obj->Palette.Table[1] = 255;
79 obj->Palette.Table[2] = 255;
80 obj->Palette.Table[3] = 255;
81 obj->Palette.Size = 1;
82 obj->Palette.IntFormat = GL_RGBA;
83 obj->Palette.Format = GL_RGBA;
84
85 /* insert into linked list */
86 if (shared) {
87 _glthread_LOCK_MUTEX(shared->Mutex);
88 obj->Next = shared->TexObjectList;
89 shared->TexObjectList = obj;
90 _glthread_UNLOCK_MUTEX(shared->Mutex);
91 }
92
93 if (name > 0) {
94 /* insert into hash table */
95 _mesa_HashInsert(shared->TexObjects, name, obj);
96 }
97 }
98 return obj;
99 }
100
101
102 /*
103 * Deallocate a texture object struct and remove it from the given
104 * shared GL state.
105 * Input: shared - the shared GL state to which the object belongs
106 * t - the texture object to delete
107 */
108 void gl_free_texture_object( struct gl_shared_state *shared,
109 struct gl_texture_object *t )
110 {
111 struct gl_texture_object *tprev, *tcurr;
112
113 assert(t);
114
115 /* Remove t from dirty list so we don't touch free'd memory later.
116 * Test for shared since Proxy texture aren't in global linked list.
117 */
118 if (shared)
119 gl_remove_texobj_from_dirty_list( shared, t );
120
121 /* unlink t from the linked list */
122 if (shared) {
123 _glthread_LOCK_MUTEX(shared->Mutex);
124 tprev = NULL;
125 tcurr = shared->TexObjectList;
126 while (tcurr) {
127 if (tcurr==t) {
128 if (tprev) {
129 tprev->Next = t->Next;
130 }
131 else {
132 shared->TexObjectList = t->Next;
133 }
134 break;
135 }
136 tprev = tcurr;
137 tcurr = tcurr->Next;
138 }
139 _glthread_UNLOCK_MUTEX(shared->Mutex);
140 }
141
142 if (t->Name) {
143 /* remove from hash table */
144 _mesa_HashRemove(shared->TexObjects, t->Name);
145 }
146
147 /* free texture image */
148 {
149 GLuint i;
150 for (i=0;i<MAX_TEXTURE_LEVELS;i++) {
151 if (t->Image[i]) {
152 gl_free_texture_image( t->Image[i] );
153 }
154 }
155 }
156 /* free this object */
157 FREE( t );
158 }
159
160
161
162 /*
163 * Examine a texture object to determine if it is complete or not.
164 * The t->Complete flag will be set to GL_TRUE or GL_FALSE accordingly.
165 */
166 void gl_test_texture_object_completeness( const GLcontext *ctx, struct gl_texture_object *t )
167 {
168 t->Complete = GL_TRUE; /* be optimistic */
169
170 /* Always need level zero image */
171 if (!t->Image[0] || !t->Image[0]->Data) {
172 t->Complete = GL_FALSE;
173 return;
174 }
175
176 /* Compute number of mipmap levels */
177 if (t->Dimensions==1) {
178 t->P = t->Image[0]->WidthLog2;
179 }
180 else if (t->Dimensions==2) {
181 t->P = MAX2(t->Image[0]->WidthLog2, t->Image[0]->HeightLog2);
182 }
183 else if (t->Dimensions==3) {
184 GLint max = MAX2(t->Image[0]->WidthLog2, t->Image[0]->HeightLog2);
185 max = MAX2(max, (GLint)(t->Image[0]->DepthLog2));
186 t->P = max;
187 }
188
189 /* Compute M (see the 1.2 spec) used during mipmapping */
190 t->M = (GLfloat) (MIN2(t->MaxLevel, t->P) - t->BaseLevel);
191
192
193 if (t->MinFilter!=GL_NEAREST && t->MinFilter!=GL_LINEAR) {
194 /*
195 * Mipmapping: determine if we have a complete set of mipmaps
196 */
197 GLint i;
198 GLint minLevel = t->BaseLevel;
199 GLint maxLevel = MIN2(t->P, ctx->Const.MaxTextureLevels-1);
200 maxLevel = MIN2(maxLevel, t->MaxLevel);
201
202 if (minLevel > maxLevel) {
203 t->Complete = GL_FALSE;
204 return;
205 }
206
207 /* Test dimension-independent attributes */
208 for (i = minLevel; i <= maxLevel; i++) {
209 if (t->Image[i]) {
210 if (!t->Image[i]->Data) {
211 t->Complete = GL_FALSE;
212 return;
213 }
214 if (t->Image[i]->Format != t->Image[0]->Format) {
215 t->Complete = GL_FALSE;
216 return;
217 }
218 if (t->Image[i]->Border != t->Image[0]->Border) {
219 t->Complete = GL_FALSE;
220 return;
221 }
222 }
223 }
224
225 /* Test things which depend on number of texture image dimensions */
226 if (t->Dimensions==1) {
227 /* Test 1-D mipmaps */
228 GLuint width = t->Image[0]->Width2;
229 for (i=1; i<ctx->Const.MaxTextureLevels; i++) {
230 if (width>1) {
231 width /= 2;
232 }
233 if (i >= minLevel && i <= maxLevel) {
234 if (!t->Image[i]) {
235 t->Complete = GL_FALSE;
236 return;
237 }
238 if (!t->Image[i]->Data) {
239 t->Complete = GL_FALSE;
240 return;
241 }
242 if (t->Image[i]->Width2 != width ) {
243 t->Complete = GL_FALSE;
244 return;
245 }
246 }
247 if (width==1) {
248 return; /* found smallest needed mipmap, all done! */
249 }
250 }
251 }
252 else if (t->Dimensions==2) {
253 /* Test 2-D mipmaps */
254 GLuint width = t->Image[0]->Width2;
255 GLuint height = t->Image[0]->Height2;
256 for (i=1; i<ctx->Const.MaxTextureLevels; i++) {
257 if (width>1) {
258 width /= 2;
259 }
260 if (height>1) {
261 height /= 2;
262 }
263 if (i >= minLevel && i <= maxLevel) {
264 if (!t->Image[i]) {
265 t->Complete = GL_FALSE;
266 return;
267 }
268 if (t->Image[i]->Width2 != width) {
269 t->Complete = GL_FALSE;
270 return;
271 }
272 if (t->Image[i]->Height2 != height) {
273 t->Complete = GL_FALSE;
274 return;
275 }
276 if (width==1 && height==1) {
277 return; /* found smallest needed mipmap, all done! */
278 }
279 }
280 }
281 }
282 else if (t->Dimensions==3) {
283 /* Test 3-D mipmaps */
284 GLuint width = t->Image[0]->Width2;
285 GLuint height = t->Image[0]->Height2;
286 GLuint depth = t->Image[0]->Depth2;
287 for (i=1; i<ctx->Const.MaxTextureLevels; i++) {
288 if (width>1) {
289 width /= 2;
290 }
291 if (height>1) {
292 height /= 2;
293 }
294 if (depth>1) {
295 depth /= 2;
296 }
297 if (i >= minLevel && i <= maxLevel) {
298 if (!t->Image[i]) {
299 t->Complete = GL_FALSE;
300 return;
301 }
302 if (t->Image[i]->Width2 != width) {
303 t->Complete = GL_FALSE;
304 return;
305 }
306 if (t->Image[i]->Height2 != height) {
307 t->Complete = GL_FALSE;
308 return;
309 }
310 if (t->Image[i]->Depth2 != depth) {
311 t->Complete = GL_FALSE;
312 return;
313 }
314 }
315 if (width==1 && height==1 && depth==1) {
316 return; /* found smallest needed mipmap, all done! */
317 }
318 }
319 }
320 else {
321 /* Dimensions = ??? */
322 gl_problem(NULL, "Bug in gl_test_texture_object_completeness\n");
323 }
324 }
325 }
326
327
328
329 /*
330 * Execute glGenTextures
331 */
332 void
333 _mesa_GenTextures( GLsizei n, GLuint *texName )
334 {
335 GET_CURRENT_CONTEXT(ctx);
336 GLuint first;
337 GLint i;
338
339 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glGenTextures");
340 if (n<0) {
341 gl_error( ctx, GL_INVALID_VALUE, "glGenTextures" );
342 return;
343 }
344
345 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n);
346
347 /* Return the texture names */
348 for (i=0;i<n;i++) {
349 texName[i] = first + i;
350 }
351
352 /* Allocate new, empty texture objects */
353 for (i=0;i<n;i++) {
354 GLuint name = first + i;
355 GLuint dims = 0;
356 (void) gl_alloc_texture_object(ctx->Shared, name, dims);
357 }
358 }
359
360
361
362 /*
363 * Execute glDeleteTextures
364 */
365 void
366 _mesa_DeleteTextures( GLsizei n, const GLuint *texName)
367 {
368 GET_CURRENT_CONTEXT(ctx);
369 GLint i;
370
371 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glDeleteTextures");
372
373 for (i=0;i<n;i++) {
374 struct gl_texture_object *t;
375 if (texName[i]>0) {
376 t = (struct gl_texture_object *)
377 _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]);
378 if (t) {
379 /* First check if this texture is currently bound.
380 * If so, unbind it and decrement the reference count.
381 */
382 GLuint u;
383 for (u = 0; u < MAX_TEXTURE_UNITS; u++) {
384 struct gl_texture_unit *unit = &ctx->Texture.Unit[u];
385 GLuint d;
386 for (d = 1 ; d <= 3 ; d++) {
387 if (unit->CurrentD[d] == t) {
388 unit->CurrentD[d] = ctx->Shared->DefaultD[d];
389 ctx->Shared->DefaultD[d]->RefCount++;
390 t->RefCount--;
391 ASSERT( t->RefCount >= 0 );
392 }
393 }
394 }
395
396 /* Decrement reference count and delete if zero */
397 t->RefCount--;
398 ASSERT( t->RefCount >= 0 );
399 if (t->RefCount == 0) {
400 if (ctx->Driver.DeleteTexture)
401 (*ctx->Driver.DeleteTexture)( ctx, t );
402 gl_free_texture_object(ctx->Shared, t);
403 }
404 }
405 }
406 }
407 }
408
409
410
411 /*
412 * Execute glBindTexture
413 */
414 void
415 _mesa_BindTexture( GLenum target, GLuint texName )
416 {
417 GET_CURRENT_CONTEXT(ctx);
418 GLuint unit = ctx->Texture.CurrentUnit;
419 struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
420 struct gl_texture_object *oldTexObj;
421 struct gl_texture_object *newTexObj;
422 GLuint dim;
423
424 if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
425 fprintf(stderr, "glBindTexture %s %d\n",
426 gl_lookup_enum_by_nr(target), (GLint) texName);
427
428 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glBindTexture");
429
430 switch (target) {
431 case GL_TEXTURE_1D:
432 dim = 1;
433 break;
434 case GL_TEXTURE_2D:
435 dim = 2;
436 break;
437 case GL_TEXTURE_3D:
438 dim = 3;
439 break;
440 default:
441 gl_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" );
442 return;
443 }
444
445 oldTexObj = texUnit->CurrentD[dim];
446
447 if (oldTexObj->Name == texName)
448 return;
449
450 if (texName == 0)
451 newTexObj = ctx->Shared->DefaultD[dim];
452 else {
453 struct _mesa_HashTable *hash = ctx->Shared->TexObjects;
454 newTexObj = (struct gl_texture_object *) _mesa_HashLookup(hash, texName);
455
456 if (!newTexObj)
457 newTexObj = gl_alloc_texture_object(ctx->Shared, texName, dim);
458
459 if (newTexObj->Dimensions != dim) {
460 if (newTexObj->Dimensions) {
461 /* the named texture object's dimensions don't match the target */
462 gl_error( ctx, GL_INVALID_OPERATION, "glBindTexture" );
463 return;
464 }
465 newTexObj->Dimensions = dim;
466 }
467 }
468
469 newTexObj->RefCount++;
470
471 texUnit->CurrentD[dim] = newTexObj;
472
473 /* If we've changed the CurrentD[123] texture object then update the
474 * ctx->Texture.Current pointer to point to the new texture object.
475 */
476 texUnit->Current = texUnit->CurrentD[texUnit->CurrentDimension];
477
478 /* Check if we may have to use a new triangle rasterizer */
479 if ((ctx->IndirectTriangles & DD_SW_RASTERIZE) &&
480 ( oldTexObj->WrapS != newTexObj->WrapS
481 || oldTexObj->WrapT != newTexObj->WrapT
482 || oldTexObj->WrapR != newTexObj->WrapR
483 || oldTexObj->MinFilter != newTexObj->MinFilter
484 || oldTexObj->MagFilter != newTexObj->MagFilter
485 || (oldTexObj->Image[0] && newTexObj->Image[0] &&
486 (oldTexObj->Image[0]->Format!=newTexObj->Image[0]->Format))))
487 {
488 ctx->NewState |= (NEW_RASTER_OPS | NEW_TEXTURING);
489 }
490
491 if (oldTexObj->Complete != newTexObj->Complete)
492 ctx->NewState |= NEW_TEXTURING;
493
494 /* Pass BindTexture call to device driver */
495 if (ctx->Driver.BindTexture) {
496 (*ctx->Driver.BindTexture)( ctx, target, newTexObj );
497 }
498
499 if (oldTexObj->Name > 0) {
500 /* never delete default (id=0) texture objects */
501 oldTexObj->RefCount--;
502 if (oldTexObj->RefCount <= 0) {
503 if (ctx->Driver.DeleteTexture) {
504 (*ctx->Driver.DeleteTexture)( ctx, oldTexObj );
505 }
506 gl_free_texture_object(ctx->Shared, oldTexObj);
507 }
508 }
509 }
510
511
512
513 /*
514 * Execute glPrioritizeTextures
515 */
516 void
517 _mesa_PrioritizeTextures( GLsizei n, const GLuint *texName,
518 const GLclampf *priorities )
519 {
520 GET_CURRENT_CONTEXT(ctx);
521 GLint i;
522
523 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPrioritizeTextures");
524 if (n<0) {
525 gl_error( ctx, GL_INVALID_VALUE, "glPrioritizeTextures" );
526 return;
527 }
528
529 for (i=0;i<n;i++) {
530 struct gl_texture_object *t;
531 if (texName[i]>0) {
532 t = (struct gl_texture_object *)
533 _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]);
534 if (t) {
535 t->Priority = CLAMP( priorities[i], 0.0F, 1.0F );
536
537 if (ctx->Driver.PrioritizeTexture)
538 ctx->Driver.PrioritizeTexture( ctx, t, t->Priority );
539 }
540 }
541 }
542 }
543
544
545
546 /*
547 * Execute glAreTexturesResident
548 */
549 GLboolean
550 _mesa_AreTexturesResident( GLsizei n, const GLuint *texName,
551 GLboolean *residences )
552 {
553 GET_CURRENT_CONTEXT(ctx);
554 GLboolean resident = GL_TRUE;
555 GLint i;
556
557 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx,
558 "glAreTexturesResident",
559 GL_FALSE);
560 if (n<0) {
561 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)" );
562 return GL_FALSE;
563 }
564
565 for (i=0;i<n;i++) {
566 struct gl_texture_object *t;
567 if (texName[i]==0) {
568 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)" );
569 return GL_FALSE;
570 }
571 t = (struct gl_texture_object *)
572 _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]);
573 if (t) {
574 if (ctx->Driver.IsTextureResident)
575 residences[i] = ctx->Driver.IsTextureResident( ctx, t );
576 else
577 residences[i] = GL_TRUE;
578 }
579 else {
580 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)" );
581 return GL_FALSE;
582 }
583 }
584 return resident;
585 }
586
587
588
589 /*
590 * Execute glIsTexture
591 */
592 GLboolean
593 _mesa_IsTexture( GLuint texture )
594 {
595 GET_CURRENT_CONTEXT(ctx);
596 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, "glIsTextures",
597 GL_FALSE);
598 if (texture>0 && _mesa_HashLookup(ctx->Shared->TexObjects, texture)) {
599 return GL_TRUE;
600 }
601 else {
602 return GL_FALSE;
603 }
604 }
605