Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
4358 Serge 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
}