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/mfeatures.h"
41#include "main/simple_list.h"
42#include "main/teximage.h"
43#include "main/texobj.h"
44
45#include "radeon_context.h"
46#include "radeon_mipmap_tree.h"
47#include "radeon_ioctl.h"
48#include "radeon_tex.h"
49
50#include "xmlpool.h"
51
52
53
54/**
55 * Set the texture wrap modes.
56 *
57 * \param t Texture object whose wrap modes are to be set
58 * \param swrap Wrap mode for the \a s texture coordinate
59 * \param twrap Wrap mode for the \a t texture coordinate
60 */
61
62static void radeonSetTexWrap( radeonTexObjPtr t, GLenum swrap, GLenum twrap )
63{
64   GLboolean  is_clamp = GL_FALSE;
65   GLboolean  is_clamp_to_border = GL_FALSE;
66
67   t->pp_txfilter &= ~(RADEON_CLAMP_S_MASK | RADEON_CLAMP_T_MASK | RADEON_BORDER_MODE_D3D);
68
69   switch ( swrap ) {
70   case GL_REPEAT:
71      t->pp_txfilter |= RADEON_CLAMP_S_WRAP;
72      break;
73   case GL_CLAMP:
74      t->pp_txfilter |= RADEON_CLAMP_S_CLAMP_GL;
75      is_clamp = GL_TRUE;
76      break;
77   case GL_CLAMP_TO_EDGE:
78      t->pp_txfilter |= RADEON_CLAMP_S_CLAMP_LAST;
79      break;
80   case GL_CLAMP_TO_BORDER:
81      t->pp_txfilter |= RADEON_CLAMP_S_CLAMP_GL;
82      is_clamp_to_border = GL_TRUE;
83      break;
84   case GL_MIRRORED_REPEAT:
85      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR;
86      break;
87   case GL_MIRROR_CLAMP_EXT:
88      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR_CLAMP_GL;
89      is_clamp = GL_TRUE;
90      break;
91   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
92      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR_CLAMP_LAST;
93      break;
94   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
95      t->pp_txfilter |= RADEON_CLAMP_S_MIRROR_CLAMP_GL;
96      is_clamp_to_border = GL_TRUE;
97      break;
98   default:
99      _mesa_problem(NULL, "bad S wrap mode in %s", __FUNCTION__);
100   }
101
102   if (t->base.Target != GL_TEXTURE_1D) {
103      switch ( twrap ) {
104      case GL_REPEAT:
105	 t->pp_txfilter |= RADEON_CLAMP_T_WRAP;
106	 break;
107      case GL_CLAMP:
108	 t->pp_txfilter |= RADEON_CLAMP_T_CLAMP_GL;
109	 is_clamp = GL_TRUE;
110	 break;
111      case GL_CLAMP_TO_EDGE:
112	 t->pp_txfilter |= RADEON_CLAMP_T_CLAMP_LAST;
113	 break;
114      case GL_CLAMP_TO_BORDER:
115	 t->pp_txfilter |= RADEON_CLAMP_T_CLAMP_GL;
116	 is_clamp_to_border = GL_TRUE;
117	 break;
118      case GL_MIRRORED_REPEAT:
119	 t->pp_txfilter |= RADEON_CLAMP_T_MIRROR;
120	 break;
121      case GL_MIRROR_CLAMP_EXT:
122	 t->pp_txfilter |= RADEON_CLAMP_T_MIRROR_CLAMP_GL;
123	 is_clamp = GL_TRUE;
124	 break;
125      case GL_MIRROR_CLAMP_TO_EDGE_EXT:
126	 t->pp_txfilter |= RADEON_CLAMP_T_MIRROR_CLAMP_LAST;
127	 break;
128      case GL_MIRROR_CLAMP_TO_BORDER_EXT:
129	 t->pp_txfilter |= RADEON_CLAMP_T_MIRROR_CLAMP_GL;
130	 is_clamp_to_border = GL_TRUE;
131	 break;
132      default:
133	 _mesa_problem(NULL, "bad T wrap mode in %s", __FUNCTION__);
134      }
135   }
136
137   if ( is_clamp_to_border ) {
138      t->pp_txfilter |= RADEON_BORDER_MODE_D3D;
139   }
140
141   t->border_fallback = (is_clamp && is_clamp_to_border);
142}
143
144static void radeonSetTexMaxAnisotropy( radeonTexObjPtr t, GLfloat max )
145{
146   t->pp_txfilter &= ~RADEON_MAX_ANISO_MASK;
147
148   if ( max == 1.0 ) {
149      t->pp_txfilter |= RADEON_MAX_ANISO_1_TO_1;
150   } else if ( max <= 2.0 ) {
151      t->pp_txfilter |= RADEON_MAX_ANISO_2_TO_1;
152   } else if ( max <= 4.0 ) {
153      t->pp_txfilter |= RADEON_MAX_ANISO_4_TO_1;
154   } else if ( max <= 8.0 ) {
155      t->pp_txfilter |= RADEON_MAX_ANISO_8_TO_1;
156   } else {
157      t->pp_txfilter |= RADEON_MAX_ANISO_16_TO_1;
158   }
159}
160
161/**
162 * Set the texture magnification and minification modes.
163 *
164 * \param t Texture whose filter modes are to be set
165 * \param minf Texture minification mode
166 * \param magf Texture magnification mode
167 */
168
169static void radeonSetTexFilter( radeonTexObjPtr t, GLenum minf, GLenum magf )
170{
171   GLuint anisotropy = (t->pp_txfilter & RADEON_MAX_ANISO_MASK);
172
173   /* Force revalidation to account for switches from/to mipmapping. */
174   t->validated = GL_FALSE;
175
176   t->pp_txfilter &= ~(RADEON_MIN_FILTER_MASK | RADEON_MAG_FILTER_MASK);
177
178   /* r100 chips can't handle mipmaps/aniso for cubemap/volume textures */
179   if ( t->base.Target == GL_TEXTURE_CUBE_MAP ) {
180      switch ( minf ) {
181      case GL_NEAREST:
182      case GL_NEAREST_MIPMAP_NEAREST:
183      case GL_NEAREST_MIPMAP_LINEAR:
184	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST;
185	 break;
186      case GL_LINEAR:
187      case GL_LINEAR_MIPMAP_NEAREST:
188      case GL_LINEAR_MIPMAP_LINEAR:
189	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR;
190	 break;
191      default:
192	 break;
193      }
194   }
195   else if ( anisotropy == RADEON_MAX_ANISO_1_TO_1 ) {
196      switch ( minf ) {
197      case GL_NEAREST:
198	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST;
199	 break;
200      case GL_LINEAR:
201	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR;
202	 break;
203      case GL_NEAREST_MIPMAP_NEAREST:
204	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST_MIP_NEAREST;
205	 break;
206      case GL_NEAREST_MIPMAP_LINEAR:
207	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR_MIP_NEAREST;
208	 break;
209      case GL_LINEAR_MIPMAP_NEAREST:
210	 t->pp_txfilter |= RADEON_MIN_FILTER_NEAREST_MIP_LINEAR;
211	 break;
212      case GL_LINEAR_MIPMAP_LINEAR:
213	 t->pp_txfilter |= RADEON_MIN_FILTER_LINEAR_MIP_LINEAR;
214	 break;
215      }
216   } else {
217      switch ( minf ) {
218      case GL_NEAREST:
219	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_NEAREST;
220	 break;
221      case GL_LINEAR:
222	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_LINEAR;
223	 break;
224      case GL_NEAREST_MIPMAP_NEAREST:
225      case GL_LINEAR_MIPMAP_NEAREST:
226	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST;
227	 break;
228      case GL_NEAREST_MIPMAP_LINEAR:
229      case GL_LINEAR_MIPMAP_LINEAR:
230	 t->pp_txfilter |= RADEON_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR;
231	 break;
232      }
233   }
234
235   switch ( magf ) {
236   case GL_NEAREST:
237      t->pp_txfilter |= RADEON_MAG_FILTER_NEAREST;
238      break;
239   case GL_LINEAR:
240      t->pp_txfilter |= RADEON_MAG_FILTER_LINEAR;
241      break;
242   }
243}
244
245static void radeonSetTexBorderColor( radeonTexObjPtr t, const GLfloat color[4] )
246{
247   GLubyte c[4];
248   CLAMPED_FLOAT_TO_UBYTE(c[0], color[0]);
249   CLAMPED_FLOAT_TO_UBYTE(c[1], color[1]);
250   CLAMPED_FLOAT_TO_UBYTE(c[2], color[2]);
251   CLAMPED_FLOAT_TO_UBYTE(c[3], color[3]);
252   t->pp_border_color = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
253}
254
255#define SCALED_FLOAT_TO_BYTE( x, scale ) \
256		(((GLuint)((255.0F / scale) * (x))) / 2)
257
258static void radeonTexEnv( struct gl_context *ctx, GLenum target,
259			  GLenum pname, const GLfloat *param )
260{
261   r100ContextPtr rmesa = R100_CONTEXT(ctx);
262   GLuint unit = ctx->Texture.CurrentUnit;
263   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
264
265   if ( RADEON_DEBUG & RADEON_STATE ) {
266      fprintf( stderr, "%s( %s )\n",
267	       __FUNCTION__, _mesa_lookup_enum_by_nr( pname ) );
268   }
269
270   switch ( pname ) {
271   case GL_TEXTURE_ENV_COLOR: {
272      GLubyte c[4];
273      GLuint envColor;
274      _mesa_unclamped_float_rgba_to_ubyte(c, texUnit->EnvColor);
275      envColor = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
276      if ( rmesa->hw.tex[unit].cmd[TEX_PP_TFACTOR] != envColor ) {
277	 RADEON_STATECHANGE( rmesa, tex[unit] );
278	 rmesa->hw.tex[unit].cmd[TEX_PP_TFACTOR] = envColor;
279      }
280      break;
281   }
282
283   case GL_TEXTURE_LOD_BIAS_EXT: {
284      GLfloat bias, min;
285      GLuint b;
286
287      /* The Radeon's LOD bias is a signed 2's complement value with a
288       * range of -1.0 <= bias < 4.0.  We break this into two linear
289       * functions, one mapping [-1.0,0.0] to [-128,0] and one mapping
290       * [0.0,4.0] to [0,127].
291       */
292      min = driQueryOptionb (&rmesa->radeon.optionCache, "no_neg_lod_bias") ?
293	  0.0 : -1.0;
294      bias = CLAMP( *param, min, 4.0 );
295      if ( bias == 0 ) {
296	 b = 0;
297      } else if ( bias > 0 ) {
298	 b = ((GLuint)SCALED_FLOAT_TO_BYTE( bias, 4.0 )) << RADEON_LOD_BIAS_SHIFT;
299      } else {
300	 b = ((GLuint)SCALED_FLOAT_TO_BYTE( bias, 1.0 )) << RADEON_LOD_BIAS_SHIFT;
301      }
302      if ( (rmesa->hw.tex[unit].cmd[TEX_PP_TXFILTER] & RADEON_LOD_BIAS_MASK) != b ) {
303	 RADEON_STATECHANGE( rmesa, tex[unit] );
304	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFILTER] &= ~RADEON_LOD_BIAS_MASK;
305	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFILTER] |= (b & RADEON_LOD_BIAS_MASK);
306      }
307      break;
308   }
309
310   default:
311      return;
312   }
313}
314
315void radeonTexUpdateParameters(struct gl_context *ctx, GLuint unit)
316{
317   struct gl_sampler_object *samp = _mesa_get_samplerobj(ctx, unit);
318   radeonTexObj* t = radeon_tex_obj(ctx->Texture.Unit[unit]._Current);
319
320   radeonSetTexMaxAnisotropy(t , samp->MaxAnisotropy);
321   radeonSetTexFilter(t, samp->MinFilter, samp->MagFilter);
322   radeonSetTexWrap(t, samp->WrapS, samp->WrapT);
323   radeonSetTexBorderColor(t, samp->BorderColor.f);
324}
325
326
327/**
328 * Changes variables and flags for a state update, which will happen at the
329 * next UpdateTextureState
330 */
331
332static void radeonTexParameter( struct gl_context *ctx, GLenum target,
333				struct gl_texture_object *texObj,
334				GLenum pname, const GLfloat *params )
335{
336   radeonTexObj* t = radeon_tex_obj(texObj);
337
338   radeon_print(RADEON_TEXTURE, RADEON_VERBOSE, "%s( %s )\n", __FUNCTION__,
339	       _mesa_lookup_enum_by_nr( pname ) );
340
341   switch ( pname ) {
342   case GL_TEXTURE_BASE_LEVEL:
343   case GL_TEXTURE_MAX_LEVEL:
344   case GL_TEXTURE_MIN_LOD:
345   case GL_TEXTURE_MAX_LOD:
346      t->validated = GL_FALSE;
347      break;
348
349   default:
350      return;
351   }
352}
353
354static void radeonDeleteTexture( struct gl_context *ctx,
355				 struct gl_texture_object *texObj )
356{
357   r100ContextPtr rmesa = R100_CONTEXT(ctx);
358   radeonTexObj* t = radeon_tex_obj(texObj);
359   int i;
360
361   radeon_print(RADEON_TEXTURE, RADEON_NORMAL,
362	 "%s( %p (target = %s) )\n", __FUNCTION__, (void *)texObj,
363	       _mesa_lookup_enum_by_nr( texObj->Target ) );
364
365   if ( rmesa ) {
366     radeon_firevertices(&rmesa->radeon);
367     for ( i = 0 ; i < rmesa->radeon.glCtx->Const.MaxTextureUnits ; i++ ) {
368       if ( t == rmesa->state.texture.unit[i].texobj ) {
369	 rmesa->state.texture.unit[i].texobj = NULL;
370	 rmesa->hw.tex[i].dirty = GL_FALSE;
371	 rmesa->hw.cube[i].dirty = GL_FALSE;
372       }
373     }
374   }
375
376   radeon_miptree_unreference(&t->mt);
377
378   /* Free mipmap images and the texture object itself */
379   _mesa_delete_texture_object(ctx, texObj);
380}
381
382/* Need:
383 *  - Same GEN_MODE for all active bits
384 *  - Same EyePlane/ObjPlane for all active bits when using Eye/Obj
385 *  - STRQ presumably all supported (matrix means incoming R values
386 *    can end up in STQ, this has implications for vertex support,
387 *    presumably ok if maos is used, though?)
388 *
389 * Basically impossible to do this on the fly - just collect some
390 * basic info & do the checks from ValidateState().
391 */
392static void radeonTexGen( struct gl_context *ctx,
393			  GLenum coord,
394			  GLenum pname,
395			  const GLfloat *params )
396{
397   r100ContextPtr rmesa = R100_CONTEXT(ctx);
398   GLuint unit = ctx->Texture.CurrentUnit;
399   rmesa->recheck_texgen[unit] = GL_TRUE;
400}
401
402/**
403 * Allocate a new texture object.
404 * Called via ctx->Driver.NewTextureObject.
405 * Note: we could use containment here to 'derive' the driver-specific
406 * texture object from the core mesa gl_texture_object.  Not done at this time.
407 */
408static struct gl_texture_object *
409radeonNewTextureObject( struct gl_context *ctx, GLuint name, GLenum target )
410{
411   r100ContextPtr rmesa = R100_CONTEXT(ctx);
412   radeonTexObj* t = CALLOC_STRUCT(radeon_tex_obj);
413
414   _mesa_initialize_texture_object(&t->base, name, target);
415   t->base.Sampler.MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
416
417   t->border_fallback = GL_FALSE;
418
419   t->pp_txfilter = RADEON_BORDER_MODE_OGL;
420   t->pp_txformat = (RADEON_TXFORMAT_ENDIAN_NO_SWAP |
421		     RADEON_TXFORMAT_PERSPECTIVE_ENABLE);
422
423   radeonSetTexWrap( t, t->base.Sampler.WrapS, t->base.Sampler.WrapT );
424   radeonSetTexMaxAnisotropy( t, t->base.Sampler.MaxAnisotropy );
425   radeonSetTexFilter( t, t->base.Sampler.MinFilter, t->base.Sampler.MagFilter );
426   radeonSetTexBorderColor( t, t->base.Sampler.BorderColor.f );
427   return &t->base;
428}
429
430
431static struct gl_sampler_object *
432radeonNewSamplerObject(struct gl_context *ctx, GLuint name)
433{
434   r100ContextPtr rmesa = R100_CONTEXT(ctx);
435   struct gl_sampler_object *samp = _mesa_new_sampler_object(ctx, name);
436   if (samp)
437      samp->MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
438   return samp;
439}
440
441
442void radeonInitTextureFuncs( radeonContextPtr radeon, struct dd_function_table *functions )
443{
444   radeon_init_common_texture_funcs(radeon, functions);
445
446   functions->NewTextureObject		= radeonNewTextureObject;
447   //   functions->BindTexture		= radeonBindTexture;
448   functions->DeleteTexture		= radeonDeleteTexture;
449
450   functions->TexEnv			= radeonTexEnv;
451   functions->TexParameter		= radeonTexParameter;
452   functions->TexGen			= radeonTexGen;
453   functions->NewSamplerObject		= radeonNewSamplerObject;
454}
455