1/**************************************************************************
2 *
3 * Copyright 2003 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include "main/glheader.h"
29#include "main/macros.h"
30#include "main/mtypes.h"
31#include "main/enums.h"
32
33#include "intel_screen.h"
34#include "intel_tex.h"
35
36#include "i830_context.h"
37#include "i830_reg.h"
38
39
40/* ================================================================
41 * Texture combine functions
42 */
43static GLuint
44pass_through(GLuint * state, GLuint blendUnit)
45{
46   state[0] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) |
47               TEXPIPE_COLOR |
48               ENABLE_TEXOUTPUT_WRT_SEL |
49               TEXOP_OUTPUT_CURRENT |
50               DISABLE_TEX_CNTRL_STAGE |
51               TEXOP_SCALE_1X | TEXOP_MODIFY_PARMS | TEXBLENDOP_ARG1);
52   state[1] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) |
53               TEXPIPE_ALPHA |
54               ENABLE_TEXOUTPUT_WRT_SEL |
55               TEXOP_OUTPUT_CURRENT |
56               TEXOP_SCALE_1X | TEXOP_MODIFY_PARMS | TEXBLENDOP_ARG1);
57   state[2] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) |
58               TEXPIPE_COLOR |
59               TEXBLEND_ARG1 |
60               TEXBLENDARG_MODIFY_PARMS | TEXBLENDARG_CURRENT);
61   state[3] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) |
62               TEXPIPE_ALPHA |
63               TEXBLEND_ARG1 |
64               TEXBLENDARG_MODIFY_PARMS | TEXBLENDARG_CURRENT);
65
66   return 4;
67}
68
69static GLuint
70emit_factor(GLuint blendUnit, GLuint * state, GLuint count,
71            const GLfloat * factor)
72{
73   GLubyte r, g, b, a;
74   GLuint col;
75
76   if (0)
77      fprintf(stderr, "emit constant %d: %.2f %.2f %.2f %.2f\n",
78              blendUnit, factor[0], factor[1], factor[2], factor[3]);
79
80   UNCLAMPED_FLOAT_TO_UBYTE(r, factor[0]);
81   UNCLAMPED_FLOAT_TO_UBYTE(g, factor[1]);
82   UNCLAMPED_FLOAT_TO_UBYTE(b, factor[2]);
83   UNCLAMPED_FLOAT_TO_UBYTE(a, factor[3]);
84
85   col = ((a << 24) | (r << 16) | (g << 8) | b);
86
87   state[count++] = _3DSTATE_COLOR_FACTOR_N_CMD(blendUnit);
88   state[count++] = col;
89
90   return count;
91}
92
93
94static inline GLuint
95GetTexelOp(GLint unit)
96{
97   switch (unit) {
98   case 0:
99      return TEXBLENDARG_TEXEL0;
100   case 1:
101      return TEXBLENDARG_TEXEL1;
102   case 2:
103      return TEXBLENDARG_TEXEL2;
104   case 3:
105      return TEXBLENDARG_TEXEL3;
106   default:
107      return TEXBLENDARG_TEXEL0;
108   }
109}
110
111
112/**
113 * Calculate the hardware instuctions to setup the current texture enviromnemt
114 * settings.  Since \c gl_texture_unit::_CurrentCombine is used, both
115 * "classic" texture enviroments and GL_ARB_texture_env_combine type texture
116 * environments are treated identically.
117 *
118 * \todo
119 * This function should return \c bool.  When \c false is returned,
120 * it means that an environment is selected that the hardware cannot do.  This
121 * is the way the Radeon and R200 drivers work.
122 *
123 * \todo
124 * Looking at i830_3d_regs.h, it seems the i830 can do part of
125 * GL_ATI_texture_env_combine3.  It can handle using \c GL_ONE and
126 * \c GL_ZERO as combine inputs (which the code already supports).  It can
127 * also handle the \c GL_MODULATE_ADD_ATI mode.  Is it worth investigating
128 * partial support for the extension?
129 */
130GLuint
131i830SetTexEnvCombine(struct i830_context * i830,
132                     const struct gl_tex_env_combine_state * combine,
133                     GLint blendUnit,
134                     GLuint texel_op, GLuint * state, const GLfloat * factor)
135{
136   const GLuint numColorArgs = combine->_NumArgsRGB;
137   GLuint numAlphaArgs = combine->_NumArgsA;
138
139   GLuint blendop;
140   GLuint ablendop;
141   GLuint args_RGB[3];
142   GLuint args_A[3];
143   GLuint rgb_shift;
144   GLuint alpha_shift;
145   bool need_factor = 0;
146   int i;
147   unsigned used;
148   static const GLuint tex_blend_rgb[3] = {
149      TEXPIPE_COLOR | TEXBLEND_ARG1 | TEXBLENDARG_MODIFY_PARMS,
150      TEXPIPE_COLOR | TEXBLEND_ARG2 | TEXBLENDARG_MODIFY_PARMS,
151      TEXPIPE_COLOR | TEXBLEND_ARG0 | TEXBLENDARG_MODIFY_PARMS,
152   };
153   static const GLuint tex_blend_a[3] = {
154      TEXPIPE_ALPHA | TEXBLEND_ARG1 | TEXBLENDARG_MODIFY_PARMS,
155      TEXPIPE_ALPHA | TEXBLEND_ARG2 | TEXBLENDARG_MODIFY_PARMS,
156      TEXPIPE_ALPHA | TEXBLEND_ARG0 | TEXBLENDARG_MODIFY_PARMS,
157   };
158
159   if (INTEL_DEBUG & DEBUG_TEXTURE)
160      fprintf(stderr, "%s\n", __func__);
161
162
163   /* The EXT version of the DOT3 extension does not support the
164    * scale factor, but the ARB version (and the version in OpenGL
165    * 1.3) does.
166    */
167   switch (combine->ModeRGB) {
168   case GL_DOT3_RGB_EXT:
169      alpha_shift = combine->ScaleShiftA;
170      rgb_shift = 0;
171      break;
172
173   case GL_DOT3_RGBA_EXT:
174      alpha_shift = 0;
175      rgb_shift = 0;
176      break;
177
178   default:
179      rgb_shift = combine->ScaleShiftRGB;
180      alpha_shift = combine->ScaleShiftA;
181      break;
182   }
183
184
185   switch (combine->ModeRGB) {
186   case GL_REPLACE:
187      blendop = TEXBLENDOP_ARG1;
188      break;
189   case GL_MODULATE:
190      blendop = TEXBLENDOP_MODULATE;
191      break;
192   case GL_ADD:
193      blendop = TEXBLENDOP_ADD;
194      break;
195   case GL_ADD_SIGNED:
196      blendop = TEXBLENDOP_ADDSIGNED;
197      break;
198   case GL_INTERPOLATE:
199      blendop = TEXBLENDOP_BLEND;
200      break;
201   case GL_SUBTRACT:
202      blendop = TEXBLENDOP_SUBTRACT;
203      break;
204   case GL_DOT3_RGB_EXT:
205   case GL_DOT3_RGB:
206      blendop = TEXBLENDOP_DOT3;
207      break;
208   case GL_DOT3_RGBA_EXT:
209   case GL_DOT3_RGBA:
210      blendop = TEXBLENDOP_DOT4;
211      break;
212   default:
213      return pass_through(state, blendUnit);
214   }
215
216   blendop |= (rgb_shift << TEXOP_SCALE_SHIFT);
217
218
219   /* Handle RGB args */
220   for (i = 0; i < 3; i++) {
221      switch (combine->SourceRGB[i]) {
222      case GL_TEXTURE:
223         args_RGB[i] = texel_op;
224         break;
225      case GL_TEXTURE0:
226      case GL_TEXTURE1:
227      case GL_TEXTURE2:
228      case GL_TEXTURE3:
229         args_RGB[i] = GetTexelOp(combine->SourceRGB[i] - GL_TEXTURE0);
230         break;
231      case GL_CONSTANT:
232         args_RGB[i] = TEXBLENDARG_FACTOR_N;
233         need_factor = 1;
234         break;
235      case GL_PRIMARY_COLOR:
236         args_RGB[i] = TEXBLENDARG_DIFFUSE;
237         break;
238      case GL_PREVIOUS:
239         args_RGB[i] = TEXBLENDARG_CURRENT;
240         break;
241      default:
242         return pass_through(state, blendUnit);
243      }
244
245      switch (combine->OperandRGB[i]) {
246      case GL_SRC_COLOR:
247         args_RGB[i] |= 0;
248         break;
249      case GL_ONE_MINUS_SRC_COLOR:
250         args_RGB[i] |= TEXBLENDARG_INV_ARG;
251         break;
252      case GL_SRC_ALPHA:
253         args_RGB[i] |= TEXBLENDARG_REPLICATE_ALPHA;
254         break;
255      case GL_ONE_MINUS_SRC_ALPHA:
256         args_RGB[i] |= (TEXBLENDARG_REPLICATE_ALPHA | TEXBLENDARG_INV_ARG);
257         break;
258      default:
259         return pass_through(state, blendUnit);
260      }
261   }
262
263
264   /* Need to knobble the alpha calculations of TEXBLENDOP_DOT4 to
265    * match the spec.  Can't use DOT3 as it won't propogate values
266    * into alpha as required:
267    *
268    * Note - the global factor is set up with alpha == .5, so
269    * the alpha part of the DOT4 calculation should be zero.
270    */
271   if (combine->ModeRGB == GL_DOT3_RGBA_EXT ||
272       combine->ModeRGB == GL_DOT3_RGBA) {
273      ablendop = TEXBLENDOP_DOT4;
274      numAlphaArgs = 2;
275      args_A[0] = TEXBLENDARG_FACTOR;   /* the global factor */
276      args_A[1] = TEXBLENDARG_FACTOR;
277      args_A[2] = TEXBLENDARG_FACTOR;
278   }
279   else {
280      switch (combine->ModeA) {
281      case GL_REPLACE:
282         ablendop = TEXBLENDOP_ARG1;
283         break;
284      case GL_MODULATE:
285         ablendop = TEXBLENDOP_MODULATE;
286         break;
287      case GL_ADD:
288         ablendop = TEXBLENDOP_ADD;
289         break;
290      case GL_ADD_SIGNED:
291         ablendop = TEXBLENDOP_ADDSIGNED;
292         break;
293      case GL_INTERPOLATE:
294         ablendop = TEXBLENDOP_BLEND;
295         break;
296      case GL_SUBTRACT:
297         ablendop = TEXBLENDOP_SUBTRACT;
298         break;
299      default:
300         return pass_through(state, blendUnit);
301      }
302
303
304      ablendop |= (alpha_shift << TEXOP_SCALE_SHIFT);
305
306      /* Handle A args */
307      for (i = 0; i < 3; i++) {
308         switch (combine->SourceA[i]) {
309         case GL_TEXTURE:
310            args_A[i] = texel_op;
311            break;
312         case GL_TEXTURE0:
313         case GL_TEXTURE1:
314         case GL_TEXTURE2:
315         case GL_TEXTURE3:
316            args_A[i] = GetTexelOp(combine->SourceA[i] - GL_TEXTURE0);
317            break;
318         case GL_CONSTANT:
319            args_A[i] = TEXBLENDARG_FACTOR_N;
320            need_factor = 1;
321            break;
322         case GL_PRIMARY_COLOR:
323            args_A[i] = TEXBLENDARG_DIFFUSE;
324            break;
325         case GL_PREVIOUS:
326            args_A[i] = TEXBLENDARG_CURRENT;
327            break;
328         default:
329            return pass_through(state, blendUnit);
330         }
331
332         switch (combine->OperandA[i]) {
333         case GL_SRC_ALPHA:
334            args_A[i] |= 0;
335            break;
336         case GL_ONE_MINUS_SRC_ALPHA:
337            args_A[i] |= TEXBLENDARG_INV_ARG;
338            break;
339         default:
340            return pass_through(state, blendUnit);
341         }
342      }
343   }
344
345
346
347   /* Native Arg1 == Arg0 in GL_EXT_texture_env_combine spec */
348   /* Native Arg2 == Arg1 in GL_EXT_texture_env_combine spec */
349   /* Native Arg0 == Arg2 in GL_EXT_texture_env_combine spec */
350
351   /* When we render we need to figure out which is the last really enabled
352    * tex unit, and put last stage on it
353    */
354
355
356   /* Build color & alpha pipelines */
357
358   used = 0;
359   state[used++] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) |
360                    TEXPIPE_COLOR |
361                    ENABLE_TEXOUTPUT_WRT_SEL |
362                    TEXOP_OUTPUT_CURRENT |
363                    DISABLE_TEX_CNTRL_STAGE | TEXOP_MODIFY_PARMS | blendop);
364   state[used++] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) |
365                    TEXPIPE_ALPHA |
366                    ENABLE_TEXOUTPUT_WRT_SEL |
367                    TEXOP_OUTPUT_CURRENT | TEXOP_MODIFY_PARMS | ablendop);
368
369   for (i = 0; i < numColorArgs; i++) {
370      state[used++] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) |
371                       tex_blend_rgb[i] | args_RGB[i]);
372   }
373
374   for (i = 0; i < numAlphaArgs; i++) {
375      state[used++] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) |
376                       tex_blend_a[i] | args_A[i]);
377   }
378
379
380   if (need_factor)
381      return emit_factor(blendUnit, state, used, factor);
382   else
383      return used;
384}
385
386
387static void
388emit_texblend(struct i830_context *i830, GLuint unit, GLuint blendUnit,
389              bool last_stage)
390{
391   struct gl_fixedfunc_texture_unit *texUnit =
392      &i830->intel.ctx.Texture.FixedFuncUnit[unit];
393   GLuint tmp[I830_TEXBLEND_SIZE], tmp_sz;
394
395
396   if (0)
397      fprintf(stderr, "%s unit %d\n", __func__, unit);
398
399   /* Update i830->state.TexBlend
400    */
401   tmp_sz = i830SetTexEnvCombine(i830, texUnit->_CurrentCombine, blendUnit,
402                                 GetTexelOp(unit), tmp, texUnit->EnvColor);
403
404   if (last_stage)
405      tmp[0] |= TEXOP_LAST_STAGE;
406
407   if (tmp_sz != i830->state.TexBlendWordsUsed[blendUnit] ||
408       memcmp(tmp, i830->state.TexBlend[blendUnit],
409              tmp_sz * sizeof(GLuint))) {
410
411      I830_STATECHANGE(i830, I830_UPLOAD_TEXBLEND(blendUnit));
412      memcpy(i830->state.TexBlend[blendUnit], tmp, tmp_sz * sizeof(GLuint));
413      i830->state.TexBlendWordsUsed[blendUnit] = tmp_sz;
414   }
415
416   I830_ACTIVESTATE(i830, I830_UPLOAD_TEXBLEND(blendUnit), true);
417}
418
419static void
420emit_passthrough(struct i830_context *i830)
421{
422   GLuint tmp[I830_TEXBLEND_SIZE], tmp_sz;
423   GLuint unit = 0;
424
425   tmp_sz = pass_through(tmp, unit);
426   tmp[0] |= TEXOP_LAST_STAGE;
427
428   if (tmp_sz != i830->state.TexBlendWordsUsed[unit] ||
429       memcmp(tmp, i830->state.TexBlend[unit], tmp_sz * sizeof(GLuint))) {
430
431      I830_STATECHANGE(i830, I830_UPLOAD_TEXBLEND(unit));
432      memcpy(i830->state.TexBlend[unit], tmp, tmp_sz * sizeof(GLuint));
433      i830->state.TexBlendWordsUsed[unit] = tmp_sz;
434   }
435
436   I830_ACTIVESTATE(i830, I830_UPLOAD_TEXBLEND(unit), true);
437}
438
439void
440i830EmitTextureBlend(struct i830_context *i830)
441{
442   struct gl_context *ctx = &i830->intel.ctx;
443   GLuint unit, blendunit = 0;
444
445   I830_ACTIVESTATE(i830, I830_UPLOAD_TEXBLEND_ALL, false);
446
447   if (ctx->Texture._MaxEnabledTexImageUnit != -1) {
448      for (unit = 0; unit <= ctx->Texture._MaxEnabledTexImageUnit; unit++)
449         if (ctx->Texture.Unit[unit]._Current)
450            emit_texblend(i830, unit, blendunit++,
451                          unit == ctx->Texture._MaxEnabledTexImageUnit);
452   } else {
453      emit_passthrough(i830);
454   }
455}
456