2004-03-29 11:09:34 +00:00
|
|
|
/*
|
|
|
|
* Mesa 3-D graphics library
|
2006-10-30 00:12:05 +00:00
|
|
|
* Version: 6.5.2
|
2004-03-29 11:09:34 +00:00
|
|
|
*
|
2006-05-24 03:30:31 +00:00
|
|
|
* Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
|
2004-03-29 11:09:34 +00:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included
|
|
|
|
* in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
|
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file program.c
|
|
|
|
* Vertex and fragment program support functions.
|
|
|
|
* \author Brian Paul
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "glheader.h"
|
|
|
|
#include "context.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "imports.h"
|
|
|
|
#include "macros.h"
|
|
|
|
#include "mtypes.h"
|
|
|
|
#include "nvfragparse.h"
|
2006-12-14 15:01:28 -07:00
|
|
|
#include "program.h"
|
|
|
|
#include "prog_parameter.h"
|
|
|
|
#include "prog_instruction.h"
|
|
|
|
#include "prog_statevars.h"
|
2004-03-29 11:09:34 +00:00
|
|
|
#include "nvvertparse.h"
|
2005-09-02 01:11:53 +00:00
|
|
|
#include "atifragshader.h"
|
2004-03-29 11:09:34 +00:00
|
|
|
|
|
|
|
|
2005-11-12 23:25:49 +00:00
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
/**********************************************************************/
|
|
|
|
/* Utility functions */
|
|
|
|
/**********************************************************************/
|
|
|
|
|
|
|
|
|
2004-09-14 22:28:27 +00:00
|
|
|
/* A pointer to this dummy program is put into the hash table when
|
|
|
|
* glGenPrograms is called.
|
|
|
|
*/
|
2006-07-20 16:49:57 +00:00
|
|
|
struct gl_program _mesa_DummyProgram;
|
2004-09-14 22:28:27 +00:00
|
|
|
|
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
/**
|
2004-08-14 14:28:11 +00:00
|
|
|
* Init context's vertex/fragment program state
|
2004-03-29 11:09:34 +00:00
|
|
|
*/
|
|
|
|
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;
|
2006-07-20 16:49:57 +00:00
|
|
|
ctx->VertexProgram.Current = (struct gl_vertex_program *) ctx->Shared->DefaultVertexProgram;
|
2004-03-29 11:09:34 +00:00
|
|
|
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;
|
2006-07-20 16:49:57 +00:00
|
|
|
ctx->FragmentProgram.Current = (struct gl_fragment_program *) ctx->Shared->DefaultFragmentProgram;
|
2004-03-29 11:09:34 +00:00
|
|
|
assert(ctx->FragmentProgram.Current);
|
|
|
|
ctx->FragmentProgram.Current->Base.RefCount++;
|
|
|
|
#endif
|
2004-12-19 03:06:59 +00:00
|
|
|
|
2005-11-19 16:43:04 +00:00
|
|
|
/* XXX probably move this stuff */
|
2004-12-19 03:06:59 +00:00
|
|
|
#if FEATURE_ATI_fragment_shader
|
|
|
|
ctx->ATIFragmentShader.Enabled = GL_FALSE;
|
|
|
|
ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader;
|
|
|
|
assert(ctx->ATIFragmentShader.Current);
|
2005-11-19 16:43:04 +00:00
|
|
|
ctx->ATIFragmentShader.Current->RefCount++;
|
2004-12-19 03:06:59 +00:00
|
|
|
#endif
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-14 14:28:11 +00:00
|
|
|
/**
|
|
|
|
* Free a context's vertex/fragment program state
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_mesa_free_program_data(GLcontext *ctx)
|
|
|
|
{
|
2006-03-01 23:11:14 +00:00
|
|
|
#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
|
2004-08-14 14:28:11 +00:00
|
|
|
if (ctx->VertexProgram.Current) {
|
|
|
|
ctx->VertexProgram.Current->Base.RefCount--;
|
|
|
|
if (ctx->VertexProgram.Current->Base.RefCount <= 0)
|
|
|
|
ctx->Driver.DeleteProgram(ctx, &(ctx->VertexProgram.Current->Base));
|
|
|
|
}
|
|
|
|
#endif
|
2006-03-01 23:11:14 +00:00
|
|
|
#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
|
2004-08-14 14:28:11 +00:00
|
|
|
if (ctx->FragmentProgram.Current) {
|
|
|
|
ctx->FragmentProgram.Current->Base.RefCount--;
|
|
|
|
if (ctx->FragmentProgram.Current->Base.RefCount <= 0)
|
|
|
|
ctx->Driver.DeleteProgram(ctx, &(ctx->FragmentProgram.Current->Base));
|
|
|
|
}
|
2004-12-19 03:06:59 +00:00
|
|
|
#endif
|
2005-11-19 16:43:04 +00:00
|
|
|
/* XXX probably move this stuff */
|
2004-12-19 03:06:59 +00:00
|
|
|
#if FEATURE_ATI_fragment_shader
|
|
|
|
if (ctx->ATIFragmentShader.Current) {
|
2005-11-19 16:43:04 +00:00
|
|
|
ctx->ATIFragmentShader.Current->RefCount--;
|
|
|
|
if (ctx->ATIFragmentShader.Current->RefCount <= 0) {
|
|
|
|
_mesa_free(ctx->ATIFragmentShader.Current);
|
|
|
|
}
|
2004-12-19 03:06:59 +00:00
|
|
|
}
|
2004-08-14 14:28:11 +00:00
|
|
|
#endif
|
|
|
|
_mesa_free((void *) ctx->Program.ErrorString);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the line number and column for 'pos' within 'string'.
|
|
|
|
* Return a copy of the line which contains 'pos'. Free the line with
|
|
|
|
* _mesa_free().
|
|
|
|
* \param string the program string
|
|
|
|
* \param pos the position within the string
|
|
|
|
* \param line returns the line number corresponding to 'pos'.
|
|
|
|
* \param col returns the column number corresponding to 'pos'.
|
|
|
|
* \return copy of the line containing 'pos'.
|
|
|
|
*/
|
|
|
|
const GLubyte *
|
|
|
|
_mesa_find_line_column(const GLubyte *string, const GLubyte *pos,
|
|
|
|
GLint *line, GLint *col)
|
|
|
|
{
|
|
|
|
const GLubyte *lineStart = string;
|
|
|
|
const GLubyte *p = string;
|
|
|
|
GLubyte *s;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
*line = 1;
|
|
|
|
|
|
|
|
while (p != pos) {
|
|
|
|
if (*p == (GLubyte) '\n') {
|
|
|
|
(*line)++;
|
|
|
|
lineStart = p + 1;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*col = (pos - lineStart) + 1;
|
|
|
|
|
|
|
|
/* return copy of this line */
|
|
|
|
while (*p != 0 && *p != '\n')
|
|
|
|
p++;
|
|
|
|
len = p - lineStart;
|
|
|
|
s = (GLubyte *) _mesa_malloc(len + 1);
|
|
|
|
_mesa_memcpy(s, lineStart, len);
|
|
|
|
s[len] = 0;
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-14 22:28:27 +00:00
|
|
|
/**
|
|
|
|
* Initialize a new vertex/fragment program object.
|
|
|
|
*/
|
2006-07-20 16:49:57 +00:00
|
|
|
static struct gl_program *
|
|
|
|
_mesa_init_program_struct( GLcontext *ctx, struct gl_program *prog,
|
2004-09-14 22:28:27 +00:00
|
|
|
GLenum target, GLuint id)
|
2004-03-29 11:09:34 +00:00
|
|
|
{
|
2004-08-25 15:59:48 +00:00
|
|
|
(void) ctx;
|
2004-03-29 11:09:34 +00:00
|
|
|
if (prog) {
|
2006-12-13 14:54:47 -07:00
|
|
|
_mesa_bzero(prog, sizeof(*prog));
|
2004-03-29 11:09:34 +00:00
|
|
|
prog->Id = id;
|
|
|
|
prog->Target = target;
|
|
|
|
prog->Resident = GL_TRUE;
|
|
|
|
prog->RefCount = 1;
|
2006-11-23 15:58:30 +00:00
|
|
|
prog->Format = GL_PROGRAM_FORMAT_ASCII_ARB;
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return prog;
|
|
|
|
}
|
|
|
|
|
2004-09-14 22:28:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize a new fragment program object.
|
|
|
|
*/
|
2006-07-20 16:49:57 +00:00
|
|
|
struct gl_program *
|
|
|
|
_mesa_init_fragment_program( GLcontext *ctx, struct gl_fragment_program *prog,
|
2004-09-14 22:28:27 +00:00
|
|
|
GLenum target, GLuint id)
|
2004-03-29 11:09:34 +00:00
|
|
|
{
|
|
|
|
if (prog)
|
|
|
|
return _mesa_init_program_struct( ctx, &prog->Base, target, id );
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-09-14 22:28:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize a new vertex program object.
|
|
|
|
*/
|
2006-07-20 16:49:57 +00:00
|
|
|
struct gl_program *
|
|
|
|
_mesa_init_vertex_program( GLcontext *ctx, struct gl_vertex_program *prog,
|
2004-09-14 22:28:27 +00:00
|
|
|
GLenum target, GLuint id)
|
2004-03-29 11:09:34 +00:00
|
|
|
{
|
|
|
|
if (prog)
|
|
|
|
return _mesa_init_program_struct( ctx, &prog->Base, target, id );
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate and initialize a new fragment/vertex program object but
|
|
|
|
* don't put it into the program hash table. Called via
|
|
|
|
* ctx->Driver.NewProgram. May be overridden (ie. replaced) by a
|
|
|
|
* device driver function to implement OO deriviation with additional
|
|
|
|
* types not understood by this function.
|
|
|
|
*
|
|
|
|
* \param ctx context
|
|
|
|
* \param id program id/number
|
|
|
|
* \param target program target/type
|
|
|
|
* \return pointer to new program object
|
|
|
|
*/
|
2006-07-20 16:49:57 +00:00
|
|
|
struct gl_program *
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_new_program(GLcontext *ctx, GLenum target, GLuint id)
|
|
|
|
{
|
|
|
|
switch (target) {
|
|
|
|
case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
|
2006-07-20 16:49:57 +00:00
|
|
|
return _mesa_init_vertex_program(ctx, CALLOC_STRUCT(gl_vertex_program),
|
|
|
|
target, id );
|
2004-03-29 11:09:34 +00:00
|
|
|
case GL_FRAGMENT_PROGRAM_NV:
|
|
|
|
case GL_FRAGMENT_PROGRAM_ARB:
|
2006-07-20 16:49:57 +00:00
|
|
|
return _mesa_init_fragment_program(ctx,
|
|
|
|
CALLOC_STRUCT(gl_fragment_program),
|
|
|
|
target, id );
|
2004-03-29 11:09:34 +00:00
|
|
|
default:
|
|
|
|
_mesa_problem(ctx, "bad target in _mesa_new_program");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete a program and remove it from the hash table, ignoring the
|
|
|
|
* reference count.
|
|
|
|
* Called via ctx->Driver.DeleteProgram. May be wrapped (OO deriviation)
|
|
|
|
* by a device driver function.
|
|
|
|
*/
|
|
|
|
void
|
2006-07-20 16:49:57 +00:00
|
|
|
_mesa_delete_program(GLcontext *ctx, struct gl_program *prog)
|
2004-03-29 11:09:34 +00:00
|
|
|
{
|
2004-08-25 15:59:48 +00:00
|
|
|
(void) ctx;
|
2004-03-29 11:09:34 +00:00
|
|
|
ASSERT(prog);
|
|
|
|
|
2006-11-23 15:58:30 +00:00
|
|
|
if (prog == &_mesa_DummyProgram)
|
|
|
|
return;
|
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
if (prog->String)
|
|
|
|
_mesa_free(prog->String);
|
2005-11-12 17:53:14 +00:00
|
|
|
|
|
|
|
if (prog->Instructions) {
|
|
|
|
GLuint i;
|
|
|
|
for (i = 0; i < prog->NumInstructions; i++) {
|
|
|
|
if (prog->Instructions[i].Data)
|
|
|
|
_mesa_free(prog->Instructions[i].Data);
|
2004-12-18 16:18:00 +00:00
|
|
|
}
|
2005-11-12 17:53:14 +00:00
|
|
|
_mesa_free(prog->Instructions);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
2005-11-12 17:53:14 +00:00
|
|
|
|
2006-05-24 03:30:31 +00:00
|
|
|
if (prog->Parameters) {
|
2005-11-12 17:53:14 +00:00
|
|
|
_mesa_free_parameter_list(prog->Parameters);
|
2006-05-24 03:30:31 +00:00
|
|
|
}
|
2005-11-12 17:53:14 +00:00
|
|
|
|
2006-12-13 14:54:47 -07:00
|
|
|
if (prog->Varying) {
|
|
|
|
_mesa_free_parameter_list(prog->Varying);
|
|
|
|
}
|
|
|
|
|
2006-08-25 19:46:31 +00:00
|
|
|
/* XXX this is a little ugly */
|
|
|
|
if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
|
|
|
|
struct gl_vertex_program *vprog = (struct gl_vertex_program *) prog;
|
|
|
|
if (vprog->TnlData)
|
|
|
|
_mesa_free(vprog->TnlData);
|
|
|
|
}
|
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_free(prog);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-23 23:10:14 +00:00
|
|
|
/**
|
|
|
|
* Return the gl_program object for a given ID.
|
|
|
|
* Basically just a wrapper for _mesa_HashLookup() to avoid a lot of
|
|
|
|
* casts elsewhere.
|
|
|
|
*/
|
|
|
|
struct gl_program *
|
|
|
|
_mesa_lookup_program(GLcontext *ctx, GLuint id)
|
|
|
|
{
|
|
|
|
if (id)
|
|
|
|
return (struct gl_program *) _mesa_HashLookup(ctx->Shared->Programs, id);
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
|
2006-12-14 13:56:58 -07:00
|
|
|
/**
|
|
|
|
* 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 = _mesa_new_program(ctx, prog->Target, prog->Id);
|
|
|
|
if (!clone)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
assert(clone->Target == prog->Target);
|
|
|
|
clone->String = (GLubyte *) _mesa_strdup((char *) prog->String);
|
|
|
|
clone->RefCount = 1;
|
|
|
|
clone->Format = prog->Format;
|
|
|
|
clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions);
|
|
|
|
if (!clone->Instructions) {
|
|
|
|
_mesa_delete_program(ctx, clone);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memcpy(clone->Instructions, prog->Instructions,
|
|
|
|
prog->NumInstructions * sizeof(struct prog_instruction));
|
|
|
|
clone->InputsRead = prog->InputsRead;
|
|
|
|
clone->OutputsWritten = prog->OutputsWritten;
|
2007-01-04 17:22:19 -07:00
|
|
|
memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed));
|
|
|
|
|
2006-12-19 09:52:07 -07:00
|
|
|
if (prog->Parameters)
|
|
|
|
clone->Parameters = _mesa_clone_parameter_list(prog->Parameters);
|
2006-12-14 13:56:58 -07:00
|
|
|
memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams));
|
2006-12-19 09:52:07 -07:00
|
|
|
if (prog->Varying)
|
|
|
|
clone->Varying = _mesa_clone_parameter_list(prog->Varying);
|
2006-12-14 13:56:58 -07:00
|
|
|
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;
|
2007-01-09 11:00:21 -07:00
|
|
|
clone->NumAluInstructions = prog->NumAluInstructions;
|
|
|
|
clone->NumTexInstructions = prog->NumTexInstructions;
|
|
|
|
clone->NumTexIndirections = prog->NumTexIndirections;
|
|
|
|
clone->NumNativeAluInstructions = prog->NumNativeAluInstructions;
|
|
|
|
clone->NumNativeTexInstructions = prog->NumNativeTexInstructions;
|
|
|
|
clone->NumNativeTexIndirections = prog->NumNativeTexIndirections;
|
2006-12-14 13:56:58 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-08-24 23:11:39 +00:00
|
|
|
/**
|
|
|
|
* Mixing ARB and NV vertex/fragment programs can be tricky.
|
|
|
|
* Note: GL_VERTEX_PROGRAM_ARB == GL_VERTEX_PROGRAM_NV
|
|
|
|
* but, GL_FRAGMENT_PROGRAM_ARB != GL_FRAGMENT_PROGRAM_NV
|
|
|
|
* The two different fragment program targets are supposed to be compatible
|
|
|
|
* to some extent (see GL_ARB_fragment_program spec).
|
|
|
|
* This function does the compatibility check.
|
|
|
|
*/
|
|
|
|
static GLboolean
|
|
|
|
compatible_program_targets(GLenum t1, GLenum t2)
|
|
|
|
{
|
|
|
|
if (t1 == t2)
|
|
|
|
return GL_TRUE;
|
|
|
|
if (t1 == GL_FRAGMENT_PROGRAM_ARB && t2 == GL_FRAGMENT_PROGRAM_NV)
|
|
|
|
return GL_TRUE;
|
|
|
|
if (t1 == GL_FRAGMENT_PROGRAM_NV && t2 == GL_FRAGMENT_PROGRAM_ARB)
|
|
|
|
return GL_TRUE;
|
|
|
|
return GL_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-05 20:18:18 +00:00
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
/**********************************************************************/
|
|
|
|
/* API functions */
|
|
|
|
/**********************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bind a program (make it current)
|
|
|
|
* \note Called from the GL API dispatcher by both glBindProgramNV
|
|
|
|
* and glBindProgramARB.
|
|
|
|
*/
|
|
|
|
void GLAPIENTRY
|
|
|
|
_mesa_BindProgram(GLenum target, GLuint id)
|
|
|
|
{
|
2006-08-25 17:18:56 +00:00
|
|
|
struct gl_program *curProg, *newProg;
|
2004-03-29 11:09:34 +00:00
|
|
|
GET_CURRENT_CONTEXT(ctx);
|
|
|
|
ASSERT_OUTSIDE_BEGIN_END(ctx);
|
|
|
|
|
|
|
|
FLUSH_VERTICES(ctx, _NEW_PROGRAM);
|
|
|
|
|
2006-08-25 17:18:56 +00:00
|
|
|
/* Error-check target and get curProg */
|
2006-03-03 15:03:04 +00:00
|
|
|
if ((target == GL_VERTEX_PROGRAM_ARB) && /* == GL_VERTEX_PROGRAM_NV */
|
|
|
|
(ctx->Extensions.NV_vertex_program ||
|
|
|
|
ctx->Extensions.ARB_vertex_program)) {
|
2006-08-25 17:18:56 +00:00
|
|
|
curProg = &ctx->VertexProgram.Current->Base;
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else if ((target == GL_FRAGMENT_PROGRAM_NV
|
|
|
|
&& ctx->Extensions.NV_fragment_program) ||
|
|
|
|
(target == GL_FRAGMENT_PROGRAM_ARB
|
|
|
|
&& ctx->Extensions.ARB_fragment_program)) {
|
2006-08-25 17:18:56 +00:00
|
|
|
curProg = &ctx->FragmentProgram.Current->Base;
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM, "glBindProgramNV/ARB(target)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-08-25 17:18:56 +00:00
|
|
|
/*
|
|
|
|
* Get pointer to new program to bind.
|
|
|
|
* NOTE: binding to a non-existant program is not an error.
|
2004-03-29 11:09:34 +00:00
|
|
|
* That's supposed to be caught in glBegin.
|
|
|
|
*/
|
|
|
|
if (id == 0) {
|
2006-08-25 17:18:56 +00:00
|
|
|
/* Bind a default program */
|
|
|
|
newProg = NULL;
|
2006-03-03 15:03:04 +00:00
|
|
|
if (target == GL_VERTEX_PROGRAM_ARB) /* == GL_VERTEX_PROGRAM_NV */
|
2006-08-25 17:18:56 +00:00
|
|
|
newProg = ctx->Shared->DefaultVertexProgram;
|
2004-03-29 11:09:34 +00:00
|
|
|
else
|
2006-08-25 17:18:56 +00:00
|
|
|
newProg = ctx->Shared->DefaultFragmentProgram;
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else {
|
2006-08-25 17:18:56 +00:00
|
|
|
/* Bind a user program */
|
|
|
|
newProg = _mesa_lookup_program(ctx, id);
|
|
|
|
if (!newProg || newProg == &_mesa_DummyProgram) {
|
2004-03-29 11:09:34 +00:00
|
|
|
/* allocate a new program now */
|
2006-08-25 17:18:56 +00:00
|
|
|
newProg = ctx->Driver.NewProgram(ctx, target, id);
|
|
|
|
if (!newProg) {
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindProgramNV/ARB");
|
|
|
|
return;
|
|
|
|
}
|
2006-08-25 17:18:56 +00:00
|
|
|
_mesa_HashInsert(ctx->Shared->Programs, id, newProg);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
2006-08-25 17:18:56 +00:00
|
|
|
else if (!compatible_program_targets(newProg->Target, target)) {
|
2004-09-14 22:28:27 +00:00
|
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
|
|
"glBindProgramNV/ARB(target mismatch)");
|
|
|
|
return;
|
|
|
|
}
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
|
2006-08-25 17:18:56 +00:00
|
|
|
/** All error checking is complete now **/
|
|
|
|
|
|
|
|
if (curProg->Id == id) {
|
|
|
|
/* binding same program - no change */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unbind/delete oldProg */
|
|
|
|
if (curProg->Id != 0) {
|
|
|
|
/* decrement refcount on previously bound fragment program */
|
|
|
|
curProg->RefCount--;
|
|
|
|
/* and delete if refcount goes below one */
|
|
|
|
if (curProg->RefCount <= 0) {
|
|
|
|
/* the program ID was already removed from the hash table */
|
|
|
|
ctx->Driver.DeleteProgram(ctx, curProg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bind newProg */
|
2006-03-03 15:03:04 +00:00
|
|
|
if (target == GL_VERTEX_PROGRAM_ARB) { /* == GL_VERTEX_PROGRAM_NV */
|
2006-08-25 17:18:56 +00:00
|
|
|
ctx->VertexProgram.Current = (struct gl_vertex_program *) newProg;
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
2006-08-25 17:18:56 +00:00
|
|
|
else if (target == GL_FRAGMENT_PROGRAM_NV ||
|
|
|
|
target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
|
|
ctx->FragmentProgram.Current = (struct gl_fragment_program *) newProg;
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
2006-08-25 17:18:56 +00:00
|
|
|
newProg->RefCount++;
|
2004-03-29 11:09:34 +00:00
|
|
|
|
2004-09-14 22:28:27 +00:00
|
|
|
/* Never null pointers */
|
|
|
|
ASSERT(ctx->VertexProgram.Current);
|
|
|
|
ASSERT(ctx->FragmentProgram.Current);
|
|
|
|
|
2004-03-29 11:09:34 +00:00
|
|
|
if (ctx->Driver.BindProgram)
|
2006-08-25 17:18:56 +00:00
|
|
|
ctx->Driver.BindProgram(ctx, target, newProg);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete a list of programs.
|
|
|
|
* \note Not compiled into display lists.
|
|
|
|
* \note Called by both glDeleteProgramsNV and glDeleteProgramsARB.
|
|
|
|
*/
|
|
|
|
void GLAPIENTRY
|
|
|
|
_mesa_DeletePrograms(GLsizei n, const GLuint *ids)
|
|
|
|
{
|
|
|
|
GLint i;
|
|
|
|
GET_CURRENT_CONTEXT(ctx);
|
|
|
|
ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
|
|
|
|
|
|
|
|
if (n < 0) {
|
|
|
|
_mesa_error( ctx, GL_INVALID_VALUE, "glDeleteProgramsNV" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (ids[i] != 0) {
|
2006-08-23 23:10:14 +00:00
|
|
|
struct gl_program *prog = _mesa_lookup_program(ctx, ids[i]);
|
2004-10-02 15:16:59 +00:00
|
|
|
if (prog == &_mesa_DummyProgram) {
|
2004-09-14 22:28:27 +00:00
|
|
|
_mesa_HashRemove(ctx->Shared->Programs, ids[i]);
|
|
|
|
}
|
|
|
|
else if (prog) {
|
|
|
|
/* Unbind program if necessary */
|
2006-03-03 15:03:04 +00:00
|
|
|
if (prog->Target == GL_VERTEX_PROGRAM_ARB || /* == GL_VERTEX_PROGRAM_NV */
|
2004-03-29 11:09:34 +00:00
|
|
|
prog->Target == GL_VERTEX_STATE_PROGRAM_NV) {
|
|
|
|
if (ctx->VertexProgram.Current &&
|
|
|
|
ctx->VertexProgram.Current->Base.Id == ids[i]) {
|
|
|
|
/* unbind this currently bound program */
|
|
|
|
_mesa_BindProgram(prog->Target, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (prog->Target == GL_FRAGMENT_PROGRAM_NV ||
|
|
|
|
prog->Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
|
|
if (ctx->FragmentProgram.Current &&
|
|
|
|
ctx->FragmentProgram.Current->Base.Id == ids[i]) {
|
|
|
|
/* unbind this currently bound program */
|
|
|
|
_mesa_BindProgram(prog->Target, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_mesa_problem(ctx, "bad target in glDeleteProgramsNV");
|
|
|
|
return;
|
|
|
|
}
|
2005-01-20 04:02:02 +00:00
|
|
|
/* The ID is immediately available for re-use now */
|
|
|
|
_mesa_HashRemove(ctx->Shared->Programs, ids[i]);
|
|
|
|
prog->RefCount--;
|
2004-03-29 11:09:34 +00:00
|
|
|
if (prog->RefCount <= 0) {
|
|
|
|
ctx->Driver.DeleteProgram(ctx, prog);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a list of new program identifiers.
|
|
|
|
* \note Not compiled into display lists.
|
|
|
|
* \note Called by both glGenProgramsNV and glGenProgramsARB.
|
|
|
|
*/
|
|
|
|
void GLAPIENTRY
|
|
|
|
_mesa_GenPrograms(GLsizei n, GLuint *ids)
|
|
|
|
{
|
|
|
|
GLuint first;
|
|
|
|
GLuint i;
|
|
|
|
GET_CURRENT_CONTEXT(ctx);
|
|
|
|
ASSERT_OUTSIDE_BEGIN_END(ctx);
|
|
|
|
|
|
|
|
if (n < 0) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_VALUE, "glGenPrograms");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ids)
|
|
|
|
return;
|
|
|
|
|
|
|
|
first = _mesa_HashFindFreeKeyBlock(ctx->Shared->Programs, n);
|
|
|
|
|
2004-09-14 22:28:27 +00:00
|
|
|
/* Insert pointer to dummy program as placeholder */
|
2004-03-29 11:09:34 +00:00
|
|
|
for (i = 0; i < (GLuint) n; i++) {
|
2004-10-02 15:16:59 +00:00
|
|
|
_mesa_HashInsert(ctx->Shared->Programs, first + i, &_mesa_DummyProgram);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the program names */
|
|
|
|
for (i = 0; i < (GLuint) n; i++) {
|
|
|
|
ids[i] = first + i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**********************************************************************/
|
|
|
|
/* GL_MESA_program_debug extension */
|
|
|
|
/**********************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX temporary */
|
2005-02-14 08:01:59 +00:00
|
|
|
GLAPI void GLAPIENTRY
|
2004-03-29 11:09:34 +00:00
|
|
|
glProgramCallbackMESA(GLenum target, GLprogramcallbackMESA callback,
|
|
|
|
GLvoid *data)
|
|
|
|
{
|
|
|
|
_mesa_ProgramCallbackMESA(target, callback, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
_mesa_ProgramCallbackMESA(GLenum target, GLprogramcallbackMESA callback,
|
|
|
|
GLvoid *data)
|
|
|
|
{
|
|
|
|
GET_CURRENT_CONTEXT(ctx);
|
|
|
|
|
|
|
|
switch (target) {
|
|
|
|
case GL_FRAGMENT_PROGRAM_ARB:
|
|
|
|
if (!ctx->Extensions.ARB_fragment_program) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ctx->FragmentProgram.Callback = callback;
|
|
|
|
ctx->FragmentProgram.CallbackData = data;
|
|
|
|
break;
|
|
|
|
case GL_FRAGMENT_PROGRAM_NV:
|
|
|
|
if (!ctx->Extensions.NV_fragment_program) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ctx->FragmentProgram.Callback = callback;
|
|
|
|
ctx->FragmentProgram.CallbackData = data;
|
|
|
|
break;
|
|
|
|
case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
|
|
|
|
if (!ctx->Extensions.ARB_vertex_program &&
|
|
|
|
!ctx->Extensions.NV_vertex_program) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ctx->VertexProgram.Callback = callback;
|
|
|
|
ctx->VertexProgram.CallbackData = data;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX temporary */
|
2005-02-14 08:01:59 +00:00
|
|
|
GLAPI void GLAPIENTRY
|
2004-03-29 11:09:34 +00:00
|
|
|
glGetProgramRegisterfvMESA(GLenum target,
|
|
|
|
GLsizei len, const GLubyte *registerName,
|
|
|
|
GLfloat *v)
|
|
|
|
{
|
|
|
|
_mesa_GetProgramRegisterfvMESA(target, len, registerName, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
_mesa_GetProgramRegisterfvMESA(GLenum target,
|
|
|
|
GLsizei len, const GLubyte *registerName,
|
|
|
|
GLfloat *v)
|
|
|
|
{
|
|
|
|
char reg[1000];
|
|
|
|
GET_CURRENT_CONTEXT(ctx);
|
|
|
|
|
|
|
|
/* We _should_ be inside glBegin/glEnd */
|
|
|
|
#if 0
|
|
|
|
if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramRegisterfvMESA");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* make null-terminated copy of registerName */
|
|
|
|
len = MIN2((unsigned int) len, sizeof(reg) - 1);
|
|
|
|
_mesa_memcpy(reg, registerName, len);
|
|
|
|
reg[len] = 0;
|
|
|
|
|
|
|
|
switch (target) {
|
2006-03-03 15:03:04 +00:00
|
|
|
case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
|
2004-03-29 11:09:34 +00:00
|
|
|
if (!ctx->Extensions.ARB_vertex_program &&
|
|
|
|
!ctx->Extensions.NV_vertex_program) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM,
|
|
|
|
"glGetProgramRegisterfvMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
2004-04-23 14:16:46 +00:00
|
|
|
if (!ctx->VertexProgram._Enabled) {
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
|
|
"glGetProgramRegisterfvMESA");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* GL_NV_vertex_program */
|
|
|
|
if (reg[0] == 'R') {
|
|
|
|
/* Temp register */
|
|
|
|
GLint i = _mesa_atoi(reg + 1);
|
2005-11-01 04:36:33 +00:00
|
|
|
if (i >= (GLint)ctx->Const.VertexProgram.MaxTemps) {
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_error(ctx, GL_INVALID_VALUE,
|
|
|
|
"glGetProgramRegisterfvMESA(registerName)");
|
|
|
|
return;
|
|
|
|
}
|
2006-10-30 00:12:05 +00:00
|
|
|
#if 0 /* FIX ME */
|
|
|
|
ctx->Driver.GetVertexProgramRegister(ctx, PROGRAM_TEMPORARY, i, v);
|
|
|
|
#endif
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else if (reg[0] == 'v' && reg[1] == '[') {
|
|
|
|
/* Vertex Input attribute */
|
|
|
|
GLuint i;
|
2005-11-01 04:36:33 +00:00
|
|
|
for (i = 0; i < ctx->Const.VertexProgram.MaxAttribs; i++) {
|
2004-03-29 11:09:34 +00:00
|
|
|
const char *name = _mesa_nv_vertex_input_register_name(i);
|
|
|
|
char number[10];
|
2005-09-16 18:14:24 +00:00
|
|
|
_mesa_sprintf(number, "%d", i);
|
2004-03-29 11:09:34 +00:00
|
|
|
if (_mesa_strncmp(reg + 2, name, 4) == 0 ||
|
|
|
|
_mesa_strncmp(reg + 2, number, _mesa_strlen(number)) == 0) {
|
2006-10-30 00:12:05 +00:00
|
|
|
#if 0 /* FIX ME */
|
|
|
|
ctx->Driver.GetVertexProgramRegister(ctx, PROGRAM_INPUT,
|
|
|
|
i, v);
|
|
|
|
#endif
|
2004-03-29 11:09:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_mesa_error(ctx, GL_INVALID_VALUE,
|
|
|
|
"glGetProgramRegisterfvMESA(registerName)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (reg[0] == 'o' && reg[1] == '[') {
|
|
|
|
/* Vertex output attribute */
|
|
|
|
}
|
|
|
|
/* GL_ARB_vertex_program */
|
|
|
|
else if (_mesa_strncmp(reg, "vertex.", 7) == 0) {
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_mesa_error(ctx, GL_INVALID_VALUE,
|
|
|
|
"glGetProgramRegisterfvMESA(registerName)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GL_FRAGMENT_PROGRAM_ARB:
|
|
|
|
if (!ctx->Extensions.ARB_fragment_program) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM,
|
|
|
|
"glGetProgramRegisterfvMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
2004-04-23 14:16:46 +00:00
|
|
|
if (!ctx->FragmentProgram._Enabled) {
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
|
|
"glGetProgramRegisterfvMESA");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* XXX to do */
|
|
|
|
break;
|
|
|
|
case GL_FRAGMENT_PROGRAM_NV:
|
|
|
|
if (!ctx->Extensions.NV_fragment_program) {
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM,
|
|
|
|
"glGetProgramRegisterfvMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
2004-04-23 14:16:46 +00:00
|
|
|
if (!ctx->FragmentProgram._Enabled) {
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_error(ctx, GL_INVALID_OPERATION,
|
|
|
|
"glGetProgramRegisterfvMESA");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (reg[0] == 'R') {
|
|
|
|
/* Temp register */
|
|
|
|
GLint i = _mesa_atoi(reg + 1);
|
2005-11-01 04:36:33 +00:00
|
|
|
if (i >= (GLint)ctx->Const.FragmentProgram.MaxTemps) {
|
2004-03-29 11:09:34 +00:00
|
|
|
_mesa_error(ctx, GL_INVALID_VALUE,
|
|
|
|
"glGetProgramRegisterfvMESA(registerName)");
|
|
|
|
return;
|
|
|
|
}
|
2006-10-10 21:43:31 +00:00
|
|
|
ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_TEMPORARY,
|
|
|
|
i, v);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else if (reg[0] == 'f' && reg[1] == '[') {
|
|
|
|
/* Fragment input attribute */
|
|
|
|
GLuint i;
|
2005-11-01 04:36:33 +00:00
|
|
|
for (i = 0; i < ctx->Const.FragmentProgram.MaxAttribs; i++) {
|
2004-03-29 11:09:34 +00:00
|
|
|
const char *name = _mesa_nv_fragment_input_register_name(i);
|
|
|
|
if (_mesa_strncmp(reg + 2, name, 4) == 0) {
|
2006-10-10 21:43:31 +00:00
|
|
|
ctx->Driver.GetFragmentProgramRegister(ctx,
|
|
|
|
PROGRAM_INPUT, i, v);
|
2004-03-29 11:09:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_mesa_error(ctx, GL_INVALID_VALUE,
|
|
|
|
"glGetProgramRegisterfvMESA(registerName)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (_mesa_strcmp(reg, "o[COLR]") == 0) {
|
|
|
|
/* Fragment output color */
|
2006-10-10 21:43:31 +00:00
|
|
|
ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_OUTPUT,
|
|
|
|
FRAG_RESULT_COLR, v);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else if (_mesa_strcmp(reg, "o[COLH]") == 0) {
|
|
|
|
/* Fragment output color */
|
2006-10-10 21:43:31 +00:00
|
|
|
ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_OUTPUT,
|
|
|
|
FRAG_RESULT_COLH, v);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else if (_mesa_strcmp(reg, "o[DEPR]") == 0) {
|
|
|
|
/* Fragment output depth */
|
2006-10-10 21:43:31 +00:00
|
|
|
ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_OUTPUT,
|
|
|
|
FRAG_RESULT_DEPR, v);
|
2004-03-29 11:09:34 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* try user-defined identifiers */
|
|
|
|
const GLfloat *value = _mesa_lookup_parameter_value(
|
2005-11-12 17:53:14 +00:00
|
|
|
ctx->FragmentProgram.Current->Base.Parameters, -1, reg);
|
2004-03-29 11:09:34 +00:00
|
|
|
if (value) {
|
|
|
|
COPY_4V(v, value);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_mesa_error(ctx, GL_INVALID_VALUE,
|
|
|
|
"glGetProgramRegisterfvMESA(registerName)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
_mesa_error(ctx, GL_INVALID_ENUM,
|
|
|
|
"glGetProgramRegisterfvMESA(target)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|