radeon_tex.c revision 7d19d2768491f4de3b674106e93c24d29712404f
1/*
2Copyright 2000, 2001 ATI Technologies Inc., Ontario, Canada, and
3                     VA Linux Systems Inc., Fremont, California.
4
5All Rights Reserved.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice (including the
16next paragraph) shall be included in all copies or substantial
17portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26*/
27
28/*
29 * Authors:
30 *    Gareth Hughes <gareth@valinux.com>
31 *    Brian Paul <brianp@valinux.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 "radeon_context.h"
47#include "radeon_mipmap_tree.h"
48#include "radeon_state.h"
49#include "radeon_ioctl.h"
50#include "radeon_swtcl.h"
51#include "radeon_tex.h"
52
53#include "xmlpool.h"
54
55
56
57/**
58 * Set the texture wrap modes.
59 *
60 * \param t Texture object whose wrap modes are to be set
61 * \param swrap Wrap mode for the \a s texture coordinate
62 * \param twrap Wrap mode for the \a t texture coordinate
63 */
64
65static void radeonSetTexWrap( radeonTexObjPtr t, GLenum swrap, GLenum twrap )
66{
67   GLboolean  is_clamp = GL_FALSE;
68   GLboolean  is_clamp_to_border = GL_FALSE;
69
70   t->pp_txfilter &= ~(RADEON_CLAMP_S_MASK | RADEON_CLAMP_T_MASK | RADEON_BORDER_MODE_D3D);
71
72   switch ( swrap ) {
73   case GL_REPEAT:
74      t->pp_txfilter |= RADEON_CLAMP_S_WRAP;
75      break;
76   case GL_CLAMP:
77      t->pp_txfilter |= RADEON_CLAMP_S_CLAMP_GL;
78      is_clamp = GL_TRUE;
79      break;
80   case GL_CLAMP_TO_EDGE:
81      t->pp_txfilter |= RADEON_CLAMP_S_CLAMP_LAST;
82      break;
83   case GL_CLAMP_TO_BORDER:
84      t->pp_txfilter |= RADEON_CLAMP_S_CLAMP_GL;
85      is_clamp_to_border = GL_TRUE;
86      break;
87   case GL_MIRRORED_REPEAT:
88      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR;
89      break;
90   case GL_MIRROR_CLAMP_EXT:
91      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR_CLAMP_GL;
92      is_clamp = GL_TRUE;
93      break;
94   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
95      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR_CLAMP_LAST;
96      break;
97   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
98      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR_CLAMP_GL;
99      is_clamp_to_border = GL_TRUE;
100      break;
101   default:
102      _mesa_problem(NULL, "bad S wrap mode in %s", __FUNCTION__);
103   }
104
105   switch ( twrap ) {
106   case GL_REPEAT:
107      t->pp_txfilter |= RADEON_CLAMP_T_WRAP;
108      break;
109   case GL_CLAMP:
110      t->pp_txfilter |= RADEON_CLAMP_T_CLAMP_GL;
111      is_clamp = GL_TRUE;
112      break;
113   case GL_CLAMP_TO_EDGE:
114      t->pp_txfilter |= RADEON_CLAMP_T_CLAMP_LAST;
115      break;
116   case GL_CLAMP_TO_BORDER:
117      t->pp_txfilter |= RADEON_CLAMP_T_CLAMP_GL;
118      is_clamp_to_border = GL_TRUE;
119      break;
120   case GL_MIRRORED_REPEAT:
121      t->pp_txfilter |= RADEON_CLAMP_T_MIRROR;
122      break;
123   case GL_MIRROR_CLAMP_EXT:
124      t->pp_txfilter |= RADEON_CLAMP_T_MIRROR_CLAMP_GL;
125      is_clamp = GL_TRUE;
126      break;
127   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
128      t->pp_txfilter |= RADEON_CLAMP_T_MIRROR_CLAMP_LAST;
129      break;
130   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
131      t->pp_txfilter |= RADEON_CLAMP_T_MIRROR_CLAMP_GL;
132      is_clamp_to_border = GL_TRUE;
133      break;
134   default:
135      _mesa_problem(NULL, "bad T wrap mode in %s", __FUNCTION__);
136   }
137
138   if ( is_clamp_to_border ) {
139      t->pp_txfilter |= RADEON_BORDER_MODE_D3D;
140   }
141
142   t->border_fallback = (is_clamp && is_clamp_to_border);
143}
144
145static void radeonSetTexMaxAnisotropy( radeonTexObjPtr t, GLfloat max )
146{
147   t->pp_txfilter &= ~RADEON_MAX_ANISO_MASK;
148
149   if ( max == 1.0 ) {
150      t->pp_txfilter |= RADEON_MAX_ANISO_1_TO_1;
151   } else if ( max <= 2.0 ) {
152      t->pp_txfilter |= RADEON_MAX_ANISO_2_TO_1;
153   } else if ( max <= 4.0 ) {
154      t->pp_txfilter |= RADEON_MAX_ANISO_4_TO_1;
155   } else if ( max <= 8.0 ) {
156      t->pp_txfilter |= RADEON_MAX_ANISO_8_TO_1;
157   } else {
158      t->pp_txfilter |= RADEON_MAX_ANISO_16_TO_1;
159   }
160}
161
162/**
163 * Set the texture magnification and minification modes.
164 *
165 * \param t Texture whose filter modes are to be set
166 * \param minf Texture minification mode
167 * \param magf Texture magnification mode
168 */
169
170static void radeonSetTexFilter( radeonTexObjPtr t, GLenum minf, GLenum magf )
171{
172   GLuint anisotropy = (t->pp_txfilter & RADEON_MAX_ANISO_MASK);
173
174   /* Force revalidation to account for switches from/to mipmapping. */
175   t->validated = GL_FALSE;
176
177   t->pp_txfilter &= ~(RADEON_MIN_FILTER_MASK | RADEON_MAG_FILTER_MASK);
178
179   /* r100 chips can't handle mipmaps/aniso for cubemap/volume textures */
180   if ( t->base.Target == GL_TEXTURE_CUBE_MAP ) {
181      switch ( minf ) {
182      case GL_NEAREST:
183      case GL_NEAREST_MIPMAP_NEAREST:
184      case GL_NEAREST_MIPMAP_LINEAR:
185	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST;
186	 break;
187      case GL_LINEAR:
188      case GL_LINEAR_MIPMAP_NEAREST:
189      case GL_LINEAR_MIPMAP_LINEAR:
190	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR;
191	 break;
192      default:
193	 break;
194      }
195   }
196   else if ( anisotropy == RADEON_MAX_ANISO_1_TO_1 ) {
197      switch ( minf ) {
198      case GL_NEAREST:
199	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST;
200	 break;
201      case GL_LINEAR:
202	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR;
203	 break;
204      case GL_NEAREST_MIPMAP_NEAREST:
205	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST_MIP_NEAREST;
206	 break;
207      case GL_NEAREST_MIPMAP_LINEAR:
208	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR_MIP_NEAREST;
209	 break;
210      case GL_LINEAR_MIPMAP_NEAREST:
211	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST_MIP_LINEAR;
212	 break;
213      case GL_LINEAR_MIPMAP_LINEAR:
214	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR_MIP_LINEAR;
215	 break;
216      }
217   } else {
218      switch ( minf ) {
219      case GL_NEAREST:
220	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_NEAREST;
221	 break;
222      case GL_LINEAR:
223	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_LINEAR;
224	 break;
225      case GL_NEAREST_MIPMAP_NEAREST:
226      case GL_LINEAR_MIPMAP_NEAREST:
227	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST;
228	 break;
229      case GL_NEAREST_MIPMAP_LINEAR:
230      case GL_LINEAR_MIPMAP_LINEAR:
231	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR;
232	 break;
233      }
234   }
235
236   switch ( magf ) {
237   case GL_NEAREST:
238      t->pp_txfilter |= RADEON_MAG_FILTER_NEAREST;
239      break;
240   case GL_LINEAR:
241      t->pp_txfilter |= RADEON_MAG_FILTER_LINEAR;
242      break;
243   }
244}
245
246static void radeonSetTexBorderColor( radeonTexObjPtr t, GLubyte c[4] )
247{
248   t->pp_border_color = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
249}
250
251#define SCALED_FLOAT_TO_BYTE( x, scale ) \
252		(((GLuint)((255.0F / scale) * (x))) / 2)
253
254static void radeonTexEnv( GLcontext *ctx, GLenum target,
255			  GLenum pname, const GLfloat *param )
256{
257   r100ContextPtr rmesa = R100_CONTEXT(ctx);
258   GLuint unit = ctx->Texture.CurrentUnit;
259   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
260
261   if ( RADEON_DEBUG & DEBUG_STATE ) {
262      fprintf( stderr, "%s( %s )\n",
263	       __FUNCTION__, _mesa_lookup_enum_by_nr( pname ) );
264   }
265
266   switch ( pname ) {
267   case GL_TEXTURE_ENV_COLOR: {
268      GLubyte c[4];
269      GLuint envColor;
270      UNCLAMPED_FLOAT_TO_RGBA_CHAN( c, texUnit->EnvColor );
271      envColor = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
272      if ( rmesa->hw.tex[unit].cmd[TEX_PP_TFACTOR] != envColor ) {
273	 RADEON_STATECHANGE( rmesa, tex[unit] );
274	 rmesa->hw.tex[unit].cmd[TEX_PP_TFACTOR] = envColor;
275      }
276      break;
277   }
278
279   case GL_TEXTURE_LOD_BIAS_EXT: {
280      GLfloat bias, min;
281      GLuint b;
282
283      /* The Radeon's LOD bias is a signed 2's complement value with a
284       * range of -1.0 <= bias < 4.0.  We break this into two linear
285       * functions, one mapping [-1.0,0.0] to [-128,0] and one mapping
286       * [0.0,4.0] to [0,127].
287       */
288      min = driQueryOptionb (&rmesa->radeon.optionCache, "no_neg_lod_bias") ?
289	  0.0 : -1.0;
290      bias = CLAMP( *param, min, 4.0 );
291      if ( bias == 0 ) {
292	 b = 0;
293      } else if ( bias > 0 ) {
294	 b = ((GLuint)SCALED_FLOAT_TO_BYTE( bias, 4.0 )) << RADEON_LOD_BIAS_SHIFT;
295      } else {
296	 b = ((GLuint)SCALED_FLOAT_TO_BYTE( bias, 1.0 )) << RADEON_LOD_BIAS_SHIFT;
297      }
298      if ( (rmesa->hw.tex[unit].cmd[TEX_PP_TXFILTER] & RADEON_LOD_BIAS_MASK) != b ) {
299	 RADEON_STATECHANGE( rmesa, tex[unit] );
300	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFILTER] &= ~RADEON_LOD_BIAS_MASK;
301	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFILTER] |= (b & RADEON_LOD_BIAS_MASK);
302      }
303      break;
304   }
305
306   default:
307      return;
308   }
309}
310
311
312/**
313 * Changes variables and flags for a state update, which will happen at the
314 * next UpdateTextureState
315 */
316
317static void radeonTexParameter( GLcontext *ctx, GLenum target,
318				struct gl_texture_object *texObj,
319				GLenum pname, const GLfloat *params )
320{
321   radeonTexObj* t = radeon_tex_obj(texObj);
322
323   if ( RADEON_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) {
324      fprintf( stderr, "%s( %s )\n", __FUNCTION__,
325	       _mesa_lookup_enum_by_nr( pname ) );
326   }
327
328   switch ( pname ) {
329   case GL_TEXTURE_MIN_FILTER:
330   case GL_TEXTURE_MAG_FILTER:
331   case GL_TEXTURE_MAX_ANISOTROPY_EXT:
332      radeonSetTexMaxAnisotropy( t, texObj->MaxAnisotropy );
333      radeonSetTexFilter( t, texObj->MinFilter, texObj->MagFilter );
334      break;
335
336   case GL_TEXTURE_WRAP_S:
337   case GL_TEXTURE_WRAP_T:
338      radeonSetTexWrap( t, texObj->WrapS, texObj->WrapT );
339      break;
340
341   case GL_TEXTURE_BORDER_COLOR:
342      radeonSetTexBorderColor( t, texObj->_BorderChan );
343      break;
344
345   case GL_TEXTURE_BASE_LEVEL:
346   case GL_TEXTURE_MAX_LEVEL:
347   case GL_TEXTURE_MIN_LOD:
348   case GL_TEXTURE_MAX_LOD:
349
350      /* This isn't the most efficient solution but there doesn't appear to
351       * be a nice alternative.  Since there's no LOD clamping,
352       * we just have to rely on loading the right subset of mipmap levels
353       * to simulate a clamped LOD.
354       */
355      if (t->mt) {
356         radeon_miptree_unreference(t->mt);
357	 t->mt = 0;
358	 t->validated = GL_FALSE;
359      }
360      break;
361
362   default:
363      return;
364   }
365
366   /* Mark this texobj as dirty (one bit per tex unit)
367    */
368   t->dirty_state = R100_TEX_ALL;
369}
370
371static void radeonDeleteTexture( GLcontext *ctx,
372				 struct gl_texture_object *texObj )
373{
374   r100ContextPtr rmesa = R100_CONTEXT(ctx);
375   radeonTexObj* t = radeon_tex_obj(texObj);
376   int i;
377
378   if ( RADEON_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) {
379      fprintf( stderr, "%s( %p (target = %s) )\n", __FUNCTION__, (void *)texObj,
380	       _mesa_lookup_enum_by_nr( texObj->Target ) );
381   }
382
383   if ( rmesa ) {
384     radeon_firevertices(&rmesa->radeon);
385     for ( i = 0 ; i < rmesa->radeon.glCtx->Const.MaxTextureUnits ; i++ ) {
386       if ( t == rmesa->state.texture.unit[i].texobj ) {
387	 rmesa->state.texture.unit[i].texobj = NULL;
388	 rmesa->hw.tex[i].dirty = GL_FALSE;
389	 rmesa->hw.cube[i].dirty = GL_FALSE;
390       }
391     }
392   }
393
394   if (t->mt) {
395      radeon_miptree_unreference(t->mt);
396      t->mt = 0;
397   }
398   /* Free mipmap images and the texture object itself */
399   _mesa_delete_texture_object(ctx, texObj);
400}
401
402/* Need:
403 *  - Same GEN_MODE for all active bits
404 *  - Same EyePlane/ObjPlane for all active bits when using Eye/Obj
405 *  - STRQ presumably all supported (matrix means incoming R values
406 *    can end up in STQ, this has implications for vertex support,
407 *    presumably ok if maos is used, though?)
408 *
409 * Basically impossible to do this on the fly - just collect some
410 * basic info & do the checks from ValidateState().
411 */
412static void radeonTexGen( GLcontext *ctx,
413			  GLenum coord,
414			  GLenum pname,
415			  const GLfloat *params )
416{
417   r100ContextPtr rmesa = R100_CONTEXT(ctx);
418   GLuint unit = ctx->Texture.CurrentUnit;
419   rmesa->recheck_texgen[unit] = GL_TRUE;
420}
421
422/**
423 * Allocate a new texture object.
424 * Called via ctx->Driver.NewTextureObject.
425 * Note: we could use containment here to 'derive' the driver-specific
426 * texture object from the core mesa gl_texture_object.  Not done at this time.
427 */
428static struct gl_texture_object *
429radeonNewTextureObject( GLcontext *ctx, GLuint name, GLenum target )
430{
431   r100ContextPtr rmesa = R100_CONTEXT(ctx);
432   radeonTexObj* t = CALLOC_STRUCT(radeon_tex_obj);
433
434   _mesa_initialize_texture_object(&t->base, name, target);
435   t->base.MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
436
437   t->border_fallback = GL_FALSE;
438
439   t->pp_txfilter = RADEON_BORDER_MODE_OGL;
440   t->pp_txformat = (RADEON_TXFORMAT_ENDIAN_NO_SWAP |
441		     RADEON_TXFORMAT_PERSPECTIVE_ENABLE);
442
443   radeonSetTexWrap( t, t->base.WrapS, t->base.WrapT );
444   radeonSetTexMaxAnisotropy( t, t->base.MaxAnisotropy );
445   radeonSetTexFilter( t, t->base.MinFilter, t->base.MagFilter );
446   radeonSetTexBorderColor( t, t->base._BorderChan );
447   return &t->base;
448}
449
450
451
452void radeonInitTextureFuncs( struct dd_function_table *functions )
453{
454   functions->ChooseTextureFormat	= radeonChooseTextureFormat;
455   functions->TexImage1D		= radeonTexImage1D;
456   functions->TexImage2D		= radeonTexImage2D;
457   functions->TexSubImage1D		= radeonTexSubImage1D;
458   functions->TexSubImage2D		= radeonTexSubImage2D;
459   functions->GetTexImage               = radeonGetTexImage;
460   functions->GetCompressedTexImage     = radeonGetCompressedTexImage;
461
462   functions->NewTextureObject		= radeonNewTextureObject;
463   //   functions->BindTexture		= radeonBindTexture;
464   functions->DeleteTexture		= radeonDeleteTexture;
465
466   functions->TexEnv			= radeonTexEnv;
467   functions->TexParameter		= radeonTexParameter;
468   functions->TexGen			= radeonTexGen;
469
470   functions->CompressedTexImage2D	= radeonCompressedTexImage2D;
471   functions->CompressedTexSubImage2D	= radeonCompressedTexSubImage2D;
472
473   functions->GenerateMipmap = radeonGenerateMipmap;
474
475   functions->NewTextureImage = radeonNewTextureImage;
476   functions->FreeTexImageData = radeonFreeTexImageData;
477   functions->MapTexture = radeonMapTexture;
478   functions->UnmapTexture = radeonUnmapTexture;
479
480   driInitTextureFormats();
481}
482