/************************************************************************** * * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ #include "main/glheader.h" #include "main/macros.h" #include "main/mtypes.h" #include "main/simple_list.h" #include "main/enums.h" #include "main/texstore.h" #include "main/mm.h" #include "intel_screen.h" #include "intel_tex.h" #include "i830_context.h" #include "i830_reg.h" /* ================================================================ * Texture combine functions */ static GLuint pass_through(GLuint * state, GLuint blendUnit) { state[0] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) | TEXPIPE_COLOR | ENABLE_TEXOUTPUT_WRT_SEL | TEXOP_OUTPUT_CURRENT | DISABLE_TEX_CNTRL_STAGE | TEXOP_SCALE_1X | TEXOP_MODIFY_PARMS | TEXBLENDOP_ARG1); state[1] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) | TEXPIPE_ALPHA | ENABLE_TEXOUTPUT_WRT_SEL | TEXOP_OUTPUT_CURRENT | TEXOP_SCALE_1X | TEXOP_MODIFY_PARMS | TEXBLENDOP_ARG1); state[2] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) | TEXPIPE_COLOR | TEXBLEND_ARG1 | TEXBLENDARG_MODIFY_PARMS | TEXBLENDARG_CURRENT); state[3] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) | TEXPIPE_ALPHA | TEXBLEND_ARG1 | TEXBLENDARG_MODIFY_PARMS | TEXBLENDARG_CURRENT); return 4; } static GLuint emit_factor(GLuint blendUnit, GLuint * state, GLuint count, const GLfloat * factor) { GLubyte r, g, b, a; GLuint col; if (0) fprintf(stderr, "emit constant %d: %.2f %.2f %.2f %.2f\n", blendUnit, factor[0], factor[1], factor[2], factor[3]); UNCLAMPED_FLOAT_TO_UBYTE(r, factor[0]); UNCLAMPED_FLOAT_TO_UBYTE(g, factor[1]); UNCLAMPED_FLOAT_TO_UBYTE(b, factor[2]); UNCLAMPED_FLOAT_TO_UBYTE(a, factor[3]); col = ((a << 24) | (r << 16) | (g << 8) | b); state[count++] = _3DSTATE_COLOR_FACTOR_N_CMD(blendUnit); state[count++] = col; return count; } static INLINE GLuint GetTexelOp(GLint unit) { switch (unit) { case 0: return TEXBLENDARG_TEXEL0; case 1: return TEXBLENDARG_TEXEL1; case 2: return TEXBLENDARG_TEXEL2; case 3: return TEXBLENDARG_TEXEL3; default: return TEXBLENDARG_TEXEL0; } } /** * Calculate the hardware instuctions to setup the current texture enviromnemt * settings. Since \c gl_texture_unit::_CurrentCombine is used, both * "classic" texture enviroments and GL_ARB_texture_env_combine type texture * environments are treated identically. * * \todo * This function should return \c GLboolean. When \c GL_FALSE is returned, * it means that an environment is selected that the hardware cannot do. This * is the way the Radeon and R200 drivers work. * * \todo * Looking at i830_3d_regs.h, it seems the i830 can do part of * GL_ATI_texture_env_combine3. It can handle using \c GL_ONE and * \c GL_ZERO as combine inputs (which the code already supports). It can * also handle the \c GL_MODULATE_ADD_ATI mode. Is it worth investigating * partial support for the extension? */ GLuint i830SetTexEnvCombine(struct i830_context * i830, const struct gl_tex_env_combine_state * combine, GLint blendUnit, GLuint texel_op, GLuint * state, const GLfloat * factor) { const GLuint numColorArgs = combine->_NumArgsRGB; const GLuint numAlphaArgs = combine->_NumArgsA; GLuint blendop; GLuint ablendop; GLuint args_RGB[3]; GLuint args_A[3]; GLuint rgb_shift; GLuint alpha_shift; GLboolean need_factor = 0; int i; unsigned used; static const GLuint tex_blend_rgb[3] = { TEXPIPE_COLOR | TEXBLEND_ARG1 | TEXBLENDARG_MODIFY_PARMS, TEXPIPE_COLOR | TEXBLEND_ARG2 | TEXBLENDARG_MODIFY_PARMS, TEXPIPE_COLOR | TEXBLEND_ARG0 | TEXBLENDARG_MODIFY_PARMS, }; static const GLuint tex_blend_a[3] = { TEXPIPE_ALPHA | TEXBLEND_ARG1 | TEXBLENDARG_MODIFY_PARMS, TEXPIPE_ALPHA | TEXBLEND_ARG2 | TEXBLENDARG_MODIFY_PARMS, TEXPIPE_ALPHA | TEXBLEND_ARG0 | TEXBLENDARG_MODIFY_PARMS, }; if (INTEL_DEBUG & DEBUG_TEXTURE) fprintf(stderr, "%s\n", __FUNCTION__); /* The EXT version of the DOT3 extension does not support the * scale factor, but the ARB version (and the version in OpenGL * 1.3) does. */ switch (combine->ModeRGB) { case GL_DOT3_RGB_EXT: alpha_shift = combine->ScaleShiftA; rgb_shift = 0; break; case GL_DOT3_RGBA_EXT: alpha_shift = 0; rgb_shift = 0; break; default: rgb_shift = combine->ScaleShiftRGB; alpha_shift = combine->ScaleShiftA; break; } switch (combine->ModeRGB) { case GL_REPLACE: blendop = TEXBLENDOP_ARG1; break; case GL_MODULATE: blendop = TEXBLENDOP_MODULATE; break; case GL_ADD: blendop = TEXBLENDOP_ADD; break; case GL_ADD_SIGNED: blendop = TEXBLENDOP_ADDSIGNED; break; case GL_INTERPOLATE: blendop = TEXBLENDOP_BLEND; break; case GL_SUBTRACT: blendop = TEXBLENDOP_SUBTRACT; break; case GL_DOT3_RGB_EXT: case GL_DOT3_RGB: blendop = TEXBLENDOP_DOT3; break; case GL_DOT3_RGBA_EXT: case GL_DOT3_RGBA: blendop = TEXBLENDOP_DOT3; break; default: return pass_through(state, blendUnit); } blendop |= (rgb_shift << TEXOP_SCALE_SHIFT); /* Handle RGB args */ for (i = 0; i < 3; i++) { switch (combine->SourceRGB[i]) { case GL_TEXTURE: args_RGB[i] = texel_op; break; case GL_TEXTURE0: case GL_TEXTURE1: case GL_TEXTURE2: case GL_TEXTURE3: args_RGB[i] = GetTexelOp(combine->SourceRGB[i] - GL_TEXTURE0); break; case GL_CONSTANT: args_RGB[i] = TEXBLENDARG_FACTOR_N; need_factor = 1; break; case GL_PRIMARY_COLOR: args_RGB[i] = TEXBLENDARG_DIFFUSE; break; case GL_PREVIOUS: args_RGB[i] = TEXBLENDARG_CURRENT; break; default: return pass_through(state, blendUnit); } switch (combine->OperandRGB[i]) { case GL_SRC_COLOR: args_RGB[i] |= 0; break; case GL_ONE_MINUS_SRC_COLOR: args_RGB[i] |= TEXBLENDARG_INV_ARG; break; case GL_SRC_ALPHA: args_RGB[i] |= TEXBLENDARG_REPLICATE_ALPHA; break; case GL_ONE_MINUS_SRC_ALPHA: args_RGB[i] |= (TEXBLENDARG_REPLICATE_ALPHA | TEXBLENDARG_INV_ARG); break; default: return pass_through(state, blendUnit); } } /* Need to knobble the alpha calculations of TEXBLENDOP_DOT4 to * match the spec. Can't use DOT3 as it won't propogate values * into alpha as required: * * Note - the global factor is set up with alpha == .5, so * the alpha part of the DOT4 calculation should be zero. */ if (combine->ModeRGB == GL_DOT3_RGBA_EXT || combine->ModeRGB == GL_DOT3_RGBA) { ablendop = TEXBLENDOP_DOT4; args_A[0] = TEXBLENDARG_FACTOR; /* the global factor */ args_A[1] = TEXBLENDARG_FACTOR; args_A[2] = TEXBLENDARG_FACTOR; } else { switch (combine->ModeA) { case GL_REPLACE: ablendop = TEXBLENDOP_ARG1; break; case GL_MODULATE: ablendop = TEXBLENDOP_MODULATE; break; case GL_ADD: ablendop = TEXBLENDOP_ADD; break; case GL_ADD_SIGNED: ablendop = TEXBLENDOP_ADDSIGNED; break; case GL_INTERPOLATE: ablendop = TEXBLENDOP_BLEND; break; case GL_SUBTRACT: ablendop = TEXBLENDOP_SUBTRACT; break; default: return pass_through(state, blendUnit); } ablendop |= (alpha_shift << TEXOP_SCALE_SHIFT); /* Handle A args */ for (i = 0; i < 3; i++) { switch (combine->SourceA[i]) { case GL_TEXTURE: args_A[i] = texel_op; break; case GL_TEXTURE0: case GL_TEXTURE1: case GL_TEXTURE2: case GL_TEXTURE3: args_A[i] = GetTexelOp(combine->SourceA[i] - GL_TEXTURE0); break; case GL_CONSTANT: args_A[i] = TEXBLENDARG_FACTOR_N; need_factor = 1; break; case GL_PRIMARY_COLOR: args_A[i] = TEXBLENDARG_DIFFUSE; break; case GL_PREVIOUS: args_A[i] = TEXBLENDARG_CURRENT; break; default: return pass_through(state, blendUnit); } switch (combine->OperandA[i]) { case GL_SRC_ALPHA: args_A[i] |= 0; break; case GL_ONE_MINUS_SRC_ALPHA: args_A[i] |= TEXBLENDARG_INV_ARG; break; default: return pass_through(state, blendUnit); } } } /* Native Arg1 == Arg0 in GL_EXT_texture_env_combine spec */ /* Native Arg2 == Arg1 in GL_EXT_texture_env_combine spec */ /* Native Arg0 == Arg2 in GL_EXT_texture_env_combine spec */ /* When we render we need to figure out which is the last really enabled * tex unit, and put last stage on it */ /* Build color & alpha pipelines */ used = 0; state[used++] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) | TEXPIPE_COLOR | ENABLE_TEXOUTPUT_WRT_SEL | TEXOP_OUTPUT_CURRENT | DISABLE_TEX_CNTRL_STAGE | TEXOP_MODIFY_PARMS | blendop); state[used++] = (_3DSTATE_MAP_BLEND_OP_CMD(blendUnit) | TEXPIPE_ALPHA | ENABLE_TEXOUTPUT_WRT_SEL | TEXOP_OUTPUT_CURRENT | TEXOP_MODIFY_PARMS | ablendop); for (i = 0; i < numColorArgs; i++) { state[used++] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) | tex_blend_rgb[i] | args_RGB[i]); } for (i = 0; i < numAlphaArgs; i++) { state[used++] = (_3DSTATE_MAP_BLEND_ARG_CMD(blendUnit) | tex_blend_a[i] | args_A[i]); } if (need_factor) return emit_factor(blendUnit, state, used, factor); else return used; } static void emit_texblend(struct i830_context *i830, GLuint unit, GLuint blendUnit, GLboolean last_stage) { struct gl_texture_unit *texUnit = &i830->intel.ctx.Texture.Unit[unit]; GLuint tmp[I830_TEXBLEND_SIZE], tmp_sz; if (0) fprintf(stderr, "%s unit %d\n", __FUNCTION__, unit); /* Update i830->state.TexBlend */ tmp_sz = i830SetTexEnvCombine(i830, texUnit->_CurrentCombine, blendUnit, GetTexelOp(unit), tmp, texUnit->EnvColor); if (last_stage) tmp[0] |= TEXOP_LAST_STAGE; if (tmp_sz != i830->state.TexBlendWordsUsed[blendUnit] || memcmp(tmp, i830->state.TexBlend[blendUnit], tmp_sz * sizeof(GLuint))) { I830_STATECHANGE(i830, I830_UPLOAD_TEXBLEND(blendUnit)); memcpy(i830->state.TexBlend[blendUnit], tmp, tmp_sz * sizeof(GLuint)); i830->state.TexBlendWordsUsed[blendUnit] = tmp_sz; } I830_ACTIVESTATE(i830, I830_UPLOAD_TEXBLEND(blendUnit), GL_TRUE); } static void emit_passthrough(struct i830_context *i830) { GLuint tmp[I830_TEXBLEND_SIZE], tmp_sz; GLuint unit = 0; tmp_sz = pass_through(tmp, unit); tmp[0] |= TEXOP_LAST_STAGE; if (tmp_sz != i830->state.TexBlendWordsUsed[unit] || memcmp(tmp, i830->state.TexBlend[unit], tmp_sz * sizeof(GLuint))) { I830_STATECHANGE(i830, I830_UPLOAD_TEXBLEND(unit)); memcpy(i830->state.TexBlend[unit], tmp, tmp_sz * sizeof(GLuint)); i830->state.TexBlendWordsUsed[unit] = tmp_sz; } I830_ACTIVESTATE(i830, I830_UPLOAD_TEXBLEND(unit), GL_TRUE); } void i830EmitTextureBlend(struct i830_context *i830) { struct gl_context *ctx = &i830->intel.ctx; GLuint unit, last_stage = 0, blendunit = 0; I830_ACTIVESTATE(i830, I830_UPLOAD_TEXBLEND_ALL, GL_FALSE); if (ctx->Texture._EnabledUnits) { for (unit = 0; unit < ctx->Const.MaxTextureUnits; unit++) if (ctx->Texture.Unit[unit]._ReallyEnabled) last_stage = unit; for (unit = 0; unit < ctx->Const.MaxTextureUnits; unit++) if (ctx->Texture.Unit[unit]._ReallyEnabled) emit_texblend(i830, unit, blendunit++, last_stage == unit); } else { emit_passthrough(i830); } }