Home | History | Annotate | Download | only in math
      1 /*
      2  * Mesa 3-D graphics library
      3  *
      4  * Copyright (C) 1999-2005  Brian Paul   All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included
     14  * in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors:
     25  *    Gareth Hughes
     26  */
     27 
     28 #include "main/glheader.h"
     29 #include "main/context.h"
     30 #include "main/macros.h"
     31 #include "main/imports.h"
     32 
     33 #include "m_matrix.h"
     34 #include "m_xform.h"
     35 
     36 #include "m_debug.h"
     37 #include "m_debug_util.h"
     38 
     39 #ifdef __UNIXOS2__
     40 /* The linker doesn't like empty files */
     41 static char dummy;
     42 #endif
     43 
     44 #ifdef DEBUG_MATH  /* This code only used for debugging */
     45 
     46 static clip_func *clip_tab[2] = {
     47    _mesa_clip_tab,
     48    _mesa_clip_np_tab
     49 };
     50 static char *cnames[2] = {
     51    "_mesa_clip_tab",
     52    "_mesa_clip_np_tab"
     53 };
     54 #ifdef RUN_DEBUG_BENCHMARK
     55 static char *cstrings[2] = {
     56    "clip, perspective divide",
     57    "clip, no divide"
     58 };
     59 #endif
     60 
     61 
     62 /* =============================================================
     63  * Reference cliptests
     64  */
     65 
     66 static GLvector4f *ref_cliptest_points4( GLvector4f *clip_vec,
     67 					 GLvector4f *proj_vec,
     68 					 GLubyte clipMask[],
     69 					 GLubyte *orMask,
     70 					 GLubyte *andMask,
     71 					 GLboolean viewport_z_clip )
     72 {
     73    const GLuint stride = clip_vec->stride;
     74    const GLuint count = clip_vec->count;
     75    const GLfloat *from = (GLfloat *)clip_vec->start;
     76    GLuint c = 0;
     77    GLfloat (*vProj)[4] = (GLfloat (*)[4])proj_vec->start;
     78    GLubyte tmpAndMask = *andMask;
     79    GLubyte tmpOrMask = *orMask;
     80    GLuint i;
     81    for ( i = 0 ; i < count ; i++, STRIDE_F(from, stride) ) {
     82       const GLfloat cx = from[0];
     83       const GLfloat cy = from[1];
     84       const GLfloat cz = from[2];
     85       const GLfloat cw = from[3];
     86       GLubyte mask = 0;
     87       if ( -cx + cw < 0 ) mask |= CLIP_RIGHT_BIT;
     88       if (  cx + cw < 0 ) mask |= CLIP_LEFT_BIT;
     89       if ( -cy + cw < 0 ) mask |= CLIP_TOP_BIT;
     90       if (  cy + cw < 0 ) mask |= CLIP_BOTTOM_BIT;
     91       if (viewport_z_clip) {
     92 	 if ( -cz + cw < 0 ) mask |= CLIP_FAR_BIT;
     93 	 if (  cz + cw < 0 ) mask |= CLIP_NEAR_BIT;
     94       }
     95       clipMask[i] = mask;
     96       if ( mask ) {
     97 	 c++;
     98 	 tmpAndMask &= mask;
     99 	 tmpOrMask |= mask;
    100 	 vProj[i][0] = 0;
    101 	 vProj[i][1] = 0;
    102 	 vProj[i][2] = 0;
    103 	 vProj[i][3] = 1;
    104       } else {
    105 	 GLfloat oow = 1.0F / cw;
    106 	 vProj[i][0] = cx * oow;
    107 	 vProj[i][1] = cy * oow;
    108 	 vProj[i][2] = cz * oow;
    109 	 vProj[i][3] = oow;
    110       }
    111    }
    112 
    113    *orMask = tmpOrMask;
    114    *andMask = (GLubyte) (c < count ? 0 : tmpAndMask);
    115 
    116    proj_vec->flags |= VEC_SIZE_4;
    117    proj_vec->size = 4;
    118    proj_vec->count = clip_vec->count;
    119    return proj_vec;
    120 }
    121 
    122 /* Keep these here for now, even though we don't use them...
    123  */
    124 static GLvector4f *ref_cliptest_points3( GLvector4f *clip_vec,
    125 					 GLvector4f *proj_vec,
    126 					 GLubyte clipMask[],
    127 					 GLubyte *orMask,
    128 					 GLubyte *andMask,
    129                                          GLboolean viewport_z_clip )
    130 {
    131    const GLuint stride = clip_vec->stride;
    132    const GLuint count = clip_vec->count;
    133    const GLfloat *from = (GLfloat *)clip_vec->start;
    134 
    135    GLubyte tmpOrMask = *orMask;
    136    GLubyte tmpAndMask = *andMask;
    137    GLuint i;
    138    for ( i = 0 ; i < count ; i++, STRIDE_F(from, stride) ) {
    139       const GLfloat cx = from[0], cy = from[1], cz = from[2];
    140       GLubyte mask = 0;
    141       if ( cx >  1.0 )		mask |= CLIP_RIGHT_BIT;
    142       else if ( cx < -1.0 )	mask |= CLIP_LEFT_BIT;
    143       if ( cy >  1.0 )		mask |= CLIP_TOP_BIT;
    144       else if ( cy < -1.0 )	mask |= CLIP_BOTTOM_BIT;
    145       if (viewport_z_clip) {
    146          if ( cz >  1.0 )		mask |= CLIP_FAR_BIT;
    147          else if ( cz < -1.0 )	mask |= CLIP_NEAR_BIT;
    148       }
    149       clipMask[i] = mask;
    150       tmpOrMask |= mask;
    151       tmpAndMask &= mask;
    152    }
    153 
    154    *orMask = tmpOrMask;
    155    *andMask = tmpAndMask;
    156    return clip_vec;
    157 }
    158 
    159 static GLvector4f * ref_cliptest_points2( GLvector4f *clip_vec,
    160 					  GLvector4f *proj_vec,
    161 					  GLubyte clipMask[],
    162 					  GLubyte *orMask,
    163 					  GLubyte *andMask,
    164                                           GLboolean viewport_z_clip )
    165 {
    166    const GLuint stride = clip_vec->stride;
    167    const GLuint count = clip_vec->count;
    168    const GLfloat *from = (GLfloat *)clip_vec->start;
    169 
    170    GLubyte tmpOrMask = *orMask;
    171    GLubyte tmpAndMask = *andMask;
    172    GLuint i;
    173 
    174    (void) viewport_z_clip;
    175 
    176    for ( i = 0 ; i < count ; i++, STRIDE_F(from, stride) ) {
    177       const GLfloat cx = from[0], cy = from[1];
    178       GLubyte mask = 0;
    179       if ( cx >  1.0 )		mask |= CLIP_RIGHT_BIT;
    180       else if ( cx < -1.0 )	mask |= CLIP_LEFT_BIT;
    181       if ( cy >  1.0 )		mask |= CLIP_TOP_BIT;
    182       else if ( cy < -1.0 )	mask |= CLIP_BOTTOM_BIT;
    183       clipMask[i] = mask;
    184       tmpOrMask |= mask;
    185       tmpAndMask &= mask;
    186    }
    187 
    188    *orMask = tmpOrMask;
    189    *andMask = tmpAndMask;
    190    return clip_vec;
    191 }
    192 
    193 static clip_func ref_cliptest[5] = {
    194    0,
    195    0,
    196    ref_cliptest_points2,
    197    ref_cliptest_points3,
    198    ref_cliptest_points4
    199 };
    200 
    201 
    202 /* =============================================================
    203  * Cliptest tests
    204  */
    205 
    206 ALIGN16(static GLfloat, s[TEST_COUNT][4]);
    207 ALIGN16(static GLfloat, d[TEST_COUNT][4]);
    208 ALIGN16(static GLfloat, r[TEST_COUNT][4]);
    209 
    210 
    211 /**
    212  * Check if X, Y or Z component of the coordinate is close to W, in terms
    213  * of the clip test.
    214  */
    215 static GLboolean
    216 xyz_close_to_w(const GLfloat c[4])
    217 {
    218    float k = 0.0001;
    219    return (fabs(c[0] - c[3]) < k ||
    220            fabs(c[1] - c[3]) < k ||
    221            fabs(c[2] - c[3]) < k ||
    222            fabs(-c[0] - c[3]) < k ||
    223            fabs(-c[1] - c[3]) < k ||
    224            fabs(-c[2] - c[3]) < k);
    225 }
    226 
    227 
    228 
    229 static int test_cliptest_function( clip_func func, int np,
    230 				   int psize, long *cycles )
    231 {
    232    GLvector4f source[1], dest[1], ref[1];
    233    GLubyte dm[TEST_COUNT], dco, dca;
    234    GLubyte rm[TEST_COUNT], rco, rca;
    235    int i, j;
    236 #ifdef  RUN_DEBUG_BENCHMARK
    237    int cycle_i;                /* the counter for the benchmarks we run */
    238 #endif
    239    GLboolean viewport_z_clip = GL_TRUE;
    240 
    241    (void) cycles;
    242 
    243    if ( psize > 4 ) {
    244       _mesa_problem( NULL, "test_cliptest_function called with psize > 4\n" );
    245       return 0;
    246    }
    247 
    248    for ( i = 0 ; i < TEST_COUNT ; i++) {
    249       ASSIGN_4V( d[i], 0.0, 0.0, 0.0, 1.0 );
    250       ASSIGN_4V( s[i], 0.0, 0.0, 0.0, 1.0 );
    251       for ( j = 0 ; j < psize ; j++ )
    252          s[i][j] = rnd();
    253    }
    254 
    255    source->data = (GLfloat(*)[4])s;
    256    source->start = (GLfloat *)s;
    257    source->count = TEST_COUNT;
    258    source->stride = sizeof(s[0]);
    259    source->size = 4;
    260    source->flags = 0;
    261 
    262    dest->data = (GLfloat(*)[4])d;
    263    dest->start = (GLfloat *)d;
    264    dest->count = TEST_COUNT;
    265    dest->stride = sizeof(float[4]);
    266    dest->size = 0;
    267    dest->flags = 0;
    268 
    269    ref->data = (GLfloat(*)[4])r;
    270    ref->start = (GLfloat *)r;
    271    ref->count = TEST_COUNT;
    272    ref->stride = sizeof(float[4]);
    273    ref->size = 0;
    274    ref->flags = 0;
    275 
    276    dco = rco = 0;
    277    dca = rca = CLIP_FRUSTUM_BITS;
    278 
    279    ref_cliptest[psize]( source, ref, rm, &rco, &rca, viewport_z_clip );
    280 
    281    if ( mesa_profile ) {
    282       BEGIN_RACE( *cycles );
    283       func( source, dest, dm, &dco, &dca, viewport_z_clip );
    284       END_RACE( *cycles );
    285    }
    286    else {
    287       func( source, dest, dm, &dco, &dca, viewport_z_clip );
    288    }
    289 
    290    if ( dco != rco ) {
    291       printf( "\n-----------------------------\n" );
    292       printf( "dco = 0x%02x   rco = 0x%02x\n", dco, rco );
    293       return 0;
    294    }
    295    if ( dca != rca ) {
    296       printf( "\n-----------------------------\n" );
    297       printf( "dca = 0x%02x   rca = 0x%02x\n", dca, rca );
    298       return 0;
    299    }
    300    for ( i = 0 ; i < TEST_COUNT ; i++ ) {
    301       if ( dm[i] != rm[i] ) {
    302          GLfloat *c = source->start;
    303          STRIDE_F(c, source->stride * i);
    304          if (psize == 4 && xyz_close_to_w(c)) {
    305             /* The coordinate is very close to the clip plane.  The clipmask
    306              * may vary depending on code path, but that's OK.
    307              */
    308             continue;
    309          }
    310 	 printf( "\n-----------------------------\n" );
    311 	 printf( "mask[%d] = 0x%02x   ref mask[%d] = 0x%02x\n", i, dm[i], i,rm[i] );
    312          printf(" coord = %f, %f, %f, %f\n",
    313                 c[0], c[1], c[2], c[3]);
    314 	 return 0;
    315       }
    316    }
    317 
    318    /* Only verify output on projected points4 case.  FIXME: Do we need
    319     * to test other cases?
    320     */
    321    if ( np || psize < 4 )
    322       return 1;
    323 
    324    for ( i = 0 ; i < TEST_COUNT ; i++ ) {
    325       for ( j = 0 ; j < 4 ; j++ ) {
    326          if ( significand_match( d[i][j], r[i][j] ) < REQUIRED_PRECISION ) {
    327             printf( "\n-----------------------------\n" );
    328             printf( "(i = %i, j = %i)  dm = 0x%02x   rm = 0x%02x\n",
    329 		    i, j, dm[i], rm[i] );
    330             printf( "%f \t %f \t [diff = %e - %i bit missed]\n",
    331 		    d[i][0], r[i][0], r[i][0]-d[i][0],
    332 		    MAX_PRECISION - significand_match( d[i][0], r[i][0] ) );
    333             printf( "%f \t %f \t [diff = %e - %i bit missed]\n",
    334 		    d[i][1], r[i][1], r[i][1]-d[i][1],
    335 		    MAX_PRECISION - significand_match( d[i][1], r[i][1] ) );
    336             printf( "%f \t %f \t [diff = %e - %i bit missed]\n",
    337 		    d[i][2], r[i][2], r[i][2]-d[i][2],
    338 		    MAX_PRECISION - significand_match( d[i][2], r[i][2] ) );
    339             printf( "%f \t %f \t [diff = %e - %i bit missed]\n",
    340 		    d[i][3], r[i][3], r[i][3]-d[i][3],
    341 		    MAX_PRECISION - significand_match( d[i][3], r[i][3] ) );
    342             return 0;
    343          }
    344       }
    345    }
    346 
    347    return 1;
    348 }
    349 
    350 void _math_test_all_cliptest_functions( char *description )
    351 {
    352    int np, psize;
    353    long benchmark_tab[2][4];
    354    static int first_time = 1;
    355 
    356    if ( first_time ) {
    357       first_time = 0;
    358       mesa_profile = getenv( "MESA_PROFILE" );
    359    }
    360 
    361 #ifdef RUN_DEBUG_BENCHMARK
    362    if ( mesa_profile ) {
    363       if ( !counter_overhead ) {
    364 	 INIT_COUNTER();
    365 	 printf( "counter overhead: %ld cycles\n\n", counter_overhead );
    366       }
    367       printf( "cliptest results after hooking in %s functions:\n", description );
    368    }
    369 #endif
    370 
    371 #ifdef RUN_DEBUG_BENCHMARK
    372    if ( mesa_profile ) {
    373       printf( "\n\t" );
    374       for ( psize = 2 ; psize <= 4 ; psize++ ) {
    375 	 printf( " p%d\t", psize );
    376       }
    377       printf( "\n--------------------------------------------------------\n\t" );
    378    }
    379 #endif
    380 
    381    for ( np = 0 ; np < 2 ; np++ ) {
    382       for ( psize = 2 ; psize <= 4 ; psize++ ) {
    383 	 clip_func func = clip_tab[np][psize];
    384 	 long *cycles = &(benchmark_tab[np][psize-1]);
    385 
    386 	 if ( test_cliptest_function( func, np, psize, cycles ) == 0 ) {
    387 	    char buf[100];
    388 	    sprintf( buf, "%s[%d] failed test (%s)",
    389 		     cnames[np], psize, description );
    390 	    _mesa_problem( NULL, "%s", buf );
    391 	 }
    392 #ifdef RUN_DEBUG_BENCHMARK
    393 	 if ( mesa_profile )
    394 	    printf( " %li\t", benchmark_tab[np][psize-1] );
    395 #endif
    396       }
    397 #ifdef RUN_DEBUG_BENCHMARK
    398       if ( mesa_profile )
    399 	 printf( " | [%s]\n\t", cstrings[np] );
    400 #endif
    401    }
    402 #ifdef RUN_DEBUG_BENCHMARK
    403    if ( mesa_profile )
    404       printf( "\n" );
    405 #endif
    406 }
    407 
    408 
    409 #endif /* DEBUG_MATH */
    410