| /* $XFree86: xc/lib/GL/mesa/src/drv/radeon/radeon_texstate.c,v 1.6 2002/12/16 16:18:59 dawes Exp $ */ |
| /************************************************************************** |
| |
| Copyright 2000, 2001 ATI Technologies Inc., Ontario, Canada, and |
| VA Linux Systems Inc., Fremont, California. |
| |
| 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, sublicense, 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 NONINFRINGEMENT. |
| IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. |
| |
| **************************************************************************/ |
| |
| /* |
| * Authors: |
| * Kevin E. Martin <martin@valinux.com> |
| * Gareth Hughes <gareth@valinux.com> |
| */ |
| |
| #include "glheader.h" |
| #include "imports.h" |
| #include "colormac.h" |
| #include "context.h" |
| #include "macros.h" |
| #include "texformat.h" |
| #include "texobj.h" |
| #include "enums.h" |
| |
| #include "radeon_context.h" |
| #include "radeon_state.h" |
| #include "radeon_ioctl.h" |
| #include "radeon_swtcl.h" |
| #include "radeon_tex.h" |
| #include "radeon_tcl.h" |
| |
| |
| #define RADEON_TXFORMAT_A8 RADEON_TXFORMAT_I8 |
| #define RADEON_TXFORMAT_L8 RADEON_TXFORMAT_I8 |
| #define RADEON_TXFORMAT_AL88 RADEON_TXFORMAT_AI88 |
| #define RADEON_TXFORMAT_YCBCR RADEON_TXFORMAT_YVYU422 |
| #define RADEON_TXFORMAT_YCBCR_REV RADEON_TXFORMAT_VYUY422 |
| #define RADEON_TXFORMAT_RGB_DXT1 RADEON_TXFORMAT_DXT1 |
| #define RADEON_TXFORMAT_RGBA_DXT1 RADEON_TXFORMAT_DXT1 |
| #define RADEON_TXFORMAT_RGBA_DXT3 RADEON_TXFORMAT_DXT23 |
| #define RADEON_TXFORMAT_RGBA_DXT5 RADEON_TXFORMAT_DXT45 |
| |
| #define _COLOR(f) \ |
| [ MESA_FORMAT_ ## f ] = { RADEON_TXFORMAT_ ## f, 0 } |
| #define _COLOR_REV(f) \ |
| [ MESA_FORMAT_ ## f ## _REV ] = { RADEON_TXFORMAT_ ## f, 0 } |
| #define _ALPHA(f) \ |
| [ MESA_FORMAT_ ## f ] = { RADEON_TXFORMAT_ ## f | RADEON_TXFORMAT_ALPHA_IN_MAP, 0 } |
| #define _ALPHA_REV(f) \ |
| [ MESA_FORMAT_ ## f ## _REV ] = { RADEON_TXFORMAT_ ## f | RADEON_TXFORMAT_ALPHA_IN_MAP, 0 } |
| #define _YUV(f) \ |
| [ MESA_FORMAT_ ## f ] = { RADEON_TXFORMAT_ ## f, RADEON_YUV_TO_RGB } |
| #define _INVALID(f) \ |
| [ MESA_FORMAT_ ## f ] = { 0xffffffff, 0 } |
| #define VALID_FORMAT(f) ( ((f) <= MESA_FORMAT_RGBA_DXT5) \ |
| && (tx_table[f].format != 0xffffffff) ) |
| |
| static const struct { |
| GLuint format, filter; |
| } |
| tx_table[] = |
| { |
| _ALPHA(RGBA8888), |
| _ALPHA_REV(RGBA8888), |
| _ALPHA(ARGB8888), |
| _ALPHA_REV(ARGB8888), |
| [ MESA_FORMAT_RGB888 ] = { RADEON_TXFORMAT_ARGB8888, 0 }, |
| _COLOR(RGB565), |
| _COLOR_REV(RGB565), |
| _ALPHA(ARGB4444), |
| _ALPHA_REV(ARGB4444), |
| _ALPHA(ARGB1555), |
| _ALPHA_REV(ARGB1555), |
| _ALPHA(AL88), |
| _ALPHA_REV(AL88), |
| _ALPHA(A8), |
| _COLOR(L8), |
| _ALPHA(I8), |
| _INVALID(CI8), |
| _YUV(YCBCR), |
| _YUV(YCBCR_REV), |
| _INVALID(RGB_FXT1), |
| _INVALID(RGBA_FXT1), |
| _COLOR(RGB_DXT1), |
| _ALPHA(RGBA_DXT1), |
| _ALPHA(RGBA_DXT3), |
| _ALPHA(RGBA_DXT5), |
| }; |
| |
| #undef _COLOR |
| #undef _ALPHA |
| #undef _INVALID |
| |
| /** |
| * This function computes the number of bytes of storage needed for |
| * the given texture object (all mipmap levels, all cube faces). |
| * The \c image[face][level].x/y/width/height parameters for upload/blitting |
| * are computed here. \c pp_txfilter, \c pp_txformat, etc. will be set here |
| * too. |
| * |
| * \param rmesa Context pointer |
| * \param tObj GL texture object whose images are to be posted to |
| * hardware state. |
| */ |
| static void radeonSetTexImages( radeonContextPtr rmesa, |
| struct gl_texture_object *tObj ) |
| { |
| radeonTexObjPtr t = (radeonTexObjPtr)tObj->DriverData; |
| const struct gl_texture_image *baseImage = tObj->Image[0][tObj->BaseLevel]; |
| GLint curOffset, blitWidth; |
| GLint i, texelBytes; |
| GLint numLevels; |
| GLint log2Width, log2Height, log2Depth; |
| |
| /* Set the hardware texture format |
| */ |
| if ( !t->image_override ) { |
| t->pp_txformat &= ~(RADEON_TXFORMAT_FORMAT_MASK | |
| RADEON_TXFORMAT_ALPHA_IN_MAP); |
| t->pp_txfilter &= ~RADEON_YUV_TO_RGB; |
| |
| if ( VALID_FORMAT( baseImage->TexFormat->MesaFormat ) ) { |
| t->pp_txformat |= tx_table[ baseImage->TexFormat->MesaFormat ].format; |
| t->pp_txfilter |= tx_table[ baseImage->TexFormat->MesaFormat ].filter; |
| } |
| else { |
| _mesa_problem(NULL, "unexpected texture format in %s", __FUNCTION__); |
| return; |
| } |
| } |
| |
| texelBytes = baseImage->TexFormat->TexelBytes; |
| |
| /* Compute which mipmap levels we really want to send to the hardware. |
| */ |
| |
| if (tObj->Target != GL_TEXTURE_CUBE_MAP) |
| driCalculateTextureFirstLastLevel( (driTextureObject *) t ); |
| else { |
| /* r100 can't handle mipmaps for cube/3d textures, so don't waste |
| memory for them */ |
| t->base.firstLevel = t->base.lastLevel = tObj->BaseLevel; |
| } |
| log2Width = tObj->Image[0][t->base.firstLevel]->WidthLog2; |
| log2Height = tObj->Image[0][t->base.firstLevel]->HeightLog2; |
| log2Depth = tObj->Image[0][t->base.firstLevel]->DepthLog2; |
| |
| numLevels = t->base.lastLevel - t->base.firstLevel + 1; |
| |
| assert(numLevels <= RADEON_MAX_TEXTURE_LEVELS); |
| |
| /* Calculate mipmap offsets and dimensions for blitting (uploading) |
| * The idea is that we lay out the mipmap levels within a block of |
| * memory organized as a rectangle of width BLIT_WIDTH_BYTES. |
| */ |
| curOffset = 0; |
| blitWidth = BLIT_WIDTH_BYTES; |
| t->tile_bits = 0; |
| |
| /* figure out if this texture is suitable for tiling. */ |
| if (texelBytes && (tObj->Target != GL_TEXTURE_RECTANGLE_NV)) { |
| if (rmesa->texmicrotile && (baseImage->Height > 1)) { |
| /* allow 32 (bytes) x 1 mip (which will use two times the space |
| the non-tiled version would use) max if base texture is large enough */ |
| if ((numLevels == 1) || |
| (((baseImage->Width * texelBytes / baseImage->Height) <= 32) && |
| (baseImage->Width * texelBytes > 64)) || |
| ((baseImage->Width * texelBytes / baseImage->Height) <= 16)) { |
| /* R100 has two microtile bits (only the txoffset reg, not the blitter) |
| weird: X2 + OPT: 32bit correct, 16bit completely hosed |
| X2: 32bit correct, 16bit correct |
| OPT: 32bit large mips correct, small mips hosed, 16bit completely hosed */ |
| t->tile_bits |= RADEON_TXO_MICRO_TILE_X2 /*| RADEON_TXO_MICRO_TILE_OPT*/; |
| } |
| } |
| if ((baseImage->Width * texelBytes >= 256) && (baseImage->Height >= 16)) { |
| /* R100 disables macro tiling only if mip width is smaller than 256 bytes, and not |
| in the case if height is smaller than 16 (not 100% sure), as does the r200, |
| so need to disable macro tiling in that case */ |
| if ((numLevels == 1) || ((baseImage->Width * texelBytes / baseImage->Height) <= 4)) { |
| t->tile_bits |= RADEON_TXO_MACRO_TILE; |
| } |
| } |
| } |
| |
| for (i = 0; i < numLevels; i++) { |
| const struct gl_texture_image *texImage; |
| GLuint size; |
| |
| texImage = tObj->Image[0][i + t->base.firstLevel]; |
| if ( !texImage ) |
| break; |
| |
| /* find image size in bytes */ |
| if (texImage->IsCompressed) { |
| /* need to calculate the size AFTER padding even though the texture is |
| submitted without padding. |
| Only handle pot textures currently - don't know if npot is even possible, |
| size calculation would certainly need (trivial) adjustments. |
| Align (and later pad) to 32byte, not sure what that 64byte blit width is |
| good for? */ |
| if ((t->pp_txformat & RADEON_TXFORMAT_FORMAT_MASK) == RADEON_TXFORMAT_DXT1) { |
| /* RGB_DXT1/RGBA_DXT1, 8 bytes per block */ |
| if ((texImage->Width + 3) < 8) /* width one block */ |
| size = texImage->CompressedSize * 4; |
| else if ((texImage->Width + 3) < 16) |
| size = texImage->CompressedSize * 2; |
| else size = texImage->CompressedSize; |
| } |
| else /* DXT3/5, 16 bytes per block */ |
| if ((texImage->Width + 3) < 8) |
| size = texImage->CompressedSize * 2; |
| else size = texImage->CompressedSize; |
| } |
| else if (tObj->Target == GL_TEXTURE_RECTANGLE_NV) { |
| size = ((texImage->Width * texelBytes + 63) & ~63) * texImage->Height; |
| } |
| else if (t->tile_bits & RADEON_TXO_MICRO_TILE_X2) { |
| /* tile pattern is 16 bytes x2. mipmaps stay 32 byte aligned, |
| though the actual offset may be different (if texture is less than |
| 32 bytes width) to the untiled case */ |
| int w = (texImage->Width * texelBytes * 2 + 31) & ~31; |
| size = (w * ((texImage->Height + 1) / 2)) * texImage->Depth; |
| blitWidth = MAX2(texImage->Width, 64 / texelBytes); |
| } |
| else { |
| int w = (texImage->Width * texelBytes + 31) & ~31; |
| size = w * texImage->Height * texImage->Depth; |
| blitWidth = MAX2(texImage->Width, 64 / texelBytes); |
| } |
| assert(size > 0); |
| |
| /* Align to 32-byte offset. It is faster to do this unconditionally |
| * (no branch penalty). |
| */ |
| |
| curOffset = (curOffset + 0x1f) & ~0x1f; |
| |
| if (texelBytes) { |
| t->image[0][i].x = curOffset; /* fix x and y coords up later together with offset */ |
| t->image[0][i].y = 0; |
| t->image[0][i].width = MIN2(size / texelBytes, blitWidth); |
| t->image[0][i].height = (size / texelBytes) / t->image[0][i].width; |
| } |
| else { |
| t->image[0][i].x = curOffset % BLIT_WIDTH_BYTES; |
| t->image[0][i].y = curOffset / BLIT_WIDTH_BYTES; |
| t->image[0][i].width = MIN2(size, BLIT_WIDTH_BYTES); |
| t->image[0][i].height = size / t->image[0][i].width; |
| } |
| |
| #if 0 |
| /* for debugging only and only applicable to non-rectangle targets */ |
| assert(size % t->image[0][i].width == 0); |
| assert(t->image[0][i].x == 0 |
| || (size < BLIT_WIDTH_BYTES && t->image[0][i].height == 1)); |
| #endif |
| |
| if (0) |
| fprintf(stderr, |
| "level %d: %dx%d x=%d y=%d w=%d h=%d size=%d at %d\n", |
| i, texImage->Width, texImage->Height, |
| t->image[0][i].x, t->image[0][i].y, |
| t->image[0][i].width, t->image[0][i].height, size, curOffset); |
| |
| curOffset += size; |
| |
| } |
| |
| /* Align the total size of texture memory block. |
| */ |
| t->base.totalSize = (curOffset + RADEON_OFFSET_MASK) & ~RADEON_OFFSET_MASK; |
| |
| /* Setup remaining cube face blits, if needed */ |
| if (tObj->Target == GL_TEXTURE_CUBE_MAP) { |
| const GLuint faceSize = t->base.totalSize; |
| GLuint face; |
| /* reuse face 0 x/y/width/height - just update the offset when uploading */ |
| for (face = 1; face < 6; face++) { |
| for (i = 0; i < numLevels; i++) { |
| t->image[face][i].x = t->image[0][i].x; |
| t->image[face][i].y = t->image[0][i].y; |
| t->image[face][i].width = t->image[0][i].width; |
| t->image[face][i].height = t->image[0][i].height; |
| } |
| } |
| t->base.totalSize = 6 * faceSize; /* total texmem needed */ |
| } |
| |
| /* Hardware state: |
| */ |
| t->pp_txfilter &= ~RADEON_MAX_MIP_LEVEL_MASK; |
| t->pp_txfilter |= (numLevels - 1) << RADEON_MAX_MIP_LEVEL_SHIFT; |
| |
| t->pp_txformat &= ~(RADEON_TXFORMAT_WIDTH_MASK | |
| RADEON_TXFORMAT_HEIGHT_MASK | |
| RADEON_TXFORMAT_CUBIC_MAP_ENABLE | |
| RADEON_TXFORMAT_F5_WIDTH_MASK | |
| RADEON_TXFORMAT_F5_HEIGHT_MASK); |
| t->pp_txformat |= ((log2Width << RADEON_TXFORMAT_WIDTH_SHIFT) | |
| (log2Height << RADEON_TXFORMAT_HEIGHT_SHIFT)); |
| |
| if (tObj->Target == GL_TEXTURE_CUBE_MAP) { |
| assert(log2Width == log2Height); |
| t->pp_txformat |= ((log2Width << RADEON_TXFORMAT_F5_WIDTH_SHIFT) | |
| (log2Height << RADEON_TXFORMAT_F5_HEIGHT_SHIFT) | |
| (RADEON_TXFORMAT_CUBIC_MAP_ENABLE)); |
| t->pp_cubic_faces = ((log2Width << RADEON_FACE_WIDTH_1_SHIFT) | |
| (log2Height << RADEON_FACE_HEIGHT_1_SHIFT) | |
| (log2Width << RADEON_FACE_WIDTH_2_SHIFT) | |
| (log2Height << RADEON_FACE_HEIGHT_2_SHIFT) | |
| (log2Width << RADEON_FACE_WIDTH_3_SHIFT) | |
| (log2Height << RADEON_FACE_HEIGHT_3_SHIFT) | |
| (log2Width << RADEON_FACE_WIDTH_4_SHIFT) | |
| (log2Height << RADEON_FACE_HEIGHT_4_SHIFT)); |
| } |
| |
| t->pp_txsize = (((tObj->Image[0][t->base.firstLevel]->Width - 1) << 0) | |
| ((tObj->Image[0][t->base.firstLevel]->Height - 1) << 16)); |
| |
| /* Only need to round to nearest 32 for textures, but the blitter |
| * requires 64-byte aligned pitches, and we may/may not need the |
| * blitter. NPOT only! |
| */ |
| if ( !t->image_override ) { |
| if (baseImage->IsCompressed) |
| t->pp_txpitch = (tObj->Image[0][t->base.firstLevel]->Width + 63) & ~(63); |
| else |
| t->pp_txpitch = ((tObj->Image[0][t->base.firstLevel]->Width * texelBytes) + 63) & ~(63); |
| t->pp_txpitch -= 32; |
| } |
| |
| t->dirty_state = TEX_ALL; |
| |
| /* FYI: radeonUploadTexImages( rmesa, t ); used to be called here */ |
| } |
| |
| |
| |
| /* ================================================================ |
| * Texture combine functions |
| */ |
| |
| /* GL_ARB_texture_env_combine support |
| */ |
| |
| /* The color tables have combine functions for GL_SRC_COLOR, |
| * GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA. |
| */ |
| static GLuint radeon_texture_color[][RADEON_MAX_TEXTURE_UNITS] = |
| { |
| { |
| RADEON_COLOR_ARG_A_T0_COLOR, |
| RADEON_COLOR_ARG_A_T1_COLOR, |
| RADEON_COLOR_ARG_A_T2_COLOR |
| }, |
| { |
| RADEON_COLOR_ARG_A_T0_COLOR | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_T1_COLOR | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_T2_COLOR | RADEON_COMP_ARG_A |
| }, |
| { |
| RADEON_COLOR_ARG_A_T0_ALPHA, |
| RADEON_COLOR_ARG_A_T1_ALPHA, |
| RADEON_COLOR_ARG_A_T2_ALPHA |
| }, |
| { |
| RADEON_COLOR_ARG_A_T0_ALPHA | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_T1_ALPHA | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_T2_ALPHA | RADEON_COMP_ARG_A |
| }, |
| }; |
| |
| static GLuint radeon_tfactor_color[] = |
| { |
| RADEON_COLOR_ARG_A_TFACTOR_COLOR, |
| RADEON_COLOR_ARG_A_TFACTOR_COLOR | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_TFACTOR_ALPHA, |
| RADEON_COLOR_ARG_A_TFACTOR_ALPHA | RADEON_COMP_ARG_A |
| }; |
| |
| static GLuint radeon_primary_color[] = |
| { |
| RADEON_COLOR_ARG_A_DIFFUSE_COLOR, |
| RADEON_COLOR_ARG_A_DIFFUSE_COLOR | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_DIFFUSE_ALPHA, |
| RADEON_COLOR_ARG_A_DIFFUSE_ALPHA | RADEON_COMP_ARG_A |
| }; |
| |
| static GLuint radeon_previous_color[] = |
| { |
| RADEON_COLOR_ARG_A_CURRENT_COLOR, |
| RADEON_COLOR_ARG_A_CURRENT_COLOR | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_CURRENT_ALPHA, |
| RADEON_COLOR_ARG_A_CURRENT_ALPHA | RADEON_COMP_ARG_A |
| }; |
| |
| /* GL_ZERO table - indices 0-3 |
| * GL_ONE table - indices 1-4 |
| */ |
| static GLuint radeon_zero_color[] = |
| { |
| RADEON_COLOR_ARG_A_ZERO, |
| RADEON_COLOR_ARG_A_ZERO | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_ZERO, |
| RADEON_COLOR_ARG_A_ZERO | RADEON_COMP_ARG_A, |
| RADEON_COLOR_ARG_A_ZERO |
| }; |
| |
| |
| /* The alpha tables only have GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA. |
| */ |
| static GLuint radeon_texture_alpha[][RADEON_MAX_TEXTURE_UNITS] = |
| { |
| { |
| RADEON_ALPHA_ARG_A_T0_ALPHA, |
| RADEON_ALPHA_ARG_A_T1_ALPHA, |
| RADEON_ALPHA_ARG_A_T2_ALPHA |
| }, |
| { |
| RADEON_ALPHA_ARG_A_T0_ALPHA | RADEON_COMP_ARG_A, |
| RADEON_ALPHA_ARG_A_T1_ALPHA | RADEON_COMP_ARG_A, |
| RADEON_ALPHA_ARG_A_T2_ALPHA | RADEON_COMP_ARG_A |
| }, |
| }; |
| |
| static GLuint radeon_tfactor_alpha[] = |
| { |
| RADEON_ALPHA_ARG_A_TFACTOR_ALPHA, |
| RADEON_ALPHA_ARG_A_TFACTOR_ALPHA | RADEON_COMP_ARG_A |
| }; |
| |
| static GLuint radeon_primary_alpha[] = |
| { |
| RADEON_ALPHA_ARG_A_DIFFUSE_ALPHA, |
| RADEON_ALPHA_ARG_A_DIFFUSE_ALPHA | RADEON_COMP_ARG_A |
| }; |
| |
| static GLuint radeon_previous_alpha[] = |
| { |
| RADEON_ALPHA_ARG_A_CURRENT_ALPHA, |
| RADEON_ALPHA_ARG_A_CURRENT_ALPHA | RADEON_COMP_ARG_A |
| }; |
| |
| /* GL_ZERO table - indices 0-1 |
| * GL_ONE table - indices 1-2 |
| */ |
| static GLuint radeon_zero_alpha[] = |
| { |
| RADEON_ALPHA_ARG_A_ZERO, |
| RADEON_ALPHA_ARG_A_ZERO | RADEON_COMP_ARG_A, |
| RADEON_ALPHA_ARG_A_ZERO |
| }; |
| |
| |
| /* Extract the arg from slot A, shift it into the correct argument slot |
| * and set the corresponding complement bit. |
| */ |
| #define RADEON_COLOR_ARG( n, arg ) \ |
| do { \ |
| color_combine |= \ |
| ((color_arg[n] & RADEON_COLOR_ARG_MASK) \ |
| << RADEON_COLOR_ARG_##arg##_SHIFT); \ |
| color_combine |= \ |
| ((color_arg[n] >> RADEON_COMP_ARG_SHIFT) \ |
| << RADEON_COMP_ARG_##arg##_SHIFT); \ |
| } while (0) |
| |
| #define RADEON_ALPHA_ARG( n, arg ) \ |
| do { \ |
| alpha_combine |= \ |
| ((alpha_arg[n] & RADEON_ALPHA_ARG_MASK) \ |
| << RADEON_ALPHA_ARG_##arg##_SHIFT); \ |
| alpha_combine |= \ |
| ((alpha_arg[n] >> RADEON_COMP_ARG_SHIFT) \ |
| << RADEON_COMP_ARG_##arg##_SHIFT); \ |
| } while (0) |
| |
| |
| /* ================================================================ |
| * Texture unit state management |
| */ |
| |
| static GLboolean radeonUpdateTextureEnv( GLcontext *ctx, int unit ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; |
| GLuint color_combine, alpha_combine; |
| const GLuint color_combine0 = RADEON_COLOR_ARG_A_ZERO | RADEON_COLOR_ARG_B_ZERO |
| | RADEON_COLOR_ARG_C_CURRENT_COLOR | RADEON_BLEND_CTL_ADD |
| | RADEON_SCALE_1X | RADEON_CLAMP_TX; |
| const GLuint alpha_combine0 = RADEON_ALPHA_ARG_A_ZERO | RADEON_ALPHA_ARG_B_ZERO |
| | RADEON_ALPHA_ARG_C_CURRENT_ALPHA | RADEON_BLEND_CTL_ADD |
| | RADEON_SCALE_1X | RADEON_CLAMP_TX; |
| |
| |
| /* texUnit->_Current can be NULL if and only if the texture unit is |
| * not actually enabled. |
| */ |
| assert( (texUnit->_ReallyEnabled == 0) |
| || (texUnit->_Current != NULL) ); |
| |
| if ( RADEON_DEBUG & DEBUG_TEXTURE ) { |
| fprintf( stderr, "%s( %p, %d )\n", __FUNCTION__, (void *)ctx, unit ); |
| } |
| |
| /* Set the texture environment state. Isn't this nice and clean? |
| * The chip will automagically set the texture alpha to 0xff when |
| * the texture format does not include an alpha component. This |
| * reduces the amount of special-casing we have to do, alpha-only |
| * textures being a notable exception. Doesn't work for luminance |
| * textures realized with I8 and ALPHA_IN_MAP not set neither (on r100). |
| */ |
| /* Don't cache these results. |
| */ |
| rmesa->state.texture.unit[unit].format = 0; |
| rmesa->state.texture.unit[unit].envMode = 0; |
| |
| if ( !texUnit->_ReallyEnabled ) { |
| color_combine = color_combine0; |
| alpha_combine = alpha_combine0; |
| } |
| else { |
| GLuint color_arg[3], alpha_arg[3]; |
| GLuint i; |
| const GLuint numColorArgs = texUnit->_CurrentCombine->_NumArgsRGB; |
| const GLuint numAlphaArgs = texUnit->_CurrentCombine->_NumArgsA; |
| GLuint RGBshift = texUnit->_CurrentCombine->ScaleShiftRGB; |
| GLuint Ashift = texUnit->_CurrentCombine->ScaleShiftA; |
| |
| |
| /* Step 1: |
| * Extract the color and alpha combine function arguments. |
| */ |
| for ( i = 0 ; i < numColorArgs ; i++ ) { |
| const GLint op = texUnit->_CurrentCombine->OperandRGB[i] - GL_SRC_COLOR; |
| const GLuint srcRGBi = texUnit->_CurrentCombine->SourceRGB[i]; |
| assert(op >= 0); |
| assert(op <= 3); |
| switch ( srcRGBi ) { |
| case GL_TEXTURE: |
| if (texUnit->_Current->Image[0][0]->_BaseFormat == GL_ALPHA) |
| color_arg[i] = radeon_zero_color[op]; |
| else |
| color_arg[i] = radeon_texture_color[op][unit]; |
| break; |
| case GL_CONSTANT: |
| color_arg[i] = radeon_tfactor_color[op]; |
| break; |
| case GL_PRIMARY_COLOR: |
| color_arg[i] = radeon_primary_color[op]; |
| break; |
| case GL_PREVIOUS: |
| color_arg[i] = radeon_previous_color[op]; |
| break; |
| case GL_ZERO: |
| color_arg[i] = radeon_zero_color[op]; |
| break; |
| case GL_ONE: |
| color_arg[i] = radeon_zero_color[op+1]; |
| break; |
| case GL_TEXTURE0: |
| case GL_TEXTURE1: |
| case GL_TEXTURE2: { |
| GLuint txunit = srcRGBi - GL_TEXTURE0; |
| if (ctx->Texture.Unit[txunit]._Current->Image[0][0]->_BaseFormat == GL_ALPHA) |
| color_arg[i] = radeon_zero_color[op]; |
| else |
| /* implement ogl 1.4/1.5 core spec here, not specification of |
| * GL_ARB_texture_env_crossbar (which would require disabling blending |
| * instead of undefined results when referencing not enabled texunit) */ |
| color_arg[i] = radeon_texture_color[op][txunit]; |
| } |
| break; |
| default: |
| return GL_FALSE; |
| } |
| } |
| |
| for ( i = 0 ; i < numAlphaArgs ; i++ ) { |
| const GLint op = texUnit->_CurrentCombine->OperandA[i] - GL_SRC_ALPHA; |
| const GLuint srcAi = texUnit->_CurrentCombine->SourceA[i]; |
| assert(op >= 0); |
| assert(op <= 1); |
| switch ( srcAi ) { |
| case GL_TEXTURE: |
| if (texUnit->_Current->Image[0][0]->_BaseFormat == GL_LUMINANCE) |
| alpha_arg[i] = radeon_zero_alpha[op+1]; |
| else |
| alpha_arg[i] = radeon_texture_alpha[op][unit]; |
| break; |
| case GL_CONSTANT: |
| alpha_arg[i] = radeon_tfactor_alpha[op]; |
| break; |
| case GL_PRIMARY_COLOR: |
| alpha_arg[i] = radeon_primary_alpha[op]; |
| break; |
| case GL_PREVIOUS: |
| alpha_arg[i] = radeon_previous_alpha[op]; |
| break; |
| case GL_ZERO: |
| alpha_arg[i] = radeon_zero_alpha[op]; |
| break; |
| case GL_ONE: |
| alpha_arg[i] = radeon_zero_alpha[op+1]; |
| break; |
| case GL_TEXTURE0: |
| case GL_TEXTURE1: |
| case GL_TEXTURE2: { |
| GLuint txunit = srcAi - GL_TEXTURE0; |
| if (ctx->Texture.Unit[txunit]._Current->Image[0][0]->_BaseFormat == GL_LUMINANCE) |
| alpha_arg[i] = radeon_zero_alpha[op+1]; |
| else |
| alpha_arg[i] = radeon_texture_alpha[op][txunit]; |
| } |
| break; |
| default: |
| return GL_FALSE; |
| } |
| } |
| |
| /* Step 2: |
| * Build up the color and alpha combine functions. |
| */ |
| switch ( texUnit->_CurrentCombine->ModeRGB ) { |
| case GL_REPLACE: |
| color_combine = (RADEON_COLOR_ARG_A_ZERO | |
| RADEON_COLOR_ARG_B_ZERO | |
| RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, C ); |
| break; |
| case GL_MODULATE: |
| color_combine = (RADEON_COLOR_ARG_C_ZERO | |
| RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, B ); |
| break; |
| case GL_ADD: |
| color_combine = (RADEON_COLOR_ARG_B_ZERO | |
| RADEON_COMP_ARG_B | |
| RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, C ); |
| break; |
| case GL_ADD_SIGNED: |
| color_combine = (RADEON_COLOR_ARG_B_ZERO | |
| RADEON_COMP_ARG_B | |
| RADEON_BLEND_CTL_ADDSIGNED | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, C ); |
| break; |
| case GL_SUBTRACT: |
| color_combine = (RADEON_COLOR_ARG_B_ZERO | |
| RADEON_COMP_ARG_B | |
| RADEON_BLEND_CTL_SUBTRACT | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, C ); |
| break; |
| case GL_INTERPOLATE: |
| color_combine = (RADEON_BLEND_CTL_BLEND | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, B ); |
| RADEON_COLOR_ARG( 1, A ); |
| RADEON_COLOR_ARG( 2, C ); |
| break; |
| |
| case GL_DOT3_RGB_EXT: |
| case GL_DOT3_RGBA_EXT: |
| /* 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. |
| */ |
| RGBshift = 0; |
| /* FALLTHROUGH */ |
| |
| case GL_DOT3_RGB: |
| case GL_DOT3_RGBA: |
| /* The R100 / RV200 only support a 1X multiplier in hardware |
| * w/the ARB version. |
| */ |
| if ( RGBshift != (RADEON_SCALE_1X >> RADEON_SCALE_SHIFT) ) { |
| return GL_FALSE; |
| } |
| |
| RGBshift += 2; |
| if ( (texUnit->_CurrentCombine->ModeRGB == GL_DOT3_RGBA_EXT) |
| || (texUnit->_CurrentCombine->ModeRGB == GL_DOT3_RGBA) ) { |
| /* is it necessary to set this or will it be ignored anyway? */ |
| Ashift = RGBshift; |
| } |
| |
| color_combine = (RADEON_COLOR_ARG_C_ZERO | |
| RADEON_BLEND_CTL_DOT3 | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, B ); |
| break; |
| |
| case GL_MODULATE_ADD_ATI: |
| color_combine = (RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, C ); |
| RADEON_COLOR_ARG( 2, B ); |
| break; |
| case GL_MODULATE_SIGNED_ADD_ATI: |
| color_combine = (RADEON_BLEND_CTL_ADDSIGNED | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, C ); |
| RADEON_COLOR_ARG( 2, B ); |
| break; |
| case GL_MODULATE_SUBTRACT_ATI: |
| color_combine = (RADEON_BLEND_CTL_SUBTRACT | |
| RADEON_CLAMP_TX); |
| RADEON_COLOR_ARG( 0, A ); |
| RADEON_COLOR_ARG( 1, C ); |
| RADEON_COLOR_ARG( 2, B ); |
| break; |
| default: |
| return GL_FALSE; |
| } |
| |
| switch ( texUnit->_CurrentCombine->ModeA ) { |
| case GL_REPLACE: |
| alpha_combine = (RADEON_ALPHA_ARG_A_ZERO | |
| RADEON_ALPHA_ARG_B_ZERO | |
| RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, C ); |
| break; |
| case GL_MODULATE: |
| alpha_combine = (RADEON_ALPHA_ARG_C_ZERO | |
| RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, A ); |
| RADEON_ALPHA_ARG( 1, B ); |
| break; |
| case GL_ADD: |
| alpha_combine = (RADEON_ALPHA_ARG_B_ZERO | |
| RADEON_COMP_ARG_B | |
| RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, A ); |
| RADEON_ALPHA_ARG( 1, C ); |
| break; |
| case GL_ADD_SIGNED: |
| alpha_combine = (RADEON_ALPHA_ARG_B_ZERO | |
| RADEON_COMP_ARG_B | |
| RADEON_BLEND_CTL_ADDSIGNED | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, A ); |
| RADEON_ALPHA_ARG( 1, C ); |
| break; |
| case GL_SUBTRACT: |
| alpha_combine = (RADEON_COLOR_ARG_B_ZERO | |
| RADEON_COMP_ARG_B | |
| RADEON_BLEND_CTL_SUBTRACT | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, A ); |
| RADEON_ALPHA_ARG( 1, C ); |
| break; |
| case GL_INTERPOLATE: |
| alpha_combine = (RADEON_BLEND_CTL_BLEND | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, B ); |
| RADEON_ALPHA_ARG( 1, A ); |
| RADEON_ALPHA_ARG( 2, C ); |
| break; |
| |
| case GL_MODULATE_ADD_ATI: |
| alpha_combine = (RADEON_BLEND_CTL_ADD | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, A ); |
| RADEON_ALPHA_ARG( 1, C ); |
| RADEON_ALPHA_ARG( 2, B ); |
| break; |
| case GL_MODULATE_SIGNED_ADD_ATI: |
| alpha_combine = (RADEON_BLEND_CTL_ADDSIGNED | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, A ); |
| RADEON_ALPHA_ARG( 1, C ); |
| RADEON_ALPHA_ARG( 2, B ); |
| break; |
| case GL_MODULATE_SUBTRACT_ATI: |
| alpha_combine = (RADEON_BLEND_CTL_SUBTRACT | |
| RADEON_CLAMP_TX); |
| RADEON_ALPHA_ARG( 0, A ); |
| RADEON_ALPHA_ARG( 1, C ); |
| RADEON_ALPHA_ARG( 2, B ); |
| break; |
| default: |
| return GL_FALSE; |
| } |
| |
| if ( (texUnit->_CurrentCombine->ModeRGB == GL_DOT3_RGB_EXT) |
| || (texUnit->_CurrentCombine->ModeRGB == GL_DOT3_RGB) ) { |
| alpha_combine |= RADEON_DOT_ALPHA_DONT_REPLICATE; |
| } |
| |
| /* Step 3: |
| * Apply the scale factor. |
| */ |
| color_combine |= (RGBshift << RADEON_SCALE_SHIFT); |
| alpha_combine |= (Ashift << RADEON_SCALE_SHIFT); |
| |
| /* All done! |
| */ |
| } |
| |
| if ( rmesa->hw.tex[unit].cmd[TEX_PP_TXCBLEND] != color_combine || |
| rmesa->hw.tex[unit].cmd[TEX_PP_TXABLEND] != alpha_combine ) { |
| RADEON_STATECHANGE( rmesa, tex[unit] ); |
| rmesa->hw.tex[unit].cmd[TEX_PP_TXCBLEND] = color_combine; |
| rmesa->hw.tex[unit].cmd[TEX_PP_TXABLEND] = alpha_combine; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| void radeonSetTexOffset(__DRIcontext * pDRICtx, GLint texname, |
| unsigned long long offset, GLint depth, GLuint pitch) |
| { |
| radeonContextPtr rmesa = pDRICtx->driverPrivate; |
| struct gl_texture_object *tObj = |
| _mesa_lookup_texture(rmesa->glCtx, texname); |
| radeonTexObjPtr t; |
| |
| if (tObj == NULL) |
| return; |
| |
| t = (radeonTexObjPtr) tObj->DriverData; |
| |
| t->image_override = GL_TRUE; |
| |
| if (!offset) |
| return; |
| |
| t->pp_txoffset = offset; |
| t->pp_txpitch = pitch - 32; |
| |
| switch (depth) { |
| case 32: |
| t->pp_txformat = tx_table[MESA_FORMAT_ARGB8888].format; |
| t->pp_txfilter |= tx_table[MESA_FORMAT_ARGB8888].filter; |
| break; |
| case 24: |
| default: |
| t->pp_txformat = tx_table[MESA_FORMAT_RGB888].format; |
| t->pp_txfilter |= tx_table[MESA_FORMAT_RGB888].filter; |
| break; |
| case 16: |
| t->pp_txformat = tx_table[MESA_FORMAT_RGB565].format; |
| t->pp_txfilter |= tx_table[MESA_FORMAT_RGB565].filter; |
| break; |
| } |
| } |
| |
| #define TEXOBJ_TXFILTER_MASK (RADEON_MAX_MIP_LEVEL_MASK | \ |
| RADEON_MIN_FILTER_MASK | \ |
| RADEON_MAG_FILTER_MASK | \ |
| RADEON_MAX_ANISO_MASK | \ |
| RADEON_YUV_TO_RGB | \ |
| RADEON_YUV_TEMPERATURE_MASK | \ |
| RADEON_CLAMP_S_MASK | \ |
| RADEON_CLAMP_T_MASK | \ |
| RADEON_BORDER_MODE_D3D ) |
| |
| #define TEXOBJ_TXFORMAT_MASK (RADEON_TXFORMAT_WIDTH_MASK | \ |
| RADEON_TXFORMAT_HEIGHT_MASK | \ |
| RADEON_TXFORMAT_FORMAT_MASK | \ |
| RADEON_TXFORMAT_F5_WIDTH_MASK | \ |
| RADEON_TXFORMAT_F5_HEIGHT_MASK | \ |
| RADEON_TXFORMAT_ALPHA_IN_MAP | \ |
| RADEON_TXFORMAT_CUBIC_MAP_ENABLE | \ |
| RADEON_TXFORMAT_NON_POWER2) |
| |
| |
| static void import_tex_obj_state( radeonContextPtr rmesa, |
| int unit, |
| radeonTexObjPtr texobj ) |
| { |
| /* do not use RADEON_DB_STATE to avoid stale texture caches */ |
| int *cmd = &rmesa->hw.tex[unit].cmd[TEX_CMD_0]; |
| GLuint se_coord_fmt = rmesa->hw.set.cmd[SET_SE_COORDFMT]; |
| |
| RADEON_STATECHANGE( rmesa, tex[unit] ); |
| |
| cmd[TEX_PP_TXFILTER] &= ~TEXOBJ_TXFILTER_MASK; |
| cmd[TEX_PP_TXFILTER] |= texobj->pp_txfilter & TEXOBJ_TXFILTER_MASK; |
| cmd[TEX_PP_TXFORMAT] &= ~TEXOBJ_TXFORMAT_MASK; |
| cmd[TEX_PP_TXFORMAT] |= texobj->pp_txformat & TEXOBJ_TXFORMAT_MASK; |
| cmd[TEX_PP_TXOFFSET] = texobj->pp_txoffset; |
| cmd[TEX_PP_BORDER_COLOR] = texobj->pp_border_color; |
| |
| if (texobj->base.tObj->Target == GL_TEXTURE_RECTANGLE_NV) { |
| GLuint *txr_cmd = RADEON_DB_STATE( txr[unit] ); |
| txr_cmd[TXR_PP_TEX_SIZE] = texobj->pp_txsize; /* NPOT only! */ |
| txr_cmd[TXR_PP_TEX_PITCH] = texobj->pp_txpitch; /* NPOT only! */ |
| RADEON_DB_STATECHANGE( rmesa, &rmesa->hw.txr[unit] ); |
| se_coord_fmt |= RADEON_VTX_ST0_NONPARAMETRIC << unit; |
| } |
| else { |
| se_coord_fmt &= ~(RADEON_VTX_ST0_NONPARAMETRIC << unit); |
| |
| if (texobj->base.tObj->Target == GL_TEXTURE_CUBE_MAP) { |
| int *cube_cmd = &rmesa->hw.cube[unit].cmd[CUBE_CMD_0]; |
| GLuint bytesPerFace = texobj->base.totalSize / 6; |
| ASSERT(texobj->base.totalSize % 6 == 0); |
| |
| RADEON_STATECHANGE( rmesa, cube[unit] ); |
| cube_cmd[CUBE_PP_CUBIC_FACES] = texobj->pp_cubic_faces; |
| /* dont know if this setup conforms to OpenGL.. |
| * at least it matches the behavior of mesa software renderer |
| */ |
| cube_cmd[CUBE_PP_CUBIC_OFFSET_0] = texobj->pp_txoffset; /* right */ |
| cube_cmd[CUBE_PP_CUBIC_OFFSET_1] = texobj->pp_txoffset + 1 * bytesPerFace; /* left */ |
| cube_cmd[CUBE_PP_CUBIC_OFFSET_2] = texobj->pp_txoffset + 2 * bytesPerFace; /* top */ |
| cube_cmd[CUBE_PP_CUBIC_OFFSET_3] = texobj->pp_txoffset + 3 * bytesPerFace; /* bottom */ |
| cube_cmd[CUBE_PP_CUBIC_OFFSET_4] = texobj->pp_txoffset + 4 * bytesPerFace; /* front */ |
| cmd[TEX_PP_TXOFFSET] = texobj->pp_txoffset + 5 * bytesPerFace; /* back */ |
| } |
| } |
| |
| if (se_coord_fmt != rmesa->hw.set.cmd[SET_SE_COORDFMT]) { |
| RADEON_STATECHANGE( rmesa, set ); |
| rmesa->hw.set.cmd[SET_SE_COORDFMT] = se_coord_fmt; |
| } |
| |
| texobj->dirty_state &= ~(1<<unit); |
| } |
| |
| |
| |
| |
| static void set_texgen_matrix( radeonContextPtr rmesa, |
| GLuint unit, |
| const GLfloat *s_plane, |
| const GLfloat *t_plane, |
| const GLfloat *r_plane, |
| const GLfloat *q_plane ) |
| { |
| rmesa->TexGenMatrix[unit].m[0] = s_plane[0]; |
| rmesa->TexGenMatrix[unit].m[4] = s_plane[1]; |
| rmesa->TexGenMatrix[unit].m[8] = s_plane[2]; |
| rmesa->TexGenMatrix[unit].m[12] = s_plane[3]; |
| |
| rmesa->TexGenMatrix[unit].m[1] = t_plane[0]; |
| rmesa->TexGenMatrix[unit].m[5] = t_plane[1]; |
| rmesa->TexGenMatrix[unit].m[9] = t_plane[2]; |
| rmesa->TexGenMatrix[unit].m[13] = t_plane[3]; |
| |
| rmesa->TexGenMatrix[unit].m[2] = r_plane[0]; |
| rmesa->TexGenMatrix[unit].m[6] = r_plane[1]; |
| rmesa->TexGenMatrix[unit].m[10] = r_plane[2]; |
| rmesa->TexGenMatrix[unit].m[14] = r_plane[3]; |
| |
| rmesa->TexGenMatrix[unit].m[3] = q_plane[0]; |
| rmesa->TexGenMatrix[unit].m[7] = q_plane[1]; |
| rmesa->TexGenMatrix[unit].m[11] = q_plane[2]; |
| rmesa->TexGenMatrix[unit].m[15] = q_plane[3]; |
| |
| rmesa->TexGenEnabled |= RADEON_TEXMAT_0_ENABLE << unit; |
| rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; |
| } |
| |
| /* Returns GL_FALSE if fallback required. |
| */ |
| static GLboolean radeon_validate_texgen( GLcontext *ctx, GLuint unit ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; |
| GLuint inputshift = RADEON_TEXGEN_0_INPUT_SHIFT + unit*4; |
| GLuint tmp = rmesa->TexGenEnabled; |
| static const GLfloat reflect[16] = { |
| -1, 0, 0, 0, |
| 0, -1, 0, 0, |
| 0, 0, -1, 0, |
| 0, 0, 0, 1 }; |
| |
| rmesa->TexGenEnabled &= ~(RADEON_TEXGEN_TEXMAT_0_ENABLE << unit); |
| rmesa->TexGenEnabled &= ~(RADEON_TEXMAT_0_ENABLE << unit); |
| rmesa->TexGenEnabled &= ~(RADEON_TEXGEN_INPUT_MASK << inputshift); |
| rmesa->TexGenNeedNormals[unit] = 0; |
| |
| if ((texUnit->TexGenEnabled & (S_BIT|T_BIT|R_BIT|Q_BIT)) == 0) { |
| /* Disabled, no fallback: |
| */ |
| rmesa->TexGenEnabled |= |
| (RADEON_TEXGEN_INPUT_TEXCOORD_0 + unit) << inputshift; |
| return GL_TRUE; |
| } |
| /* the r100 cannot do texgen for some coords and not for others |
| * we do not detect such cases (certainly can't do it here) and just |
| * ASSUME that when S and T are texgen enabled we do not need other |
| * non-texgen enabled coords, no matter if the R and Q bits are texgen |
| * enabled. Still check for mixed mode texgen for all coords. |
| */ |
| else if ( (texUnit->TexGenEnabled & S_BIT) && |
| (texUnit->TexGenEnabled & T_BIT) && |
| (texUnit->GenModeS == texUnit->GenModeT) ) { |
| if ( ((texUnit->TexGenEnabled & R_BIT) && |
| (texUnit->GenModeS != texUnit->GenModeR)) || |
| ((texUnit->TexGenEnabled & Q_BIT) && |
| (texUnit->GenModeS != texUnit->GenModeQ)) ) { |
| /* Mixed modes, fallback: |
| */ |
| if (RADEON_DEBUG & DEBUG_FALLBACKS) |
| fprintf(stderr, "fallback mixed texgen\n"); |
| return GL_FALSE; |
| } |
| rmesa->TexGenEnabled |= RADEON_TEXGEN_TEXMAT_0_ENABLE << unit; |
| } |
| else { |
| /* some texgen mode not including both S and T bits */ |
| if (RADEON_DEBUG & DEBUG_FALLBACKS) |
| fprintf(stderr, "fallback mixed texgen/nontexgen\n"); |
| return GL_FALSE; |
| } |
| |
| if ((texUnit->TexGenEnabled & (R_BIT | Q_BIT)) != 0) { |
| /* need this here for vtxfmt presumably. Argh we need to set |
| this from way too many places, would be much easier if we could leave |
| tcl q coord always enabled as on r200) */ |
| RADEON_STATECHANGE( rmesa, tcl ); |
| rmesa->hw.tcl.cmd[TCL_OUTPUT_VTXFMT] |= RADEON_Q_BIT(unit); |
| } |
| |
| switch (texUnit->GenModeS) { |
| case GL_OBJECT_LINEAR: |
| rmesa->TexGenEnabled |= RADEON_TEXGEN_INPUT_OBJ << inputshift; |
| set_texgen_matrix( rmesa, unit, |
| texUnit->ObjectPlaneS, |
| texUnit->ObjectPlaneT, |
| texUnit->ObjectPlaneR, |
| texUnit->ObjectPlaneQ); |
| break; |
| |
| case GL_EYE_LINEAR: |
| rmesa->TexGenEnabled |= RADEON_TEXGEN_INPUT_EYE << inputshift; |
| set_texgen_matrix( rmesa, unit, |
| texUnit->EyePlaneS, |
| texUnit->EyePlaneT, |
| texUnit->EyePlaneR, |
| texUnit->EyePlaneQ); |
| break; |
| |
| case GL_REFLECTION_MAP_NV: |
| rmesa->TexGenNeedNormals[unit] = GL_TRUE; |
| rmesa->TexGenEnabled |= RADEON_TEXGEN_INPUT_EYE_REFLECT << inputshift; |
| /* TODO: unknown if this is needed/correct */ |
| set_texgen_matrix( rmesa, unit, reflect, reflect + 4, |
| reflect + 8, reflect + 12 ); |
| break; |
| |
| case GL_NORMAL_MAP_NV: |
| rmesa->TexGenNeedNormals[unit] = GL_TRUE; |
| rmesa->TexGenEnabled |= RADEON_TEXGEN_INPUT_EYE_NORMAL << inputshift; |
| break; |
| |
| case GL_SPHERE_MAP: |
| /* the mode which everyone uses :-( */ |
| default: |
| /* Unsupported mode, fallback: |
| */ |
| if (RADEON_DEBUG & DEBUG_FALLBACKS) |
| fprintf(stderr, "fallback GL_SPHERE_MAP\n"); |
| return GL_FALSE; |
| } |
| |
| if (tmp != rmesa->TexGenEnabled) { |
| rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| |
| static void disable_tex( GLcontext *ctx, int unit ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| |
| if (rmesa->hw.ctx.cmd[CTX_PP_CNTL] & (RADEON_TEX_0_ENABLE<<unit)) { |
| /* Texture unit disabled */ |
| if ( rmesa->state.texture.unit[unit].texobj != NULL ) { |
| /* The old texture is no longer bound to this texture unit. |
| * Mark it as such. |
| */ |
| |
| rmesa->state.texture.unit[unit].texobj->base.bound &= ~(1UL << unit); |
| rmesa->state.texture.unit[unit].texobj = NULL; |
| } |
| |
| RADEON_STATECHANGE( rmesa, ctx ); |
| rmesa->hw.ctx.cmd[CTX_PP_CNTL] &= |
| ~((RADEON_TEX_0_ENABLE | RADEON_TEX_BLEND_0_ENABLE) << unit); |
| |
| RADEON_STATECHANGE( rmesa, tcl ); |
| rmesa->hw.tcl.cmd[TCL_OUTPUT_VTXFMT] &= ~(RADEON_ST_BIT(unit) | |
| RADEON_Q_BIT(unit)); |
| |
| if (rmesa->TclFallback & (RADEON_TCL_FALLBACK_TEXGEN_0<<unit)) { |
| TCL_FALLBACK( ctx, (RADEON_TCL_FALLBACK_TEXGEN_0<<unit), GL_FALSE); |
| rmesa->recheck_texgen[unit] = GL_TRUE; |
| } |
| |
| if (rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT] & RADEON_TXFORMAT_CUBIC_MAP_ENABLE) { |
| /* this seems to be a genuine (r100 only?) hw bug. Need to remove the |
| cubic_map bit on unit 2 when the unit is disabled, otherwise every |
| 2nd (2d) mipmap on unit 0 will be broken (may not be needed for other |
| units, better be safe than sorry though).*/ |
| RADEON_STATECHANGE( rmesa, tex[unit] ); |
| rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT] &= ~RADEON_TXFORMAT_CUBIC_MAP_ENABLE; |
| } |
| |
| { |
| GLuint inputshift = RADEON_TEXGEN_0_INPUT_SHIFT + unit*4; |
| GLuint tmp = rmesa->TexGenEnabled; |
| |
| rmesa->TexGenEnabled &= ~(RADEON_TEXGEN_TEXMAT_0_ENABLE<<unit); |
| rmesa->TexGenEnabled &= ~(RADEON_TEXMAT_0_ENABLE<<unit); |
| rmesa->TexGenEnabled &= ~(RADEON_TEXGEN_INPUT_MASK<<inputshift); |
| rmesa->TexGenNeedNormals[unit] = 0; |
| rmesa->TexGenEnabled |= |
| (RADEON_TEXGEN_INPUT_TEXCOORD_0+unit) << inputshift; |
| |
| if (tmp != rmesa->TexGenEnabled) { |
| rmesa->recheck_texgen[unit] = GL_TRUE; |
| rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; |
| } |
| } |
| } |
| } |
| |
| static GLboolean enable_tex_2d( GLcontext *ctx, int unit ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; |
| struct gl_texture_object *tObj = texUnit->_Current; |
| radeonTexObjPtr t = (radeonTexObjPtr) tObj->DriverData; |
| |
| /* Need to load the 2d images associated with this unit. |
| */ |
| if (t->pp_txformat & RADEON_TXFORMAT_NON_POWER2) { |
| t->pp_txformat &= ~RADEON_TXFORMAT_NON_POWER2; |
| t->base.dirty_images[0] = ~0; |
| } |
| |
| ASSERT(tObj->Target == GL_TEXTURE_2D || tObj->Target == GL_TEXTURE_1D); |
| |
| if ( t->base.dirty_images[0] ) { |
| RADEON_FIREVERTICES( rmesa ); |
| radeonSetTexImages( rmesa, tObj ); |
| radeonUploadTexImages( rmesa, (radeonTexObjPtr) tObj->DriverData, 0 ); |
| if ( !t->base.memBlock && !t->image_override ) |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| static GLboolean enable_tex_cube( GLcontext *ctx, int unit ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; |
| struct gl_texture_object *tObj = texUnit->_Current; |
| radeonTexObjPtr t = (radeonTexObjPtr) tObj->DriverData; |
| GLuint face; |
| |
| /* Need to load the 2d images associated with this unit. |
| */ |
| if (t->pp_txformat & RADEON_TXFORMAT_NON_POWER2) { |
| t->pp_txformat &= ~RADEON_TXFORMAT_NON_POWER2; |
| for (face = 0; face < 6; face++) |
| t->base.dirty_images[face] = ~0; |
| } |
| |
| ASSERT(tObj->Target == GL_TEXTURE_CUBE_MAP); |
| |
| if ( t->base.dirty_images[0] || t->base.dirty_images[1] || |
| t->base.dirty_images[2] || t->base.dirty_images[3] || |
| t->base.dirty_images[4] || t->base.dirty_images[5] ) { |
| /* flush */ |
| RADEON_FIREVERTICES( rmesa ); |
| /* layout memory space, once for all faces */ |
| radeonSetTexImages( rmesa, tObj ); |
| } |
| |
| /* upload (per face) */ |
| for (face = 0; face < 6; face++) { |
| if (t->base.dirty_images[face]) { |
| radeonUploadTexImages( rmesa, (radeonTexObjPtr) tObj->DriverData, face ); |
| } |
| } |
| |
| if ( !t->base.memBlock ) { |
| /* texmem alloc failed, use s/w fallback */ |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| static GLboolean enable_tex_rect( GLcontext *ctx, int unit ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; |
| struct gl_texture_object *tObj = texUnit->_Current; |
| radeonTexObjPtr t = (radeonTexObjPtr) tObj->DriverData; |
| |
| if (!(t->pp_txformat & RADEON_TXFORMAT_NON_POWER2)) { |
| t->pp_txformat |= RADEON_TXFORMAT_NON_POWER2; |
| t->base.dirty_images[0] = ~0; |
| } |
| |
| ASSERT(tObj->Target == GL_TEXTURE_RECTANGLE_NV); |
| |
| if ( t->base.dirty_images[0] ) { |
| RADEON_FIREVERTICES( rmesa ); |
| radeonSetTexImages( rmesa, tObj ); |
| radeonUploadTexImages( rmesa, (radeonTexObjPtr) tObj->DriverData, 0 ); |
| if ( !t->base.memBlock && |
| !t->image_override /* && !rmesa->prefer_gart_client_texturing FIXME */ ) { |
| fprintf(stderr, "%s: upload failed\n", __FUNCTION__); |
| return GL_FALSE; |
| } |
| } |
| |
| return GL_TRUE; |
| } |
| |
| |
| static GLboolean update_tex_common( GLcontext *ctx, int unit ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; |
| struct gl_texture_object *tObj = texUnit->_Current; |
| radeonTexObjPtr t = (radeonTexObjPtr) tObj->DriverData; |
| GLenum format; |
| |
| /* Fallback if there's a texture border */ |
| if ( tObj->Image[0][tObj->BaseLevel]->Border > 0 ) { |
| fprintf(stderr, "%s: border\n", __FUNCTION__); |
| return GL_FALSE; |
| } |
| /* yuv conversion only works in first unit */ |
| if (unit != 0 && (t->pp_txfilter & RADEON_YUV_TO_RGB)) |
| return GL_FALSE; |
| |
| /* Update state if this is a different texture object to last |
| * time. |
| */ |
| if ( rmesa->state.texture.unit[unit].texobj != t ) { |
| if ( rmesa->state.texture.unit[unit].texobj != NULL ) { |
| /* The old texture is no longer bound to this texture unit. |
| * Mark it as such. |
| */ |
| |
| rmesa->state.texture.unit[unit].texobj->base.bound &= |
| ~(1UL << unit); |
| } |
| |
| rmesa->state.texture.unit[unit].texobj = t; |
| t->base.bound |= (1UL << unit); |
| t->dirty_state |= 1<<unit; |
| driUpdateTextureLRU( (driTextureObject *) t ); /* XXX: should be locked! */ |
| } |
| |
| |
| /* Newly enabled? |
| */ |
| if ( !(rmesa->hw.ctx.cmd[CTX_PP_CNTL] & (RADEON_TEX_0_ENABLE<<unit))) { |
| RADEON_STATECHANGE( rmesa, ctx ); |
| rmesa->hw.ctx.cmd[CTX_PP_CNTL] |= |
| (RADEON_TEX_0_ENABLE | RADEON_TEX_BLEND_0_ENABLE) << unit; |
| |
| RADEON_STATECHANGE( rmesa, tcl ); |
| |
| rmesa->hw.tcl.cmd[TCL_OUTPUT_VTXFMT] |= RADEON_ST_BIT(unit); |
| |
| rmesa->recheck_texgen[unit] = GL_TRUE; |
| } |
| |
| if (t->dirty_state & (1<<unit)) { |
| import_tex_obj_state( rmesa, unit, t ); |
| /* may need to update texture matrix (for texrect adjustments) */ |
| rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; |
| } |
| |
| if (rmesa->recheck_texgen[unit]) { |
| GLboolean fallback = !radeon_validate_texgen( ctx, unit ); |
| TCL_FALLBACK( ctx, (RADEON_TCL_FALLBACK_TEXGEN_0<<unit), fallback); |
| rmesa->recheck_texgen[unit] = 0; |
| rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; |
| } |
| |
| format = tObj->Image[0][tObj->BaseLevel]->_BaseFormat; |
| if ( rmesa->state.texture.unit[unit].format != format || |
| rmesa->state.texture.unit[unit].envMode != texUnit->EnvMode ) { |
| rmesa->state.texture.unit[unit].format = format; |
| rmesa->state.texture.unit[unit].envMode = texUnit->EnvMode; |
| if ( ! radeonUpdateTextureEnv( ctx, unit ) ) { |
| return GL_FALSE; |
| } |
| } |
| |
| FALLBACK( rmesa, RADEON_FALLBACK_BORDER_MODE, t->border_fallback ); |
| return !t->border_fallback; |
| } |
| |
| |
| |
| static GLboolean radeonUpdateTextureUnit( GLcontext *ctx, int unit ) |
| { |
| struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; |
| |
| if ( texUnit->_ReallyEnabled & (TEXTURE_RECT_BIT) ) { |
| return (enable_tex_rect( ctx, unit ) && |
| update_tex_common( ctx, unit )); |
| } |
| else if ( texUnit->_ReallyEnabled & (TEXTURE_1D_BIT | TEXTURE_2D_BIT) ) { |
| return (enable_tex_2d( ctx, unit ) && |
| update_tex_common( ctx, unit )); |
| } |
| else if ( texUnit->_ReallyEnabled & (TEXTURE_CUBE_BIT) ) { |
| return (enable_tex_cube( ctx, unit ) && |
| update_tex_common( ctx, unit )); |
| } |
| else if ( texUnit->_ReallyEnabled ) { |
| return GL_FALSE; |
| } |
| else { |
| disable_tex( ctx, unit ); |
| return GL_TRUE; |
| } |
| } |
| |
| void radeonUpdateTextureState( GLcontext *ctx ) |
| { |
| radeonContextPtr rmesa = RADEON_CONTEXT(ctx); |
| GLboolean ok; |
| |
| ok = (radeonUpdateTextureUnit( ctx, 0 ) && |
| radeonUpdateTextureUnit( ctx, 1 ) && |
| radeonUpdateTextureUnit( ctx, 2 )); |
| |
| FALLBACK( rmesa, RADEON_FALLBACK_TEXTURE, !ok ); |
| |
| if (rmesa->TclFallback) |
| radeonChooseVertexState( ctx ); |
| } |