Home | History | Annotate | Download | only in main
      1 /*
      2  * Mesa 3-D graphics library
      3  * Version:  7.5
      4  *
      5  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
      6  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the "Software"),
     10  * to deal in the Software without restriction, including without limitation
     11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     12  * and/or sell copies of the Software, and to permit persons to whom the
     13  * Software is furnished to do so, subject to the following conditions:
     14  *
     15  * The above copyright notice and this permission notice shall be included
     16  * in all copies or substantial portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     21  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     22  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     24  */
     25 
     26 /**
     27  * \file feedback.c
     28  * Selection and feedback modes functions.
     29  */
     30 
     31 
     32 #include "glheader.h"
     33 #include "colormac.h"
     34 #include "context.h"
     35 #include "enums.h"
     36 #include "feedback.h"
     37 #include "macros.h"
     38 #include "mfeatures.h"
     39 #include "mtypes.h"
     40 #include "main/dispatch.h"
     41 
     42 
     43 #if FEATURE_feedback
     44 
     45 
     46 #define FB_3D		0x01
     47 #define FB_4D		0x02
     48 #define FB_COLOR	0x04
     49 #define FB_TEXTURE	0X08
     50 
     51 
     52 
     53 static void GLAPIENTRY
     54 _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
     55 {
     56    GET_CURRENT_CONTEXT(ctx);
     57    ASSERT_OUTSIDE_BEGIN_END(ctx);
     58 
     59    if (ctx->RenderMode==GL_FEEDBACK) {
     60       _mesa_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
     61       return;
     62    }
     63    if (size<0) {
     64       _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
     65       return;
     66    }
     67    if (!buffer && size > 0) {
     68       _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(buffer==NULL)" );
     69       ctx->Feedback.BufferSize = 0;
     70       return;
     71    }
     72 
     73    switch (type) {
     74       case GL_2D:
     75 	 ctx->Feedback._Mask = 0;
     76 	 break;
     77       case GL_3D:
     78 	 ctx->Feedback._Mask = FB_3D;
     79 	 break;
     80       case GL_3D_COLOR:
     81 	 ctx->Feedback._Mask = (FB_3D | FB_COLOR);
     82 	 break;
     83       case GL_3D_COLOR_TEXTURE:
     84 	 ctx->Feedback._Mask = (FB_3D | FB_COLOR | FB_TEXTURE);
     85 	 break;
     86       case GL_4D_COLOR_TEXTURE:
     87 	 ctx->Feedback._Mask = (FB_3D | FB_4D | FB_COLOR | FB_TEXTURE);
     88 	 break;
     89       default:
     90          _mesa_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
     91 	 return;
     92    }
     93 
     94    FLUSH_VERTICES(ctx, _NEW_RENDERMODE); /* Always flush */
     95    ctx->Feedback.Type = type;
     96    ctx->Feedback.BufferSize = size;
     97    ctx->Feedback.Buffer = buffer;
     98    ctx->Feedback.Count = 0;	              /* Becaues of this. */
     99 }
    100 
    101 
    102 static void GLAPIENTRY
    103 _mesa_PassThrough( GLfloat token )
    104 {
    105    GET_CURRENT_CONTEXT(ctx);
    106    ASSERT_OUTSIDE_BEGIN_END(ctx);
    107 
    108    if (ctx->RenderMode==GL_FEEDBACK) {
    109       FLUSH_VERTICES(ctx, 0);
    110       _mesa_feedback_token( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
    111       _mesa_feedback_token( ctx, token );
    112    }
    113 }
    114 
    115 
    116 /**
    117  * Put a vertex into the feedback buffer.
    118  */
    119 void
    120 _mesa_feedback_vertex(struct gl_context *ctx,
    121                       const GLfloat win[4],
    122                       const GLfloat color[4],
    123                       const GLfloat texcoord[4])
    124 {
    125    _mesa_feedback_token( ctx, win[0] );
    126    _mesa_feedback_token( ctx, win[1] );
    127    if (ctx->Feedback._Mask & FB_3D) {
    128       _mesa_feedback_token( ctx, win[2] );
    129    }
    130    if (ctx->Feedback._Mask & FB_4D) {
    131       _mesa_feedback_token( ctx, win[3] );
    132    }
    133    if (ctx->Feedback._Mask & FB_COLOR) {
    134       _mesa_feedback_token( ctx, color[0] );
    135       _mesa_feedback_token( ctx, color[1] );
    136       _mesa_feedback_token( ctx, color[2] );
    137       _mesa_feedback_token( ctx, color[3] );
    138    }
    139    if (ctx->Feedback._Mask & FB_TEXTURE) {
    140       _mesa_feedback_token( ctx, texcoord[0] );
    141       _mesa_feedback_token( ctx, texcoord[1] );
    142       _mesa_feedback_token( ctx, texcoord[2] );
    143       _mesa_feedback_token( ctx, texcoord[3] );
    144    }
    145 }
    146 
    147 
    148 /**********************************************************************/
    149 /** \name Selection */
    150 /*@{*/
    151 
    152 /**
    153  * Establish a buffer for selection mode values.
    154  *
    155  * \param size buffer size.
    156  * \param buffer buffer.
    157  *
    158  * \sa glSelectBuffer().
    159  *
    160  * \note this function can't be put in a display list.
    161  *
    162  * Verifies we're not in selection mode, flushes the vertices and initialize
    163  * the fields in __struct gl_contextRec::Select with the given buffer.
    164  */
    165 static void GLAPIENTRY
    166 _mesa_SelectBuffer( GLsizei size, GLuint *buffer )
    167 {
    168    GET_CURRENT_CONTEXT(ctx);
    169    ASSERT_OUTSIDE_BEGIN_END(ctx);
    170 
    171    if (size < 0) {
    172       _mesa_error(ctx, GL_INVALID_VALUE, "glSelectBuffer(size)");
    173       return;
    174    }
    175 
    176    if (ctx->RenderMode==GL_SELECT) {
    177       _mesa_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
    178       return;			/* KW: added return */
    179    }
    180 
    181    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    182    ctx->Select.Buffer = buffer;
    183    ctx->Select.BufferSize = size;
    184    ctx->Select.BufferCount = 0;
    185    ctx->Select.HitFlag = GL_FALSE;
    186    ctx->Select.HitMinZ = 1.0;
    187    ctx->Select.HitMaxZ = 0.0;
    188 }
    189 
    190 
    191 /**
    192  * Write a value of a record into the selection buffer.
    193  *
    194  * \param ctx GL context.
    195  * \param value value.
    196  *
    197  * Verifies there is free space in the buffer to write the value and
    198  * increments the pointer.
    199  */
    200 static inline void
    201 write_record(struct gl_context *ctx, GLuint value)
    202 {
    203    if (ctx->Select.BufferCount < ctx->Select.BufferSize) {
    204       ctx->Select.Buffer[ctx->Select.BufferCount] = value;
    205    }
    206    ctx->Select.BufferCount++;
    207 }
    208 
    209 
    210 /**
    211  * Update the hit flag and the maximum and minimum depth values.
    212  *
    213  * \param ctx GL context.
    214  * \param z depth.
    215  *
    216  * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
    217  * gl_selection::HitMaxZ.
    218  */
    219 void
    220 _mesa_update_hitflag(struct gl_context *ctx, GLfloat z)
    221 {
    222    ctx->Select.HitFlag = GL_TRUE;
    223    if (z < ctx->Select.HitMinZ) {
    224       ctx->Select.HitMinZ = z;
    225    }
    226    if (z > ctx->Select.HitMaxZ) {
    227       ctx->Select.HitMaxZ = z;
    228    }
    229 }
    230 
    231 
    232 /**
    233  * Write the hit record.
    234  *
    235  * \param ctx GL context.
    236  *
    237  * Write the hit record, i.e., the number of names in the stack, the minimum and
    238  * maximum depth values and the number of names in the name stack at the time
    239  * of the event. Resets the hit flag.
    240  *
    241  * \sa gl_selection.
    242  */
    243 static void
    244 write_hit_record(struct gl_context *ctx)
    245 {
    246    GLuint i;
    247    GLuint zmin, zmax, zscale = (~0u);
    248 
    249    /* HitMinZ and HitMaxZ are in [0,1].  Multiply these values by */
    250    /* 2^32-1 and round to nearest unsigned integer. */
    251 
    252    assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
    253    zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
    254    zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
    255 
    256    write_record( ctx, ctx->Select.NameStackDepth );
    257    write_record( ctx, zmin );
    258    write_record( ctx, zmax );
    259    for (i = 0; i < ctx->Select.NameStackDepth; i++) {
    260       write_record( ctx, ctx->Select.NameStack[i] );
    261    }
    262 
    263    ctx->Select.Hits++;
    264    ctx->Select.HitFlag = GL_FALSE;
    265    ctx->Select.HitMinZ = 1.0;
    266    ctx->Select.HitMaxZ = -1.0;
    267 }
    268 
    269 
    270 /**
    271  * Initialize the name stack.
    272  *
    273  * Verifies we are in select mode and resets the name stack depth and resets
    274  * the hit record data in gl_selection. Marks new render mode in
    275  * __struct gl_contextRec::NewState.
    276  */
    277 static void GLAPIENTRY
    278 _mesa_InitNames( void )
    279 {
    280    GET_CURRENT_CONTEXT(ctx);
    281    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
    282 
    283    /* Record the hit before the HitFlag is wiped out again. */
    284    if (ctx->RenderMode == GL_SELECT) {
    285       if (ctx->Select.HitFlag) {
    286          write_hit_record( ctx );
    287       }
    288    }
    289    ctx->Select.NameStackDepth = 0;
    290    ctx->Select.HitFlag = GL_FALSE;
    291    ctx->Select.HitMinZ = 1.0;
    292    ctx->Select.HitMaxZ = 0.0;
    293    ctx->NewState |= _NEW_RENDERMODE;
    294 }
    295 
    296 
    297 /**
    298  * Load the top-most name of the name stack.
    299  *
    300  * \param name name.
    301  *
    302  * Verifies we are in selection mode and that the name stack is not empty.
    303  * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
    304  * and replace the top-most name in the stack.
    305  *
    306  * sa __struct gl_contextRec::Select.
    307  */
    308 static void GLAPIENTRY
    309 _mesa_LoadName( GLuint name )
    310 {
    311    GET_CURRENT_CONTEXT(ctx);
    312    ASSERT_OUTSIDE_BEGIN_END(ctx);
    313 
    314    if (ctx->RenderMode != GL_SELECT) {
    315       return;
    316    }
    317    if (ctx->Select.NameStackDepth == 0) {
    318       _mesa_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
    319       return;
    320    }
    321 
    322    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    323 
    324    if (ctx->Select.HitFlag) {
    325       write_hit_record( ctx );
    326    }
    327    if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
    328       ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
    329    }
    330    else {
    331       ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
    332    }
    333 }
    334 
    335 
    336 /**
    337  * Push a name into the name stack.
    338  *
    339  * \param name name.
    340  *
    341  * Verifies we are in selection mode and that the name stack is not full.
    342  * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
    343  * and adds the name to the top of the name stack.
    344  *
    345  * sa __struct gl_contextRec::Select.
    346  */
    347 static void GLAPIENTRY
    348 _mesa_PushName( GLuint name )
    349 {
    350    GET_CURRENT_CONTEXT(ctx);
    351    ASSERT_OUTSIDE_BEGIN_END(ctx);
    352 
    353    if (ctx->RenderMode != GL_SELECT) {
    354       return;
    355    }
    356 
    357    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    358    if (ctx->Select.HitFlag) {
    359       write_hit_record( ctx );
    360    }
    361    if (ctx->Select.NameStackDepth >= MAX_NAME_STACK_DEPTH) {
    362       _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
    363    }
    364    else
    365       ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
    366 }
    367 
    368 
    369 /**
    370  * Pop a name into the name stack.
    371  *
    372  * Verifies we are in selection mode and that the name stack is not empty.
    373  * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
    374  * and removes top-most name in the name stack.
    375  *
    376  * sa __struct gl_contextRec::Select.
    377  */
    378 static void GLAPIENTRY
    379 _mesa_PopName( void )
    380 {
    381    GET_CURRENT_CONTEXT(ctx);
    382    ASSERT_OUTSIDE_BEGIN_END(ctx);
    383 
    384    if (ctx->RenderMode != GL_SELECT) {
    385       return;
    386    }
    387 
    388    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    389    if (ctx->Select.HitFlag) {
    390       write_hit_record( ctx );
    391    }
    392    if (ctx->Select.NameStackDepth == 0) {
    393       _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
    394    }
    395    else
    396       ctx->Select.NameStackDepth--;
    397 }
    398 
    399 /*@}*/
    400 
    401 
    402 /**********************************************************************/
    403 /** \name Render Mode */
    404 /*@{*/
    405 
    406 /**
    407  * Set rasterization mode.
    408  *
    409  * \param mode rasterization mode.
    410  *
    411  * \note this function can't be put in a display list.
    412  *
    413  * \sa glRenderMode().
    414  *
    415  * Flushes the vertices and do the necessary cleanup according to the previous
    416  * rasterization mode, such as writing the hit record or resent the select
    417  * buffer index when exiting the select mode. Updates
    418  * __struct gl_contextRec::RenderMode and notifies the driver via the
    419  * dd_function_table::RenderMode callback.
    420  */
    421 GLint GLAPIENTRY
    422 _mesa_RenderMode( GLenum mode )
    423 {
    424    GET_CURRENT_CONTEXT(ctx);
    425    GLint result;
    426    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
    427 
    428    if (MESA_VERBOSE & VERBOSE_API)
    429       _mesa_debug(ctx, "glRenderMode %s\n", _mesa_lookup_enum_by_nr(mode));
    430 
    431    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    432 
    433    switch (ctx->RenderMode) {
    434       case GL_RENDER:
    435 	 result = 0;
    436 	 break;
    437       case GL_SELECT:
    438 	 if (ctx->Select.HitFlag) {
    439 	    write_hit_record( ctx );
    440 	 }
    441 	 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
    442 	    /* overflow */
    443 #ifdef DEBUG
    444             _mesa_warning(ctx, "Feedback buffer overflow");
    445 #endif
    446 	    result = -1;
    447 	 }
    448 	 else {
    449 	    result = ctx->Select.Hits;
    450 	 }
    451 	 ctx->Select.BufferCount = 0;
    452 	 ctx->Select.Hits = 0;
    453 	 ctx->Select.NameStackDepth = 0;
    454 	 break;
    455 #if _HAVE_FULL_GL
    456       case GL_FEEDBACK:
    457 	 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
    458 	    /* overflow */
    459 	    result = -1;
    460 	 }
    461 	 else {
    462 	    result = ctx->Feedback.Count;
    463 	 }
    464 	 ctx->Feedback.Count = 0;
    465 	 break;
    466 #endif
    467       default:
    468 	 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
    469 	 return 0;
    470    }
    471 
    472    switch (mode) {
    473       case GL_RENDER:
    474          break;
    475       case GL_SELECT:
    476 	 if (ctx->Select.BufferSize==0) {
    477 	    /* haven't called glSelectBuffer yet */
    478 	    _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
    479 	 }
    480 	 break;
    481 #if _HAVE_FULL_GL
    482       case GL_FEEDBACK:
    483 	 if (ctx->Feedback.BufferSize==0) {
    484 	    /* haven't called glFeedbackBuffer yet */
    485 	    _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
    486 	 }
    487 	 break;
    488 #endif
    489       default:
    490 	 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
    491 	 return 0;
    492    }
    493 
    494    ctx->RenderMode = mode;
    495    if (ctx->Driver.RenderMode)
    496       ctx->Driver.RenderMode( ctx, mode );
    497 
    498    return result;
    499 }
    500 
    501 /*@}*/
    502 
    503 
    504 void
    505 _mesa_init_feedback_dispatch(struct _glapi_table *disp)
    506 {
    507    SET_InitNames(disp, _mesa_InitNames);
    508    SET_FeedbackBuffer(disp, _mesa_FeedbackBuffer);
    509    SET_LoadName(disp, _mesa_LoadName);
    510    SET_PassThrough(disp, _mesa_PassThrough);
    511    SET_PopName(disp, _mesa_PopName);
    512    SET_PushName(disp, _mesa_PushName);
    513    SET_SelectBuffer(disp, _mesa_SelectBuffer);
    514    SET_RenderMode(disp, _mesa_RenderMode);
    515 }
    516 
    517 
    518 #endif /* FEATURE_feedback */
    519 
    520 
    521 /**********************************************************************/
    522 /** \name Initialization */
    523 /*@{*/
    524 
    525 /**
    526  * Initialize context feedback data.
    527  */
    528 void _mesa_init_feedback( struct gl_context * ctx )
    529 {
    530    /* Feedback */
    531    ctx->Feedback.Type = GL_2D;   /* TODO: verify */
    532    ctx->Feedback.Buffer = NULL;
    533    ctx->Feedback.BufferSize = 0;
    534    ctx->Feedback.Count = 0;
    535 
    536    /* Selection/picking */
    537    ctx->Select.Buffer = NULL;
    538    ctx->Select.BufferSize = 0;
    539    ctx->Select.BufferCount = 0;
    540    ctx->Select.Hits = 0;
    541    ctx->Select.NameStackDepth = 0;
    542 
    543    /* Miscellaneous */
    544    ctx->RenderMode = GL_RENDER;
    545 }
    546 
    547 /*@}*/
    548