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