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 <keithw@vmware.com>
32 */
33
34#include "main/glheader.h"
35#include "main/imports.h"
36#include "main/context.h"
37#include "main/enums.h"
38#include "main/image.h"
39#include "main/teximage.h"
40#include "main/texobj.h"
41#include "main/samplerobj.h"
42
43#include "radeon_mipmap_tree.h"
44#include "r200_context.h"
45#include "r200_ioctl.h"
46#include "r200_tex.h"
47
48#include "xmlpool.h"
49
50
51
52/**
53 * Set the texture wrap modes.
54 *
55 * \param t Texture object whose wrap modes are to be set
56 * \param swrap Wrap mode for the \a s texture coordinate
57 * \param twrap Wrap mode for the \a t texture coordinate
58 */
59
60static void r200SetTexWrap( radeonTexObjPtr t, GLenum swrap, GLenum twrap, GLenum rwrap )
61{
62   GLboolean  is_clamp = GL_FALSE;
63   GLboolean  is_clamp_to_border = GL_FALSE;
64   struct gl_texture_object *tObj = &t->base;
65
66   radeon_print(RADEON_TEXTURE, RADEON_TRACE,
67		"%s(tex %p) sw %s, tw %s, rw %s\n",
68		__func__, t,
69		_mesa_enum_to_string(swrap),
70		_mesa_enum_to_string(twrap),
71		_mesa_enum_to_string(rwrap));
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", __func__);
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", __func__);
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", __func__);
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   radeon_print(RADEON_TEXTURE, RADEON_TRACE,
189	"%s(tex %p) max %f.\n",
190	__func__, t, max);
191
192   if ( max <= 1.0 ) {
193      t->pp_txfilter |= R200_MAX_ANISO_1_TO_1;
194   } else if ( max <= 2.0 ) {
195      t->pp_txfilter |= R200_MAX_ANISO_2_TO_1;
196   } else if ( max <= 4.0 ) {
197      t->pp_txfilter |= R200_MAX_ANISO_4_TO_1;
198   } else if ( max <= 8.0 ) {
199      t->pp_txfilter |= R200_MAX_ANISO_8_TO_1;
200   } else {
201      t->pp_txfilter |= R200_MAX_ANISO_16_TO_1;
202   }
203}
204
205/**
206 * Set the texture magnification and minification modes.
207 *
208 * \param t Texture whose filter modes are to be set
209 * \param minf Texture minification mode
210 * \param magf Texture magnification mode
211 */
212
213static void r200SetTexFilter( radeonTexObjPtr t, GLenum minf, GLenum magf )
214{
215   GLuint anisotropy = (t->pp_txfilter & R200_MAX_ANISO_MASK);
216
217   /* Force revalidation to account for switches from/to mipmapping. */
218   t->validated = GL_FALSE;
219
220   t->pp_txfilter &= ~(R200_MIN_FILTER_MASK | R200_MAG_FILTER_MASK);
221   t->pp_txformat_x &= ~R200_VOLUME_FILTER_MASK;
222
223   radeon_print(RADEON_TEXTURE, RADEON_TRACE,
224	"%s(tex %p) minf %s, maxf %s, anisotropy %d.\n",
225	__func__, t,
226	_mesa_enum_to_string(minf),
227	_mesa_enum_to_string(magf),
228	anisotropy);
229
230   if ( anisotropy == R200_MAX_ANISO_1_TO_1 ) {
231      switch ( minf ) {
232      case GL_NEAREST:
233	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST;
234	 break;
235      case GL_LINEAR:
236	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR;
237	 break;
238      case GL_NEAREST_MIPMAP_NEAREST:
239	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_NEAREST;
240	 break;
241      case GL_NEAREST_MIPMAP_LINEAR:
242	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_NEAREST;
243	 break;
244      case GL_LINEAR_MIPMAP_NEAREST:
245	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_LINEAR;
246	 break;
247      case GL_LINEAR_MIPMAP_LINEAR:
248	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_LINEAR;
249	 break;
250      }
251   } else {
252      switch ( minf ) {
253      case GL_NEAREST:
254	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST;
255	 break;
256      case GL_LINEAR:
257	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_LINEAR;
258	 break;
259      case GL_NEAREST_MIPMAP_NEAREST:
260      case GL_LINEAR_MIPMAP_NEAREST:
261	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST;
262	 break;
263      case GL_NEAREST_MIPMAP_LINEAR:
264      case GL_LINEAR_MIPMAP_LINEAR:
265	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR;
266	 break;
267      }
268   }
269
270   /* Note we don't have 3D mipmaps so only use the mag filter setting
271    * to set the 3D texture filter mode.
272    */
273   switch ( magf ) {
274   case GL_NEAREST:
275      t->pp_txfilter |= R200_MAG_FILTER_NEAREST;
276      t->pp_txformat_x |= R200_VOLUME_FILTER_NEAREST;
277      break;
278   case GL_LINEAR:
279      t->pp_txfilter |= R200_MAG_FILTER_LINEAR;
280      t->pp_txformat_x |= R200_VOLUME_FILTER_LINEAR;
281      break;
282   }
283}
284
285static void r200SetTexBorderColor( radeonTexObjPtr t, const GLfloat color[4] )
286{
287   GLubyte c[4];
288   CLAMPED_FLOAT_TO_UBYTE(c[0], color[0]);
289   CLAMPED_FLOAT_TO_UBYTE(c[1], color[1]);
290   CLAMPED_FLOAT_TO_UBYTE(c[2], color[2]);
291   CLAMPED_FLOAT_TO_UBYTE(c[3], color[3]);
292   t->pp_border_color = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
293}
294
295static void r200TexEnv( struct gl_context *ctx, GLenum target,
296			  GLenum pname, const GLfloat *param )
297{
298   r200ContextPtr rmesa = R200_CONTEXT(ctx);
299   GLuint unit = ctx->Texture.CurrentUnit;
300   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
301
302   radeon_print(RADEON_TEXTURE | RADEON_STATE, RADEON_VERBOSE, "%s( %s )\n",
303	       __func__, _mesa_enum_to_string( pname ) );
304
305   /* This is incorrect: Need to maintain this data for each of
306    * GL_TEXTURE_{123}D, GL_TEXTURE_RECTANGLE_NV, etc, and switch
307    * between them according to _Current->Target.
308    */
309   switch ( pname ) {
310   case GL_TEXTURE_ENV_COLOR: {
311      GLubyte c[4];
312      GLuint envColor;
313      _mesa_unclamped_float_rgba_to_ubyte(c, texUnit->EnvColor);
314      envColor = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
315      if ( rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] != envColor ) {
316	 R200_STATECHANGE( rmesa, tf );
317	 rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] = envColor;
318      }
319      break;
320   }
321
322   case GL_TEXTURE_LOD_BIAS_EXT: {
323      GLfloat bias, min;
324      GLuint b;
325      const int fixed_one = R200_LOD_BIAS_FIXED_ONE;
326
327      /* The R200's LOD bias is a signed 2's complement value with a
328       * range of -16.0 <= bias < 16.0.
329       *
330       * NOTE: Add a small bias to the bias for conform mipsel.c test.
331       */
332      bias = *param;
333      min = driQueryOptionb (&rmesa->radeon.optionCache, "no_neg_lod_bias") ?
334	  0.0 : -16.0;
335      bias = CLAMP( bias, min, 16.0 );
336      b = ((int)(bias * fixed_one)
337		+ R200_LOD_BIAS_CORRECTION) & R200_LOD_BIAS_MASK;
338
339      if ( (rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] & R200_LOD_BIAS_MASK) != b ) {
340	 R200_STATECHANGE( rmesa, tex[unit] );
341	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] &= ~R200_LOD_BIAS_MASK;
342	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] |= b;
343      }
344      break;
345   }
346   case GL_COORD_REPLACE_ARB:
347      if (ctx->Point.PointSprite) {
348	 R200_STATECHANGE( rmesa, spr );
349	 if ((GLenum)param[0]) {
350	    rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] |= R200_PS_GEN_TEX_0 << unit;
351	 } else {
352	    rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] &= ~(R200_PS_GEN_TEX_0 << unit);
353	 }
354      }
355      break;
356   default:
357      return;
358   }
359}
360
361void r200TexUpdateParameters(struct gl_context *ctx, GLuint unit)
362{
363   struct gl_sampler_object *samp = _mesa_get_samplerobj(ctx, unit);
364   radeonTexObj* t = radeon_tex_obj(ctx->Texture.Unit[unit]._Current);
365
366   r200SetTexMaxAnisotropy(t , samp->MaxAnisotropy);
367   r200SetTexFilter(t, samp->MinFilter, samp->MagFilter);
368   r200SetTexWrap(t, samp->WrapS, samp->WrapT, samp->WrapR);
369   r200SetTexBorderColor(t, samp->BorderColor.f);
370}
371
372/**
373 * Changes variables and flags for a state update, which will happen at the
374 * next UpdateTextureState
375 */
376static void r200TexParameter(struct gl_context *ctx,
377                             struct gl_texture_object *texObj,
378                             GLenum pname)
379{
380   radeonTexObj* t = radeon_tex_obj(texObj);
381
382   radeon_print(RADEON_TEXTURE | RADEON_STATE, RADEON_VERBOSE,
383		"%s(%p, tex %p)  pname %s\n",
384		__func__, ctx, texObj,
385	       _mesa_enum_to_string( pname ) );
386
387   switch ( pname ) {
388   case GL_TEXTURE_MIN_FILTER:
389   case GL_TEXTURE_MAG_FILTER:
390   case GL_TEXTURE_MAX_ANISOTROPY_EXT:
391   case GL_TEXTURE_WRAP_S:
392   case GL_TEXTURE_WRAP_T:
393   case GL_TEXTURE_WRAP_R:
394   case GL_TEXTURE_BORDER_COLOR:
395   case GL_TEXTURE_BASE_LEVEL:
396   case GL_TEXTURE_MAX_LEVEL:
397   case GL_TEXTURE_MIN_LOD:
398   case GL_TEXTURE_MAX_LOD:
399      t->validated = GL_FALSE;
400      break;
401
402   default:
403      return;
404   }
405}
406
407
408static void r200DeleteTexture(struct gl_context * ctx, struct gl_texture_object *texObj)
409{
410   r200ContextPtr rmesa = R200_CONTEXT(ctx);
411   radeonTexObj* t = radeon_tex_obj(texObj);
412
413   radeon_print(RADEON_TEXTURE | RADEON_STATE, RADEON_NORMAL,
414           "%s( %p (target = %s) )\n", __func__,
415	   (void *)texObj,
416	   _mesa_enum_to_string(texObj->Target));
417
418   if (rmesa) {
419      int i;
420      radeon_firevertices(&rmesa->radeon);
421      for ( i = 0 ; i < rmesa->radeon.glCtx.Const.MaxTextureUnits ; i++ ) {
422	 if ( t == rmesa->state.texture.unit[i].texobj ) {
423	    rmesa->state.texture.unit[i].texobj = NULL;
424	    rmesa->hw.tex[i].dirty = GL_FALSE;
425	    rmesa->hw.cube[i].dirty = GL_FALSE;
426	 }
427      }
428   }
429
430   radeon_miptree_unreference(&t->mt);
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( struct gl_context *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(struct gl_context * 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   radeon_print(RADEON_STATE | RADEON_TEXTURE, RADEON_NORMAL,
472           "%s(%p) target %s, new texture %p.\n",
473	   __func__, ctx,
474	   _mesa_enum_to_string(target), t);
475
476   _mesa_initialize_texture_object(ctx, &t->base, name, target);
477   t->base.Sampler.MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
478
479   /* Initialize hardware state */
480   r200SetTexWrap( t, t->base.Sampler.WrapS, t->base.Sampler.WrapT, t->base.Sampler.WrapR );
481   r200SetTexMaxAnisotropy( t, t->base.Sampler.MaxAnisotropy );
482   r200SetTexFilter(t, t->base.Sampler.MinFilter, t->base.Sampler.MagFilter);
483   r200SetTexBorderColor(t, t->base.Sampler.BorderColor.f);
484
485   return &t->base;
486}
487
488static struct gl_sampler_object *
489r200NewSamplerObject(struct gl_context *ctx, GLuint name)
490{
491   r200ContextPtr rmesa = R200_CONTEXT(ctx);
492   struct gl_sampler_object *samp = _mesa_new_sampler_object(ctx, name);
493   if (samp)
494      samp->MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
495   return samp;
496}
497
498
499
500void r200InitTextureFuncs( radeonContextPtr radeon, struct dd_function_table *functions )
501{
502   /* Note: we only plug in the functions we implement in the driver
503    * since _mesa_init_driver_functions() was already called.
504    */
505
506   radeon_init_common_texture_funcs(radeon, functions);
507
508   functions->NewTextureObject		= r200NewTextureObject;
509   //   functions->BindTexture		= r200BindTexture;
510   functions->DeleteTexture		= r200DeleteTexture;
511
512   functions->TexEnv			= r200TexEnv;
513   functions->TexParameter		= r200TexParameter;
514   functions->TexGen			= r200TexGen;
515   functions->NewSamplerObject		= r200NewSamplerObject;
516}
517