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
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 "util/u_memory.h"
49#include "util/driconf.h"
50
51
52
53/**
54 * Set the texture wrap modes.
55 *
56 * \param t Texture object whose wrap modes are to be set
57 * \param swrap Wrap mode for the \a s texture coordinate
58 * \param twrap Wrap mode for the \a t texture coordinate
59 */
60
61static void r200SetTexWrap( radeonTexObjPtr t, GLenum swrap, GLenum twrap, GLenum rwrap )
62{
63   GLboolean  is_clamp = GL_FALSE;
64   GLboolean  is_clamp_to_border = GL_FALSE;
65   struct gl_texture_object *tObj = &t->base;
66
67   radeon_print(RADEON_TEXTURE, RADEON_TRACE,
68		"%s(tex %p) sw %s, tw %s, rw %s\n",
69		__func__, t,
70		_mesa_enum_to_string(swrap),
71		_mesa_enum_to_string(twrap),
72		_mesa_enum_to_string(rwrap));
73
74   t->pp_txfilter &= ~(R200_CLAMP_S_MASK | R200_CLAMP_T_MASK | R200_BORDER_MODE_D3D);
75
76   switch ( swrap ) {
77   case GL_REPEAT:
78      t->pp_txfilter |= R200_CLAMP_S_WRAP;
79      break;
80   case GL_CLAMP:
81      t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL;
82      is_clamp = GL_TRUE;
83      break;
84   case GL_CLAMP_TO_EDGE:
85      t->pp_txfilter |= R200_CLAMP_S_CLAMP_LAST;
86      break;
87   case GL_CLAMP_TO_BORDER:
88      t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL;
89      is_clamp_to_border = GL_TRUE;
90      break;
91   case GL_MIRRORED_REPEAT:
92      t->pp_txfilter |= R200_CLAMP_S_MIRROR;
93      break;
94   case GL_MIRROR_CLAMP_EXT:
95      t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL;
96      is_clamp = GL_TRUE;
97      break;
98   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
99      t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_LAST;
100      break;
101   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
102      t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL;
103      is_clamp_to_border = GL_TRUE;
104      break;
105   default:
106      _mesa_problem(NULL, "bad S wrap mode in %s", __func__);
107   }
108
109   if (tObj->Target != GL_TEXTURE_1D) {
110      switch ( twrap ) {
111      case GL_REPEAT:
112         t->pp_txfilter |= R200_CLAMP_T_WRAP;
113         break;
114      case GL_CLAMP:
115         t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL;
116         is_clamp = GL_TRUE;
117         break;
118      case GL_CLAMP_TO_EDGE:
119         t->pp_txfilter |= R200_CLAMP_T_CLAMP_LAST;
120         break;
121      case GL_CLAMP_TO_BORDER:
122         t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL;
123         is_clamp_to_border = GL_TRUE;
124         break;
125      case GL_MIRRORED_REPEAT:
126         t->pp_txfilter |= R200_CLAMP_T_MIRROR;
127         break;
128      case GL_MIRROR_CLAMP_EXT:
129         t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL;
130         is_clamp = GL_TRUE;
131         break;
132      case GL_MIRROR_CLAMP_TO_EDGE_EXT:
133         t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_LAST;
134         break;
135      case GL_MIRROR_CLAMP_TO_BORDER_EXT:
136         t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL;
137         is_clamp_to_border = GL_TRUE;
138         break;
139      default:
140         _mesa_problem(NULL, "bad T wrap mode in %s", __func__);
141      }
142   }
143
144   t->pp_txformat_x &= ~R200_CLAMP_Q_MASK;
145
146   switch ( rwrap ) {
147   case GL_REPEAT:
148      t->pp_txformat_x |= R200_CLAMP_Q_WRAP;
149      break;
150   case GL_CLAMP:
151      t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL;
152      is_clamp = GL_TRUE;
153      break;
154   case GL_CLAMP_TO_EDGE:
155      t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_LAST;
156      break;
157   case GL_CLAMP_TO_BORDER:
158      t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL;
159      is_clamp_to_border = GL_TRUE;
160      break;
161   case GL_MIRRORED_REPEAT:
162      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR;
163      break;
164   case GL_MIRROR_CLAMP_EXT:
165      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL;
166      is_clamp = GL_TRUE;
167      break;
168   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
169      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_LAST;
170      break;
171   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
172      t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL;
173      is_clamp_to_border = GL_TRUE;
174      break;
175   default:
176      _mesa_problem(NULL, "bad R wrap mode in %s", __func__);
177   }
178
179   if ( is_clamp_to_border ) {
180      t->pp_txfilter |= R200_BORDER_MODE_D3D;
181   }
182
183   t->border_fallback = (is_clamp && is_clamp_to_border);
184}
185
186static void r200SetTexMaxAnisotropy( radeonTexObjPtr t, GLfloat max )
187{
188   t->pp_txfilter &= ~R200_MAX_ANISO_MASK;
189   radeon_print(RADEON_TEXTURE, RADEON_TRACE,
190	"%s(tex %p) max %f.\n",
191	__func__, t, max);
192
193   if ( max <= 1.0 ) {
194      t->pp_txfilter |= R200_MAX_ANISO_1_TO_1;
195   } else if ( max <= 2.0 ) {
196      t->pp_txfilter |= R200_MAX_ANISO_2_TO_1;
197   } else if ( max <= 4.0 ) {
198      t->pp_txfilter |= R200_MAX_ANISO_4_TO_1;
199   } else if ( max <= 8.0 ) {
200      t->pp_txfilter |= R200_MAX_ANISO_8_TO_1;
201   } else {
202      t->pp_txfilter |= R200_MAX_ANISO_16_TO_1;
203   }
204}
205
206/**
207 * Set the texture magnification and minification modes.
208 *
209 * \param t Texture whose filter modes are to be set
210 * \param minf Texture minification mode
211 * \param magf Texture magnification mode
212 */
213
214static void r200SetTexFilter( radeonTexObjPtr t, GLenum minf, GLenum magf )
215{
216   GLuint anisotropy = (t->pp_txfilter & R200_MAX_ANISO_MASK);
217
218   /* Force revalidation to account for switches from/to mipmapping. */
219   t->validated = GL_FALSE;
220
221   t->pp_txfilter &= ~(R200_MIN_FILTER_MASK | R200_MAG_FILTER_MASK);
222   t->pp_txformat_x &= ~R200_VOLUME_FILTER_MASK;
223
224   radeon_print(RADEON_TEXTURE, RADEON_TRACE,
225	"%s(tex %p) minf %s, maxf %s, anisotropy %d.\n",
226	__func__, t,
227	_mesa_enum_to_string(minf),
228	_mesa_enum_to_string(magf),
229	anisotropy);
230
231   if ( anisotropy == R200_MAX_ANISO_1_TO_1 ) {
232      switch ( minf ) {
233      case GL_NEAREST:
234	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST;
235	 break;
236      case GL_LINEAR:
237	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR;
238	 break;
239      case GL_NEAREST_MIPMAP_NEAREST:
240	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_NEAREST;
241	 break;
242      case GL_NEAREST_MIPMAP_LINEAR:
243	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_NEAREST;
244	 break;
245      case GL_LINEAR_MIPMAP_NEAREST:
246	 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_LINEAR;
247	 break;
248      case GL_LINEAR_MIPMAP_LINEAR:
249	 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_LINEAR;
250	 break;
251      }
252   } else {
253      switch ( minf ) {
254      case GL_NEAREST:
255	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST;
256	 break;
257      case GL_LINEAR:
258	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_LINEAR;
259	 break;
260      case GL_NEAREST_MIPMAP_NEAREST:
261      case GL_LINEAR_MIPMAP_NEAREST:
262	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST;
263	 break;
264      case GL_NEAREST_MIPMAP_LINEAR:
265      case GL_LINEAR_MIPMAP_LINEAR:
266	 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR;
267	 break;
268      }
269   }
270
271   /* Note we don't have 3D mipmaps so only use the mag filter setting
272    * to set the 3D texture filter mode.
273    */
274   switch ( magf ) {
275   case GL_NEAREST:
276      t->pp_txfilter |= R200_MAG_FILTER_NEAREST;
277      t->pp_txformat_x |= R200_VOLUME_FILTER_NEAREST;
278      break;
279   case GL_LINEAR:
280      t->pp_txfilter |= R200_MAG_FILTER_LINEAR;
281      t->pp_txformat_x |= R200_VOLUME_FILTER_LINEAR;
282      break;
283   }
284}
285
286static void r200SetTexBorderColor( radeonTexObjPtr t, const GLfloat color[4] )
287{
288   GLubyte c[4];
289   CLAMPED_FLOAT_TO_UBYTE(c[0], color[0]);
290   CLAMPED_FLOAT_TO_UBYTE(c[1], color[1]);
291   CLAMPED_FLOAT_TO_UBYTE(c[2], color[2]);
292   CLAMPED_FLOAT_TO_UBYTE(c[3], color[3]);
293   t->pp_border_color = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
294}
295
296static void r200TexEnv( struct gl_context *ctx, GLenum target,
297			  GLenum pname, const GLfloat *param )
298{
299   r200ContextPtr rmesa = R200_CONTEXT(ctx);
300   GLuint unit = ctx->Texture.CurrentUnit;
301   struct gl_fixedfunc_texture_unit *texUnit =
302      &ctx->Texture.FixedFuncUnit[unit];
303
304   radeon_print(RADEON_TEXTURE | RADEON_STATE, RADEON_VERBOSE, "%s( %s )\n",
305	       __func__, _mesa_enum_to_string( pname ) );
306
307   /* This is incorrect: Need to maintain this data for each of
308    * GL_TEXTURE_{123}D, GL_TEXTURE_RECTANGLE_NV, etc, and switch
309    * between them according to _Current->Target.
310    */
311   switch ( pname ) {
312   case GL_TEXTURE_ENV_COLOR: {
313      GLubyte c[4];
314      GLuint envColor;
315      _mesa_unclamped_float_rgba_to_ubyte(c, texUnit->EnvColor);
316      envColor = radeonPackColor( 4, c[0], c[1], c[2], c[3] );
317      if ( rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] != envColor ) {
318	 R200_STATECHANGE( rmesa, tf );
319	 rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] = envColor;
320      }
321      break;
322   }
323
324   case GL_TEXTURE_LOD_BIAS_EXT: {
325      GLfloat bias, min;
326      GLuint b;
327      const int fixed_one = R200_LOD_BIAS_FIXED_ONE;
328
329      /* The R200's LOD bias is a signed 2's complement value with a
330       * range of -16.0 <= bias < 16.0.
331       *
332       * NOTE: Add a small bias to the bias for conform mipsel.c test.
333       */
334      bias = *param;
335      min = driQueryOptionb (&rmesa->radeon.optionCache, "no_neg_lod_bias") ?
336	  0.0 : -16.0;
337      bias = CLAMP( bias, min, 16.0 );
338      b = ((int)(bias * fixed_one)
339		+ R200_LOD_BIAS_CORRECTION) & R200_LOD_BIAS_MASK;
340
341      if ( (rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] & R200_LOD_BIAS_MASK) != b ) {
342	 R200_STATECHANGE( rmesa, tex[unit] );
343	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] &= ~R200_LOD_BIAS_MASK;
344	 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] |= b;
345      }
346      break;
347   }
348   case GL_COORD_REPLACE_ARB:
349      if (ctx->Point.PointSprite) {
350	 R200_STATECHANGE( rmesa, spr );
351	 if ((GLenum)param[0]) {
352	    rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] |= R200_PS_GEN_TEX_0 << unit;
353	 } else {
354	    rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] &= ~(R200_PS_GEN_TEX_0 << unit);
355	 }
356      }
357      break;
358   default:
359      return;
360   }
361}
362
363void r200TexUpdateParameters(struct gl_context *ctx, GLuint unit)
364{
365   struct gl_sampler_object *samp = _mesa_get_samplerobj(ctx, unit);
366   radeonTexObj* t = radeon_tex_obj(ctx->Texture.Unit[unit]._Current);
367
368   r200SetTexMaxAnisotropy(t , samp->Attrib.MaxAnisotropy);
369   r200SetTexFilter(t, samp->Attrib.MinFilter, samp->Attrib.MagFilter);
370   r200SetTexWrap(t, samp->Attrib.WrapS, samp->Attrib.WrapT, samp->Attrib.WrapR);
371   r200SetTexBorderColor(t, samp->Attrib.state.border_color.f);
372}
373
374/**
375 * Changes variables and flags for a state update, which will happen at the
376 * next UpdateTextureState
377 */
378static void r200TexParameter(struct gl_context *ctx,
379                             struct gl_texture_object *texObj,
380                             GLenum pname)
381{
382   radeonTexObj* t = radeon_tex_obj(texObj);
383
384   radeon_print(RADEON_TEXTURE | RADEON_STATE, RADEON_VERBOSE,
385		"%s(%p, tex %p)  pname %s\n",
386		__func__, ctx, texObj,
387	       _mesa_enum_to_string( pname ) );
388
389   switch ( pname ) {
390   case GL_ALL_ATTRIB_BITS: /* meaning is all pnames, internal */
391   case GL_TEXTURE_MIN_FILTER:
392   case GL_TEXTURE_MAG_FILTER:
393   case GL_TEXTURE_MAX_ANISOTROPY_EXT:
394   case GL_TEXTURE_WRAP_S:
395   case GL_TEXTURE_WRAP_T:
396   case GL_TEXTURE_WRAP_R:
397   case GL_TEXTURE_BORDER_COLOR:
398   case GL_TEXTURE_BASE_LEVEL:
399   case GL_TEXTURE_MAX_LEVEL:
400   case GL_TEXTURE_MIN_LOD:
401   case GL_TEXTURE_MAX_LOD:
402      t->validated = GL_FALSE;
403      break;
404
405   default:
406      return;
407   }
408}
409
410
411static void r200DeleteTexture(struct gl_context * ctx, struct gl_texture_object *texObj)
412{
413   r200ContextPtr rmesa = R200_CONTEXT(ctx);
414   radeonTexObj* t = radeon_tex_obj(texObj);
415
416   radeon_print(RADEON_TEXTURE | RADEON_STATE, RADEON_NORMAL,
417           "%s( %p (target = %s) )\n", __func__,
418	   (void *)texObj,
419	   _mesa_enum_to_string(texObj->Target));
420
421   if (rmesa) {
422      int i;
423      radeon_firevertices(&rmesa->radeon);
424      for ( i = 0 ; i < rmesa->radeon.glCtx.Const.MaxTextureUnits ; i++ ) {
425	 if ( t == rmesa->state.texture.unit[i].texobj ) {
426	    rmesa->state.texture.unit[i].texobj = NULL;
427	    rmesa->hw.tex[i].dirty = GL_FALSE;
428	    rmesa->hw.cube[i].dirty = GL_FALSE;
429	 }
430      }
431   }
432
433   radeon_miptree_unreference(&t->mt);
434
435   _mesa_delete_texture_object(ctx, texObj);
436}
437
438/* Need:
439 *  - Same GEN_MODE for all active bits
440 *  - Same EyePlane/ObjPlane for all active bits when using Eye/Obj
441 *  - STRQ presumably all supported (matrix means incoming R values
442 *    can end up in STQ, this has implications for vertex support,
443 *    presumably ok if maos is used, though?)
444 *
445 * Basically impossible to do this on the fly - just collect some
446 * basic info & do the checks from ValidateState().
447 */
448static void r200TexGen( struct gl_context *ctx,
449			  GLenum coord,
450			  GLenum pname,
451			  const GLfloat *params )
452{
453   r200ContextPtr rmesa = R200_CONTEXT(ctx);
454   GLuint unit = ctx->Texture.CurrentUnit;
455   rmesa->recheck_texgen[unit] = GL_TRUE;
456}
457
458
459/**
460 * Allocate a new texture object.
461 * Called via ctx->Driver.NewTextureObject.
462 * Note: this function will be called during context creation to
463 * allocate the default texture objects.
464 * Fixup MaxAnisotropy according to user preference.
465 */
466static struct gl_texture_object *r200NewTextureObject(struct gl_context * ctx,
467						      GLuint name,
468						      GLenum target)
469{
470   r200ContextPtr rmesa = R200_CONTEXT(ctx);
471   radeonTexObj* t = CALLOC_STRUCT(radeon_tex_obj);
472
473
474   radeon_print(RADEON_STATE | RADEON_TEXTURE, RADEON_NORMAL,
475           "%s(%p) target %s, new texture %p.\n",
476	   __func__, ctx,
477	   _mesa_enum_to_string(target), t);
478
479   _mesa_initialize_texture_object(ctx, &t->base, name, target);
480   t->base.Sampler.Attrib.MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
481
482   /* Initialize hardware state */
483   r200SetTexWrap( t, t->base.Sampler.Attrib.WrapS, t->base.Sampler.Attrib.WrapT, t->base.Sampler.Attrib.WrapR );
484   r200SetTexMaxAnisotropy( t, t->base.Sampler.Attrib.MaxAnisotropy );
485   r200SetTexFilter(t, t->base.Sampler.Attrib.MinFilter, t->base.Sampler.Attrib.MagFilter);
486   r200SetTexBorderColor(t, t->base.Sampler.Attrib.state.border_color.f);
487
488   return &t->base;
489}
490
491static struct gl_sampler_object *
492r200NewSamplerObject(struct gl_context *ctx, GLuint name)
493{
494   r200ContextPtr rmesa = R200_CONTEXT(ctx);
495   struct gl_sampler_object *samp = _mesa_new_sampler_object(ctx, name);
496   if (samp)
497      samp->Attrib.MaxAnisotropy = rmesa->radeon.initialMaxAnisotropy;
498   return samp;
499}
500
501
502
503void r200InitTextureFuncs( radeonContextPtr radeon, struct dd_function_table *functions )
504{
505   /* Note: we only plug in the functions we implement in the driver
506    * since _mesa_init_driver_functions() was already called.
507    */
508
509   radeon_init_common_texture_funcs(radeon, functions);
510
511   functions->NewTextureObject		= r200NewTextureObject;
512   //   functions->BindTexture		= r200BindTexture;
513   functions->DeleteTexture		= r200DeleteTexture;
514
515   functions->TexEnv			= r200TexEnv;
516   functions->TexParameter		= r200TexParameter;
517   functions->TexGen			= r200TexGen;
518   functions->NewSamplerObject		= r200NewSamplerObject;
519}
520