r200_tex.c revision 692ca82116485a9c6191e5265c5b369d5b4f82f3
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/simple_list.h" 41#include "main/texformat.h" 42#include "main/texstore.h" 43#include "main/teximage.h" 44#include "main/texobj.h" 45 46#include "texmem.h" 47 48#include "r200_context.h" 49#include "r200_state.h" 50#include "r200_ioctl.h" 51#include "r200_swtcl.h" 52#include "r200_tex.h" 53 54#include "xmlpool.h" 55 56 57 58/** 59 * Set the texture wrap modes. 60 * 61 * \param t Texture object whose wrap modes are to be set 62 * \param swrap Wrap mode for the \a s texture coordinate 63 * \param twrap Wrap mode for the \a t texture coordinate 64 */ 65 66static void r200SetTexWrap( radeonTexObjPtr t, GLenum swrap, GLenum twrap, GLenum rwrap ) 67{ 68 GLboolean is_clamp = GL_FALSE; 69 GLboolean is_clamp_to_border = GL_FALSE; 70 71 t->pp_txfilter &= ~(R200_CLAMP_S_MASK | R200_CLAMP_T_MASK | R200_BORDER_MODE_D3D); 72 73 switch ( swrap ) { 74 case GL_REPEAT: 75 t->pp_txfilter |= R200_CLAMP_S_WRAP; 76 break; 77 case GL_CLAMP: 78 t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL; 79 is_clamp = GL_TRUE; 80 break; 81 case GL_CLAMP_TO_EDGE: 82 t->pp_txfilter |= R200_CLAMP_S_CLAMP_LAST; 83 break; 84 case GL_CLAMP_TO_BORDER: 85 t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL; 86 is_clamp_to_border = GL_TRUE; 87 break; 88 case GL_MIRRORED_REPEAT: 89 t->pp_txfilter |= R200_CLAMP_S_MIRROR; 90 break; 91 case GL_MIRROR_CLAMP_EXT: 92 t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL; 93 is_clamp = GL_TRUE; 94 break; 95 case GL_MIRROR_CLAMP_TO_EDGE_EXT: 96 t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_LAST; 97 break; 98 case GL_MIRROR_CLAMP_TO_BORDER_EXT: 99 t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL; 100 is_clamp_to_border = GL_TRUE; 101 break; 102 default: 103 _mesa_problem(NULL, "bad S wrap mode in %s", __FUNCTION__); 104 } 105 106 if (t->base.tObj->Target != GL_TEXTURE_1D) { 107 switch ( twrap ) { 108 case GL_REPEAT: 109 t->pp_txfilter |= R200_CLAMP_T_WRAP; 110 break; 111 case GL_CLAMP: 112 t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL; 113 is_clamp = GL_TRUE; 114 break; 115 case GL_CLAMP_TO_EDGE: 116 t->pp_txfilter |= R200_CLAMP_T_CLAMP_LAST; 117 break; 118 case GL_CLAMP_TO_BORDER: 119 t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL; 120 is_clamp_to_border = GL_TRUE; 121 break; 122 case GL_MIRRORED_REPEAT: 123 t->pp_txfilter |= R200_CLAMP_T_MIRROR; 124 break; 125 case GL_MIRROR_CLAMP_EXT: 126 t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL; 127 is_clamp = GL_TRUE; 128 break; 129 case GL_MIRROR_CLAMP_TO_EDGE_EXT: 130 t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_LAST; 131 break; 132 case GL_MIRROR_CLAMP_TO_BORDER_EXT: 133 t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL; 134 is_clamp_to_border = GL_TRUE; 135 break; 136 default: 137 _mesa_problem(NULL, "bad T wrap mode in %s", __FUNCTION__); 138 } 139 } 140 141 t->pp_txformat_x &= ~R200_CLAMP_Q_MASK; 142 143 switch ( rwrap ) { 144 case GL_REPEAT: 145 t->pp_txformat_x |= R200_CLAMP_Q_WRAP; 146 break; 147 case GL_CLAMP: 148 t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL; 149 is_clamp = GL_TRUE; 150 break; 151 case GL_CLAMP_TO_EDGE: 152 t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_LAST; 153 break; 154 case GL_CLAMP_TO_BORDER: 155 t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL; 156 is_clamp_to_border = GL_TRUE; 157 break; 158 case GL_MIRRORED_REPEAT: 159 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR; 160 break; 161 case GL_MIRROR_CLAMP_EXT: 162 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL; 163 is_clamp = GL_TRUE; 164 break; 165 case GL_MIRROR_CLAMP_TO_EDGE_EXT: 166 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_LAST; 167 break; 168 case GL_MIRROR_CLAMP_TO_BORDER_EXT: 169 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL; 170 is_clamp_to_border = GL_TRUE; 171 break; 172 default: 173 _mesa_problem(NULL, "bad R wrap mode in %s", __FUNCTION__); 174 } 175 176 if ( is_clamp_to_border ) { 177 t->pp_txfilter |= R200_BORDER_MODE_D3D; 178 } 179 180 t->border_fallback = (is_clamp && is_clamp_to_border); 181} 182 183static void r200SetTexMaxAnisotropy( radeonTexObjPtr t, GLfloat max ) 184{ 185 t->pp_txfilter &= ~R200_MAX_ANISO_MASK; 186 187 if ( max <= 1.0 ) { 188 t->pp_txfilter |= R200_MAX_ANISO_1_TO_1; 189 } else if ( max <= 2.0 ) { 190 t->pp_txfilter |= R200_MAX_ANISO_2_TO_1; 191 } else if ( max <= 4.0 ) { 192 t->pp_txfilter |= R200_MAX_ANISO_4_TO_1; 193 } else if ( max <= 8.0 ) { 194 t->pp_txfilter |= R200_MAX_ANISO_8_TO_1; 195 } else { 196 t->pp_txfilter |= R200_MAX_ANISO_16_TO_1; 197 } 198} 199 200/** 201 * Set the texture magnification and minification modes. 202 * 203 * \param t Texture whose filter modes are to be set 204 * \param minf Texture minification mode 205 * \param magf Texture magnification mode 206 */ 207 208static void r200SetTexFilter( radeonTexObjPtr t, GLenum minf, GLenum magf ) 209{ 210 GLuint anisotropy = (t->pp_txfilter & R200_MAX_ANISO_MASK); 211 212 t->pp_txfilter &= ~(R200_MIN_FILTER_MASK | R200_MAG_FILTER_MASK); 213 t->pp_txformat_x &= ~R200_VOLUME_FILTER_MASK; 214 215 if ( anisotropy == R200_MAX_ANISO_1_TO_1 ) { 216 switch ( minf ) { 217 case GL_NEAREST: 218 t->pp_txfilter |= R200_MIN_FILTER_NEAREST; 219 break; 220 case GL_LINEAR: 221 t->pp_txfilter |= R200_MIN_FILTER_LINEAR; 222 break; 223 case GL_NEAREST_MIPMAP_NEAREST: 224 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_NEAREST; 225 break; 226 case GL_NEAREST_MIPMAP_LINEAR: 227 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_NEAREST; 228 break; 229 case GL_LINEAR_MIPMAP_NEAREST: 230 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_LINEAR; 231 break; 232 case GL_LINEAR_MIPMAP_LINEAR: 233 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_LINEAR; 234 break; 235 } 236 } else { 237 switch ( minf ) { 238 case GL_NEAREST: 239 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST; 240 break; 241 case GL_LINEAR: 242 t->pp_txfilter |= R200_MIN_FILTER_ANISO_LINEAR; 243 break; 244 case GL_NEAREST_MIPMAP_NEAREST: 245 case GL_LINEAR_MIPMAP_NEAREST: 246 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST; 247 break; 248 case GL_NEAREST_MIPMAP_LINEAR: 249 case GL_LINEAR_MIPMAP_LINEAR: 250 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR; 251 break; 252 } 253 } 254 255 /* Note we don't have 3D mipmaps so only use the mag filter setting 256 * to set the 3D texture filter mode. 257 */ 258 switch ( magf ) { 259 case GL_NEAREST: 260 t->pp_txfilter |= R200_MAG_FILTER_NEAREST; 261 t->pp_txformat_x |= R200_VOLUME_FILTER_NEAREST; 262 break; 263 case GL_LINEAR: 264 t->pp_txfilter |= R200_MAG_FILTER_LINEAR; 265 t->pp_txformat_x |= R200_VOLUME_FILTER_LINEAR; 266 break; 267 } 268} 269 270static void r200SetTexBorderColor( radeonTexObjPtr t, GLubyte c[4] ) 271{ 272 t->pp_border_color = radeonPackColor( 4, c[0], c[1], c[2], c[3] ); 273} 274 275 276/** 277 * Allocate space for and load the mesa images into the texture memory block. 278 * This will happen before drawing with a new texture, or drawing with a 279 * texture after it was swapped out or teximaged again. 280 */ 281 282static radeonTexObjPtr r200AllocTexObj( struct gl_texture_object *texObj ) 283{ 284 radeonTexObjPtr t; 285 286 t = CALLOC_STRUCT( radeon_tex_obj ); 287 texObj->DriverData = t; 288 if ( t != NULL ) { 289 if ( R200_DEBUG & DEBUG_TEXTURE ) { 290 fprintf( stderr, "%s( %p, %p )\n", __FUNCTION__, (void *)texObj, 291 (void *)t ); 292 } 293 294 /* Initialize non-image-dependent parts of the state: 295 */ 296 t->base.tObj = texObj; 297 t->border_fallback = GL_FALSE; 298 299 make_empty_list( & t->base ); 300 301 r200SetTexWrap( t, texObj->WrapS, texObj->WrapT, texObj->WrapR ); 302 r200SetTexMaxAnisotropy( t, texObj->MaxAnisotropy ); 303 r200SetTexFilter( t, texObj->MinFilter, texObj->MagFilter ); 304 r200SetTexBorderColor( t, texObj->_BorderChan ); 305 } 306 307 return t; 308} 309 310/* try to find a format which will only need a memcopy */ 311static const struct gl_texture_format * 312r200Choose8888TexFormat( GLenum srcFormat, GLenum srcType ) 313{ 314 const GLuint ui = 1; 315 const GLubyte littleEndian = *((const GLubyte *) &ui); 316 317 if ((srcFormat == GL_RGBA && srcType == GL_UNSIGNED_INT_8_8_8_8) || 318 (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE && !littleEndian) || 319 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_INT_8_8_8_8_REV) || 320 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_BYTE && littleEndian)) { 321 return &_mesa_texformat_rgba8888; 322 } 323 else if ((srcFormat == GL_RGBA && srcType == GL_UNSIGNED_INT_8_8_8_8_REV) || 324 (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE && littleEndian) || 325 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_INT_8_8_8_8) || 326 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_BYTE && !littleEndian)) { 327 return &_mesa_texformat_rgba8888_rev; 328 } 329 else return _dri_texformat_argb8888; 330} 331 332static const struct gl_texture_format * 333r200ChooseTextureFormat( GLcontext *ctx, GLint internalFormat, 334 GLenum format, GLenum type ) 335{ 336 r200ContextPtr rmesa = R200_CONTEXT(ctx); 337 const GLboolean do32bpt = 338 ( rmesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_32 ); 339 const GLboolean force16bpt = 340 ( rmesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_FORCE_16 ); 341 (void) format; 342 343 switch ( internalFormat ) { 344 case 4: 345 case GL_RGBA: 346 case GL_COMPRESSED_RGBA: 347 switch ( type ) { 348 case GL_UNSIGNED_INT_10_10_10_2: 349 case GL_UNSIGNED_INT_2_10_10_10_REV: 350 return do32bpt ? _dri_texformat_argb8888 : _dri_texformat_argb1555; 351 case GL_UNSIGNED_SHORT_4_4_4_4: 352 case GL_UNSIGNED_SHORT_4_4_4_4_REV: 353 return _dri_texformat_argb4444; 354 case GL_UNSIGNED_SHORT_5_5_5_1: 355 case GL_UNSIGNED_SHORT_1_5_5_5_REV: 356 return _dri_texformat_argb1555; 357 default: 358 return do32bpt ? 359 r200Choose8888TexFormat(format, type) : _dri_texformat_argb4444; 360 } 361 362 case 3: 363 case GL_RGB: 364 case GL_COMPRESSED_RGB: 365 switch ( type ) { 366 case GL_UNSIGNED_SHORT_4_4_4_4: 367 case GL_UNSIGNED_SHORT_4_4_4_4_REV: 368 return _dri_texformat_argb4444; 369 case GL_UNSIGNED_SHORT_5_5_5_1: 370 case GL_UNSIGNED_SHORT_1_5_5_5_REV: 371 return _dri_texformat_argb1555; 372 case GL_UNSIGNED_SHORT_5_6_5: 373 case GL_UNSIGNED_SHORT_5_6_5_REV: 374 return _dri_texformat_rgb565; 375 default: 376 return do32bpt ? _dri_texformat_argb8888 : _dri_texformat_rgb565; 377 } 378 379 case GL_RGBA8: 380 case GL_RGB10_A2: 381 case GL_RGBA12: 382 case GL_RGBA16: 383 return !force16bpt ? 384 r200Choose8888TexFormat(format, type) : _dri_texformat_argb4444; 385 386 case GL_RGBA4: 387 case GL_RGBA2: 388 return _dri_texformat_argb4444; 389 390 case GL_RGB5_A1: 391 return _dri_texformat_argb1555; 392 393 case GL_RGB8: 394 case GL_RGB10: 395 case GL_RGB12: 396 case GL_RGB16: 397 return !force16bpt ? _dri_texformat_argb8888 : _dri_texformat_rgb565; 398 399 case GL_RGB5: 400 case GL_RGB4: 401 case GL_R3_G3_B2: 402 return _dri_texformat_rgb565; 403 404 case GL_ALPHA: 405 case GL_ALPHA4: 406 case GL_ALPHA8: 407 case GL_ALPHA12: 408 case GL_ALPHA16: 409 case GL_COMPRESSED_ALPHA: 410 /* can't use a8 format since interpreting hw I8 as a8 would result 411 in wrong rgb values (same as alpha value instead of 0). */ 412 return _dri_texformat_al88; 413 414 case 1: 415 case GL_LUMINANCE: 416 case GL_LUMINANCE4: 417 case GL_LUMINANCE8: 418 case GL_LUMINANCE12: 419 case GL_LUMINANCE16: 420 case GL_COMPRESSED_LUMINANCE: 421 return _dri_texformat_l8; 422 423 case 2: 424 case GL_LUMINANCE_ALPHA: 425 case GL_LUMINANCE4_ALPHA4: 426 case GL_LUMINANCE6_ALPHA2: 427 case GL_LUMINANCE8_ALPHA8: 428 case GL_LUMINANCE12_ALPHA4: 429 case GL_LUMINANCE12_ALPHA12: 430 case GL_LUMINANCE16_ALPHA16: 431 case GL_COMPRESSED_LUMINANCE_ALPHA: 432 return _dri_texformat_al88; 433 434 case GL_INTENSITY: 435 case GL_INTENSITY4: 436 case GL_INTENSITY8: 437 case GL_INTENSITY12: 438 case GL_INTENSITY16: 439 case GL_COMPRESSED_INTENSITY: 440 return _dri_texformat_i8; 441 442 case GL_YCBCR_MESA: 443 if (type == GL_UNSIGNED_SHORT_8_8_APPLE || 444 type == GL_UNSIGNED_BYTE) 445 return &_mesa_texformat_ycbcr; 446 else 447 return &_mesa_texformat_ycbcr_rev; 448 449 case GL_RGB_S3TC: 450 case GL_RGB4_S3TC: 451 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: 452 return &_mesa_texformat_rgb_dxt1; 453 454 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: 455 return &_mesa_texformat_rgba_dxt1; 456 457 case GL_RGBA_S3TC: 458 case GL_RGBA4_S3TC: 459 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: 460 return &_mesa_texformat_rgba_dxt3; 461 462 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: 463 return &_mesa_texformat_rgba_dxt5; 464 465 default: 466 _mesa_problem(ctx, 467 "unexpected internalFormat 0x%x in r200ChooseTextureFormat", 468 (int) internalFormat); 469 return NULL; 470 } 471 472 return NULL; /* never get here */ 473} 474 475 476static GLboolean 477r200ValidateClientStorage( GLcontext *ctx, GLenum target, 478 GLint internalFormat, 479 GLint srcWidth, GLint srcHeight, 480 GLenum format, GLenum type, const void *pixels, 481 const struct gl_pixelstore_attrib *packing, 482 struct gl_texture_object *texObj, 483 struct gl_texture_image *texImage) 484 485{ 486 r200ContextPtr rmesa = R200_CONTEXT(ctx); 487 488 if ( R200_DEBUG & DEBUG_TEXTURE ) 489 fprintf(stderr, "intformat %s format %s type %s\n", 490 _mesa_lookup_enum_by_nr( internalFormat ), 491 _mesa_lookup_enum_by_nr( format ), 492 _mesa_lookup_enum_by_nr( type )); 493 494 if (!ctx->Unpack.ClientStorage) 495 return 0; 496 497 if (ctx->_ImageTransferState || 498 texImage->IsCompressed || 499 texObj->GenerateMipmap) 500 return 0; 501 502 503 /* This list is incomplete, may be different on ppc??? 504 */ 505 switch ( internalFormat ) { 506 case GL_RGBA: 507 if ( format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8_REV ) { 508 texImage->TexFormat = _dri_texformat_argb8888; 509 } 510 else 511 return 0; 512 break; 513 514 case GL_RGB: 515 if ( format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5 ) { 516 texImage->TexFormat = _dri_texformat_rgb565; 517 } 518 else 519 return 0; 520 break; 521 522 case GL_YCBCR_MESA: 523 if ( format == GL_YCBCR_MESA && 524 type == GL_UNSIGNED_SHORT_8_8_REV_APPLE ) { 525 texImage->TexFormat = &_mesa_texformat_ycbcr_rev; 526 } 527 else if ( format == GL_YCBCR_MESA && 528 (type == GL_UNSIGNED_SHORT_8_8_APPLE || 529 type == GL_UNSIGNED_BYTE)) { 530 texImage->TexFormat = &_mesa_texformat_ycbcr; 531 } 532 else 533 return 0; 534 break; 535 536 default: 537 return 0; 538 } 539 540 /* Could deal with these packing issues, but currently don't: 541 */ 542 if (packing->SkipPixels || 543 packing->SkipRows || 544 packing->SwapBytes || 545 packing->LsbFirst) { 546 return 0; 547 } 548 549 { 550 GLint srcRowStride = _mesa_image_row_stride(packing, srcWidth, 551 format, type); 552 553 554 if ( R200_DEBUG & DEBUG_TEXTURE ) 555 fprintf(stderr, "%s: srcRowStride %d/%x\n", 556 __FUNCTION__, srcRowStride, srcRowStride); 557 558 /* Could check this later in upload, pitch restrictions could be 559 * relaxed, but would need to store the image pitch somewhere, 560 * as packing details might change before image is uploaded: 561 */ 562 if (!r200IsGartMemory( rmesa, pixels, srcHeight * srcRowStride ) || 563 (srcRowStride & 63)) 564 return 0; 565 566 567 /* Have validated that _mesa_transfer_teximage would be a straight 568 * memcpy at this point. NOTE: future calls to TexSubImage will 569 * overwrite the client data. This is explicitly mentioned in the 570 * extension spec. 571 */ 572 texImage->Data = (void *)pixels; 573 texImage->IsClientData = GL_TRUE; 574 texImage->RowStride = srcRowStride / texImage->TexFormat->TexelBytes; 575 576 return 1; 577 } 578} 579 580 581static void r200TexImage1D( GLcontext *ctx, GLenum target, GLint level, 582 GLint internalFormat, 583 GLint width, GLint border, 584 GLenum format, GLenum type, const GLvoid *pixels, 585 const struct gl_pixelstore_attrib *packing, 586 struct gl_texture_object *texObj, 587 struct gl_texture_image *texImage ) 588{ 589 driTextureObject * t = (driTextureObject *) texObj->DriverData; 590 591 if ( t ) { 592 driSwapOutTextureObject( t ); 593 } 594 else { 595 t = (driTextureObject *) r200AllocTexObj( texObj ); 596 if (!t) { 597 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage1D"); 598 return; 599 } 600 } 601 602 /* Note, this will call ChooseTextureFormat */ 603 _mesa_store_teximage1d(ctx, target, level, internalFormat, 604 width, border, format, type, pixels, 605 &ctx->Unpack, texObj, texImage); 606 607 t->dirty_images[0] |= (1 << level); 608} 609 610 611static void r200TexSubImage1D( GLcontext *ctx, GLenum target, GLint level, 612 GLint xoffset, 613 GLsizei width, 614 GLenum format, GLenum type, 615 const GLvoid *pixels, 616 const struct gl_pixelstore_attrib *packing, 617 struct gl_texture_object *texObj, 618 struct gl_texture_image *texImage ) 619{ 620 driTextureObject * t = (driTextureObject *) texObj->DriverData; 621 622 assert( t ); /* this _should_ be true */ 623 if ( t ) { 624 driSwapOutTextureObject( t ); 625 } 626 else { 627 t = (driTextureObject *) r200AllocTexObj( texObj ); 628 if (!t) { 629 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage1D"); 630 return; 631 } 632 } 633 634 _mesa_store_texsubimage1d(ctx, target, level, xoffset, width, 635 format, type, pixels, packing, texObj, 636 texImage); 637 638 t->dirty_images[0] |= (1 << level); 639} 640 641 642static void r200TexImage2D( GLcontext *ctx, GLenum target, GLint level, 643 GLint internalFormat, 644 GLint width, GLint height, GLint border, 645 GLenum format, GLenum type, const GLvoid *pixels, 646 const struct gl_pixelstore_attrib *packing, 647 struct gl_texture_object *texObj, 648 struct gl_texture_image *texImage ) 649{ 650 driTextureObject * t = (driTextureObject *) texObj->DriverData; 651 GLuint face; 652 653 /* which cube face or ordinary 2D image */ 654 switch (target) { 655 case GL_TEXTURE_CUBE_MAP_POSITIVE_X: 656 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 657 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 658 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 659 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 660 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 661 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X; 662 ASSERT(face < 6); 663 break; 664 default: 665 face = 0; 666 } 667 668 if ( t != NULL ) { 669 driSwapOutTextureObject( t ); 670 } 671 else { 672 t = (driTextureObject *) r200AllocTexObj( texObj ); 673 if (!t) { 674 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D"); 675 return; 676 } 677 } 678 679 texImage->IsClientData = GL_FALSE; 680 681 if (r200ValidateClientStorage( ctx, target, 682 internalFormat, 683 width, height, 684 format, type, pixels, 685 packing, texObj, texImage)) { 686 if (R200_DEBUG & DEBUG_TEXTURE) 687 fprintf(stderr, "%s: Using client storage\n", __FUNCTION__); 688 } 689 else { 690 if (R200_DEBUG & DEBUG_TEXTURE) 691 fprintf(stderr, "%s: Using normal storage\n", __FUNCTION__); 692 693 /* Normal path: copy (to cached memory) and eventually upload 694 * via another copy to GART memory and then a blit... Could 695 * eliminate one copy by going straight to (permanent) GART. 696 * 697 * Note, this will call r200ChooseTextureFormat. 698 */ 699 _mesa_store_teximage2d(ctx, target, level, internalFormat, 700 width, height, border, format, type, pixels, 701 &ctx->Unpack, texObj, texImage); 702 703 t->dirty_images[face] |= (1 << level); 704 } 705} 706 707 708static void r200TexSubImage2D( GLcontext *ctx, GLenum target, GLint level, 709 GLint xoffset, GLint yoffset, 710 GLsizei width, GLsizei height, 711 GLenum format, GLenum type, 712 const GLvoid *pixels, 713 const struct gl_pixelstore_attrib *packing, 714 struct gl_texture_object *texObj, 715 struct gl_texture_image *texImage ) 716{ 717 driTextureObject * t = (driTextureObject *) texObj->DriverData; 718 GLuint face; 719 720 /* which cube face or ordinary 2D image */ 721 switch (target) { 722 case GL_TEXTURE_CUBE_MAP_POSITIVE_X: 723 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 724 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 725 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 726 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 727 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 728 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X; 729 ASSERT(face < 6); 730 break; 731 default: 732 face = 0; 733 } 734 735 assert( t ); /* this _should_ be true */ 736 if ( t ) { 737 driSwapOutTextureObject( t ); 738 } 739 else { 740 t = (driTextureObject *) r200AllocTexObj( texObj ); 741 if (!t) { 742 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage2D"); 743 return; 744 } 745 } 746 747 _mesa_store_texsubimage2d(ctx, target, level, xoffset, yoffset, width, 748 height, format, type, pixels, packing, texObj, 749 texImage); 750 751 t->dirty_images[face] |= (1 << level); 752} 753 754 755static void r200CompressedTexImage2D( GLcontext *ctx, GLenum target, GLint level, 756 GLint internalFormat, 757 GLint width, GLint height, GLint border, 758 GLsizei imageSize, const GLvoid *data, 759 struct gl_texture_object *texObj, 760 struct gl_texture_image *texImage ) 761{ 762 driTextureObject * t = (driTextureObject *) texObj->DriverData; 763 GLuint face; 764 765 /* which cube face or ordinary 2D image */ 766 switch (target) { 767 case GL_TEXTURE_CUBE_MAP_POSITIVE_X: 768 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 769 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 770 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 771 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 772 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 773 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X; 774 ASSERT(face < 6); 775 break; 776 default: 777 face = 0; 778 } 779 780 if ( t != NULL ) { 781 driSwapOutTextureObject( t ); 782 } 783 else { 784 t = (driTextureObject *) r200AllocTexObj( texObj ); 785 if (!t) { 786 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2D"); 787 return; 788 } 789 } 790 791 texImage->IsClientData = GL_FALSE; 792/* can't call this, different parameters. Would never evaluate to true anyway currently 793 if (r200ValidateClientStorage( ctx, target, 794 internalFormat, 795 width, height, 796 format, type, pixels, 797 packing, texObj, texImage)) { 798 if (R200_DEBUG & DEBUG_TEXTURE) 799 fprintf(stderr, "%s: Using client storage\n", __FUNCTION__); 800 } 801 else */{ 802 if (R200_DEBUG & DEBUG_TEXTURE) 803 fprintf(stderr, "%s: Using normal storage\n", __FUNCTION__); 804 805 /* Normal path: copy (to cached memory) and eventually upload 806 * via another copy to GART memory and then a blit... Could 807 * eliminate one copy by going straight to (permanent) GART. 808 * 809 * Note, this will call r200ChooseTextureFormat. 810 */ 811 _mesa_store_compressed_teximage2d(ctx, target, level, internalFormat, width, 812 height, border, imageSize, data, texObj, texImage); 813 814 t->dirty_images[face] |= (1 << level); 815 } 816} 817 818 819static void r200CompressedTexSubImage2D( GLcontext *ctx, GLenum target, GLint level, 820 GLint xoffset, GLint yoffset, 821 GLsizei width, GLsizei height, 822 GLenum format, 823 GLsizei imageSize, const GLvoid *data, 824 struct gl_texture_object *texObj, 825 struct gl_texture_image *texImage ) 826{ 827 driTextureObject * t = (driTextureObject *) texObj->DriverData; 828 GLuint face; 829 830 831 /* which cube face or ordinary 2D image */ 832 switch (target) { 833 case GL_TEXTURE_CUBE_MAP_POSITIVE_X: 834 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 835 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 836 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 837 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 838 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 839 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X; 840 ASSERT(face < 6); 841 break; 842 default: 843 face = 0; 844 } 845 846 assert( t ); /* this _should_ be true */ 847 if ( t ) { 848 driSwapOutTextureObject( t ); 849 } 850 else { 851 t = (driTextureObject *) r200AllocTexObj( texObj ); 852 if (!t) { 853 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexSubImage2D"); 854 return; 855 } 856 } 857 858 _mesa_store_compressed_texsubimage2d(ctx, target, level, xoffset, yoffset, width, 859 height, format, imageSize, data, texObj, texImage); 860 861 t->dirty_images[face] |= (1 << level); 862} 863 864 865#if ENABLE_HW_3D_TEXTURE 866static void r200TexImage3D( GLcontext *ctx, GLenum target, GLint level, 867 GLint internalFormat, 868 GLint width, GLint height, GLint depth, 869 GLint border, 870 GLenum format, GLenum type, const GLvoid *pixels, 871 const struct gl_pixelstore_attrib *packing, 872 struct gl_texture_object *texObj, 873 struct gl_texture_image *texImage ) 874{ 875 driTextureObject * t = (driTextureObject *) texObj->DriverData; 876 877 if ( t ) { 878 driSwapOutTextureObject( t ); 879 } 880 else { 881 t = (driTextureObject *) r200AllocTexObj( texObj ); 882 if (!t) { 883 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage3D"); 884 return; 885 } 886 } 887 888 texImage->IsClientData = GL_FALSE; 889 890#if 0 891 if (r200ValidateClientStorage( ctx, target, 892 internalFormat, 893 width, height, 894 format, type, pixels, 895 packing, texObj, texImage)) { 896 if (R200_DEBUG & DEBUG_TEXTURE) 897 fprintf(stderr, "%s: Using client storage\n", __FUNCTION__); 898 } 899 else 900#endif 901 { 902 if (R200_DEBUG & DEBUG_TEXTURE) 903 fprintf(stderr, "%s: Using normal storage\n", __FUNCTION__); 904 905 /* Normal path: copy (to cached memory) and eventually upload 906 * via another copy to GART memory and then a blit... Could 907 * eliminate one copy by going straight to (permanent) GART. 908 * 909 * Note, this will call r200ChooseTextureFormat. 910 */ 911 _mesa_store_teximage3d(ctx, target, level, internalFormat, 912 width, height, depth, border, 913 format, type, pixels, 914 &ctx->Unpack, texObj, texImage); 915 916 t->dirty_images[0] |= (1 << level); 917 } 918} 919#endif 920 921 922#if ENABLE_HW_3D_TEXTURE 923static void 924r200TexSubImage3D( GLcontext *ctx, GLenum target, GLint level, 925 GLint xoffset, GLint yoffset, GLint zoffset, 926 GLsizei width, GLsizei height, GLsizei depth, 927 GLenum format, GLenum type, 928 const GLvoid *pixels, 929 const struct gl_pixelstore_attrib *packing, 930 struct gl_texture_object *texObj, 931 struct gl_texture_image *texImage ) 932{ 933 driTextureObject * t = (driTextureObject *) texObj->DriverData; 934 935/* fprintf(stderr, "%s\n", __FUNCTION__); */ 936 937 assert( t ); /* this _should_ be true */ 938 if ( t ) { 939 driSwapOutTextureObject( t ); 940 } 941 else { 942 t = (driTextureObject *) r200AllocTexObj( texObj ); 943 if (!t) { 944 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage3D"); 945 return; 946 } 947 texObj->DriverData = t; 948 } 949 950 _mesa_store_texsubimage3d(ctx, target, level, xoffset, yoffset, zoffset, 951 width, height, depth, 952 format, type, pixels, packing, texObj, texImage); 953 954 t->dirty_images[0] |= (1 << level); 955} 956#endif 957 958 959 960static void r200TexEnv( GLcontext *ctx, GLenum target, 961 GLenum pname, const GLfloat *param ) 962{ 963 r200ContextPtr rmesa = R200_CONTEXT(ctx); 964 GLuint unit = ctx->Texture.CurrentUnit; 965 struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; 966 967 if ( R200_DEBUG & DEBUG_STATE ) { 968 fprintf( stderr, "%s( %s )\n", 969 __FUNCTION__, _mesa_lookup_enum_by_nr( pname ) ); 970 } 971 972 /* This is incorrect: Need to maintain this data for each of 973 * GL_TEXTURE_{123}D, GL_TEXTURE_RECTANGLE_NV, etc, and switch 974 * between them according to _ReallyEnabled. 975 */ 976 switch ( pname ) { 977 case GL_TEXTURE_ENV_COLOR: { 978 GLubyte c[4]; 979 GLuint envColor; 980 UNCLAMPED_FLOAT_TO_RGBA_CHAN( c, texUnit->EnvColor ); 981 envColor = radeonPackColor( 4, c[0], c[1], c[2], c[3] ); 982 if ( rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] != envColor ) { 983 R200_STATECHANGE( rmesa, tf ); 984 rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] = envColor; 985 } 986 break; 987 } 988 989 case GL_TEXTURE_LOD_BIAS_EXT: { 990 GLfloat bias, min; 991 GLuint b; 992 const int fixed_one = 0x8000000; 993 994 /* The R200's LOD bias is a signed 2's complement value with a 995 * range of -16.0 <= bias < 16.0. 996 * 997 * NOTE: Add a small bias to the bias for conform mipsel.c test. 998 */ 999 bias = *param + .01; 1000 min = driQueryOptionb (&rmesa->optionCache, "no_neg_lod_bias") ? 1001 0.0 : -16.0; 1002 bias = CLAMP( bias, min, 16.0 ); 1003 b = (int)(bias * fixed_one) & R200_LOD_BIAS_MASK; 1004 1005 if ( (rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] & R200_LOD_BIAS_MASK) != b ) { 1006 R200_STATECHANGE( rmesa, tex[unit] ); 1007 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] &= ~R200_LOD_BIAS_MASK; 1008 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] |= b; 1009 } 1010 break; 1011 } 1012 case GL_COORD_REPLACE_ARB: 1013 if (ctx->Point.PointSprite) { 1014 R200_STATECHANGE( rmesa, spr ); 1015 if ((GLenum)param[0]) { 1016 rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] |= R200_PS_GEN_TEX_0 << unit; 1017 } else { 1018 rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] &= ~(R200_PS_GEN_TEX_0 << unit); 1019 } 1020 } 1021 break; 1022 default: 1023 return; 1024 } 1025} 1026 1027 1028/** 1029 * Changes variables and flags for a state update, which will happen at the 1030 * next UpdateTextureState 1031 */ 1032 1033static void r200TexParameter( GLcontext *ctx, GLenum target, 1034 struct gl_texture_object *texObj, 1035 GLenum pname, const GLfloat *params ) 1036{ 1037 radeonTexObjPtr t = (radeonTexObjPtr) texObj->DriverData; 1038 1039 if ( R200_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) { 1040 fprintf( stderr, "%s( %s )\n", __FUNCTION__, 1041 _mesa_lookup_enum_by_nr( pname ) ); 1042 } 1043 1044 switch ( pname ) { 1045 case GL_TEXTURE_MIN_FILTER: 1046 case GL_TEXTURE_MAG_FILTER: 1047 case GL_TEXTURE_MAX_ANISOTROPY_EXT: 1048 r200SetTexMaxAnisotropy( t, texObj->MaxAnisotropy ); 1049 r200SetTexFilter( t, texObj->MinFilter, texObj->MagFilter ); 1050 break; 1051 1052 case GL_TEXTURE_WRAP_S: 1053 case GL_TEXTURE_WRAP_T: 1054 case GL_TEXTURE_WRAP_R: 1055 r200SetTexWrap( t, texObj->WrapS, texObj->WrapT, texObj->WrapR ); 1056 break; 1057 1058 case GL_TEXTURE_BORDER_COLOR: 1059 r200SetTexBorderColor( t, texObj->_BorderChan ); 1060 break; 1061 1062 case GL_TEXTURE_BASE_LEVEL: 1063 case GL_TEXTURE_MAX_LEVEL: 1064 case GL_TEXTURE_MIN_LOD: 1065 case GL_TEXTURE_MAX_LOD: 1066 /* This isn't the most efficient solution but there doesn't appear to 1067 * be a nice alternative. Since there's no LOD clamping, 1068 * we just have to rely on loading the right subset of mipmap levels 1069 * to simulate a clamped LOD. 1070 */ 1071 driSwapOutTextureObject( (driTextureObject *) t ); 1072 break; 1073 1074 default: 1075 return; 1076 } 1077 1078 /* Mark this texobj as dirty (one bit per tex unit) 1079 */ 1080 t->dirty_state = R200_TEX_ALL; 1081} 1082 1083 1084 1085static void r200BindTexture( GLcontext *ctx, GLenum target, 1086 struct gl_texture_object *texObj ) 1087{ 1088 if ( R200_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) { 1089 fprintf( stderr, "%s( %p ) unit=%d\n", __FUNCTION__, (void *)texObj, 1090 ctx->Texture.CurrentUnit ); 1091 } 1092 1093 if ( (target == GL_TEXTURE_1D) 1094 || (target == GL_TEXTURE_2D) 1095#if ENABLE_HW_3D_TEXTURE 1096 || (target == GL_TEXTURE_3D) 1097#endif 1098 || (target == GL_TEXTURE_CUBE_MAP) 1099 || (target == GL_TEXTURE_RECTANGLE_NV) ) { 1100 assert( texObj->DriverData != NULL ); 1101 } 1102} 1103 1104 1105static void r200DeleteTexture( GLcontext *ctx, 1106 struct gl_texture_object *texObj ) 1107{ 1108 r200ContextPtr rmesa = R200_CONTEXT(ctx); 1109 driTextureObject * t = (driTextureObject *) texObj->DriverData; 1110 1111 if ( R200_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) { 1112 fprintf( stderr, "%s( %p (target = %s) )\n", __FUNCTION__, (void *)texObj, 1113 _mesa_lookup_enum_by_nr( texObj->Target ) ); 1114 } 1115 1116 if ( t != NULL ) { 1117 if ( rmesa ) { 1118 R200_FIREVERTICES( rmesa ); 1119 } 1120 1121 driDestroyTextureObject( t ); 1122 } 1123 /* Free mipmap images and the texture object itself */ 1124 _mesa_delete_texture_object(ctx, texObj); 1125} 1126 1127/* Need: 1128 * - Same GEN_MODE for all active bits 1129 * - Same EyePlane/ObjPlane for all active bits when using Eye/Obj 1130 * - STRQ presumably all supported (matrix means incoming R values 1131 * can end up in STQ, this has implications for vertex support, 1132 * presumably ok if maos is used, though?) 1133 * 1134 * Basically impossible to do this on the fly - just collect some 1135 * basic info & do the checks from ValidateState(). 1136 */ 1137static void r200TexGen( GLcontext *ctx, 1138 GLenum coord, 1139 GLenum pname, 1140 const GLfloat *params ) 1141{ 1142 r200ContextPtr rmesa = R200_CONTEXT(ctx); 1143 GLuint unit = ctx->Texture.CurrentUnit; 1144 rmesa->recheck_texgen[unit] = GL_TRUE; 1145} 1146 1147 1148/** 1149 * Allocate a new texture object. 1150 * Called via ctx->Driver.NewTextureObject. 1151 * Note: this function will be called during context creation to 1152 * allocate the default texture objects. 1153 * Note: we could use containment here to 'derive' the driver-specific 1154 * texture object from the core mesa gl_texture_object. Not done at this time. 1155 * Fixup MaxAnisotropy according to user preference. 1156 */ 1157static struct gl_texture_object * 1158r200NewTextureObject( GLcontext *ctx, GLuint name, GLenum target ) 1159{ 1160 r200ContextPtr rmesa = R200_CONTEXT(ctx); 1161 struct gl_texture_object *obj; 1162 obj = _mesa_new_texture_object(ctx, name, target); 1163 if (!obj) 1164 return NULL; 1165 obj->MaxAnisotropy = rmesa->initialMaxAnisotropy; 1166 r200AllocTexObj( obj ); 1167 return obj; 1168} 1169 1170 1171void r200InitTextureFuncs( struct dd_function_table *functions ) 1172{ 1173 /* Note: we only plug in the functions we implement in the driver 1174 * since _mesa_init_driver_functions() was already called. 1175 */ 1176 functions->ChooseTextureFormat = r200ChooseTextureFormat; 1177 functions->TexImage1D = r200TexImage1D; 1178 functions->TexImage2D = r200TexImage2D; 1179#if ENABLE_HW_3D_TEXTURE 1180 functions->TexImage3D = r200TexImage3D; 1181#else 1182 functions->TexImage3D = _mesa_store_teximage3d; 1183#endif 1184 functions->TexSubImage1D = r200TexSubImage1D; 1185 functions->TexSubImage2D = r200TexSubImage2D; 1186#if ENABLE_HW_3D_TEXTURE 1187 functions->TexSubImage3D = r200TexSubImage3D; 1188#else 1189 functions->TexSubImage3D = _mesa_store_texsubimage3d; 1190#endif 1191 functions->NewTextureObject = r200NewTextureObject; 1192 functions->BindTexture = r200BindTexture; 1193 functions->DeleteTexture = r200DeleteTexture; 1194 functions->IsTextureResident = driIsTextureResident; 1195 1196 functions->TexEnv = r200TexEnv; 1197 functions->TexParameter = r200TexParameter; 1198 functions->TexGen = r200TexGen; 1199 1200 functions->CompressedTexImage2D = r200CompressedTexImage2D; 1201 functions->CompressedTexSubImage2D = r200CompressedTexSubImage2D; 1202 1203 driInitTextureFormats(); 1204 1205#if 000 1206 /* moved or obsolete code */ 1207 r200ContextPtr rmesa = R200_CONTEXT(ctx); 1208 driInitTextureObjects( ctx, & rmesa->swapped, 1209 DRI_TEXMGR_DO_TEXTURE_1D 1210 | DRI_TEXMGR_DO_TEXTURE_2D ); 1211 1212 /* Hack: r200NewTextureObject is not yet installed when the 1213 * default textures are created. Therefore set MaxAnisotropy of the 1214 * default 2D texture now. */ 1215 ctx->Shared->Default2D->MaxAnisotropy = driQueryOptionf (&rmesa->optionCache, 1216 "def_max_anisotropy"); 1217#endif 1218} 1219