13464ebd5Sriastradh/* 23464ebd5Sriastradh * Mesa 3-D graphics library 33464ebd5Sriastradh * 43464ebd5Sriastradh * Copyright (C) 2010 VMware, Inc. All Rights Reserved. 53464ebd5Sriastradh * 63464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a 73464ebd5Sriastradh * copy of this software and associated documentation files (the "Software"), 83464ebd5Sriastradh * to deal in the Software without restriction, including without limitation 93464ebd5Sriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense, 103464ebd5Sriastradh * and/or sell copies of the Software, and to permit persons to whom the 113464ebd5Sriastradh * Software is furnished to do so, subject to the following conditions: 123464ebd5Sriastradh * 133464ebd5Sriastradh * The above copyright notice and this permission notice shall be included 143464ebd5Sriastradh * in all copies or substantial portions of the Software. 153464ebd5Sriastradh * 163464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 173464ebd5Sriastradh * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 183464ebd5Sriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20af69d88dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21af69d88dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22af69d88dSmrg * OTHER DEALINGS IN THE SOFTWARE. 233464ebd5Sriastradh */ 243464ebd5Sriastradh 253464ebd5Sriastradh 263464ebd5Sriastradh/* 27af69d88dSmrg * Transform feedback support. 283464ebd5Sriastradh * 293464ebd5Sriastradh * Authors: 303464ebd5Sriastradh * Brian Paul 313464ebd5Sriastradh */ 323464ebd5Sriastradh 333464ebd5Sriastradh 343464ebd5Sriastradh#include "buffers.h" 353464ebd5Sriastradh#include "context.h" 367ec681f3Smrg#include "draw_validate.h" 373464ebd5Sriastradh#include "hash.h" 38af69d88dSmrg#include "macros.h" 393464ebd5Sriastradh#include "mtypes.h" 403464ebd5Sriastradh#include "transformfeedback.h" 413464ebd5Sriastradh#include "shaderapi.h" 423464ebd5Sriastradh#include "shaderobj.h" 433464ebd5Sriastradh 4401e04c3fSmrg#include "program/program.h" 453464ebd5Sriastradh#include "program/prog_parameter.h" 463464ebd5Sriastradh 477ec681f3Smrg#include "util/u_memory.h" 487ec681f3Smrg 49af69d88dSmrgstruct using_program_tuple 50af69d88dSmrg{ 5101e04c3fSmrg struct gl_program *prog; 52af69d88dSmrg bool found; 53af69d88dSmrg}; 54af69d88dSmrg 55af69d88dSmrgstatic void 567ec681f3Smrgactive_xfb_object_references_program(void *data, void *user_data) 57af69d88dSmrg{ 58af69d88dSmrg struct using_program_tuple *callback_data = user_data; 59af69d88dSmrg struct gl_transform_feedback_object *obj = data; 6001e04c3fSmrg if (obj->Active && obj->program == callback_data->prog) 61af69d88dSmrg callback_data->found = true; 62af69d88dSmrg} 63af69d88dSmrg 64af69d88dSmrg/** 65af69d88dSmrg * Return true if any active transform feedback object is using a program. 66af69d88dSmrg */ 67af69d88dSmrgbool 68af69d88dSmrg_mesa_transform_feedback_is_using_program(struct gl_context *ctx, 69af69d88dSmrg struct gl_shader_program *shProg) 70af69d88dSmrg{ 7101e04c3fSmrg if (!shProg->last_vert_prog) 7201e04c3fSmrg return false; 7301e04c3fSmrg 74af69d88dSmrg struct using_program_tuple callback_data; 75af69d88dSmrg callback_data.found = false; 7601e04c3fSmrg callback_data.prog = shProg->last_vert_prog; 773464ebd5Sriastradh 7801e04c3fSmrg _mesa_HashWalkLocked(ctx->TransformFeedback.Objects, 7901e04c3fSmrg active_xfb_object_references_program, &callback_data); 803464ebd5Sriastradh 81af69d88dSmrg /* Also check DefaultObject, as it's not in the Objects hash table. */ 827ec681f3Smrg active_xfb_object_references_program(ctx->TransformFeedback.DefaultObject, 83af69d88dSmrg &callback_data); 84af69d88dSmrg 85af69d88dSmrg return callback_data.found; 86af69d88dSmrg} 873464ebd5Sriastradh 883464ebd5Sriastradh/** 893464ebd5Sriastradh * Do reference counting of transform feedback buffers. 903464ebd5Sriastradh */ 913464ebd5Sriastradhstatic void 923464ebd5Sriastradhreference_transform_feedback_object(struct gl_transform_feedback_object **ptr, 933464ebd5Sriastradh struct gl_transform_feedback_object *obj) 943464ebd5Sriastradh{ 953464ebd5Sriastradh if (*ptr == obj) 963464ebd5Sriastradh return; 973464ebd5Sriastradh 983464ebd5Sriastradh if (*ptr) { 993464ebd5Sriastradh /* Unreference the old object */ 1003464ebd5Sriastradh struct gl_transform_feedback_object *oldObj = *ptr; 1013464ebd5Sriastradh 10201e04c3fSmrg assert(oldObj->RefCount > 0); 1033464ebd5Sriastradh oldObj->RefCount--; 1043464ebd5Sriastradh 1053464ebd5Sriastradh if (oldObj->RefCount == 0) { 1063464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 1073464ebd5Sriastradh if (ctx) 1083464ebd5Sriastradh ctx->Driver.DeleteTransformFeedback(ctx, oldObj); 1093464ebd5Sriastradh } 1103464ebd5Sriastradh 1113464ebd5Sriastradh *ptr = NULL; 1123464ebd5Sriastradh } 11301e04c3fSmrg assert(!*ptr); 1143464ebd5Sriastradh 1153464ebd5Sriastradh if (obj) { 11601e04c3fSmrg assert(obj->RefCount > 0); 11701e04c3fSmrg 1183464ebd5Sriastradh /* reference new object */ 11901e04c3fSmrg obj->RefCount++; 12001e04c3fSmrg obj->EverBound = GL_TRUE; 12101e04c3fSmrg *ptr = obj; 1223464ebd5Sriastradh } 1233464ebd5Sriastradh} 1243464ebd5Sriastradh 1253464ebd5Sriastradh 1263464ebd5Sriastradh/** 1273464ebd5Sriastradh * Per-context init for transform feedback. 1283464ebd5Sriastradh */ 1293464ebd5Sriastradhvoid 1303464ebd5Sriastradh_mesa_init_transform_feedback(struct gl_context *ctx) 1313464ebd5Sriastradh{ 1323464ebd5Sriastradh /* core mesa expects this, even a dummy one, to be available */ 13301e04c3fSmrg assert(ctx->Driver.NewTransformFeedback); 1343464ebd5Sriastradh 1353464ebd5Sriastradh ctx->TransformFeedback.DefaultObject = 1363464ebd5Sriastradh ctx->Driver.NewTransformFeedback(ctx, 0); 1373464ebd5Sriastradh 1383464ebd5Sriastradh assert(ctx->TransformFeedback.DefaultObject->RefCount == 1); 1393464ebd5Sriastradh 1403464ebd5Sriastradh reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 1413464ebd5Sriastradh ctx->TransformFeedback.DefaultObject); 1423464ebd5Sriastradh 1433464ebd5Sriastradh assert(ctx->TransformFeedback.DefaultObject->RefCount == 2); 1443464ebd5Sriastradh 1453464ebd5Sriastradh ctx->TransformFeedback.Objects = _mesa_NewHashTable(); 1463464ebd5Sriastradh 1473464ebd5Sriastradh _mesa_reference_buffer_object(ctx, 1487ec681f3Smrg &ctx->TransformFeedback.CurrentBuffer, NULL); 1493464ebd5Sriastradh} 1503464ebd5Sriastradh 1513464ebd5Sriastradh 1523464ebd5Sriastradh 1533464ebd5Sriastradh/** 1543464ebd5Sriastradh * Callback for _mesa_HashDeleteAll(). 1553464ebd5Sriastradh */ 1563464ebd5Sriastradhstatic void 1577ec681f3Smrgdelete_cb(void *data, void *userData) 1583464ebd5Sriastradh{ 1593464ebd5Sriastradh struct gl_context *ctx = (struct gl_context *) userData; 1603464ebd5Sriastradh struct gl_transform_feedback_object *obj = 1613464ebd5Sriastradh (struct gl_transform_feedback_object *) data; 1623464ebd5Sriastradh 1633464ebd5Sriastradh ctx->Driver.DeleteTransformFeedback(ctx, obj); 1643464ebd5Sriastradh} 1653464ebd5Sriastradh 1663464ebd5Sriastradh 1673464ebd5Sriastradh/** 1683464ebd5Sriastradh * Per-context free/clean-up for transform feedback. 1693464ebd5Sriastradh */ 1703464ebd5Sriastradhvoid 1713464ebd5Sriastradh_mesa_free_transform_feedback(struct gl_context *ctx) 1723464ebd5Sriastradh{ 1733464ebd5Sriastradh /* core mesa expects this, even a dummy one, to be available */ 17401e04c3fSmrg assert(ctx->Driver.NewTransformFeedback); 1753464ebd5Sriastradh 1763464ebd5Sriastradh _mesa_reference_buffer_object(ctx, 1773464ebd5Sriastradh &ctx->TransformFeedback.CurrentBuffer, 1783464ebd5Sriastradh NULL); 1793464ebd5Sriastradh 1803464ebd5Sriastradh /* Delete all feedback objects */ 1813464ebd5Sriastradh _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx); 1823464ebd5Sriastradh _mesa_DeleteHashTable(ctx->TransformFeedback.Objects); 1833464ebd5Sriastradh 1843464ebd5Sriastradh /* Delete the default feedback object */ 1853464ebd5Sriastradh assert(ctx->Driver.DeleteTransformFeedback); 1863464ebd5Sriastradh ctx->Driver.DeleteTransformFeedback(ctx, 1873464ebd5Sriastradh ctx->TransformFeedback.DefaultObject); 1883464ebd5Sriastradh 1893464ebd5Sriastradh ctx->TransformFeedback.CurrentObject = NULL; 1903464ebd5Sriastradh} 1913464ebd5Sriastradh 1923464ebd5Sriastradh 193af69d88dSmrg/** Initialize the fields of a gl_transform_feedback_object. */ 1943464ebd5Sriastradhvoid 195af69d88dSmrg_mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj, 196af69d88dSmrg GLuint name) 1973464ebd5Sriastradh{ 198af69d88dSmrg obj->Name = name; 199af69d88dSmrg obj->RefCount = 1; 200af69d88dSmrg obj->EverBound = GL_FALSE; 2013464ebd5Sriastradh} 2023464ebd5Sriastradh 2037ec681f3Smrg/** 2047ec681f3Smrg * Delete a transform feedback object. Called via 2057ec681f3Smrg * ctx->Driver->DeleteTransformFeedback, if not overwritten by driver. In 2067ec681f3Smrg * the latter case, called from the driver after all driver-specific clean-up 2077ec681f3Smrg * has been done. 2087ec681f3Smrg * 2097ec681f3Smrg * \param ctx GL context to wich transform feedback object belongs. 2107ec681f3Smrg * \param obj Transform feedback object due to be deleted. 2117ec681f3Smrg */ 2127ec681f3Smrgvoid 2137ec681f3Smrg_mesa_delete_transform_feedback_object(struct gl_context *ctx, 2147ec681f3Smrg struct gl_transform_feedback_object 2157ec681f3Smrg *obj) 2167ec681f3Smrg{ 2177ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(obj->Buffers); i++) { 2187ec681f3Smrg _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL); 2197ec681f3Smrg } 2207ec681f3Smrg 2217ec681f3Smrg free(obj->Label); 2227ec681f3Smrg free(obj); 2237ec681f3Smrg} 2243464ebd5Sriastradh 2253464ebd5Sriastradh/** Default fallback for ctx->Driver.NewTransformFeedback() */ 2263464ebd5Sriastradhstatic struct gl_transform_feedback_object * 22701e04c3fSmrgnew_transform_feedback_fallback(struct gl_context *ctx, GLuint name) 2283464ebd5Sriastradh{ 2293464ebd5Sriastradh struct gl_transform_feedback_object *obj; 23001e04c3fSmrg 2313464ebd5Sriastradh obj = CALLOC_STRUCT(gl_transform_feedback_object); 23201e04c3fSmrg if (!obj) 23301e04c3fSmrg return NULL; 23401e04c3fSmrg 235af69d88dSmrg _mesa_init_transform_feedback_object(obj, name); 2363464ebd5Sriastradh return obj; 2373464ebd5Sriastradh} 2383464ebd5Sriastradh 2393464ebd5Sriastradh/** Default fallback for ctx->Driver.BeginTransformFeedback() */ 2403464ebd5Sriastradhstatic void 24101e04c3fSmrgbegin_transform_feedback_fallback(struct gl_context *ctx, GLenum mode, 24201e04c3fSmrg struct gl_transform_feedback_object *obj) 2433464ebd5Sriastradh{ 2443464ebd5Sriastradh /* nop */ 2453464ebd5Sriastradh} 2463464ebd5Sriastradh 2473464ebd5Sriastradh/** Default fallback for ctx->Driver.EndTransformFeedback() */ 2483464ebd5Sriastradhstatic void 24901e04c3fSmrgend_transform_feedback_fallback(struct gl_context *ctx, 25001e04c3fSmrg struct gl_transform_feedback_object *obj) 2513464ebd5Sriastradh{ 2523464ebd5Sriastradh /* nop */ 2533464ebd5Sriastradh} 2543464ebd5Sriastradh 2553464ebd5Sriastradh/** Default fallback for ctx->Driver.PauseTransformFeedback() */ 2563464ebd5Sriastradhstatic void 25701e04c3fSmrgpause_transform_feedback_fallback(struct gl_context *ctx, 25801e04c3fSmrg struct gl_transform_feedback_object *obj) 2593464ebd5Sriastradh{ 2603464ebd5Sriastradh /* nop */ 2613464ebd5Sriastradh} 2623464ebd5Sriastradh 2633464ebd5Sriastradh/** Default fallback for ctx->Driver.ResumeTransformFeedback() */ 2643464ebd5Sriastradhstatic void 26501e04c3fSmrgresume_transform_feedback_fallback(struct gl_context *ctx, 26601e04c3fSmrg struct gl_transform_feedback_object *obj) 2673464ebd5Sriastradh{ 2683464ebd5Sriastradh /* nop */ 2693464ebd5Sriastradh} 2703464ebd5Sriastradh 2713464ebd5Sriastradh 2723464ebd5Sriastradh/** 2733464ebd5Sriastradh * Plug in default device driver functions for transform feedback. 2743464ebd5Sriastradh * Most drivers will override some/all of these. 2753464ebd5Sriastradh */ 2763464ebd5Sriastradhvoid 2773464ebd5Sriastradh_mesa_init_transform_feedback_functions(struct dd_function_table *driver) 2783464ebd5Sriastradh{ 27901e04c3fSmrg driver->NewTransformFeedback = new_transform_feedback_fallback; 2807ec681f3Smrg driver->DeleteTransformFeedback = _mesa_delete_transform_feedback_object; 28101e04c3fSmrg driver->BeginTransformFeedback = begin_transform_feedback_fallback; 28201e04c3fSmrg driver->EndTransformFeedback = end_transform_feedback_fallback; 28301e04c3fSmrg driver->PauseTransformFeedback = pause_transform_feedback_fallback; 28401e04c3fSmrg driver->ResumeTransformFeedback = resume_transform_feedback_fallback; 2853464ebd5Sriastradh} 2863464ebd5Sriastradh 2873464ebd5Sriastradh 288af69d88dSmrg/** 289af69d88dSmrg * Fill in the correct Size value for each buffer in \c obj. 290af69d88dSmrg * 291af69d88dSmrg * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed 292af69d88dSmrg * Targets"): 293af69d88dSmrg * 294af69d88dSmrg * BindBufferBase binds the entire buffer, even when the size of the buffer 295af69d88dSmrg * is changed after the binding is established. It is equivalent to calling 296af69d88dSmrg * BindBufferRange with offset zero, while size is determined by the size of 297af69d88dSmrg * the bound buffer at the time the binding is used. 298af69d88dSmrg * 299af69d88dSmrg * Regardless of the size specified with BindBufferRange, or indirectly with 300af69d88dSmrg * BindBufferBase, the GL will never read or write beyond the end of a bound 301af69d88dSmrg * buffer. In some cases this constraint may result in visibly different 302af69d88dSmrg * behavior when a buffer overflow would otherwise result, such as described 303af69d88dSmrg * for transform feedback operations in section 13.2.2. 304af69d88dSmrg */ 305af69d88dSmrgstatic void 306af69d88dSmrgcompute_transform_feedback_buffer_sizes( 307af69d88dSmrg struct gl_transform_feedback_object *obj) 3083464ebd5Sriastradh{ 309af69d88dSmrg unsigned i = 0; 310af69d88dSmrg for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) { 311af69d88dSmrg GLintptr offset = obj->Offset[i]; 312af69d88dSmrg GLsizeiptr buffer_size 313af69d88dSmrg = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size; 314af69d88dSmrg GLsizeiptr available_space 315af69d88dSmrg = buffer_size <= offset ? 0 : buffer_size - offset; 316af69d88dSmrg GLsizeiptr computed_size; 317af69d88dSmrg if (obj->RequestedSize[i] == 0) { 318af69d88dSmrg /* No size was specified at the time the buffer was bound, so allow 319af69d88dSmrg * writing to all available space in the buffer. 320af69d88dSmrg */ 321af69d88dSmrg computed_size = available_space; 322af69d88dSmrg } else { 323af69d88dSmrg /* A size was specified at the time the buffer was bound, however 324af69d88dSmrg * it's possible that the buffer has shrunk since then. So only 325af69d88dSmrg * allow writing to the minimum of the specified size and the space 326af69d88dSmrg * available. 327af69d88dSmrg */ 328af69d88dSmrg computed_size = MIN2(available_space, obj->RequestedSize[i]); 329af69d88dSmrg } 330af69d88dSmrg 331af69d88dSmrg /* Legal sizes must be multiples of four, so round down if necessary. */ 332af69d88dSmrg obj->Size[i] = computed_size & ~0x3; 333af69d88dSmrg } 334af69d88dSmrg} 335af69d88dSmrg 336af69d88dSmrg 337af69d88dSmrg/** 338af69d88dSmrg * Compute the maximum number of vertices that can be written to the currently 339af69d88dSmrg * enabled transform feedback buffers without overflowing any of them. 340af69d88dSmrg */ 341af69d88dSmrgunsigned 34201e04c3fSmrg_mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx, 343af69d88dSmrg const struct gl_transform_feedback_object *obj, 344af69d88dSmrg const struct gl_transform_feedback_info *info) 345af69d88dSmrg{ 346af69d88dSmrg unsigned max_index = 0xffffffff; 347af69d88dSmrg unsigned i; 348af69d88dSmrg 34901e04c3fSmrg for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) { 35001e04c3fSmrg if ((info->ActiveBuffers >> i) & 1) { 35101e04c3fSmrg unsigned stride = info->Buffers[i].Stride; 35201e04c3fSmrg unsigned max_for_this_buffer; 353af69d88dSmrg 35401e04c3fSmrg /* Skip any inactive buffers, which have a stride of 0. */ 35501e04c3fSmrg if (stride == 0) 35601e04c3fSmrg continue; 357af69d88dSmrg 35801e04c3fSmrg max_for_this_buffer = obj->Size[i] / (4 * stride); 35901e04c3fSmrg max_index = MIN2(max_index, max_for_this_buffer); 36001e04c3fSmrg } 361af69d88dSmrg } 362af69d88dSmrg 363af69d88dSmrg return max_index; 3643464ebd5Sriastradh} 3653464ebd5Sriastradh 3663464ebd5Sriastradh 3673464ebd5Sriastradh/** 3683464ebd5Sriastradh ** Begin API functions 3693464ebd5Sriastradh **/ 3703464ebd5Sriastradh 3713464ebd5Sriastradh 372af69d88dSmrg/** 373af69d88dSmrg * Figure out which stage of the pipeline is the source of transform feedback 37401e04c3fSmrg * data given the current context state, and return its gl_program. 375af69d88dSmrg * 376af69d88dSmrg * If no active program can generate transform feedback data (i.e. no vertex 377af69d88dSmrg * shader is active), returns NULL. 378af69d88dSmrg */ 37901e04c3fSmrgstatic struct gl_program * 380af69d88dSmrgget_xfb_source(struct gl_context *ctx) 381af69d88dSmrg{ 382af69d88dSmrg int i; 383af69d88dSmrg for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) { 384af69d88dSmrg if (ctx->_Shader->CurrentProgram[i] != NULL) 385af69d88dSmrg return ctx->_Shader->CurrentProgram[i]; 386af69d88dSmrg } 387af69d88dSmrg return NULL; 388af69d88dSmrg} 389af69d88dSmrg 390af69d88dSmrg 39101e04c3fSmrgstatic ALWAYS_INLINE void 39201e04c3fSmrgbegin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error) 3933464ebd5Sriastradh{ 3943464ebd5Sriastradh struct gl_transform_feedback_object *obj; 395af69d88dSmrg struct gl_transform_feedback_info *info = NULL; 39601e04c3fSmrg struct gl_program *source; 397af69d88dSmrg GLuint i; 398af69d88dSmrg unsigned vertices_per_prim; 3993464ebd5Sriastradh 4003464ebd5Sriastradh obj = ctx->TransformFeedback.CurrentObject; 4013464ebd5Sriastradh 402af69d88dSmrg /* Figure out what pipeline stage is the source of data for transform 403af69d88dSmrg * feedback. 404af69d88dSmrg */ 405af69d88dSmrg source = get_xfb_source(ctx); 40601e04c3fSmrg if (!no_error && source == NULL) { 407af69d88dSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 408af69d88dSmrg "glBeginTransformFeedback(no program active)"); 409af69d88dSmrg return; 410af69d88dSmrg } 411af69d88dSmrg 41201e04c3fSmrg info = source->sh.LinkedTransformFeedback; 413af69d88dSmrg 41401e04c3fSmrg if (!no_error && info->NumOutputs == 0) { 415af69d88dSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 416af69d88dSmrg "glBeginTransformFeedback(no varyings to record)"); 417af69d88dSmrg return; 418af69d88dSmrg } 419af69d88dSmrg 4203464ebd5Sriastradh switch (mode) { 4213464ebd5Sriastradh case GL_POINTS: 422af69d88dSmrg vertices_per_prim = 1; 423af69d88dSmrg break; 4243464ebd5Sriastradh case GL_LINES: 425af69d88dSmrg vertices_per_prim = 2; 426af69d88dSmrg break; 4273464ebd5Sriastradh case GL_TRIANGLES: 428af69d88dSmrg vertices_per_prim = 3; 4293464ebd5Sriastradh break; 4303464ebd5Sriastradh default: 43101e04c3fSmrg if (!no_error) { 43201e04c3fSmrg _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)"); 43301e04c3fSmrg return; 43401e04c3fSmrg } else { 43501e04c3fSmrg /* Stop compiler warnings */ 43601e04c3fSmrg unreachable("Error in API use when using KHR_no_error"); 43701e04c3fSmrg } 4383464ebd5Sriastradh } 4393464ebd5Sriastradh 44001e04c3fSmrg if (!no_error) { 44101e04c3fSmrg if (obj->Active) { 442af69d88dSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 44301e04c3fSmrg "glBeginTransformFeedback(already active)"); 444af69d88dSmrg return; 445af69d88dSmrg } 44601e04c3fSmrg 44701e04c3fSmrg for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) { 44801e04c3fSmrg if ((info->ActiveBuffers >> i) & 1) { 44901e04c3fSmrg if (obj->BufferNames[i] == 0) { 45001e04c3fSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 45101e04c3fSmrg "glBeginTransformFeedback(binding point %d does not " 45201e04c3fSmrg "have a buffer object bound)", i); 45301e04c3fSmrg return; 45401e04c3fSmrg } 45501e04c3fSmrg } 45601e04c3fSmrg } 457af69d88dSmrg } 458af69d88dSmrg 4597ec681f3Smrg FLUSH_VERTICES(ctx, 0, 0); 460af69d88dSmrg ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 461af69d88dSmrg 4623464ebd5Sriastradh obj->Active = GL_TRUE; 4633464ebd5Sriastradh ctx->TransformFeedback.Mode = mode; 4643464ebd5Sriastradh 465af69d88dSmrg compute_transform_feedback_buffer_sizes(obj); 466af69d88dSmrg 467af69d88dSmrg if (_mesa_is_gles3(ctx)) { 468af69d88dSmrg /* In GLES3, we are required to track the usage of the transform 469af69d88dSmrg * feedback buffer and report INVALID_OPERATION if a draw call tries to 470af69d88dSmrg * exceed it. So compute the maximum number of vertices that we can 471af69d88dSmrg * write without overflowing any of the buffers currently being used for 472af69d88dSmrg * feedback. 473af69d88dSmrg */ 474af69d88dSmrg unsigned max_vertices 47501e04c3fSmrg = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info); 476af69d88dSmrg obj->GlesRemainingPrims = max_vertices / vertices_per_prim; 477af69d88dSmrg } 478af69d88dSmrg 47901e04c3fSmrg if (obj->program != source) { 480af69d88dSmrg ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg; 48101e04c3fSmrg _mesa_reference_program_(ctx, &obj->program, source); 48201e04c3fSmrg obj->program = source; 483af69d88dSmrg } 484af69d88dSmrg 4853464ebd5Sriastradh assert(ctx->Driver.BeginTransformFeedback); 4863464ebd5Sriastradh ctx->Driver.BeginTransformFeedback(ctx, mode, obj); 4877ec681f3Smrg _mesa_update_valid_to_render_state(ctx); 4883464ebd5Sriastradh} 4893464ebd5Sriastradh 4903464ebd5Sriastradh 49101e04c3fSmrgvoid GLAPIENTRY 49201e04c3fSmrg_mesa_BeginTransformFeedback_no_error(GLenum mode) 49301e04c3fSmrg{ 49401e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 49501e04c3fSmrg begin_transform_feedback(ctx, mode, true); 49601e04c3fSmrg} 49701e04c3fSmrg 49801e04c3fSmrg 49901e04c3fSmrgvoid GLAPIENTRY 50001e04c3fSmrg_mesa_BeginTransformFeedback(GLenum mode) 50101e04c3fSmrg{ 50201e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 50301e04c3fSmrg begin_transform_feedback(ctx, mode, false); 50401e04c3fSmrg} 50501e04c3fSmrg 50601e04c3fSmrg 50701e04c3fSmrgstatic void 50801e04c3fSmrgend_transform_feedback(struct gl_context *ctx, 50901e04c3fSmrg struct gl_transform_feedback_object *obj) 51001e04c3fSmrg{ 5117ec681f3Smrg FLUSH_VERTICES(ctx, 0, 0); 51201e04c3fSmrg ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 51301e04c3fSmrg 51401e04c3fSmrg assert(ctx->Driver.EndTransformFeedback); 51501e04c3fSmrg ctx->Driver.EndTransformFeedback(ctx, obj); 51601e04c3fSmrg 51701e04c3fSmrg _mesa_reference_program_(ctx, &obj->program, NULL); 51801e04c3fSmrg ctx->TransformFeedback.CurrentObject->Active = GL_FALSE; 51901e04c3fSmrg ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE; 52001e04c3fSmrg ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE; 5217ec681f3Smrg _mesa_update_valid_to_render_state(ctx); 52201e04c3fSmrg} 52301e04c3fSmrg 52401e04c3fSmrg 52501e04c3fSmrgvoid GLAPIENTRY 52601e04c3fSmrg_mesa_EndTransformFeedback_no_error(void) 52701e04c3fSmrg{ 52801e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 52901e04c3fSmrg end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 53001e04c3fSmrg} 53101e04c3fSmrg 53201e04c3fSmrg 5333464ebd5Sriastradhvoid GLAPIENTRY 5343464ebd5Sriastradh_mesa_EndTransformFeedback(void) 5353464ebd5Sriastradh{ 5363464ebd5Sriastradh struct gl_transform_feedback_object *obj; 5373464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 5383464ebd5Sriastradh 5393464ebd5Sriastradh obj = ctx->TransformFeedback.CurrentObject; 5403464ebd5Sriastradh 5413464ebd5Sriastradh if (!obj->Active) { 5423464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_OPERATION, 5433464ebd5Sriastradh "glEndTransformFeedback(not active)"); 5443464ebd5Sriastradh return; 5453464ebd5Sriastradh } 5463464ebd5Sriastradh 54701e04c3fSmrg end_transform_feedback(ctx, obj); 5483464ebd5Sriastradh} 5493464ebd5Sriastradh 5503464ebd5Sriastradh 5513464ebd5Sriastradh/** 5523464ebd5Sriastradh * Helper used by BindBufferRange() and BindBufferBase(). 5533464ebd5Sriastradh */ 5543464ebd5Sriastradhstatic void 55501e04c3fSmrgbind_buffer_range(struct gl_context *ctx, 55601e04c3fSmrg struct gl_transform_feedback_object *obj, 55701e04c3fSmrg GLuint index, 5583464ebd5Sriastradh struct gl_buffer_object *bufObj, 55901e04c3fSmrg GLintptr offset, GLsizeiptr size, 56001e04c3fSmrg bool dsa) 5613464ebd5Sriastradh{ 562af69d88dSmrg /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because 563af69d88dSmrg * transform feedback buffers can't be changed while transform feedback is 564af69d88dSmrg * active. 565af69d88dSmrg */ 566af69d88dSmrg 56701e04c3fSmrg if (!dsa) { 56801e04c3fSmrg /* The general binding point */ 56901e04c3fSmrg _mesa_reference_buffer_object(ctx, 57001e04c3fSmrg &ctx->TransformFeedback.CurrentBuffer, 57101e04c3fSmrg bufObj); 57201e04c3fSmrg } 5733464ebd5Sriastradh 5743464ebd5Sriastradh /* The per-attribute binding point */ 575af69d88dSmrg _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size); 5763464ebd5Sriastradh} 5773464ebd5Sriastradh 5783464ebd5Sriastradh 5793464ebd5Sriastradh/** 58001e04c3fSmrg * Validate the buffer object to receive transform feedback results. Plus, 58101e04c3fSmrg * validate the starting offset to place the results, and max size. 58201e04c3fSmrg * Called from the glBindBufferRange() and glTransformFeedbackBufferRange 58301e04c3fSmrg * functions. 5843464ebd5Sriastradh */ 58501e04c3fSmrgbool 58601e04c3fSmrg_mesa_validate_buffer_range_xfb(struct gl_context *ctx, 58701e04c3fSmrg struct gl_transform_feedback_object *obj, 58801e04c3fSmrg GLuint index, struct gl_buffer_object *bufObj, 58901e04c3fSmrg GLintptr offset, GLsizeiptr size, bool dsa) 5903464ebd5Sriastradh{ 59101e04c3fSmrg const char *gl_methd_name; 59201e04c3fSmrg if (dsa) 59301e04c3fSmrg gl_methd_name = "glTransformFeedbackBufferRange"; 59401e04c3fSmrg else 59501e04c3fSmrg gl_methd_name = "glBindBufferRange"; 5963464ebd5Sriastradh 5973464ebd5Sriastradh 5983464ebd5Sriastradh if (obj->Active) { 59901e04c3fSmrg _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)", 60001e04c3fSmrg gl_methd_name); 60101e04c3fSmrg return false; 6023464ebd5Sriastradh } 6033464ebd5Sriastradh 604af69d88dSmrg if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 60501e04c3fSmrg /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 60601e04c3fSmrg * generated if index is greater than or equal to the number of binding 60701e04c3fSmrg * points for transform feedback, as described in section 6.7.1." 60801e04c3fSmrg */ 60901e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)", 61001e04c3fSmrg gl_methd_name, index); 61101e04c3fSmrg return false; 6123464ebd5Sriastradh } 6133464ebd5Sriastradh 614af69d88dSmrg if (size & 0x3) { 61501e04c3fSmrg /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */ 61601e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of " 61701e04c3fSmrg "four)", gl_methd_name, (int) size); 61801e04c3fSmrg return false; 61901e04c3fSmrg } 6203464ebd5Sriastradh 6213464ebd5Sriastradh if (offset & 0x3) { 62201e04c3fSmrg /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */ 62301e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple " 62401e04c3fSmrg "of four)", gl_methd_name, (int) offset); 62501e04c3fSmrg return false; 62601e04c3fSmrg } 62701e04c3fSmrg 62801e04c3fSmrg if (offset < 0) { 62901e04c3fSmrg /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 63001e04c3fSmrg * generated by BindBufferRange if offset is negative." 63101e04c3fSmrg * 63201e04c3fSmrg * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error 63301e04c3fSmrg * is generated by TransformFeedbackBufferRange if offset is negative." 63401e04c3fSmrg */ 63501e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)", 63601e04c3fSmrg gl_methd_name, 63701e04c3fSmrg (int) offset); 63801e04c3fSmrg return false; 63901e04c3fSmrg } 6403464ebd5Sriastradh 6417ec681f3Smrg if (size <= 0 && (dsa || bufObj)) { 64201e04c3fSmrg /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 64301e04c3fSmrg * generated by BindBufferRange if buffer is non-zero and size is less 64401e04c3fSmrg * than or equal to zero." 64501e04c3fSmrg * 64601e04c3fSmrg * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error 64701e04c3fSmrg * is generated by TransformFeedbackBufferRange if size is less than or 64801e04c3fSmrg * equal to zero." 64901e04c3fSmrg */ 65001e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)", 65101e04c3fSmrg gl_methd_name, (int) size); 65201e04c3fSmrg return false; 65301e04c3fSmrg } 65401e04c3fSmrg 65501e04c3fSmrg return true; 6563464ebd5Sriastradh} 6573464ebd5Sriastradh 6583464ebd5Sriastradh 6593464ebd5Sriastradh/** 660af69d88dSmrg * Specify a buffer object to receive transform feedback results. 6613464ebd5Sriastradh * As above, but start at offset = 0. 66201e04c3fSmrg * Called from the glBindBufferBase() and glTransformFeedbackBufferBase() 66301e04c3fSmrg * functions. 6643464ebd5Sriastradh */ 665af69d88dSmrgvoid 666af69d88dSmrg_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx, 66701e04c3fSmrg struct gl_transform_feedback_object *obj, 66801e04c3fSmrg GLuint index, 66901e04c3fSmrg struct gl_buffer_object *bufObj, 67001e04c3fSmrg bool dsa) 6713464ebd5Sriastradh{ 6723464ebd5Sriastradh if (obj->Active) { 6733464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_OPERATION, 67401e04c3fSmrg "%s(transform feedback active)", 67501e04c3fSmrg dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase"); 6763464ebd5Sriastradh return; 6773464ebd5Sriastradh } 6783464ebd5Sriastradh 679af69d88dSmrg if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 68001e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)", 68101e04c3fSmrg dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase", 68201e04c3fSmrg index); 68301e04c3fSmrg return; 68401e04c3fSmrg } 68501e04c3fSmrg 68601e04c3fSmrg bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa); 68701e04c3fSmrg} 68801e04c3fSmrg 68901e04c3fSmrg/** 69001e04c3fSmrg * Wrapper around lookup_transform_feedback_object that throws 69101e04c3fSmrg * GL_INVALID_OPERATION if id is not in the hash table. After calling 69201e04c3fSmrg * _mesa_error, it returns NULL. 69301e04c3fSmrg */ 69401e04c3fSmrgstatic struct gl_transform_feedback_object * 69501e04c3fSmrglookup_transform_feedback_object_err(struct gl_context *ctx, 69601e04c3fSmrg GLuint xfb, const char* func) 69701e04c3fSmrg{ 69801e04c3fSmrg struct gl_transform_feedback_object *obj; 69901e04c3fSmrg 70001e04c3fSmrg obj = _mesa_lookup_transform_feedback_object(ctx, xfb); 70101e04c3fSmrg if (!obj) { 70201e04c3fSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 70301e04c3fSmrg "%s(xfb=%u: non-generated object name)", func, xfb); 70401e04c3fSmrg } 70501e04c3fSmrg 70601e04c3fSmrg return obj; 70701e04c3fSmrg} 70801e04c3fSmrg 70901e04c3fSmrg/** 71001e04c3fSmrg * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id 71101e04c3fSmrg * is not in the hash table. Specialised version for the 71201e04c3fSmrg * transform-feedback-related functions. After calling _mesa_error, it 71301e04c3fSmrg * returns NULL. 71401e04c3fSmrg */ 71501e04c3fSmrgstatic struct gl_buffer_object * 71601e04c3fSmrglookup_transform_feedback_bufferobj_err(struct gl_context *ctx, 7177ec681f3Smrg GLuint buffer, const char* func, 7187ec681f3Smrg bool *error) 71901e04c3fSmrg{ 7207ec681f3Smrg struct gl_buffer_object *bufObj = NULL; 7217ec681f3Smrg 7227ec681f3Smrg *error = false; 72301e04c3fSmrg 72401e04c3fSmrg /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the 72501e04c3fSmrg * name of an existing buffer object. 72601e04c3fSmrg */ 7277ec681f3Smrg if (buffer) { 72801e04c3fSmrg bufObj = _mesa_lookup_bufferobj(ctx, buffer); 72901e04c3fSmrg if (!bufObj) { 73001e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func, 73101e04c3fSmrg buffer); 7327ec681f3Smrg *error = true; 73301e04c3fSmrg } 73401e04c3fSmrg } 73501e04c3fSmrg 73601e04c3fSmrg return bufObj; 73701e04c3fSmrg} 73801e04c3fSmrg 73901e04c3fSmrgvoid GLAPIENTRY 74001e04c3fSmrg_mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer) 74101e04c3fSmrg{ 74201e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 74301e04c3fSmrg struct gl_transform_feedback_object *obj; 74401e04c3fSmrg struct gl_buffer_object *bufObj; 74501e04c3fSmrg 74601e04c3fSmrg obj = lookup_transform_feedback_object_err(ctx, xfb, 74701e04c3fSmrg "glTransformFeedbackBufferBase"); 74801e04c3fSmrg if (!obj) { 74901e04c3fSmrg return; 75001e04c3fSmrg } 75101e04c3fSmrg 7527ec681f3Smrg bool error; 75301e04c3fSmrg bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer, 7547ec681f3Smrg "glTransformFeedbackBufferBase", 7557ec681f3Smrg &error); 7567ec681f3Smrg if (error) { 7573464ebd5Sriastradh return; 7583464ebd5Sriastradh } 7593464ebd5Sriastradh 76001e04c3fSmrg _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true); 7613464ebd5Sriastradh} 7623464ebd5Sriastradh 76301e04c3fSmrgvoid GLAPIENTRY 76401e04c3fSmrg_mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer, 76501e04c3fSmrg GLintptr offset, GLsizeiptr size) 76601e04c3fSmrg{ 76701e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 76801e04c3fSmrg struct gl_transform_feedback_object *obj; 76901e04c3fSmrg struct gl_buffer_object *bufObj; 77001e04c3fSmrg 77101e04c3fSmrg obj = lookup_transform_feedback_object_err(ctx, xfb, 77201e04c3fSmrg "glTransformFeedbackBufferRange"); 77301e04c3fSmrg if (!obj) { 77401e04c3fSmrg return; 77501e04c3fSmrg } 77601e04c3fSmrg 7777ec681f3Smrg bool error; 77801e04c3fSmrg bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer, 7797ec681f3Smrg "glTransformFeedbackBufferRange", 7807ec681f3Smrg &error); 7817ec681f3Smrg if (error) { 78201e04c3fSmrg return; 78301e04c3fSmrg } 78401e04c3fSmrg 78501e04c3fSmrg if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset, 78601e04c3fSmrg size, true)) 78701e04c3fSmrg return; 78801e04c3fSmrg 78901e04c3fSmrg /* The per-attribute binding point */ 79001e04c3fSmrg _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, 79101e04c3fSmrg size); 79201e04c3fSmrg} 7933464ebd5Sriastradh 7943464ebd5Sriastradh/** 795af69d88dSmrg * Specify a buffer object to receive transform feedback results, plus the 7963464ebd5Sriastradh * offset in the buffer to start placing results. 7973464ebd5Sriastradh * This function is part of GL_EXT_transform_feedback, but not GL3. 7983464ebd5Sriastradh */ 79901e04c3fSmrgstatic ALWAYS_INLINE void 80001e04c3fSmrgbind_buffer_offset(struct gl_context *ctx, 80101e04c3fSmrg struct gl_transform_feedback_object *obj, GLuint index, 80201e04c3fSmrg GLuint buffer, GLintptr offset, bool no_error) 80301e04c3fSmrg{ 80401e04c3fSmrg struct gl_buffer_object *bufObj; 80501e04c3fSmrg 80601e04c3fSmrg if (buffer == 0) { 8077ec681f3Smrg bufObj = NULL; 80801e04c3fSmrg } else { 80901e04c3fSmrg bufObj = _mesa_lookup_bufferobj(ctx, buffer); 81001e04c3fSmrg if (!no_error && !bufObj) { 81101e04c3fSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 81201e04c3fSmrg "glBindBufferOffsetEXT(invalid buffer=%u)", buffer); 81301e04c3fSmrg return; 81401e04c3fSmrg } 81501e04c3fSmrg } 81601e04c3fSmrg 81701e04c3fSmrg _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0); 81801e04c3fSmrg} 81901e04c3fSmrg 82001e04c3fSmrg 82101e04c3fSmrgvoid GLAPIENTRY 82201e04c3fSmrg_mesa_BindBufferOffsetEXT_no_error(GLenum target, GLuint index, GLuint buffer, 82301e04c3fSmrg GLintptr offset) 82401e04c3fSmrg{ 82501e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 82601e04c3fSmrg bind_buffer_offset(ctx, ctx->TransformFeedback.CurrentObject, index, buffer, 82701e04c3fSmrg offset, true); 82801e04c3fSmrg} 82901e04c3fSmrg 83001e04c3fSmrg 8313464ebd5Sriastradhvoid GLAPIENTRY 8323464ebd5Sriastradh_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, 8333464ebd5Sriastradh GLintptr offset) 8343464ebd5Sriastradh{ 8353464ebd5Sriastradh struct gl_transform_feedback_object *obj; 8363464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 8373464ebd5Sriastradh 8383464ebd5Sriastradh if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { 8393464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)"); 8403464ebd5Sriastradh return; 8413464ebd5Sriastradh } 8423464ebd5Sriastradh 8433464ebd5Sriastradh obj = ctx->TransformFeedback.CurrentObject; 8443464ebd5Sriastradh 8453464ebd5Sriastradh if (obj->Active) { 8463464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_OPERATION, 8473464ebd5Sriastradh "glBindBufferOffsetEXT(transform feedback active)"); 8483464ebd5Sriastradh return; 8493464ebd5Sriastradh } 8503464ebd5Sriastradh 851af69d88dSmrg if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 8523464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_VALUE, 8533464ebd5Sriastradh "glBindBufferOffsetEXT(index=%d)", index); 8543464ebd5Sriastradh return; 8553464ebd5Sriastradh } 8563464ebd5Sriastradh 857af69d88dSmrg if (offset & 0x3) { 858af69d88dSmrg /* must be multiple of four */ 859af69d88dSmrg _mesa_error(ctx, GL_INVALID_VALUE, 860af69d88dSmrg "glBindBufferOffsetEXT(offset=%d)", (int) offset); 861af69d88dSmrg return; 862af69d88dSmrg } 863af69d88dSmrg 86401e04c3fSmrg bind_buffer_offset(ctx, obj, index, buffer, offset, false); 86501e04c3fSmrg} 86601e04c3fSmrg 86701e04c3fSmrg 86801e04c3fSmrg/** 86901e04c3fSmrg * This function specifies the transform feedback outputs to be written 87001e04c3fSmrg * to the feedback buffer(s), and in what order. 87101e04c3fSmrg */ 87201e04c3fSmrgstatic ALWAYS_INLINE void 87301e04c3fSmrgtransform_feedback_varyings(struct gl_context *ctx, 87401e04c3fSmrg struct gl_shader_program *shProg, GLsizei count, 87501e04c3fSmrg const GLchar *const *varyings, GLenum bufferMode) 87601e04c3fSmrg{ 87701e04c3fSmrg GLint i; 87801e04c3fSmrg 87901e04c3fSmrg /* free existing varyings, if any */ 88001e04c3fSmrg for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) { 88101e04c3fSmrg free(shProg->TransformFeedback.VaryingNames[i]); 882af69d88dSmrg } 88301e04c3fSmrg free(shProg->TransformFeedback.VaryingNames); 884af69d88dSmrg 88501e04c3fSmrg /* allocate new memory for varying names */ 88601e04c3fSmrg shProg->TransformFeedback.VaryingNames = 88701e04c3fSmrg malloc(count * sizeof(GLchar *)); 88801e04c3fSmrg 88901e04c3fSmrg if (!shProg->TransformFeedback.VaryingNames) { 89001e04c3fSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()"); 8913464ebd5Sriastradh return; 8923464ebd5Sriastradh } 8933464ebd5Sriastradh 89401e04c3fSmrg /* Save the new names and the count */ 89501e04c3fSmrg for (i = 0; i < count; i++) { 89601e04c3fSmrg shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]); 89701e04c3fSmrg } 89801e04c3fSmrg shProg->TransformFeedback.NumVarying = count; 89901e04c3fSmrg 90001e04c3fSmrg shProg->TransformFeedback.BufferMode = bufferMode; 90101e04c3fSmrg 90201e04c3fSmrg /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since 90301e04c3fSmrg * the varyings won't be used until shader link time. 90401e04c3fSmrg */ 9053464ebd5Sriastradh} 9063464ebd5Sriastradh 9073464ebd5Sriastradh 90801e04c3fSmrgvoid GLAPIENTRY 90901e04c3fSmrg_mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count, 91001e04c3fSmrg const GLchar *const *varyings, 91101e04c3fSmrg GLenum bufferMode) 91201e04c3fSmrg{ 91301e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 91401e04c3fSmrg 91501e04c3fSmrg struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program); 91601e04c3fSmrg transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode); 91701e04c3fSmrg} 91801e04c3fSmrg 9193464ebd5Sriastradhvoid GLAPIENTRY 9203464ebd5Sriastradh_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, 921af69d88dSmrg const GLchar * const *varyings, 922af69d88dSmrg GLenum bufferMode) 9233464ebd5Sriastradh{ 9243464ebd5Sriastradh struct gl_shader_program *shProg; 925af69d88dSmrg GLint i; 9263464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 9273464ebd5Sriastradh 928af69d88dSmrg /* From the ARB_transform_feedback2 specification: 929af69d88dSmrg * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings 930af69d88dSmrg * if the current transform feedback object is active, even if paused." 931af69d88dSmrg */ 932af69d88dSmrg if (ctx->TransformFeedback.CurrentObject->Active) { 933af69d88dSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 934af69d88dSmrg "glTransformFeedbackVaryings(current object is active)"); 935af69d88dSmrg return; 936af69d88dSmrg } 937af69d88dSmrg 9383464ebd5Sriastradh switch (bufferMode) { 9393464ebd5Sriastradh case GL_INTERLEAVED_ATTRIBS: 9403464ebd5Sriastradh break; 9413464ebd5Sriastradh case GL_SEPARATE_ATTRIBS: 9423464ebd5Sriastradh break; 9433464ebd5Sriastradh default: 9443464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_ENUM, 9453464ebd5Sriastradh "glTransformFeedbackVaryings(bufferMode)"); 9463464ebd5Sriastradh return; 9473464ebd5Sriastradh } 9483464ebd5Sriastradh 949af69d88dSmrg if (count < 0 || 950af69d88dSmrg (bufferMode == GL_SEPARATE_ATTRIBS && 951af69d88dSmrg (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) { 9523464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_VALUE, 9533464ebd5Sriastradh "glTransformFeedbackVaryings(count=%d)", count); 9543464ebd5Sriastradh return; 9553464ebd5Sriastradh } 9563464ebd5Sriastradh 95701e04c3fSmrg shProg = _mesa_lookup_shader_program_err(ctx, program, 95801e04c3fSmrg "glTransformFeedbackVaryings"); 95901e04c3fSmrg if (!shProg) 9603464ebd5Sriastradh return; 9613464ebd5Sriastradh 962af69d88dSmrg if (ctx->Extensions.ARB_transform_feedback3) { 963af69d88dSmrg if (bufferMode == GL_INTERLEAVED_ATTRIBS) { 964af69d88dSmrg unsigned buffers = 1; 965af69d88dSmrg 966af69d88dSmrg for (i = 0; i < count; i++) { 967af69d88dSmrg if (strcmp(varyings[i], "gl_NextBuffer") == 0) 968af69d88dSmrg buffers++; 969af69d88dSmrg } 970af69d88dSmrg 971af69d88dSmrg if (buffers > ctx->Const.MaxTransformFeedbackBuffers) { 972af69d88dSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 973af69d88dSmrg "glTransformFeedbackVaryings(too many gl_NextBuffer " 97401e04c3fSmrg "occurrences)"); 975af69d88dSmrg return; 976af69d88dSmrg } 977af69d88dSmrg } else { 978af69d88dSmrg for (i = 0; i < count; i++) { 979af69d88dSmrg if (strcmp(varyings[i], "gl_NextBuffer") == 0 || 980af69d88dSmrg strcmp(varyings[i], "gl_SkipComponents1") == 0 || 981af69d88dSmrg strcmp(varyings[i], "gl_SkipComponents2") == 0 || 982af69d88dSmrg strcmp(varyings[i], "gl_SkipComponents3") == 0 || 983af69d88dSmrg strcmp(varyings[i], "gl_SkipComponents4") == 0) { 984af69d88dSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 985af69d88dSmrg "glTransformFeedbackVaryings(SEPARATE_ATTRIBS," 986af69d88dSmrg "varying=%s)", 987af69d88dSmrg varyings[i]); 988af69d88dSmrg return; 989af69d88dSmrg } 990af69d88dSmrg } 991af69d88dSmrg } 992af69d88dSmrg } 993af69d88dSmrg 99401e04c3fSmrg transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode); 9953464ebd5Sriastradh} 9963464ebd5Sriastradh 9973464ebd5Sriastradh 9983464ebd5Sriastradh/** 999af69d88dSmrg * Get info about the transform feedback outputs which are to be written 10003464ebd5Sriastradh * to the feedback buffer(s). 10013464ebd5Sriastradh */ 10023464ebd5Sriastradhvoid GLAPIENTRY 10033464ebd5Sriastradh_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index, 10043464ebd5Sriastradh GLsizei bufSize, GLsizei *length, 10053464ebd5Sriastradh GLsizei *size, GLenum *type, GLchar *name) 10063464ebd5Sriastradh{ 10073464ebd5Sriastradh const struct gl_shader_program *shProg; 100801e04c3fSmrg struct gl_program_resource *res; 10093464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 10103464ebd5Sriastradh 101101e04c3fSmrg shProg = _mesa_lookup_shader_program_err(ctx, program, 101201e04c3fSmrg "glGetTransformFeedbackVarying"); 101301e04c3fSmrg if (!shProg) 10143464ebd5Sriastradh return; 10153464ebd5Sriastradh 101601e04c3fSmrg res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg, 101701e04c3fSmrg GL_TRANSFORM_FEEDBACK_VARYING, 101801e04c3fSmrg index); 101901e04c3fSmrg if (!res) { 10203464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_VALUE, 1021af69d88dSmrg "glGetTransformFeedbackVarying(index=%u)", index); 10223464ebd5Sriastradh return; 10233464ebd5Sriastradh } 10243464ebd5Sriastradh 1025af69d88dSmrg /* return the varying's name and length */ 102601e04c3fSmrg _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res)); 10273464ebd5Sriastradh 1028af69d88dSmrg /* return the datatype and value's size (in datatype units) */ 1029af69d88dSmrg if (type) 103001e04c3fSmrg _mesa_program_resource_prop((struct gl_shader_program *) shProg, 103101e04c3fSmrg res, index, GL_TYPE, (GLint*) type, 10327ec681f3Smrg false, "glGetTransformFeedbackVarying"); 1033af69d88dSmrg if (size) 103401e04c3fSmrg _mesa_program_resource_prop((struct gl_shader_program *) shProg, 103501e04c3fSmrg res, index, GL_ARRAY_SIZE, (GLint*) size, 10367ec681f3Smrg false, "glGetTransformFeedbackVarying"); 10373464ebd5Sriastradh} 10383464ebd5Sriastradh 10393464ebd5Sriastradh 10403464ebd5Sriastradh 1041af69d88dSmrgstruct gl_transform_feedback_object * 1042af69d88dSmrg_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name) 10433464ebd5Sriastradh{ 104401e04c3fSmrg /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating 104501e04c3fSmrg * the default transform feedback object, or the name of an existing 104601e04c3fSmrg * transform feedback object." 104701e04c3fSmrg */ 10483464ebd5Sriastradh if (name == 0) { 10493464ebd5Sriastradh return ctx->TransformFeedback.DefaultObject; 10503464ebd5Sriastradh } 10513464ebd5Sriastradh else 10523464ebd5Sriastradh return (struct gl_transform_feedback_object *) 105301e04c3fSmrg _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name); 10543464ebd5Sriastradh} 10553464ebd5Sriastradh 105601e04c3fSmrgstatic void 105701e04c3fSmrgcreate_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids, 105801e04c3fSmrg bool dsa) 10593464ebd5Sriastradh{ 106001e04c3fSmrg const char* func; 106101e04c3fSmrg 106201e04c3fSmrg if (dsa) 106301e04c3fSmrg func = "glCreateTransformFeedbacks"; 106401e04c3fSmrg else 106501e04c3fSmrg func = "glGenTransformFeedbacks"; 10663464ebd5Sriastradh 10673464ebd5Sriastradh if (n < 0) { 106801e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func); 10693464ebd5Sriastradh return; 10703464ebd5Sriastradh } 10713464ebd5Sriastradh 107201e04c3fSmrg if (!ids) 10733464ebd5Sriastradh return; 10743464ebd5Sriastradh 10757ec681f3Smrg if (_mesa_HashFindFreeKeys(ctx->TransformFeedback.Objects, ids, n)) { 10763464ebd5Sriastradh GLsizei i; 10773464ebd5Sriastradh for (i = 0; i < n; i++) { 10783464ebd5Sriastradh struct gl_transform_feedback_object *obj 10797ec681f3Smrg = ctx->Driver.NewTransformFeedback(ctx, ids[i]); 10803464ebd5Sriastradh if (!obj) { 108101e04c3fSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 10823464ebd5Sriastradh return; 10833464ebd5Sriastradh } 10847ec681f3Smrg _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, ids[i], 10857ec681f3Smrg obj, true); 108601e04c3fSmrg if (dsa) { 108701e04c3fSmrg /* this is normally done at bind time in the non-dsa case */ 108801e04c3fSmrg obj->EverBound = GL_TRUE; 108901e04c3fSmrg } 10903464ebd5Sriastradh } 10913464ebd5Sriastradh } 10923464ebd5Sriastradh else { 109301e04c3fSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 10943464ebd5Sriastradh } 10953464ebd5Sriastradh} 10963464ebd5Sriastradh 109701e04c3fSmrg/** 109801e04c3fSmrg * Create new transform feedback objects. Transform feedback objects 109901e04c3fSmrg * encapsulate the state related to transform feedback to allow quickly 110001e04c3fSmrg * switching state (and drawing the results, below). 110101e04c3fSmrg * Part of GL_ARB_transform_feedback2. 110201e04c3fSmrg */ 110301e04c3fSmrgvoid GLAPIENTRY 110401e04c3fSmrg_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names) 110501e04c3fSmrg{ 110601e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 110701e04c3fSmrg 110801e04c3fSmrg /* GenTransformFeedbacks should just reserve the object names that a 110901e04c3fSmrg * subsequent call to BindTransformFeedback should actively create. For 111001e04c3fSmrg * the sake of simplicity, we reserve the names and create the objects 111101e04c3fSmrg * straight away. 111201e04c3fSmrg */ 111301e04c3fSmrg 111401e04c3fSmrg create_transform_feedbacks(ctx, n, names, false); 111501e04c3fSmrg} 111601e04c3fSmrg 111701e04c3fSmrg/** 111801e04c3fSmrg * Create new transform feedback objects. Transform feedback objects 111901e04c3fSmrg * encapsulate the state related to transform feedback to allow quickly 112001e04c3fSmrg * switching state (and drawing the results, below). 112101e04c3fSmrg * Part of GL_ARB_direct_state_access. 112201e04c3fSmrg */ 112301e04c3fSmrgvoid GLAPIENTRY 112401e04c3fSmrg_mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names) 112501e04c3fSmrg{ 112601e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 112701e04c3fSmrg 112801e04c3fSmrg create_transform_feedbacks(ctx, n, names, true); 112901e04c3fSmrg} 113001e04c3fSmrg 11313464ebd5Sriastradh 11323464ebd5Sriastradh/** 11333464ebd5Sriastradh * Is the given ID a transform feedback object? 11343464ebd5Sriastradh * Part of GL_ARB_transform_feedback2. 11353464ebd5Sriastradh */ 11363464ebd5SriastradhGLboolean GLAPIENTRY 11373464ebd5Sriastradh_mesa_IsTransformFeedback(GLuint name) 11383464ebd5Sriastradh{ 1139af69d88dSmrg struct gl_transform_feedback_object *obj; 11403464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 11413464ebd5Sriastradh 11423464ebd5Sriastradh ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 11433464ebd5Sriastradh 1144af69d88dSmrg if (name == 0) 11453464ebd5Sriastradh return GL_FALSE; 1146af69d88dSmrg 1147af69d88dSmrg obj = _mesa_lookup_transform_feedback_object(ctx, name); 1148af69d88dSmrg if (obj == NULL) 1149af69d88dSmrg return GL_FALSE; 1150af69d88dSmrg 1151af69d88dSmrg return obj->EverBound; 11523464ebd5Sriastradh} 11533464ebd5Sriastradh 11543464ebd5Sriastradh 11553464ebd5Sriastradh/** 11563464ebd5Sriastradh * Bind the given transform feedback object. 11573464ebd5Sriastradh * Part of GL_ARB_transform_feedback2. 11583464ebd5Sriastradh */ 115901e04c3fSmrgstatic ALWAYS_INLINE void 116001e04c3fSmrgbind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error) 116101e04c3fSmrg{ 116201e04c3fSmrg struct gl_transform_feedback_object *obj; 116301e04c3fSmrg 116401e04c3fSmrg obj = _mesa_lookup_transform_feedback_object(ctx, name); 116501e04c3fSmrg if (!no_error && !obj) { 116601e04c3fSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 116701e04c3fSmrg "glBindTransformFeedback(name=%u)", name); 116801e04c3fSmrg return; 116901e04c3fSmrg } 117001e04c3fSmrg 117101e04c3fSmrg reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 117201e04c3fSmrg obj); 117301e04c3fSmrg} 117401e04c3fSmrg 117501e04c3fSmrg 117601e04c3fSmrgvoid GLAPIENTRY 117701e04c3fSmrg_mesa_BindTransformFeedback_no_error(GLenum target, GLuint name) 117801e04c3fSmrg{ 117901e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 118001e04c3fSmrg bind_transform_feedback(ctx, name, true); 118101e04c3fSmrg} 118201e04c3fSmrg 118301e04c3fSmrg 11843464ebd5Sriastradhvoid GLAPIENTRY 11853464ebd5Sriastradh_mesa_BindTransformFeedback(GLenum target, GLuint name) 11863464ebd5Sriastradh{ 11873464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 11883464ebd5Sriastradh 11893464ebd5Sriastradh if (target != GL_TRANSFORM_FEEDBACK) { 11903464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)"); 11913464ebd5Sriastradh return; 11923464ebd5Sriastradh } 11933464ebd5Sriastradh 1194af69d88dSmrg if (_mesa_is_xfb_active_and_unpaused(ctx)) { 11953464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_OPERATION, 11963464ebd5Sriastradh "glBindTransformFeedback(transform is active, or not paused)"); 11973464ebd5Sriastradh return; 11983464ebd5Sriastradh } 11993464ebd5Sriastradh 120001e04c3fSmrg bind_transform_feedback(ctx, name, false); 12013464ebd5Sriastradh} 12023464ebd5Sriastradh 12033464ebd5Sriastradh 12043464ebd5Sriastradh/** 12053464ebd5Sriastradh * Delete the given transform feedback objects. 12063464ebd5Sriastradh * Part of GL_ARB_transform_feedback2. 12073464ebd5Sriastradh */ 12083464ebd5Sriastradhvoid GLAPIENTRY 12093464ebd5Sriastradh_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names) 12103464ebd5Sriastradh{ 12113464ebd5Sriastradh GLint i; 12123464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 12133464ebd5Sriastradh 12143464ebd5Sriastradh if (n < 0) { 12153464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)"); 12163464ebd5Sriastradh return; 12173464ebd5Sriastradh } 12183464ebd5Sriastradh 12193464ebd5Sriastradh if (!names) 12203464ebd5Sriastradh return; 12213464ebd5Sriastradh 12223464ebd5Sriastradh for (i = 0; i < n; i++) { 12233464ebd5Sriastradh if (names[i] > 0) { 12243464ebd5Sriastradh struct gl_transform_feedback_object *obj 1225af69d88dSmrg = _mesa_lookup_transform_feedback_object(ctx, names[i]); 12263464ebd5Sriastradh if (obj) { 12273464ebd5Sriastradh if (obj->Active) { 12283464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_OPERATION, 12293464ebd5Sriastradh "glDeleteTransformFeedbacks(object %u is active)", 12303464ebd5Sriastradh names[i]); 12313464ebd5Sriastradh return; 12323464ebd5Sriastradh } 123301e04c3fSmrg _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]); 12343464ebd5Sriastradh /* unref, but object may not be deleted until later */ 123501e04c3fSmrg if (obj == ctx->TransformFeedback.CurrentObject) { 123601e04c3fSmrg reference_transform_feedback_object( 123701e04c3fSmrg &ctx->TransformFeedback.CurrentObject, 123801e04c3fSmrg ctx->TransformFeedback.DefaultObject); 123901e04c3fSmrg } 12403464ebd5Sriastradh reference_transform_feedback_object(&obj, NULL); 12413464ebd5Sriastradh } 12423464ebd5Sriastradh } 12433464ebd5Sriastradh } 12443464ebd5Sriastradh} 12453464ebd5Sriastradh 12463464ebd5Sriastradh 12473464ebd5Sriastradh/** 12483464ebd5Sriastradh * Pause transform feedback. 12493464ebd5Sriastradh * Part of GL_ARB_transform_feedback2. 12503464ebd5Sriastradh */ 125101e04c3fSmrgstatic void 125201e04c3fSmrgpause_transform_feedback(struct gl_context *ctx, 125301e04c3fSmrg struct gl_transform_feedback_object *obj) 125401e04c3fSmrg{ 12557ec681f3Smrg FLUSH_VERTICES(ctx, 0, 0); 125601e04c3fSmrg ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 125701e04c3fSmrg 125801e04c3fSmrg assert(ctx->Driver.PauseTransformFeedback); 125901e04c3fSmrg ctx->Driver.PauseTransformFeedback(ctx, obj); 126001e04c3fSmrg 126101e04c3fSmrg obj->Paused = GL_TRUE; 12627ec681f3Smrg _mesa_update_valid_to_render_state(ctx); 126301e04c3fSmrg} 126401e04c3fSmrg 126501e04c3fSmrg 126601e04c3fSmrgvoid GLAPIENTRY 126701e04c3fSmrg_mesa_PauseTransformFeedback_no_error(void) 126801e04c3fSmrg{ 126901e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 127001e04c3fSmrg pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 127101e04c3fSmrg} 127201e04c3fSmrg 127301e04c3fSmrg 12743464ebd5Sriastradhvoid GLAPIENTRY 12753464ebd5Sriastradh_mesa_PauseTransformFeedback(void) 12763464ebd5Sriastradh{ 12773464ebd5Sriastradh struct gl_transform_feedback_object *obj; 12783464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 12793464ebd5Sriastradh 12803464ebd5Sriastradh obj = ctx->TransformFeedback.CurrentObject; 12813464ebd5Sriastradh 1282af69d88dSmrg if (!_mesa_is_xfb_active_and_unpaused(ctx)) { 12833464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_OPERATION, 12843464ebd5Sriastradh "glPauseTransformFeedback(feedback not active or already paused)"); 12853464ebd5Sriastradh return; 12863464ebd5Sriastradh } 12873464ebd5Sriastradh 128801e04c3fSmrg pause_transform_feedback(ctx, obj); 12893464ebd5Sriastradh} 12903464ebd5Sriastradh 12913464ebd5Sriastradh 12923464ebd5Sriastradh/** 12933464ebd5Sriastradh * Resume transform feedback. 12943464ebd5Sriastradh * Part of GL_ARB_transform_feedback2. 12953464ebd5Sriastradh */ 129601e04c3fSmrgstatic void 129701e04c3fSmrgresume_transform_feedback(struct gl_context *ctx, 129801e04c3fSmrg struct gl_transform_feedback_object *obj) 129901e04c3fSmrg{ 13007ec681f3Smrg FLUSH_VERTICES(ctx, 0, 0); 130101e04c3fSmrg ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 130201e04c3fSmrg 130301e04c3fSmrg obj->Paused = GL_FALSE; 130401e04c3fSmrg 130501e04c3fSmrg assert(ctx->Driver.ResumeTransformFeedback); 130601e04c3fSmrg ctx->Driver.ResumeTransformFeedback(ctx, obj); 13077ec681f3Smrg _mesa_update_valid_to_render_state(ctx); 130801e04c3fSmrg} 130901e04c3fSmrg 131001e04c3fSmrg 131101e04c3fSmrgvoid GLAPIENTRY 131201e04c3fSmrg_mesa_ResumeTransformFeedback_no_error(void) 131301e04c3fSmrg{ 131401e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 131501e04c3fSmrg resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 131601e04c3fSmrg} 131701e04c3fSmrg 131801e04c3fSmrg 13193464ebd5Sriastradhvoid GLAPIENTRY 13203464ebd5Sriastradh_mesa_ResumeTransformFeedback(void) 13213464ebd5Sriastradh{ 13223464ebd5Sriastradh struct gl_transform_feedback_object *obj; 13233464ebd5Sriastradh GET_CURRENT_CONTEXT(ctx); 13243464ebd5Sriastradh 13253464ebd5Sriastradh obj = ctx->TransformFeedback.CurrentObject; 13263464ebd5Sriastradh 13273464ebd5Sriastradh if (!obj->Active || !obj->Paused) { 13283464ebd5Sriastradh _mesa_error(ctx, GL_INVALID_OPERATION, 13293464ebd5Sriastradh "glResumeTransformFeedback(feedback not active or not paused)"); 13303464ebd5Sriastradh return; 13313464ebd5Sriastradh } 13323464ebd5Sriastradh 1333af69d88dSmrg /* From the ARB_transform_feedback2 specification: 1334af69d88dSmrg * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if 1335af69d88dSmrg * the program object being used by the current transform feedback object 1336af69d88dSmrg * is not active." 1337af69d88dSmrg */ 133801e04c3fSmrg if (obj->program != get_xfb_source(ctx)) { 1339af69d88dSmrg _mesa_error(ctx, GL_INVALID_OPERATION, 1340af69d88dSmrg "glResumeTransformFeedback(wrong program bound)"); 13413464ebd5Sriastradh return; 13423464ebd5Sriastradh } 13433464ebd5Sriastradh 134401e04c3fSmrg resume_transform_feedback(ctx, obj); 134501e04c3fSmrg} 13463464ebd5Sriastradh 134701e04c3fSmrgextern void GLAPIENTRY 134801e04c3fSmrg_mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param) 134901e04c3fSmrg{ 135001e04c3fSmrg struct gl_transform_feedback_object *obj; 135101e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 135201e04c3fSmrg 135301e04c3fSmrg obj = lookup_transform_feedback_object_err(ctx, xfb, 135401e04c3fSmrg "glGetTransformFeedbackiv"); 135501e04c3fSmrg if (!obj) { 135601e04c3fSmrg return; 135701e04c3fSmrg } 135801e04c3fSmrg 135901e04c3fSmrg switch(pname) { 136001e04c3fSmrg case GL_TRANSFORM_FEEDBACK_PAUSED: 136101e04c3fSmrg *param = obj->Paused; 136201e04c3fSmrg break; 136301e04c3fSmrg case GL_TRANSFORM_FEEDBACK_ACTIVE: 136401e04c3fSmrg *param = obj->Active; 136501e04c3fSmrg break; 136601e04c3fSmrg default: 136701e04c3fSmrg _mesa_error(ctx, GL_INVALID_ENUM, 136801e04c3fSmrg "glGetTransformFeedbackiv(pname=%i)", pname); 136901e04c3fSmrg } 137001e04c3fSmrg} 13713464ebd5Sriastradh 137201e04c3fSmrgextern void GLAPIENTRY 137301e04c3fSmrg_mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index, 137401e04c3fSmrg GLint *param) 137501e04c3fSmrg{ 137601e04c3fSmrg struct gl_transform_feedback_object *obj; 137701e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 137801e04c3fSmrg 137901e04c3fSmrg obj = lookup_transform_feedback_object_err(ctx, xfb, 138001e04c3fSmrg "glGetTransformFeedbacki_v"); 138101e04c3fSmrg if (!obj) { 138201e04c3fSmrg return; 138301e04c3fSmrg } 138401e04c3fSmrg 138501e04c3fSmrg if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 138601e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, 138701e04c3fSmrg "glGetTransformFeedbacki_v(index=%i)", index); 138801e04c3fSmrg return; 138901e04c3fSmrg } 139001e04c3fSmrg 139101e04c3fSmrg switch(pname) { 139201e04c3fSmrg case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: 139301e04c3fSmrg *param = obj->BufferNames[index]; 139401e04c3fSmrg break; 139501e04c3fSmrg default: 139601e04c3fSmrg _mesa_error(ctx, GL_INVALID_ENUM, 139701e04c3fSmrg "glGetTransformFeedbacki_v(pname=%i)", pname); 139801e04c3fSmrg } 139901e04c3fSmrg} 140001e04c3fSmrg 140101e04c3fSmrgextern void GLAPIENTRY 140201e04c3fSmrg_mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index, 140301e04c3fSmrg GLint64 *param) 140401e04c3fSmrg{ 140501e04c3fSmrg struct gl_transform_feedback_object *obj; 140601e04c3fSmrg GET_CURRENT_CONTEXT(ctx); 140701e04c3fSmrg 140801e04c3fSmrg obj = lookup_transform_feedback_object_err(ctx, xfb, 140901e04c3fSmrg "glGetTransformFeedbacki64_v"); 141001e04c3fSmrg if (!obj) { 141101e04c3fSmrg return; 141201e04c3fSmrg } 141301e04c3fSmrg 141401e04c3fSmrg if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 141501e04c3fSmrg _mesa_error(ctx, GL_INVALID_VALUE, 141601e04c3fSmrg "glGetTransformFeedbacki64_v(index=%i)", index); 141701e04c3fSmrg return; 141801e04c3fSmrg } 141901e04c3fSmrg 142001e04c3fSmrg /** 142101e04c3fSmrg * This follows the same general rules used for BindBufferBase: 142201e04c3fSmrg * 142301e04c3fSmrg * "To query the starting offset or size of the range of a buffer 142401e04c3fSmrg * object binding in an indexed array, call GetInteger64i_v with 142501e04c3fSmrg * target set to respectively the starting offset or binding size 142601e04c3fSmrg * name from table 6.5 for that array. Index must be in the range 142701e04c3fSmrg * zero to the number of bind points supported minus one. If the 142801e04c3fSmrg * starting offset or size was not specified when the buffer object 142901e04c3fSmrg * was bound (e.g. if it was bound with BindBufferBase), or if no 143001e04c3fSmrg * buffer object is bound to the target array at index, zero is 143101e04c3fSmrg * returned." 143201e04c3fSmrg */ 143301e04c3fSmrg if (obj->RequestedSize[index] == 0 && 143401e04c3fSmrg (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START || 143501e04c3fSmrg pname == GL_TRANSFORM_FEEDBACK_BUFFER_SIZE)) { 143601e04c3fSmrg *param = 0; 143701e04c3fSmrg return; 143801e04c3fSmrg } 143901e04c3fSmrg 144001e04c3fSmrg compute_transform_feedback_buffer_sizes(obj); 144101e04c3fSmrg switch(pname) { 144201e04c3fSmrg case GL_TRANSFORM_FEEDBACK_BUFFER_START: 144301e04c3fSmrg assert(obj->RequestedSize[index] > 0); 144401e04c3fSmrg *param = obj->Offset[index]; 144501e04c3fSmrg break; 144601e04c3fSmrg case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: 144701e04c3fSmrg assert(obj->RequestedSize[index] > 0); 144801e04c3fSmrg *param = obj->Size[index]; 144901e04c3fSmrg break; 145001e04c3fSmrg default: 145101e04c3fSmrg _mesa_error(ctx, GL_INVALID_ENUM, 145201e04c3fSmrg "glGetTransformFeedbacki64_v(pname=%i)", pname); 145301e04c3fSmrg } 1454af69d88dSmrg} 1455