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