/*
* Mesa 3-D graphics library
- * Version: 6.5
+ * Version: 7.0
*
- * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
+ * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*/
-#include "glheader.h"
+#include "main/glheader.h"
#include "arbprogram.h"
#include "arbprogparse.h"
-#include "context.h"
-#include "imports.h"
-#include "macros.h"
-#include "mtypes.h"
+#include "main/context.h"
+#include "main/hash.h"
+#include "main/imports.h"
+#include "main/macros.h"
+#include "main/mtypes.h"
+#include "program.h"
+
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * 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)
+{
+ struct gl_program *curProg, *newProg;
+ GET_CURRENT_CONTEXT(ctx);
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+ /* Error-check target and get curProg */
+ if ((target == GL_VERTEX_PROGRAM_ARB) && /* == GL_VERTEX_PROGRAM_NV */
+ (ctx->Extensions.NV_vertex_program ||
+ ctx->Extensions.ARB_vertex_program)) {
+ curProg = &ctx->VertexProgram.Current->Base;
+ }
+ else if ((target == GL_FRAGMENT_PROGRAM_NV
+ && ctx->Extensions.NV_fragment_program) ||
+ (target == GL_FRAGMENT_PROGRAM_ARB
+ && ctx->Extensions.ARB_fragment_program)) {
+ curProg = &ctx->FragmentProgram.Current->Base;
+ }
+ else {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBindProgramNV/ARB(target)");
+ return;
+ }
+
+ /*
+ * Get pointer to new program to bind.
+ * NOTE: binding to a non-existant program is not an error.
+ * That's supposed to be caught in glBegin.
+ */
+ if (id == 0) {
+ /* Bind a default program */
+ newProg = NULL;
+ if (target == GL_VERTEX_PROGRAM_ARB) /* == GL_VERTEX_PROGRAM_NV */
+ newProg = &ctx->Shared->DefaultVertexProgram->Base;
+ else
+ newProg = &ctx->Shared->DefaultFragmentProgram->Base;
+ }
+ else {
+ /* Bind a user program */
+ newProg = _mesa_lookup_program(ctx, id);
+ if (!newProg || newProg == &_mesa_DummyProgram) {
+ /* allocate a new program now */
+ newProg = ctx->Driver.NewProgram(ctx, target, id);
+ if (!newProg) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindProgramNV/ARB");
+ return;
+ }
+ _mesa_HashInsert(ctx->Shared->Programs, id, newProg);
+ }
+ else if (!compatible_program_targets(newProg->Target, target)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBindProgramNV/ARB(target mismatch)");
+ return;
+ }
+ }
+
+ /** All error checking is complete now **/
+
+ if (curProg->Id == id) {
+ /* binding same program - no change */
+ return;
+ }
+
+ /* bind newProg */
+ if (target == GL_VERTEX_PROGRAM_ARB) { /* == GL_VERTEX_PROGRAM_NV */
+ _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current,
+ (struct gl_vertex_program *) newProg);
+ }
+ else if (target == GL_FRAGMENT_PROGRAM_NV ||
+ target == GL_FRAGMENT_PROGRAM_ARB) {
+ _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current,
+ (struct gl_fragment_program *) newProg);
+ }
+
+ /* Never null pointers */
+ ASSERT(ctx->VertexProgram.Current);
+ ASSERT(ctx->FragmentProgram.Current);
+
+ if (ctx->Driver.BindProgram)
+ ctx->Driver.BindProgram(ctx, target, newProg);
+}
+
+
+/**
+ * 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) {
+ struct gl_program *prog = _mesa_lookup_program(ctx, ids[i]);
+ if (prog == &_mesa_DummyProgram) {
+ _mesa_HashRemove(ctx->Shared->Programs, ids[i]);
+ }
+ else if (prog) {
+ /* Unbind program if necessary */
+ if (prog->Target == GL_VERTEX_PROGRAM_ARB || /* == GL_VERTEX_PROGRAM_NV */
+ 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;
+ }
+ /* The ID is immediately available for re-use now */
+ _mesa_HashRemove(ctx->Shared->Programs, ids[i]);
+ _mesa_reference_program(ctx, &prog, NULL);
+ }
+ }
+ }
+}
+
+
+/**
+ * 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);
+
+ /* Insert pointer to dummy program as placeholder */
+ for (i = 0; i < (GLuint) n; i++) {
+ _mesa_HashInsert(ctx->Shared->Programs, first + i, &_mesa_DummyProgram);
+ }
+
+ /* Return the program names */
+ for (i = 0; i < (GLuint) n; i++) {
+ ids[i] = first + i;
+ }
+}
void GLAPIENTRY
}
FLUSH_VERTICES(ctx, _NEW_ARRAY);
- ctx->Array.VertexAttrib[index].Enabled = GL_TRUE;
- ctx->Array._Enabled |= _NEW_ARRAY_ATTRIB(index);
+ ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_TRUE;
+ ctx->Array.ArrayObj->_Enabled |= _NEW_ARRAY_ATTRIB(index);
ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index);
}
}
FLUSH_VERTICES(ctx, _NEW_ARRAY);
- ctx->Array.VertexAttrib[index].Enabled = GL_FALSE;
- ctx->Array._Enabled &= ~_NEW_ARRAY_ATTRIB(index);
+ ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_FALSE;
+ ctx->Array.ArrayObj->_Enabled &= ~_NEW_ARRAY_ATTRIB(index);
ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index);
}
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
- if (index == 0 || index >= MAX_VERTEX_PROGRAM_ATTRIBS) {
+ if (index >= MAX_VERTEX_PROGRAM_ATTRIBS) {
_mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribfvARB(index)");
return;
}
switch (pname) {
case GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB:
- params[0] = (GLfloat) ctx->Array.VertexAttrib[index].Enabled;
+ params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Enabled;
break;
case GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB:
- params[0] = (GLfloat) ctx->Array.VertexAttrib[index].Size;
+ params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Size;
break;
case GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB:
- params[0] = (GLfloat) ctx->Array.VertexAttrib[index].Stride;
+ params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Stride;
break;
case GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB:
- params[0] = (GLfloat) ctx->Array.VertexAttrib[index].Type;
+ params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Type;
break;
case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB:
- params[0] = ctx->Array.VertexAttrib[index].Normalized;
+ params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Normalized;
break;
case GL_CURRENT_VERTEX_ATTRIB_ARB:
- FLUSH_CURRENT(ctx, 0);
- /* XXX should read:
- COPY_4V(params, ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]);
- */
- COPY_4V(params, ctx->Current.Attrib[index]);
+ if (index == 0) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glGetVertexAttribfvARB(index==0)");
+ return;
+ }
+ FLUSH_CURRENT(ctx, 0);
+ COPY_4V(params, ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]);
break;
case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB:
if (!ctx->Extensions.ARB_vertex_buffer_object) {
_mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribfvARB(pname)");
return;
}
- params[0] = (GLfloat) ctx->Array.VertexAttrib[index].BufferObj->Name;
+ params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].BufferObj->Name;
+ break;
default:
_mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribfvARB(pname)");
return;
return;
}
- *pointer = (GLvoid *) ctx->Array.VertexAttrib[index].Ptr;;
+ *pointer = (GLvoid *) ctx->Array.ArrayObj->VertexAttrib[index].Ptr;
+}
+
+
+/**
+ * Determine if id names a vertex or fragment program.
+ * \note Not compiled into display lists.
+ * \note Called from both glIsProgramNV and glIsProgramARB.
+ * \param id is the program identifier
+ * \return GL_TRUE if id is a program, else GL_FALSE.
+ */
+GLboolean GLAPIENTRY
+_mesa_IsProgramARB(GLuint id)
+{
+ struct gl_program *prog = NULL;
+ GET_CURRENT_CONTEXT(ctx);
+ ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
+
+ if (id == 0)
+ return GL_FALSE;
+
+ prog = _mesa_lookup_program(ctx, id);
+ if (prog && (prog != &_mesa_DummyProgram))
+ return GL_TRUE;
+ else
+ return GL_FALSE;
}
if (target == GL_VERTEX_PROGRAM_ARB
&& ctx->Extensions.ARB_vertex_program) {
- struct vertex_program *prog = ctx->VertexProgram.Current;
+ struct gl_vertex_program *prog = ctx->VertexProgram.Current;
_mesa_parse_arb_vertex_program(ctx, target, string, len, prog);
- if (ctx->Driver.ProgramStringNotify)
+ if (ctx->Program.ErrorPos == -1 && ctx->Driver.ProgramStringNotify)
ctx->Driver.ProgramStringNotify( ctx, target, &prog->Base );
}
else if (target == GL_FRAGMENT_PROGRAM_ARB
&& ctx->Extensions.ARB_fragment_program) {
- struct fragment_program *prog = ctx->FragmentProgram.Current;
+ struct gl_fragment_program *prog = ctx->FragmentProgram.Current;
_mesa_parse_arb_fragment_program(ctx, target, string, len, prog);
- if (ctx->Driver.ProgramStringNotify)
+ if (ctx->Program.ErrorPos == -1 && ctx->Driver.ProgramStringNotify)
ctx->Driver.ProgramStringNotify( ctx, target, &prog->Base );
}
else {
}
+/**
+ * Set a program env parameter register.
+ * \note Called from the GL API dispatcher.
+ * Note, this function is also used by the GL_NV_vertex_program extension
+ * (alias to ProgramParameterdNV)
+ */
void GLAPIENTRY
_mesa_ProgramEnvParameter4dARB(GLenum target, GLuint index,
GLdouble x, GLdouble y, GLdouble z, GLdouble w)
}
+/**
+ * Set a program env parameter register.
+ * \note Called from the GL API dispatcher.
+ * Note, this function is also used by the GL_NV_vertex_program extension
+ * (alias to ProgramParameterdvNV)
+ */
void GLAPIENTRY
_mesa_ProgramEnvParameter4dvARB(GLenum target, GLuint index,
const GLdouble *params)
}
+/**
+ * Set a program env parameter register.
+ * \note Called from the GL API dispatcher.
+ * Note, this function is also used by the GL_NV_vertex_program extension
+ * (alias to ProgramParameterfNV)
+ */
void GLAPIENTRY
_mesa_ProgramEnvParameter4fARB(GLenum target, GLuint index,
GLfloat x, GLfloat y, GLfloat z, GLfloat w)
}
ASSIGN_4V(ctx->FragmentProgram.Parameters[index], x, y, z, w);
}
- else if (target == GL_VERTEX_PROGRAM_ARB
- && ctx->Extensions.ARB_vertex_program) {
+ else if (target == GL_VERTEX_PROGRAM_ARB /* == GL_VERTEX_PROGRAM_NV */
+ && (ctx->Extensions.ARB_vertex_program || ctx->Extensions.NV_vertex_program)) {
if (index >= ctx->Const.VertexProgram.MaxEnvParams) {
_mesa_error(ctx, GL_INVALID_VALUE, "glProgramEnvParameter(index)");
return;
}
}
-
+/**
+ * Set a program env parameter register.
+ * \note Called from the GL API dispatcher.
+ * Note, this function is also used by the GL_NV_vertex_program extension
+ * (alias to ProgramParameterfvNV)
+ */
void GLAPIENTRY
_mesa_ProgramEnvParameter4fvARB(GLenum target, GLuint index,
const GLfloat *params)
}
+void GLAPIENTRY
+_mesa_ProgramEnvParameters4fvEXT(GLenum target, GLuint index, GLsizei count,
+ const GLfloat *params)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ GLint i;
+ GLfloat * dest;
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+ if (count <= 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glProgramEnvParameters4fv(count)");
+ }
+
+ if (target == GL_FRAGMENT_PROGRAM_ARB
+ && ctx->Extensions.ARB_fragment_program) {
+ if ((index + count) > ctx->Const.FragmentProgram.MaxEnvParams) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glProgramEnvParameters4fv(index + count)");
+ return;
+ }
+ dest = ctx->FragmentProgram.Parameters[index];
+ }
+ else if (target == GL_VERTEX_PROGRAM_ARB
+ && ctx->Extensions.ARB_vertex_program) {
+ if ((index + count) > ctx->Const.VertexProgram.MaxEnvParams) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glProgramEnvParameters4fv(index + count)");
+ return;
+ }
+ dest = ctx->VertexProgram.Parameters[index];
+ }
+ else {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glProgramEnvParameters4fv(target)");
+ return;
+ }
+
+ for ( i = 0 ; i < count ; i++ ) {
+ COPY_4V(dest, params);
+ params += 4;
+ dest += 4;
+ }
+}
+
+
void GLAPIENTRY
_mesa_GetProgramEnvParameterdvARB(GLenum target, GLuint index,
GLdouble *params)
GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
GET_CURRENT_CONTEXT(ctx);
- struct program *prog;
+ struct gl_program *prog;
ASSERT_OUTSIDE_BEGIN_END(ctx);
FLUSH_VERTICES(ctx, _NEW_PROGRAM);
}
+void GLAPIENTRY
+_mesa_ProgramLocalParameters4fvEXT(GLenum target, GLuint index, GLsizei count,
+ const GLfloat *params)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_program *prog;
+ GLint i;
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+ if (count <= 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glProgramLocalParameters4fv(count)");
+ }
+
+ if (target == GL_FRAGMENT_PROGRAM_ARB
+ && ctx->Extensions.ARB_fragment_program) {
+ if ((index + count) > ctx->Const.FragmentProgram.MaxLocalParams) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glProgramLocalParameters4fvEXT(index + count)");
+ return;
+ }
+ prog = &(ctx->FragmentProgram.Current->Base);
+ }
+ else if (target == GL_VERTEX_PROGRAM_ARB
+ && ctx->Extensions.ARB_vertex_program) {
+ if ((index + count) > ctx->Const.VertexProgram.MaxLocalParams) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glProgramLocalParameters4fvEXT(index + count)");
+ return;
+ }
+ prog = &(ctx->VertexProgram.Current->Base);
+ }
+ else {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glProgramLocalParameters4fvEXT(target)");
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ ASSERT((index + i) < MAX_PROGRAM_LOCAL_PARAMS);
+ COPY_4V(prog->LocalParams[index + i], params);
+ params += 4;
+ }
+}
+
+
/**
* Note, this function is also used by the GL_NV_fragment_program extension.
*/
_mesa_GetProgramLocalParameterfvARB(GLenum target, GLuint index,
GLfloat *params)
{
- const struct program *prog;
+ const struct gl_program *prog;
GLuint maxParams;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
_mesa_GetProgramivARB(GLenum target, GLenum pname, GLint *params)
{
const struct gl_program_constants *limits;
- struct program *prog;
+ struct gl_program *prog;
GET_CURRENT_CONTEXT(ctx);
if (!ctx->_CurrentProgram)
* The spec says that even if this query returns true, there's
* no guarantee that the program will run in hardware.
*/
- if (ctx->Driver.IsProgramNative)
+ if (prog->Id == 0) {
+ /* default/null program */
+ *params = GL_FALSE;
+ }
+ else if (ctx->Driver.IsProgramNative) {
+ /* ask the driver */
*params = ctx->Driver.IsProgramNative( ctx, target, prog );
- else
+ }
+ else {
+ /* probably running in software */
*params = GL_TRUE;
+ }
return;
default:
/* continue with fragment-program only queries below */
* The following apply to fragment programs only (at this time)
*/
if (target == GL_FRAGMENT_PROGRAM_ARB) {
- const struct fragment_program *fp = ctx->FragmentProgram.Current;
+ const struct gl_fragment_program *fp = ctx->FragmentProgram.Current;
switch (pname) {
case GL_PROGRAM_ALU_INSTRUCTIONS_ARB:
- *params = fp->NumNativeAluInstructions;
+ *params = fp->Base.NumNativeAluInstructions;
return;
case GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB:
- *params = fp->NumAluInstructions;
+ *params = fp->Base.NumAluInstructions;
return;
case GL_PROGRAM_TEX_INSTRUCTIONS_ARB:
- *params = fp->NumTexInstructions;
+ *params = fp->Base.NumTexInstructions;
return;
case GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB:
- *params = fp->NumNativeTexInstructions;
+ *params = fp->Base.NumNativeTexInstructions;
return;
case GL_PROGRAM_TEX_INDIRECTIONS_ARB:
- *params = fp->NumTexIndirections;
+ *params = fp->Base.NumTexIndirections;
return;
case GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB:
- *params = fp->NumNativeTexIndirections;
+ *params = fp->Base.NumNativeTexIndirections;
return;
case GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB:
*params = limits->MaxAluInstructions;
void GLAPIENTRY
_mesa_GetProgramStringARB(GLenum target, GLenum pname, GLvoid *string)
{
- const struct program *prog;
+ const struct gl_program *prog;
+ char *dst = (char *) string;
GET_CURRENT_CONTEXT(ctx);
if (!ctx->_CurrentProgram)
return;
}
- _mesa_memcpy(string, prog->String, _mesa_strlen((char *) prog->String));
+ if (prog->String)
+ _mesa_memcpy(dst, prog->String, _mesa_strlen((char *) prog->String));
+ else
+ *dst = '\0';
}