Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

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