/** * Helper function used by the functions below. */ static GLint add_parameter(struct program_parameter_list *paramList, const char *name, const GLfloat values[4], enum parameter_type type) { const GLuint n = paramList->NumParameters; paramList->Parameters = (struct program_parameter *) _mesa_realloc(paramList->Parameters, n * sizeof(struct program_parameter), (n + 1) * sizeof(struct program_parameter)); if (!paramList->Parameters) { /* out of memory */ paramList->NumParameters = 0; return -1; } else { paramList->NumParameters = n + 1; paramList->Parameters[n].Name = _mesa_strdup(name); paramList->Parameters[n].Type = type; if (values) COPY_4V(paramList->Parameters[n].Values, values); return (GLint) n; } }
/** * Init context's vertex/fragment program state */ void _mesa_init_program(GLcontext *ctx) { GLuint i; ctx->Program.ErrorPos = -1; ctx->Program.ErrorString = _mesa_strdup(""); #if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program ctx->VertexProgram.Enabled = GL_FALSE; ctx->VertexProgram.PointSizeEnabled = GL_FALSE; ctx->VertexProgram.TwoSideEnabled = GL_FALSE; ctx->VertexProgram.Current = NULL; ctx->VertexProgram.Current = (struct vertex_program *) ctx->Shared->DefaultVertexProgram; assert(ctx->VertexProgram.Current); ctx->VertexProgram.Current->Base.RefCount++; for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { ctx->VertexProgram.TrackMatrix[i] = GL_NONE; ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV; } #endif #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program ctx->FragmentProgram.Enabled = GL_FALSE; ctx->FragmentProgram.Current = (struct fragment_program *) ctx->Shared->DefaultFragmentProgram; assert(ctx->FragmentProgram.Current); ctx->FragmentProgram.Current->Base.RefCount++; #endif }
/** * Set the vertex/fragment program error state (position and error string). * This is generally called from within the parsers. */ void _mesa_set_program_error(GLcontext *ctx, GLint pos, const char *string) { ctx->Program.ErrorPos = pos; _mesa_free((void *) ctx->Program.ErrorString); if (!string) string = ""; ctx->Program.ErrorString = _mesa_strdup(string); }
/** * Record a linking error. */ static void link_error(struct gl_shader_program *shProg, const char *msg) { if (shProg->InfoLog) { free(shProg->InfoLog); } shProg->InfoLog = _mesa_strdup(msg); shProg->LinkStatus = GL_FALSE; }
/** * Copy an array of program instructions. * \param dest pointer to destination. * \param src pointer to source. * \param n number of instructions to copy. * \return pointer to destination. */ struct prog_instruction * _mesa_copy_instructions(struct prog_instruction *dest, const struct prog_instruction *src, GLuint n) { GLuint i; memcpy(dest, src, n * sizeof(struct prog_instruction)); for (i = 0; i < n; i++) { if (src[i].Comment) dest[i].Comment = _mesa_strdup(src[i].Comment); } return dest; }
/** * Init context's vertex/fragment program state */ void _mesa_init_program(GLcontext *ctx) { GLuint i; /* * If this assertion fails, we need to increase the field * size for register indexes. */ ASSERT(ctx->Const.VertexProgram.MaxUniformComponents / 4 <= (1 << INST_INDEX_BITS)); ASSERT(ctx->Const.FragmentProgram.MaxUniformComponents / 4 <= (1 << INST_INDEX_BITS)); ctx->Program.ErrorPos = -1; ctx->Program.ErrorString = _mesa_strdup(""); #if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program ctx->VertexProgram.Enabled = GL_FALSE; #if FEATURE_es2_glsl ctx->VertexProgram.PointSizeEnabled = GL_TRUE; #else ctx->VertexProgram.PointSizeEnabled = GL_FALSE; #endif ctx->VertexProgram.TwoSideEnabled = GL_FALSE; _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, ctx->Shared->DefaultVertexProgram); assert(ctx->VertexProgram.Current); for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { ctx->VertexProgram.TrackMatrix[i] = GL_NONE; ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV; } ctx->VertexProgram.Cache = _mesa_new_program_cache(); #endif #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program ctx->FragmentProgram.Enabled = GL_FALSE; _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, ctx->Shared->DefaultFragmentProgram); assert(ctx->FragmentProgram.Current); ctx->FragmentProgram.Cache = _mesa_new_program_cache(); #endif /* XXX probably move this stuff */ #if FEATURE_ATI_fragment_shader ctx->ATIFragmentShader.Enabled = GL_FALSE; ctx->ATIFragmentShader.Current = ctx->Shared->DefaultFragmentShader; assert(ctx->ATIFragmentShader.Current); ctx->ATIFragmentShader.Current->RefCount++; #endif }
static void shader_error(GLcontext *ctx, struct gl_program *prog, const char *msg) { struct gl_shader_program *shader; shader = _mesa_lookup_shader_program(ctx, prog->Id); if (shader) { if (shader->InfoLog) { free(shader->InfoLog); } shader->InfoLog = _mesa_strdup(msg); shader->LinkStatus = GL_FALSE; } }
/** * For GL_EXT_separate_shader_objects */ GLuint GLAPIENTRY _mesa_CreateShaderProgramEXT(GLenum type, const GLchar *string) { GET_CURRENT_CONTEXT(ctx); const GLuint shader = create_shader(ctx, type); GLuint program = 0; if (shader) { shader_source(ctx, shader, _mesa_strdup(string)); compile_shader(ctx, shader); program = create_shader_program(ctx); if (program) { struct gl_shader_program *shProg; struct gl_shader *sh; GLint compiled = GL_FALSE; shProg = _mesa_lookup_shader_program(ctx, program); sh = _mesa_lookup_shader(ctx, shader); get_shaderiv(ctx, shader, GL_COMPILE_STATUS, &compiled); if (compiled) { attach_shader(ctx, program, shader); link_program(ctx, program); detach_shader(ctx, program, shader); #if 0 /* Possibly... */ if (active-user-defined-varyings-in-linked-program) { append-error-to-info-log; shProg->LinkStatus = GL_FALSE; } #endif } ralloc_strcat(&shProg->InfoLog, sh->InfoLog); } delete_shader(ctx, shader); } return program; }
/** * Helper for _mesa_ObjectLabel() and _mesa_ObjectPtrLabel(). */ static void set_label(struct gl_context *ctx, char **labelPtr, const char *label, int length, const char *caller) { free(*labelPtr); *labelPtr = NULL; /* set new label string */ if (label) { if (length >= 0) { if (length >= MAX_LABEL_LENGTH) _mesa_error(ctx, GL_INVALID_VALUE, "%s(length=%d, which is not less than " "GL_MAX_LABEL_LENGTH=%d)", caller, length, MAX_LABEL_LENGTH); /* explicit length */ *labelPtr = malloc(length+1); if (*labelPtr) { memcpy(*labelPtr, label, length); /* length is not required to include the null terminator so * add one just in case */ (*labelPtr)[length] = '\0'; } } else { int len = strlen(label); if (len >= MAX_LABEL_LENGTH) _mesa_error(ctx, GL_INVALID_VALUE, "%s(label length=%d, which is not less than " "GL_MAX_LABEL_LENGTH=%d)", caller, len, MAX_LABEL_LENGTH); /* null-terminated string */ *labelPtr = _mesa_strdup(label); } } }
/** * Read shader source code from a file. * Useful for debugging to override an app's shader. */ static GLcharARB * read_shader(const char *fname) { const int max = 50*1000; FILE *f = fopen(fname, "r"); GLcharARB *buffer, *shader; int len; if (!f) { return NULL; } buffer = (char *) malloc(max); len = fread(buffer, 1, max, f); buffer[len] = 0; fclose(f); shader = _mesa_strdup(buffer); free(buffer); return shader; }
/** * Return a copy of a program. * XXX Problem here if the program object is actually OO-derivation * made by a device driver. */ struct gl_program * _mesa_clone_program(struct gl_context *ctx, const struct gl_program *prog) { struct gl_program *clone; clone = ctx->Driver.NewProgram(ctx, prog->Target, prog->Id); if (!clone) return NULL; assert(clone->Target == prog->Target); assert(clone->RefCount == 1); clone->String = (GLubyte *) _mesa_strdup((char *) prog->String); clone->Format = prog->Format; clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions); if (!clone->Instructions) { _mesa_reference_program(ctx, &clone, NULL); return NULL; } _mesa_copy_instructions(clone->Instructions, prog->Instructions, prog->NumInstructions); clone->InputsRead = prog->InputsRead; clone->OutputsWritten = prog->OutputsWritten; clone->SamplersUsed = prog->SamplersUsed; clone->ShadowSamplers = prog->ShadowSamplers; memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed)); if (prog->Parameters) clone->Parameters = _mesa_clone_parameter_list(prog->Parameters); memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); clone->IndirectRegisterFiles = prog->IndirectRegisterFiles; clone->NumInstructions = prog->NumInstructions; clone->NumTemporaries = prog->NumTemporaries; clone->NumParameters = prog->NumParameters; clone->NumAttributes = prog->NumAttributes; clone->NumAddressRegs = prog->NumAddressRegs; clone->NumNativeInstructions = prog->NumNativeInstructions; clone->NumNativeTemporaries = prog->NumNativeTemporaries; clone->NumNativeParameters = prog->NumNativeParameters; clone->NumNativeAttributes = prog->NumNativeAttributes; clone->NumNativeAddressRegs = prog->NumNativeAddressRegs; clone->NumAluInstructions = prog->NumAluInstructions; clone->NumTexInstructions = prog->NumTexInstructions; clone->NumTexIndirections = prog->NumTexIndirections; clone->NumNativeAluInstructions = prog->NumNativeAluInstructions; clone->NumNativeTexInstructions = prog->NumNativeTexInstructions; clone->NumNativeTexIndirections = prog->NumNativeTexIndirections; switch (prog->Target) { case GL_VERTEX_PROGRAM_ARB: { const struct gl_vertex_program *vp = gl_vertex_program_const(prog); struct gl_vertex_program *vpc = gl_vertex_program(clone); vpc->IsPositionInvariant = vp->IsPositionInvariant; vpc->IsNVProgram = vp->IsNVProgram; } break; case GL_FRAGMENT_PROGRAM_ARB: { const struct gl_fragment_program *fp = gl_fragment_program_const(prog); struct gl_fragment_program *fpc = gl_fragment_program(clone); fpc->UsesKill = fp->UsesKill; fpc->UsesDFdy = fp->UsesDFdy; fpc->OriginUpperLeft = fp->OriginUpperLeft; fpc->PixelCenterInteger = fp->PixelCenterInteger; } break; case MESA_GEOMETRY_PROGRAM: { const struct gl_geometry_program *gp = gl_geometry_program_const(prog); struct gl_geometry_program *gpc = gl_geometry_program(clone); gpc->VerticesOut = gp->VerticesOut; gpc->InputType = gp->InputType; gpc->OutputType = gp->OutputType; } break; default: _mesa_problem(NULL, "Unexpected target in _mesa_clone_program"); } return clone; }
/** * Init context's vertex/fragment program state */ void _mesa_init_program(struct gl_context *ctx) { GLuint i; /* * If this assertion fails, we need to increase the field * size for register indexes (see INST_INDEX_BITS). */ ASSERT(ctx->Const.VertexProgram.MaxUniformComponents / 4 <= (1 << INST_INDEX_BITS)); ASSERT(ctx->Const.FragmentProgram.MaxUniformComponents / 4 <= (1 << INST_INDEX_BITS)); ASSERT(ctx->Const.VertexProgram.MaxTemps <= (1 << INST_INDEX_BITS)); ASSERT(ctx->Const.VertexProgram.MaxLocalParams <= (1 << INST_INDEX_BITS)); ASSERT(ctx->Const.FragmentProgram.MaxTemps <= (1 << INST_INDEX_BITS)); ASSERT(ctx->Const.FragmentProgram.MaxLocalParams <= (1 << INST_INDEX_BITS)); ASSERT(ctx->Const.VertexProgram.MaxUniformComponents <= 4 * MAX_UNIFORMS); ASSERT(ctx->Const.FragmentProgram.MaxUniformComponents <= 4 * MAX_UNIFORMS); /* If this fails, increase prog_instruction::TexSrcUnit size */ ASSERT(MAX_TEXTURE_UNITS < (1 << 5)); /* If this fails, increase prog_instruction::TexSrcTarget size */ ASSERT(NUM_TEXTURE_TARGETS < (1 << 3)); ctx->Program.ErrorPos = -1; ctx->Program.ErrorString = _mesa_strdup(""); #if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program ctx->VertexProgram.Enabled = GL_FALSE; #if FEATURE_es2_glsl ctx->VertexProgram.PointSizeEnabled = (ctx->API == API_OPENGLES2) ? GL_TRUE : GL_FALSE; #else ctx->VertexProgram.PointSizeEnabled = GL_FALSE; #endif ctx->VertexProgram.TwoSideEnabled = GL_FALSE; _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, ctx->Shared->DefaultVertexProgram); assert(ctx->VertexProgram.Current); for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { ctx->VertexProgram.TrackMatrix[i] = GL_NONE; ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV; } ctx->VertexProgram.Cache = _mesa_new_program_cache(); #endif #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program ctx->FragmentProgram.Enabled = GL_FALSE; _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, ctx->Shared->DefaultFragmentProgram); assert(ctx->FragmentProgram.Current); ctx->FragmentProgram.Cache = _mesa_new_program_cache(); #endif #if FEATURE_ARB_geometry_shader4 ctx->GeometryProgram.Enabled = GL_FALSE; /* right now by default we don't have a geometry program */ _mesa_reference_geomprog(ctx, &ctx->GeometryProgram.Current, NULL); ctx->GeometryProgram.Cache = _mesa_new_program_cache(); #endif /* XXX probably move this stuff */ #if FEATURE_ATI_fragment_shader ctx->ATIFragmentShader.Enabled = GL_FALSE; ctx->ATIFragmentShader.Current = ctx->Shared->DefaultFragmentShader; assert(ctx->ATIFragmentShader.Current); ctx->ATIFragmentShader.Current->RefCount++; #endif }
/** * Return a copy of a program. * XXX Problem here if the program object is actually OO-derivation * made by a device driver. */ struct gl_program * _mesa_clone_program(GLcontext *ctx, const struct gl_program *prog) { struct gl_program *clone; clone = ctx->Driver.NewProgram(ctx, prog->Target, prog->Id); if (!clone) return NULL; assert(clone->Target == prog->Target); assert(clone->RefCount == 1); clone->String = (GLubyte *) _mesa_strdup((char *) prog->String); clone->Format = prog->Format; clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions); if (!clone->Instructions) { _mesa_reference_program(ctx, &clone, NULL); return NULL; } _mesa_copy_instructions(clone->Instructions, prog->Instructions, prog->NumInstructions); clone->InputsRead = prog->InputsRead; clone->OutputsWritten = prog->OutputsWritten; clone->SamplersUsed = prog->SamplersUsed; clone->ShadowSamplers = prog->ShadowSamplers; memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed)); if (prog->Parameters) clone->Parameters = _mesa_clone_parameter_list(prog->Parameters); memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); if (prog->Varying) clone->Varying = _mesa_clone_parameter_list(prog->Varying); if (prog->Attributes) clone->Attributes = _mesa_clone_parameter_list(prog->Attributes); memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); clone->NumInstructions = prog->NumInstructions; clone->NumTemporaries = prog->NumTemporaries; clone->NumParameters = prog->NumParameters; clone->NumAttributes = prog->NumAttributes; clone->NumAddressRegs = prog->NumAddressRegs; clone->NumNativeInstructions = prog->NumNativeInstructions; clone->NumNativeTemporaries = prog->NumNativeTemporaries; clone->NumNativeParameters = prog->NumNativeParameters; clone->NumNativeAttributes = prog->NumNativeAttributes; clone->NumNativeAddressRegs = prog->NumNativeAddressRegs; clone->NumAluInstructions = prog->NumAluInstructions; clone->NumTexInstructions = prog->NumTexInstructions; clone->NumTexIndirections = prog->NumTexIndirections; clone->NumNativeAluInstructions = prog->NumNativeAluInstructions; clone->NumNativeTexInstructions = prog->NumNativeTexInstructions; clone->NumNativeTexIndirections = prog->NumNativeTexIndirections; switch (prog->Target) { case GL_VERTEX_PROGRAM_ARB: { const struct gl_vertex_program *vp = (const struct gl_vertex_program *) prog; struct gl_vertex_program *vpc = (struct gl_vertex_program *) clone; vpc->IsPositionInvariant = vp->IsPositionInvariant; } break; case GL_FRAGMENT_PROGRAM_ARB: { const struct gl_fragment_program *fp = (const struct gl_fragment_program *) prog; struct gl_fragment_program *fpc = (struct gl_fragment_program *) clone; fpc->FogOption = fp->FogOption; fpc->UsesKill = fp->UsesKill; } break; default: _mesa_problem(NULL, "Unexpected target in _mesa_clone_program"); } return clone; }
/** * Add a new parameter to a parameter list. * Note that parameter values are usually 4-element GLfloat vectors. * When size > 4 we'll allocate a sequential block of parameters to * store all the values (in blocks of 4). * * \param paramList the list to add the parameter to * \param type type of parameter, such as * \param name the parameter name, will be duplicated/copied! * \param size number of elements in 'values' vector (1..4, or more) * \param values initial parameter value, up to 4 GLfloats, or NULL * \param state state indexes, or NULL * \return index of new parameter in the list, or -1 if error (out of mem) */ GLint _mesa_add_parameter(struct gl_program_parameter_list *paramList, enum register_file type, const char *name, GLuint size, GLenum datatype, const GLfloat *values, const gl_state_index state[STATE_LENGTH]) { const GLuint oldNum = paramList->NumParameters; const GLuint sz4 = (size + 3) / 4; /* no. of new param slots needed */ assert(size > 0); if (oldNum + sz4 > paramList->Size) { /* Need to grow the parameter list array (alloc some extra) */ paramList->Size = paramList->Size + 4 * sz4; /* realloc arrays */ paramList->Parameters = (struct gl_program_parameter *) _mesa_realloc(paramList->Parameters, oldNum * sizeof(struct gl_program_parameter), paramList->Size * sizeof(struct gl_program_parameter)); paramList->ParameterValues = (GLfloat (*)[4]) _mesa_align_realloc(paramList->ParameterValues, /* old buf */ oldNum * 4 * sizeof(GLfloat), /* old size */ paramList->Size * 4 *sizeof(GLfloat), /* new sz */ 16); } if (!paramList->Parameters || !paramList->ParameterValues) { /* out of memory */ paramList->NumParameters = 0; paramList->Size = 0; return -1; } else { GLuint i; paramList->NumParameters = oldNum + sz4; _mesa_memset(¶mList->Parameters[oldNum], 0, sz4 * sizeof(struct gl_program_parameter)); for (i = 0; i < sz4; i++) { struct gl_program_parameter *p = paramList->Parameters + oldNum + i; p->Name = name ? _mesa_strdup(name) : NULL; p->Type = type; p->Size = size; p->DataType = datatype; if (values) { COPY_4V(paramList->ParameterValues[oldNum + i], values); values += 4; } else { /* silence valgrind */ ASSIGN_4V(paramList->ParameterValues[oldNum + i], 0, 0, 0, 0); } size -= 4; } if (state) { for (i = 0; i < STATE_LENGTH; i++) paramList->Parameters[oldNum].StateIndexes[i] = state[i]; } return (GLint) oldNum; } }
/** * Make a string from the given state vector. * For example, return "state.matrix.texture[2].inverse". * Use free() to deallocate the string. */ char * _mesa_program_state_string(const gl_state_index state[STATE_LENGTH]) { char str[1000] = ""; char tmp[30]; append(str, "state."); append_token(str, state[0]); switch (state[0]) { case STATE_MATERIAL: append_face(str, state[1]); append_token(str, state[2]); break; case STATE_LIGHT: append_index(str, state[1]); /* light number [i]. */ append_token(str, state[2]); /* coefficients */ break; case STATE_LIGHTMODEL_AMBIENT: append(str, "lightmodel.ambient"); break; case STATE_LIGHTMODEL_SCENECOLOR: if (state[1] == 0) { append(str, "lightmodel.front.scenecolor"); } else { append(str, "lightmodel.back.scenecolor"); } break; case STATE_LIGHTPROD: append_index(str, state[1]); /* light number [i]. */ append_face(str, state[2]); append_token(str, state[3]); break; case STATE_TEXGEN: append_index(str, state[1]); /* tex unit [i] */ append_token(str, state[2]); /* plane coef */ break; case STATE_TEXENV_COLOR: append_index(str, state[1]); /* tex unit [i] */ append(str, "color"); break; case STATE_CLIPPLANE: append_index(str, state[1]); /* plane [i] */ append(str, ".plane"); break; case STATE_MODELVIEW_MATRIX: case STATE_PROJECTION_MATRIX: case STATE_MVP_MATRIX: case STATE_TEXTURE_MATRIX: case STATE_PROGRAM_MATRIX: { /* state[0] = modelview, projection, texture, etc. */ /* state[1] = which texture matrix or program matrix */ /* state[2] = first row to fetch */ /* state[3] = last row to fetch */ /* state[4] = transpose, inverse or invtrans */ const gl_state_index mat = state[0]; const GLuint index = (GLuint) state[1]; const GLuint firstRow = (GLuint) state[2]; const GLuint lastRow = (GLuint) state[3]; const gl_state_index modifier = state[4]; if (index || mat == STATE_TEXTURE_MATRIX || mat == STATE_PROGRAM_MATRIX) append_index(str, index); if (modifier) append_token(str, modifier); if (firstRow == lastRow) sprintf(tmp, ".row[%d]", firstRow); else sprintf(tmp, ".row[%d..%d]", firstRow, lastRow); append(str, tmp); } break; case STATE_POINT_SIZE: break; case STATE_POINT_ATTENUATION: break; case STATE_FOG_PARAMS: break; case STATE_FOG_COLOR: break; case STATE_NUM_SAMPLES: break; case STATE_DEPTH_RANGE: break; case STATE_FRAGMENT_PROGRAM: case STATE_VERTEX_PROGRAM: /* state[1] = {STATE_ENV, STATE_LOCAL} */ /* state[2] = parameter index */ append_token(str, state[1]); append_index(str, state[2]); break; case STATE_NORMAL_SCALE: break; case STATE_INTERNAL: append_token(str, state[1]); if (state[1] == STATE_CURRENT_ATTRIB) append_index(str, state[2]); break; default: _mesa_problem(NULL, "Invalid state in _mesa_program_state_string"); break; } return _mesa_strdup(str); }
char *slang_string_duplicate (const char *src) { return _mesa_strdup (src); }
/** * This function specifies the vertex shader outputs to be written * to the feedback buffer(s), and in what order. */ void GLAPIENTRY _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar * const *varyings, GLenum bufferMode) { struct gl_shader_program *shProg; GLint i; GET_CURRENT_CONTEXT(ctx); switch (bufferMode) { case GL_INTERLEAVED_ATTRIBS: break; case GL_SEPARATE_ATTRIBS: break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glTransformFeedbackVaryings(bufferMode)"); return; } if (count < 0 || (bufferMode == GL_SEPARATE_ATTRIBS && (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) { _mesa_error(ctx, GL_INVALID_VALUE, "glTransformFeedbackVaryings(count=%d)", count); return; } shProg = _mesa_lookup_shader_program(ctx, program); if (!shProg) { _mesa_error(ctx, GL_INVALID_VALUE, "glTransformFeedbackVaryings(program=%u)", program); return; } if (ctx->Extensions.ARB_transform_feedback3) { if (bufferMode == GL_INTERLEAVED_ATTRIBS) { unsigned buffers = 1; for (i = 0; i < count; i++) { if (strcmp(varyings[i], "gl_NextBuffer") == 0) buffers++; } if (buffers > ctx->Const.MaxTransformFeedbackBuffers) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTransformFeedbackVaryings(too many gl_NextBuffer " "occurences)"); return; } } else { for (i = 0; i < count; i++) { if (strcmp(varyings[i], "gl_NextBuffer") == 0 || strcmp(varyings[i], "gl_SkipComponents1") == 0 || strcmp(varyings[i], "gl_SkipComponents2") == 0 || strcmp(varyings[i], "gl_SkipComponents3") == 0 || strcmp(varyings[i], "gl_SkipComponents4") == 0) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTransformFeedbackVaryings(SEPARATE_ATTRIBS," "varying=%s)", varyings[i]); return; } } } } /* free existing varyings, if any */ for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) { free(shProg->TransformFeedback.VaryingNames[i]); } free(shProg->TransformFeedback.VaryingNames); /* allocate new memory for varying names */ shProg->TransformFeedback.VaryingNames = malloc(count * sizeof(GLchar *)); if (!shProg->TransformFeedback.VaryingNames) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()"); return; } /* Save the new names and the count */ for (i = 0; i < count; i++) { shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]); } shProg->TransformFeedback.NumVarying = count; shProg->TransformFeedback.BufferMode = bufferMode; /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since * the varyings won't be used until shader link time. */ }