r200_tex.c revision 3f59bee43862519e84a52d371fc370cac32ae350
1/*
2Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
3
4The Weather Channel (TM) funded Tungsten Graphics to develop the
5initial release of the Radeon 8500 driver under the XFree86 license.
6This notice must be preserved.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice (including the
17next paragraph) shall be included in all copies or substantial
18portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
24LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27*/
28
29/*
30 * Authors:
31 *   Keith Whitwell <keith@tungstengraphics.com>
32 */
33
34#include "main/glheader.h"
35#include "main/imports.h"
36#include "main/colormac.h"
37#include "main/context.h"
38#include "main/enums.h"
39#include "main/image.h"
40#include "main/simple_list.h"
41#include "main/texformat.h"
42#include "main/texstore.h"
43#include "main/teximage.h"
44#include "main/texobj.h"
45
46#include "texmem.h"
47
48#include "radeon_mipmap_tree.h"
49#include "r200_context.h"
50#include "r200_state.h"
51#include "r200_ioctl.h"
52#include "r200_swtcl.h"
53#include "r200_tex.h"
54
55#include "xmlpool.h"
56
57
58
59/**
60 * Set the texture wrap modes.
61 *
62 * \param t Texture object whose wrap modes are to be set
63 * \param swrap Wrap mode for the \a s texture coordinate
64 * \param twrap Wrap mode for the \a t texture coordinate
65 */
66
67static void r200SetTexWrap( radeonTexObjPtr t, GLenum swrap, GLenum twrap, GLenum rwrap )
68{
69   GLboolean  is_clamp = GL_FALSE;
70   GLboolean  is_clamp_to_border = GL_FALSE;
71   struct gl_texture_object *tObj = &t->base;
72
73   t->pp_txfilter &= ~(R200_CLAMP_S_MASK | R200_CLAMP_T_MASK | R200_BORDER_MODE_D3D);
74
75   switch ( swrap ) {
76   case GL_REPEAT:
77      t->pp_txfilter |= R200_CLAMP_S_WRAP;
78      break;
79   case GL_CLAMP:
80      t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL;
81      is_clamp = GL_TRUE;
82      break;
83   case GL_CLAMP_TO_EDGE:
84      t->pp_txfilter |= R200_CLAMP_S_CLAMP_LAST;
85      break;
86   case GL_CLAMP_TO_BORDER:
87      t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL;
88      is_clamp_to_border = GL_TRUE;
89      break;
90   case GL_MIRRORED_REPEAT:
91      t->pp_txfilter |= R200_CLAMP_S_MIRROR;
92      break;
93   case GL_MIRROR_CLAMP_EXT:
94      t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL;
95      is_clamp = GL_TRUE;
96      break;
97   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
98      t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_LAST;
99      break;
100   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
101      t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL;
102      is_clamp_to_border = GL_TRUE;
103      break;
104   default:
105      _mesa_problem(NULL, "bad S wrap mode in %s", __FUNCTION__);
106   }
107
108   if (tObj->Target != GL_TEXTURE_1D) {
109      switch ( twrap ) {
110      case GL_REPEAT:
111         t->pp_txfilter |= R200_CLAMP_T_WRAP;
112         break;
113      case GL_CLAMP:
114         t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL;
115         is_clamp = GL_TRUE;
116         break;
117      case GL_CLAMP_TO_EDGE:
118         t->pp_txfilter |= R200_CLAMP_T_CLAMP_LAST;
119         break;
120      case GL_CLAMP_TO_BORDER:
121         t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL;
122         is_clamp_to_border = GL_TRUE;
123         break;
124      case GL_MIRRORED_REPEAT:
125         t->pp_txfilter |= R200_CLAMP_T_MIRROR;
126         break;
127      case GL_MIRROR_CLAMP_EXT:
128         t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL;
129         is_clamp = GL_TRUE;
130         break;
131      case GL_MIRROR_CLAMP_TO_EDGE_EXT:
132         t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_LAST;
133         break;
134      case GL_MIRROR_CLAMP_TO_BORDER_EXT:
135         t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL;
136         is_clamp_to_border = GL_TRUE;
137         break;
138      default:
139         _mesa_problem(NULL, "bad T wrap mode in %s", __FUNCTION__);
140      }
141   }
142
143   t->pp_txformat_x &= ~R200_CLAMP_Q_MASK;
144
145   switch ( rwrap ) {
146   case GL_REPEAT:
147      t->pp_txformat_x |= R200_CLAMP_Q_WRAP;
148      break;
149   case GL_CLAMP:
150      t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL;
151      is_clamp = GL_TRUE;
152      break;
153   case GL_CLAMP_TO_EDGE:
154      t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_LAST;
155      break;
156   case GL_CLAMP_TO_BORDER:
157      t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL;
158      is_clamp_to_border = GL_TRUE;
159      break;
160   case GL_MIRRORED_REPEAT:
161      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR;
162      break;
163   case GL_MIRROR_CLAMP_EXT:
164      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL;
165      is_clamp = GL_TRUE;
166      break;
167   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
168      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_LAST;
169      break;
170   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
171      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL;
172      is_clamp_to_border = GL_TRUE;
173      break;
174   default:
175      _mesa_problem(NULL, "bad R wrap mode in %s", __FUNCTION__);
176   }
177
178   if ( is_clamp_to_border ) {
179      t->pp_txfilter |= R200_BORDER_MODE_D3D;
180   }
181
182   t->border_fallback = (is_clamp && is_clamp_to_border);
183}
184
185static void r200SetTexMaxAnisotropy( radeonTexObjPtr t, GLfloat max )
186{
187   t->pp_txfilter &= ~R200_MAX_ANISO_MASK;
188
189   if ( max <= 1.0 ) {
190      t->pp_txfilter |= R200_MAX_ANISO_1_TO_1;
191   } else if ( max <= 2.0 ) {
192      t->pp_txfilter |= R200_MAX_ANISO_2_TO_1;
193   } else if ( max <= 4.0 ) {
194      t->pp_txfilter |= R200_MAX_ANISO_4_TO_1;
195   } else if ( max <= 8.0 ) {
196      t->pp_txfilter |= R200_MAX_ANISO_8_TO_1;
197   } else {
198      t->pp_txfilter |= R200_MAX_ANISO_16_TO_1;
199   }
200}
201
202/**
203 * Set the texture magnification and minification modes.
204 *
205 * \param t Texture whose filter modes are to be set
206 * \param minf Texture minification mode
207 * \param magf Texture magnification mode
208 */
209
210static void r200SetTexFilter( radeonTexObjPtr t, GLenum minf, GLenum magf )
211{
212   GLuint anisotropy = (t->pp_txfilter & R200_MAX_ANISO_MASK);
213
214   t->pp_txfilter &= ~(R200_MIN_FILTER_MASK | R200_MAG_FILTER_MASK);
215   t->pp_txformat_x &= ~R200_VOLUME_FILTER_MASK;
216
217   if ( anisotropy == R200_MAX_ANISO_1_TO_1 ) {
218      switch ( minf ) {
219      case GL_NEAREST:
220	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST;
221	 break;
222      case GL_LINEAR:
223	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR;
224	 break;
225      case GL_NEAREST_MIPMAP_NEAREST:
226	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_NEAREST;
227	 break;
228      case GL_NEAREST_MIPMAP_LINEAR:
229	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_NEAREST;
230	 break;
231      case GL_LINEAR_MIPMAP_NEAREST:
232	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_LINEAR;
233	 break;
234      case GL_LINEAR_MIPMAP_LINEAR:
235	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_LINEAR;
236	 break;
237      }
238   } else {
239      switch ( minf ) {
240      case GL_NEAREST:
241	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST;
242	 break;
243      case GL_LINEAR:
244	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_LINEAR;
245	 break;
246      case GL_NEAREST_MIPMAP_NEAREST:
247      case GL_LINEAR_MIPMAP_NEAREST:
248	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST;
249	 break;
250      case GL_NEAREST_MIPMAP_LINEAR:
251      case GL_LINEAR_MIPMAP_LINEAR:
252	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR;
253	 break;
254      }
255   }
256
257   /* Note we don't have 3D mipmaps so only use the mag filter setting
258    * to set the 3D texture filter mode.
259    */
260   switch ( magf ) {
261   case GL_NEAREST:
262      t->pp_txfilter |= R200_MAG_FILTER_NEAREST;
263      t->pp_txformat_x |= R200_VOLUME_FILTER_NEAREST;
264      break;
265   case GL_LINEAR:
266      t->pp_txfilter |= R200_MAG_FILTER_LINEAR;
267      t->pp_txformat_x |= R200_VOLUME_FILTER_LINEAR;
268      break;
269   }
270}
271
272static void r200SetTexBorderColor( radeonTexObjPtr t, GLubyte c[4] )
273{
274   t->pp_border_color = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
275}
276
277
278
279
280
281static void r200TexEnv( GLcontext *ctx, GLenum target,
282			  GLenum pname, const GLfloat *param )
283{
284   r200ContextPtr rmesa = R200_CONTEXT(ctx);
285   GLuint unit = ctx->Texture.CurrentUnit;
286   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
287
288   if ( R200_DEBUG & DEBUG_STATE ) {
289      fprintf( stderr, "%s( %s )\n",
290	       __FUNCTION__, _mesa_lookup_enum_by_nr( pname ) );
291   }
292
293   /* This is incorrect: Need to maintain this data for each of
294    * GL_TEXTURE_{123}D, GL_TEXTURE_RECTANGLE_NV, etc, and switch
295    * between them according to _ReallyEnabled.
296    */
297   switch ( pname ) {
298   case GL_TEXTURE_ENV_COLOR: {
299      GLubyte c[4];
300      GLuint envColor;
301      UNCLAMPED_FLOAT_TO_RGBA_CHAN( c, texUnit->EnvColor );
302      envColor = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
303      if ( rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] != envColor ) {
304	 R200_STATECHANGE( rmesa, tf );
305	 rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] = envColor;
306      }
307      break;
308   }
309
310   case GL_TEXTURE_LOD_BIAS_EXT: {
311      GLfloat bias, min;
312      GLuint b;
313      const int fixed_one = 0x8000000;
314
315      /* The R200's LOD bias is a signed 2's complement value with a
316       * range of -16.0 <= bias < 16.0.
317       *
318       * NOTE: Add a small bias to the bias for conform mipsel.c test.
319       */
320      bias = *param + .01;
321      min = driQueryOptionb (&rmesa->radeon.optionCache, "no_neg_lod_bias") ?
322	  0.0 : -16.0;
323      bias = CLAMP( bias, min, 16.0 );
324      b = (int)(bias * fixed_one) & R200_LOD_BIAS_MASK;
325
326      if ( (rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] & R200_LOD_BIAS_MASK) != b ) {
327	 R200_STATECHANGE( rmesa, tex[unit] );
328	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] &= ~R200_LOD_BIAS_MASK;
329	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] |= b;
330      }
331      break;
332   }
333   case GL_COORD_REPLACE_ARB:
334      if (ctx->Point.PointSprite) {
335	 R200_STATECHANGE( rmesa, spr );
336	 if ((GLenum)param[0]) {
337	    rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] |= R200_PS_GEN_TEX_0 << unit;
338	 } else {
339	    rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] &= ~(R200_PS_GEN_TEX_0 << unit);
340	 }
341      }
342      break;
343   default:
344      return;
345   }
346}
347
348
349/**
350 * Changes variables and flags for a state update, which will happen at the
351 * next UpdateTextureState
352 */
353
354static void r200TexParameter( GLcontext *ctx, GLenum target,
355				struct gl_texture_object *texObj,
356				GLenum pname, const GLfloat *params )
357{
358   radeonTexObj* t = radeon_tex_obj(texObj);
359
360   if ( R200_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) {
361      fprintf( stderr, "%s( %s )\n", __FUNCTION__,
362	       _mesa_lookup_enum_by_nr( pname ) );
363   }
364
365   switch ( pname ) {
366   case GL_TEXTURE_MIN_FILTER:
367   case GL_TEXTURE_MAG_FILTER:
368   case GL_TEXTURE_MAX_ANISOTROPY_EXT:
369      r200SetTexMaxAnisotropy( t, texObj->MaxAnisotropy );
370      r200SetTexFilter( t, texObj->MinFilter, texObj->MagFilter );
371      break;
372
373   case GL_TEXTURE_WRAP_S:
374   case GL_TEXTURE_WRAP_T:
375   case GL_TEXTURE_WRAP_R:
376      r200SetTexWrap( t, texObj->WrapS, texObj->WrapT, texObj->WrapR );
377      break;
378
379   case GL_TEXTURE_BORDER_COLOR:
380      r200SetTexBorderColor( t, texObj->_BorderChan );
381      break;
382
383   case GL_TEXTURE_BASE_LEVEL:
384   case GL_TEXTURE_MAX_LEVEL:
385   case GL_TEXTURE_MIN_LOD:
386   case GL_TEXTURE_MAX_LOD:
387      /* This isn't the most efficient solution but there doesn't appear to
388       * be a nice alternative.  Since there's no LOD clamping,
389       * we just have to rely on loading the right subset of mipmap levels
390       * to simulate a clamped LOD.
391       */
392      driSwapOutTextureObject( (driTextureObject *) t );
393      break;
394
395   default:
396      return;
397   }
398
399   /* Mark this texobj as dirty (one bit per tex unit)
400    */
401   t->dirty_state = R200_TEX_ALL;
402}
403
404
405static void r200DeleteTexture(GLcontext * ctx, struct gl_texture_object *texObj)
406{
407   r200ContextPtr rmesa = R200_CONTEXT(ctx);
408   radeonTexObj* t = radeon_tex_obj(texObj);
409
410   if (RADEON_DEBUG & (DEBUG_STATE | DEBUG_TEXTURE)) {
411      fprintf(stderr, "%s( %p (target = %s) )\n", __FUNCTION__,
412	      (void *)texObj,
413	      _mesa_lookup_enum_by_nr(texObj->Target));
414   }
415
416   if (rmesa) {
417      int i;
418      R200_FIREVERTICES(rmesa);
419      for ( i = 0 ; i < rmesa->radeon.glCtx->Const.MaxTextureUnits ; i++ ) {
420	 if ( t == rmesa->state.texture.unit[i].texobj ) {
421	    rmesa->state.texture.unit[i].texobj = NULL;
422	    rmesa->hw.tex[i].dirty = GL_FALSE;
423	    rmesa->hw.cube[i].dirty = GL_FALSE;
424	 }
425      }
426   }
427
428   if (t->mt) {
429      radeon_miptree_unreference(t->mt);
430      t->mt = 0;
431   }
432   _mesa_delete_texture_object(ctx, texObj);
433}
434
435/* Need:
436 *  - Same GEN_MODE for all active bits
437 *  - Same EyePlane/ObjPlane for all active bits when using Eye/Obj
438 *  - STRQ presumably all supported (matrix means incoming R values
439 *    can end up in STQ, this has implications for vertex support,
440 *    presumably ok if maos is used, though?)
441 *
442 * Basically impossible to do this on the fly - just collect some
443 * basic info & do the checks from ValidateState().
444 */
445static void r200TexGen( GLcontext *ctx,
446			  GLenum coord,
447			  GLenum pname,
448			  const GLfloat *params )
449{
450   r200ContextPtr rmesa = R200_CONTEXT(ctx);
451   GLuint unit = ctx->Texture.CurrentUnit;
452   rmesa->recheck_texgen[unit] = GL_TRUE;
453}
454
455
456/**
457 * Allocate a new texture object.
458 * Called via ctx->Driver.NewTextureObject.
459 * Note: this function will be called during context creation to
460 * allocate the default texture objects.
461 * Fixup MaxAnisotropy according to user preference.
462 */
463static struct gl_texture_object *r200NewTextureObject(GLcontext * ctx,
464						      GLuint name,
465						      GLenum target)
466{
467   r200ContextPtr rmesa = R200_CONTEXT(ctx);
468   radeonTexObj* t = CALLOC_STRUCT(radeon_tex_obj);
469
470
471   if (RADEON_DEBUG & (DEBUG_STATE | DEBUG_TEXTURE)) {
472     fprintf(stderr, "%s( %p (target = %s) )\n", __FUNCTION__,
473	     t, _mesa_lookup_enum_by_nr(target));
474   }
475
476   _mesa_initialize_texture_object(&t->base, name, target);
477   t->base.MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
478
479   /* Initialize hardware state */
480   r200SetTexWrap( t, t->base.WrapS, t->base.WrapT, t->base.WrapR );
481   r200SetTexMaxAnisotropy( t, t->base.MaxAnisotropy );
482   r200SetTexFilter(t, t->base.MinFilter, t->base.MagFilter);
483   r200SetTexBorderColor(t, t->base._BorderChan);
484
485   return &t->base;
486}
487
488
489
490void r200InitTextureFuncs( struct dd_function_table *functions )
491{
492   /* Note: we only plug in the functions we implement in the driver
493    * since _mesa_init_driver_functions() was already called.
494    */
495   functions->ChooseTextureFormat	= radeonChooseTextureFormat;
496   functions->TexImage1D		= radeonTexImage1D;
497   functions->TexImage2D		= radeonTexImage2D;
498#if ENABLE_HW_3D_TEXTURE
499   functions->TexImage3D		= radeonTexImage3D;
500#else
501   functions->TexImage3D		= _mesa_store_teximage3d;
502#endif
503   functions->TexSubImage1D		= radeonTexSubImage1D;
504   functions->TexSubImage2D		= radeonTexSubImage2D;
505#if ENABLE_HW_3D_TEXTURE
506   functions->TexSubImage3D		= radeonTexSubImage3D;
507#else
508   functions->TexSubImage3D		= _mesa_store_texsubimage3d;
509#endif
510   functions->NewTextureObject		= r200NewTextureObject;
511   //   functions->BindTexture		= r200BindTexture;
512   functions->DeleteTexture		= r200DeleteTexture;
513   functions->IsTextureResident		= driIsTextureResident;
514
515   functions->TexEnv			= r200TexEnv;
516   functions->TexParameter		= r200TexParameter;
517   functions->TexGen			= r200TexGen;
518
519   functions->CompressedTexImage2D	= radeonCompressedTexImage2D;
520   functions->CompressedTexSubImage2D	= radeonCompressedTexSubImage2D;
521
522   functions->GenerateMipmap = radeonGenerateMipmap;
523
524   functions->NewTextureImage = radeonNewTextureImage;
525   functions->FreeTexImageData = radeonFreeTexImageData;
526   functions->MapTexture = radeonMapTexture;
527   functions->UnmapTexture = radeonUnmapTexture;
528
529   driInitTextureFormats();
530
531}
532