Home | History | Annotate | Download | only in main
      1 /*
      2  * Mesa 3-D graphics library
      3  *
      4  * Copyright (C) 2010  VMware, Inc.  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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     20  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 
     25 /*
     26  * Vertex transform feedback support.
     27  *
     28  * Authors:
     29  *   Brian Paul
     30  */
     31 
     32 
     33 #include "buffers.h"
     34 #include "bufferobj.h"
     35 #include "context.h"
     36 #include "hash.h"
     37 #include "mfeatures.h"
     38 #include "mtypes.h"
     39 #include "transformfeedback.h"
     40 #include "shaderapi.h"
     41 #include "shaderobj.h"
     42 #include "main/dispatch.h"
     43 
     44 #include "program/prog_parameter.h"
     45 
     46 
     47 #if FEATURE_EXT_transform_feedback
     48 
     49 
     50 /**
     51  * Do reference counting of transform feedback buffers.
     52  */
     53 static void
     54 reference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
     55                                     struct gl_transform_feedback_object *obj)
     56 {
     57    if (*ptr == obj)
     58       return;
     59 
     60    if (*ptr) {
     61       /* Unreference the old object */
     62       struct gl_transform_feedback_object *oldObj = *ptr;
     63 
     64       ASSERT(oldObj->RefCount > 0);
     65       oldObj->RefCount--;
     66 
     67       if (oldObj->RefCount == 0) {
     68          GET_CURRENT_CONTEXT(ctx);
     69          if (ctx)
     70             ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
     71       }
     72 
     73       *ptr = NULL;
     74    }
     75    ASSERT(!*ptr);
     76 
     77    if (obj) {
     78       /* reference new object */
     79       if (obj->RefCount == 0) {
     80          _mesa_problem(NULL, "referencing deleted transform feedback object");
     81          *ptr = NULL;
     82       }
     83       else {
     84          obj->RefCount++;
     85          *ptr = obj;
     86       }
     87    }
     88 }
     89 
     90 
     91 /**
     92  * Check that all the buffer objects currently bound for transform
     93  * feedback actually exist.  Raise a GL_INVALID_OPERATION error if
     94  * any buffers are missing.
     95  * \return GL_TRUE for success, GL_FALSE if error
     96  */
     97 GLboolean
     98 _mesa_validate_transform_feedback_buffers(struct gl_context *ctx)
     99 {
    100    /* XXX to do */
    101    return GL_TRUE;
    102 }
    103 
    104 
    105 
    106 /**
    107  * Per-context init for transform feedback.
    108  */
    109 void
    110 _mesa_init_transform_feedback(struct gl_context *ctx)
    111 {
    112    /* core mesa expects this, even a dummy one, to be available */
    113    ASSERT(ctx->Driver.NewTransformFeedback);
    114 
    115    ctx->TransformFeedback.DefaultObject =
    116       ctx->Driver.NewTransformFeedback(ctx, 0);
    117 
    118    assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
    119 
    120    reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
    121                                        ctx->TransformFeedback.DefaultObject);
    122 
    123    assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
    124 
    125    ctx->TransformFeedback.Objects = _mesa_NewHashTable();
    126 
    127    _mesa_reference_buffer_object(ctx,
    128                                  &ctx->TransformFeedback.CurrentBuffer,
    129                                  ctx->Shared->NullBufferObj);
    130 }
    131 
    132 
    133 
    134 /**
    135  * Callback for _mesa_HashDeleteAll().
    136  */
    137 static void
    138 delete_cb(GLuint key, void *data, void *userData)
    139 {
    140    struct gl_context *ctx = (struct gl_context *) userData;
    141    struct gl_transform_feedback_object *obj =
    142       (struct gl_transform_feedback_object *) data;
    143 
    144    ctx->Driver.DeleteTransformFeedback(ctx, obj);
    145 }
    146 
    147 
    148 /**
    149  * Per-context free/clean-up for transform feedback.
    150  */
    151 void
    152 _mesa_free_transform_feedback(struct gl_context *ctx)
    153 {
    154    /* core mesa expects this, even a dummy one, to be available */
    155    ASSERT(ctx->Driver.NewTransformFeedback);
    156 
    157    _mesa_reference_buffer_object(ctx,
    158                                  &ctx->TransformFeedback.CurrentBuffer,
    159                                  NULL);
    160 
    161    /* Delete all feedback objects */
    162    _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
    163    _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
    164 
    165    /* Delete the default feedback object */
    166    assert(ctx->Driver.DeleteTransformFeedback);
    167    ctx->Driver.DeleteTransformFeedback(ctx,
    168                                        ctx->TransformFeedback.DefaultObject);
    169 
    170    ctx->TransformFeedback.CurrentObject = NULL;
    171 }
    172 
    173 
    174 #else /* FEATURE_EXT_transform_feedback */
    175 
    176 /* forward declarations */
    177 static struct gl_transform_feedback_object *
    178 new_transform_feedback(struct gl_context *ctx, GLuint name);
    179 
    180 static void
    181 delete_transform_feedback(struct gl_context *ctx,
    182                           struct gl_transform_feedback_object *obj);
    183 
    184 /* dummy per-context init/clean-up for transform feedback */
    185 void
    186 _mesa_init_transform_feedback(struct gl_context *ctx)
    187 {
    188    ctx->TransformFeedback.DefaultObject = new_transform_feedback(ctx, 0);
    189    ctx->TransformFeedback.CurrentObject = ctx->TransformFeedback.DefaultObject;
    190    _mesa_reference_buffer_object(ctx,
    191                                  &ctx->TransformFeedback.CurrentBuffer,
    192                                  ctx->Shared->NullBufferObj);
    193 }
    194 
    195 void
    196 _mesa_free_transform_feedback(struct gl_context *ctx)
    197 {
    198    _mesa_reference_buffer_object(ctx,
    199                                  &ctx->TransformFeedback.CurrentBuffer,
    200                                  NULL);
    201    ctx->TransformFeedback.CurrentObject = NULL;
    202    delete_transform_feedback(ctx, ctx->TransformFeedback.DefaultObject);
    203 }
    204 
    205 #endif /* FEATURE_EXT_transform_feedback */
    206 
    207 
    208 /** Default fallback for ctx->Driver.NewTransformFeedback() */
    209 static struct gl_transform_feedback_object *
    210 new_transform_feedback(struct gl_context *ctx, GLuint name)
    211 {
    212    struct gl_transform_feedback_object *obj;
    213    obj = CALLOC_STRUCT(gl_transform_feedback_object);
    214    if (obj) {
    215       obj->Name = name;
    216       obj->RefCount = 1;
    217    }
    218    return obj;
    219 }
    220 
    221 /** Default fallback for ctx->Driver.DeleteTransformFeedback() */
    222 static void
    223 delete_transform_feedback(struct gl_context *ctx,
    224                           struct gl_transform_feedback_object *obj)
    225 {
    226    GLuint i;
    227 
    228    for (i = 0; i < Elements(obj->Buffers); i++) {
    229       _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
    230    }
    231 
    232    free(obj);
    233 }
    234 
    235 
    236 #if FEATURE_EXT_transform_feedback
    237 
    238 
    239 /** Default fallback for ctx->Driver.BeginTransformFeedback() */
    240 static void
    241 begin_transform_feedback(struct gl_context *ctx, GLenum mode,
    242                          struct gl_transform_feedback_object *obj)
    243 {
    244    /* nop */
    245 }
    246 
    247 /** Default fallback for ctx->Driver.EndTransformFeedback() */
    248 static void
    249 end_transform_feedback(struct gl_context *ctx,
    250                        struct gl_transform_feedback_object *obj)
    251 {
    252    /* nop */
    253 }
    254 
    255 /** Default fallback for ctx->Driver.PauseTransformFeedback() */
    256 static void
    257 pause_transform_feedback(struct gl_context *ctx,
    258                          struct gl_transform_feedback_object *obj)
    259 {
    260    /* nop */
    261 }
    262 
    263 /** Default fallback for ctx->Driver.ResumeTransformFeedback() */
    264 static void
    265 resume_transform_feedback(struct gl_context *ctx,
    266                           struct gl_transform_feedback_object *obj)
    267 {
    268    /* nop */
    269 }
    270 
    271 
    272 /**
    273  * Plug in default device driver functions for transform feedback.
    274  * Most drivers will override some/all of these.
    275  */
    276 void
    277 _mesa_init_transform_feedback_functions(struct dd_function_table *driver)
    278 {
    279    driver->NewTransformFeedback = new_transform_feedback;
    280    driver->DeleteTransformFeedback = delete_transform_feedback;
    281    driver->BeginTransformFeedback = begin_transform_feedback;
    282    driver->EndTransformFeedback = end_transform_feedback;
    283    driver->PauseTransformFeedback = pause_transform_feedback;
    284    driver->ResumeTransformFeedback = resume_transform_feedback;
    285 }
    286 
    287 
    288 void
    289 _mesa_init_transform_feedback_dispatch(struct _glapi_table *disp)
    290 {
    291    /* EXT_transform_feedback */
    292    SET_BeginTransformFeedbackEXT(disp, _mesa_BeginTransformFeedback);
    293    SET_EndTransformFeedbackEXT(disp, _mesa_EndTransformFeedback);
    294    SET_BindBufferOffsetEXT(disp, _mesa_BindBufferOffsetEXT);
    295    SET_TransformFeedbackVaryingsEXT(disp, _mesa_TransformFeedbackVaryings);
    296    SET_GetTransformFeedbackVaryingEXT(disp, _mesa_GetTransformFeedbackVarying);
    297    /* ARB_transform_feedback2 */
    298    SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback);
    299    SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks);
    300    SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks);
    301    SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback);
    302    SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback);
    303    SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback);
    304 }
    305 
    306 
    307 /**
    308  ** Begin API functions
    309  **/
    310 
    311 
    312 void GLAPIENTRY
    313 _mesa_BeginTransformFeedback(GLenum mode)
    314 {
    315    struct gl_transform_feedback_object *obj;
    316    struct gl_transform_feedback_info *info;
    317    int i;
    318    GET_CURRENT_CONTEXT(ctx);
    319 
    320    obj = ctx->TransformFeedback.CurrentObject;
    321 
    322    if (ctx->Shader.CurrentVertexProgram == NULL) {
    323       _mesa_error(ctx, GL_INVALID_OPERATION,
    324                   "glBeginTransformFeedback(no program active)");
    325       return;
    326    }
    327 
    328    info = &ctx->Shader.CurrentVertexProgram->LinkedTransformFeedback;
    329 
    330    if (info->NumOutputs == 0) {
    331       _mesa_error(ctx, GL_INVALID_OPERATION,
    332                   "glBeginTransformFeedback(no varyings to record)");
    333       return;
    334    }
    335 
    336    switch (mode) {
    337    case GL_POINTS:
    338    case GL_LINES:
    339    case GL_TRIANGLES:
    340       /* legal */
    341       break;
    342    default:
    343       _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
    344       return;
    345    }
    346 
    347    if (obj->Active) {
    348       _mesa_error(ctx, GL_INVALID_OPERATION,
    349                   "glBeginTransformFeedback(already active)");
    350       return;
    351    }
    352 
    353    for (i = 0; i < info->NumBuffers; ++i) {
    354       if (obj->BufferNames[i] == 0) {
    355          _mesa_error(ctx, GL_INVALID_OPERATION,
    356                      "glBeginTransformFeedback(binding point %d does not have "
    357                      "a buffer object bound)", i);
    358          return;
    359       }
    360    }
    361 
    362    FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
    363    obj->Active = GL_TRUE;
    364    ctx->TransformFeedback.Mode = mode;
    365 
    366    assert(ctx->Driver.BeginTransformFeedback);
    367    ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
    368 }
    369 
    370 
    371 void GLAPIENTRY
    372 _mesa_EndTransformFeedback(void)
    373 {
    374    struct gl_transform_feedback_object *obj;
    375    GET_CURRENT_CONTEXT(ctx);
    376 
    377    obj = ctx->TransformFeedback.CurrentObject;
    378 
    379    if (!obj->Active) {
    380       _mesa_error(ctx, GL_INVALID_OPERATION,
    381                   "glEndTransformFeedback(not active)");
    382       return;
    383    }
    384 
    385    FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
    386    ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
    387    ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
    388    ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
    389 
    390    assert(ctx->Driver.EndTransformFeedback);
    391    ctx->Driver.EndTransformFeedback(ctx, obj);
    392 }
    393 
    394 
    395 /**
    396  * Helper used by BindBufferRange() and BindBufferBase().
    397  */
    398 static void
    399 bind_buffer_range(struct gl_context *ctx, GLuint index,
    400                   struct gl_buffer_object *bufObj,
    401                   GLintptr offset, GLsizeiptr size)
    402 {
    403    struct gl_transform_feedback_object *obj =
    404       ctx->TransformFeedback.CurrentObject;
    405 
    406    /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because
    407     * transform feedback buffers can't be changed while transform feedback is
    408     * active.
    409     */
    410 
    411    /* The general binding point */
    412    _mesa_reference_buffer_object(ctx,
    413                                  &ctx->TransformFeedback.CurrentBuffer,
    414                                  bufObj);
    415 
    416    /* The per-attribute binding point */
    417    _mesa_reference_buffer_object(ctx,
    418                                  &obj->Buffers[index],
    419                                  bufObj);
    420 
    421    obj->BufferNames[index] = bufObj->Name;
    422 
    423    obj->Offset[index] = offset;
    424    obj->Size[index] = size;
    425 }
    426 
    427 
    428 /**
    429  * Specify a buffer object to receive vertex shader results.  Plus,
    430  * specify the starting offset to place the results, and max size.
    431  * Called from the glBindBufferRange() function.
    432  */
    433 void
    434 _mesa_bind_buffer_range_transform_feedback(struct gl_context *ctx,
    435 					   GLuint index,
    436 					   struct gl_buffer_object *bufObj,
    437 					   GLintptr offset,
    438 					   GLsizeiptr size)
    439 {
    440    struct gl_transform_feedback_object *obj;
    441 
    442    obj = ctx->TransformFeedback.CurrentObject;
    443 
    444    if (obj->Active) {
    445       _mesa_error(ctx, GL_INVALID_OPERATION,
    446                   "glBindBufferRange(transform feedback active)");
    447       return;
    448    }
    449 
    450    if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
    451       _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
    452       return;
    453    }
    454 
    455    if (size & 0x3) {
    456       /* must a multiple of four */
    457       _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size);
    458       return;
    459    }
    460 
    461    if (offset & 0x3) {
    462       /* must be multiple of four */
    463       _mesa_error(ctx, GL_INVALID_VALUE,
    464                   "glBindBufferRange(offset=%d)", (int) offset);
    465       return;
    466    }
    467 
    468    bind_buffer_range(ctx, index, bufObj, offset, size);
    469 }
    470 
    471 
    472 /**
    473  * Specify a buffer object to receive vertex shader results.
    474  * As above, but start at offset = 0.
    475  * Called from the glBindBufferBase() function.
    476  */
    477 void
    478 _mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
    479 					  GLuint index,
    480 					  struct gl_buffer_object *bufObj)
    481 {
    482    struct gl_transform_feedback_object *obj;
    483    GLsizeiptr size;
    484 
    485    obj = ctx->TransformFeedback.CurrentObject;
    486 
    487    if (obj->Active) {
    488       _mesa_error(ctx, GL_INVALID_OPERATION,
    489                   "glBindBufferBase(transform feedback active)");
    490       return;
    491    }
    492 
    493    if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
    494       _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
    495       return;
    496    }
    497 
    498    /* default size is the buffer size rounded down to nearest
    499     * multiple of four.
    500     */
    501    size = bufObj->Size & ~0x3;
    502 
    503    bind_buffer_range(ctx, index, bufObj, 0, size);
    504 }
    505 
    506 
    507 /**
    508  * Specify a buffer object to receive vertex shader results, plus the
    509  * offset in the buffer to start placing results.
    510  * This function is part of GL_EXT_transform_feedback, but not GL3.
    511  */
    512 void GLAPIENTRY
    513 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
    514                           GLintptr offset)
    515 {
    516    struct gl_transform_feedback_object *obj;
    517    struct gl_buffer_object *bufObj;
    518    GET_CURRENT_CONTEXT(ctx);
    519    GLsizeiptr size;
    520 
    521    if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
    522       _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
    523       return;
    524    }
    525 
    526    obj = ctx->TransformFeedback.CurrentObject;
    527 
    528    if (obj->Active) {
    529       _mesa_error(ctx, GL_INVALID_OPERATION,
    530                   "glBindBufferOffsetEXT(transform feedback active)");
    531       return;
    532    }
    533 
    534    if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
    535       _mesa_error(ctx, GL_INVALID_VALUE,
    536                   "glBindBufferOffsetEXT(index=%d)", index);
    537       return;
    538    }
    539 
    540    if (offset & 0x3) {
    541       /* must be multiple of four */
    542       _mesa_error(ctx, GL_INVALID_VALUE,
    543                   "glBindBufferOffsetEXT(offset=%d)", (int) offset);
    544       return;
    545    }
    546 
    547    if (buffer == 0) {
    548       bufObj = ctx->Shared->NullBufferObj;
    549    } else {
    550       bufObj = _mesa_lookup_bufferobj(ctx, buffer);
    551    }
    552 
    553    if (!bufObj) {
    554       _mesa_error(ctx, GL_INVALID_OPERATION,
    555                   "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
    556       return;
    557    }
    558 
    559    /* default size is the buffer size rounded down to nearest
    560     * multiple of four.
    561     */
    562    size = (bufObj->Size - offset) & ~0x3;
    563 
    564    bind_buffer_range(ctx, index, bufObj, offset, size);
    565 }
    566 
    567 
    568 /**
    569  * This function specifies the vertex shader outputs to be written
    570  * to the feedback buffer(s), and in what order.
    571  */
    572 void GLAPIENTRY
    573 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
    574                                 const GLchar **varyings, GLenum bufferMode)
    575 {
    576    struct gl_shader_program *shProg;
    577    GLuint i;
    578    GET_CURRENT_CONTEXT(ctx);
    579 
    580    switch (bufferMode) {
    581    case GL_INTERLEAVED_ATTRIBS:
    582       break;
    583    case GL_SEPARATE_ATTRIBS:
    584       break;
    585    default:
    586       _mesa_error(ctx, GL_INVALID_ENUM,
    587                   "glTransformFeedbackVaryings(bufferMode)");
    588       return;
    589    }
    590 
    591    if (count < 0 ||
    592        (bufferMode == GL_SEPARATE_ATTRIBS &&
    593         (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
    594       _mesa_error(ctx, GL_INVALID_VALUE,
    595                   "glTransformFeedbackVaryings(count=%d)", count);
    596       return;
    597    }
    598 
    599    shProg = _mesa_lookup_shader_program(ctx, program);
    600    if (!shProg) {
    601       _mesa_error(ctx, GL_INVALID_VALUE,
    602                   "glTransformFeedbackVaryings(program=%u)", program);
    603       return;
    604    }
    605 
    606    if (ctx->Extensions.ARB_transform_feedback3) {
    607       if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
    608          unsigned buffers = 1;
    609 
    610          for (i = 0; i < count; i++) {
    611             if (strcmp(varyings[i], "gl_NextBuffer") == 0)
    612                buffers++;
    613          }
    614 
    615          if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
    616             _mesa_error(ctx, GL_INVALID_OPERATION,
    617                         "glTransformFeedbackVaryings(too many gl_NextBuffer "
    618                         "occurences)");
    619             return;
    620          }
    621       } else {
    622          for (i = 0; i < count; i++) {
    623             if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
    624                 strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
    625                 strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
    626                 strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
    627                 strcmp(varyings[i], "gl_SkipComponents4") == 0) {
    628                _mesa_error(ctx, GL_INVALID_OPERATION,
    629                            "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
    630                            "varying=%s)",
    631                            varyings[i]);
    632                return;
    633             }
    634          }
    635       }
    636    }
    637 
    638    /* free existing varyings, if any */
    639    for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
    640       free(shProg->TransformFeedback.VaryingNames[i]);
    641    }
    642    free(shProg->TransformFeedback.VaryingNames);
    643 
    644    /* allocate new memory for varying names */
    645    shProg->TransformFeedback.VaryingNames =
    646       (GLchar **) malloc(count * sizeof(GLchar *));
    647 
    648    if (!shProg->TransformFeedback.VaryingNames) {
    649       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
    650       return;
    651    }
    652 
    653    /* Save the new names and the count */
    654    for (i = 0; i < (GLuint) count; i++) {
    655       shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
    656    }
    657    shProg->TransformFeedback.NumVarying = count;
    658 
    659    shProg->TransformFeedback.BufferMode = bufferMode;
    660 
    661    /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since
    662     * the varyings won't be used until shader link time.
    663     */
    664 }
    665 
    666 
    667 /**
    668  * Get info about the vertex shader's outputs which are to be written
    669  * to the feedback buffer(s).
    670  */
    671 void GLAPIENTRY
    672 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
    673                                   GLsizei bufSize, GLsizei *length,
    674                                   GLsizei *size, GLenum *type, GLchar *name)
    675 {
    676    const struct gl_shader_program *shProg;
    677    const struct gl_transform_feedback_info *linked_xfb_info;
    678    GET_CURRENT_CONTEXT(ctx);
    679 
    680    shProg = _mesa_lookup_shader_program(ctx, program);
    681    if (!shProg) {
    682       _mesa_error(ctx, GL_INVALID_VALUE,
    683                   "glGetTransformFeedbackVaryings(program=%u)", program);
    684       return;
    685    }
    686 
    687    linked_xfb_info = &shProg->LinkedTransformFeedback;
    688    if (index >= linked_xfb_info->NumVarying) {
    689       _mesa_error(ctx, GL_INVALID_VALUE,
    690                   "glGetTransformFeedbackVaryings(index=%u)", index);
    691       return;
    692    }
    693 
    694    /* return the varying's name and length */
    695    _mesa_copy_string(name, bufSize, length,
    696 		     linked_xfb_info->Varyings[index].Name);
    697 
    698    /* return the datatype and value's size (in datatype units) */
    699    if (type)
    700       *type = linked_xfb_info->Varyings[index].Type;
    701    if (size)
    702       *size = linked_xfb_info->Varyings[index].Size;
    703 }
    704 
    705 
    706 
    707 struct gl_transform_feedback_object *
    708 _mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
    709 {
    710    if (name == 0) {
    711       return ctx->TransformFeedback.DefaultObject;
    712    }
    713    else
    714       return (struct gl_transform_feedback_object *)
    715          _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
    716 }
    717 
    718 
    719 /**
    720  * Create new transform feedback objects.   Transform feedback objects
    721  * encapsulate the state related to transform feedback to allow quickly
    722  * switching state (and drawing the results, below).
    723  * Part of GL_ARB_transform_feedback2.
    724  */
    725 void GLAPIENTRY
    726 _mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
    727 {
    728    GLuint first;
    729    GET_CURRENT_CONTEXT(ctx);
    730 
    731    ASSERT_OUTSIDE_BEGIN_END(ctx);
    732 
    733    if (n < 0) {
    734       _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
    735       return;
    736    }
    737 
    738    if (!names)
    739       return;
    740 
    741    /* we don't need contiguous IDs, but this might be faster */
    742    first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
    743    if (first) {
    744       GLsizei i;
    745       for (i = 0; i < n; i++) {
    746          struct gl_transform_feedback_object *obj
    747             = ctx->Driver.NewTransformFeedback(ctx, first + i);
    748          if (!obj) {
    749             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
    750             return;
    751          }
    752          names[i] = first + i;
    753          _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
    754       }
    755    }
    756    else {
    757       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
    758    }
    759 }
    760 
    761 
    762 /**
    763  * Is the given ID a transform feedback object?
    764  * Part of GL_ARB_transform_feedback2.
    765  */
    766 GLboolean GLAPIENTRY
    767 _mesa_IsTransformFeedback(GLuint name)
    768 {
    769    GET_CURRENT_CONTEXT(ctx);
    770 
    771    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
    772 
    773    if (name && _mesa_lookup_transform_feedback_object(ctx, name))
    774       return GL_TRUE;
    775    else
    776       return GL_FALSE;
    777 }
    778 
    779 
    780 /**
    781  * Bind the given transform feedback object.
    782  * Part of GL_ARB_transform_feedback2.
    783  */
    784 void GLAPIENTRY
    785 _mesa_BindTransformFeedback(GLenum target, GLuint name)
    786 {
    787    struct gl_transform_feedback_object *obj;
    788    GET_CURRENT_CONTEXT(ctx);
    789 
    790    if (target != GL_TRANSFORM_FEEDBACK) {
    791       _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
    792       return;
    793    }
    794 
    795    if (ctx->TransformFeedback.CurrentObject->Active &&
    796        !ctx->TransformFeedback.CurrentObject->Paused) {
    797       _mesa_error(ctx, GL_INVALID_OPERATION,
    798               "glBindTransformFeedback(transform is active, or not paused)");
    799       return;
    800    }
    801 
    802    obj = _mesa_lookup_transform_feedback_object(ctx, name);
    803    if (!obj) {
    804       _mesa_error(ctx, GL_INVALID_OPERATION,
    805                   "glBindTransformFeedback(name=%u)", name);
    806       return;
    807    }
    808 
    809    reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
    810                                        obj);
    811 }
    812 
    813 
    814 /**
    815  * Delete the given transform feedback objects.
    816  * Part of GL_ARB_transform_feedback2.
    817  */
    818 void GLAPIENTRY
    819 _mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
    820 {
    821    GLint i;
    822    GET_CURRENT_CONTEXT(ctx);
    823 
    824    ASSERT_OUTSIDE_BEGIN_END(ctx);
    825 
    826    if (n < 0) {
    827       _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
    828       return;
    829    }
    830 
    831    if (!names)
    832       return;
    833 
    834    for (i = 0; i < n; i++) {
    835       if (names[i] > 0) {
    836          struct gl_transform_feedback_object *obj
    837             = _mesa_lookup_transform_feedback_object(ctx, names[i]);
    838          if (obj) {
    839             if (obj->Active) {
    840                _mesa_error(ctx, GL_INVALID_OPERATION,
    841                            "glDeleteTransformFeedbacks(object %u is active)",
    842                            names[i]);
    843                return;
    844             }
    845             _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
    846             /* unref, but object may not be deleted until later */
    847             reference_transform_feedback_object(&obj, NULL);
    848          }
    849       }
    850    }
    851 }
    852 
    853 
    854 /**
    855  * Pause transform feedback.
    856  * Part of GL_ARB_transform_feedback2.
    857  */
    858 void GLAPIENTRY
    859 _mesa_PauseTransformFeedback(void)
    860 {
    861    struct gl_transform_feedback_object *obj;
    862    GET_CURRENT_CONTEXT(ctx);
    863 
    864    obj = ctx->TransformFeedback.CurrentObject;
    865 
    866    if (!obj->Active || obj->Paused) {
    867       _mesa_error(ctx, GL_INVALID_OPERATION,
    868            "glPauseTransformFeedback(feedback not active or already paused)");
    869       return;
    870    }
    871 
    872    FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
    873    obj->Paused = GL_TRUE;
    874 
    875    assert(ctx->Driver.PauseTransformFeedback);
    876    ctx->Driver.PauseTransformFeedback(ctx, obj);
    877 }
    878 
    879 
    880 /**
    881  * Resume transform feedback.
    882  * Part of GL_ARB_transform_feedback2.
    883  */
    884 void GLAPIENTRY
    885 _mesa_ResumeTransformFeedback(void)
    886 {
    887    struct gl_transform_feedback_object *obj;
    888    GET_CURRENT_CONTEXT(ctx);
    889 
    890    obj = ctx->TransformFeedback.CurrentObject;
    891 
    892    if (!obj->Active || !obj->Paused) {
    893       _mesa_error(ctx, GL_INVALID_OPERATION,
    894                "glResumeTransformFeedback(feedback not active or not paused)");
    895       return;
    896    }
    897 
    898    FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
    899    obj->Paused = GL_FALSE;
    900 
    901    assert(ctx->Driver.ResumeTransformFeedback);
    902    ctx->Driver.ResumeTransformFeedback(ctx, obj);
    903 }
    904 
    905 #endif /* FEATURE_EXT_transform_feedback */
    906