implement full reference counting for vertex/fragment programs
[mesa.git] / src / mesa / shader / program.c
index 1b26b6c93211f6b441bab19a74e91b82f2586d0f..8166e7e935c28cf22fdbc4a7e139010885dfec70 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Mesa 3-D graphics library
- * Version:  6.5.2
+ * Version:  6.5.3
  *
- * Copyright (C) 1999-2006  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 "context.h"
 #include "hash.h"
-#include "imports.h"
-#include "macros.h"
-#include "mtypes.h"
-#include "nvfragparse.h"
 #include "program.h"
 #include "prog_parameter.h"
 #include "prog_instruction.h"
-#include "prog_statevars.h"
-#include "nvvertparse.h"
-#include "atifragshader.h"
 
 
-
-/**********************************************************************/
-/* Utility functions                                                  */
-/**********************************************************************/
-
-
-/* A pointer to this dummy program is put into the hash table when
+/**
+ * A pointer to this dummy program is put into the hash table when
  * glGenPrograms is called.
  */
 struct gl_program _mesa_DummyProgram;
@@ -71,9 +59,9 @@ _mesa_init_program(GLcontext *ctx)
    ctx->VertexProgram.Enabled = GL_FALSE;
    ctx->VertexProgram.PointSizeEnabled = GL_FALSE;
    ctx->VertexProgram.TwoSideEnabled = GL_FALSE;
-   ctx->VertexProgram.Current = (struct gl_vertex_program *) ctx->Shared->DefaultVertexProgram;
+   _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current,
+                            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;
@@ -82,7 +70,8 @@ _mesa_init_program(GLcontext *ctx)
 
 #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
    ctx->FragmentProgram.Enabled = GL_FALSE;
-   ctx->FragmentProgram.Current = (struct gl_fragment_program *) ctx->Shared->DefaultFragmentProgram;
+   _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current,
+                            ctx->Shared->DefaultFragmentProgram);
    assert(ctx->FragmentProgram.Current);
    ctx->FragmentProgram.Current->Base.RefCount++;
 #endif
@@ -103,20 +92,55 @@ _mesa_init_program(GLcontext *ctx)
 void
 _mesa_free_program_data(GLcontext *ctx)
 {
+#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
+   _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, NULL);
+#endif
+#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
+   _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, NULL);
+#endif
+   /* XXX probably move this stuff */
+#if FEATURE_ATI_fragment_shader
+   if (ctx->ATIFragmentShader.Current) {
+      ctx->ATIFragmentShader.Current->RefCount--;
+      if (ctx->ATIFragmentShader.Current->RefCount <= 0) {
+         _mesa_free(ctx->ATIFragmentShader.Current);
+      }
+   }
+#endif
+   _mesa_free((void *) ctx->Program.ErrorString);
+}
+
+
+/**
+ * Update the default program objects in the given context to reference those
+ * specified in the shared state and release those referencing the old 
+ * shared state.
+ */
+void
+_mesa_update_default_objects_program(GLcontext *ctx)
+{
 #if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
    if (ctx->VertexProgram.Current) {
       ctx->VertexProgram.Current->Base.RefCount--;
       if (ctx->VertexProgram.Current->Base.RefCount <= 0)
          ctx->Driver.DeleteProgram(ctx, &(ctx->VertexProgram.Current->Base));
    }
+   ctx->VertexProgram.Current = (struct gl_vertex_program *) ctx->Shared->DefaultVertexProgram;
+   assert(ctx->VertexProgram.Current);
+   ctx->VertexProgram.Current->Base.RefCount++;
 #endif
+
 #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
    if (ctx->FragmentProgram.Current) {
       ctx->FragmentProgram.Current->Base.RefCount--;
       if (ctx->FragmentProgram.Current->Base.RefCount <= 0)
          ctx->Driver.DeleteProgram(ctx, &(ctx->FragmentProgram.Current->Base));
    }
+   ctx->FragmentProgram.Current = (struct gl_fragment_program *) ctx->Shared->DefaultFragmentProgram;
+   assert(ctx->FragmentProgram.Current);
+   ctx->FragmentProgram.Current->Base.RefCount++;
 #endif
+
    /* XXX probably move this stuff */
 #if FEATURE_ATI_fragment_shader
    if (ctx->ATIFragmentShader.Current) {
@@ -125,13 +149,13 @@ _mesa_free_program_data(GLcontext *ctx)
          _mesa_free(ctx->ATIFragmentShader.Current);
       }
    }
+   ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader;
+   assert(ctx->ATIFragmentShader.Current);
+   ctx->ATIFragmentShader.Current->RefCount++;
 #endif
-   _mesa_free((void *) ctx->Program.ErrorString);
 }
 
 
-
-
 /**
  * Set the vertex/fragment program error state (position and error string).
  * This is generally called from within the parsers.
@@ -199,7 +223,6 @@ _mesa_init_program_struct( GLcontext *ctx, struct gl_program *prog,
 {
    (void) ctx;
    if (prog) {
-      _mesa_bzero(prog, sizeof(*prog));
       prog->Id = id;
       prog->Target = target;
       prog->Resident = GL_TRUE;
@@ -293,6 +316,8 @@ _mesa_delete_program(GLcontext *ctx, struct gl_program *prog)
       for (i = 0; i < prog->NumInstructions; i++) {
          if (prog->Instructions[i].Data)
             _mesa_free(prog->Instructions[i].Data);
+         if (prog->Instructions[i].Comment)
+            _mesa_free((char *) prog->Instructions[i].Comment);
       }
       _mesa_free(prog->Instructions);
    }
@@ -300,10 +325,12 @@ _mesa_delete_program(GLcontext *ctx, struct gl_program *prog)
    if (prog->Parameters) {
       _mesa_free_parameter_list(prog->Parameters);
    }
-
    if (prog->Varying) {
       _mesa_free_parameter_list(prog->Varying);
    }
+   if (prog->Attributes) {
+      _mesa_free_parameter_list(prog->Attributes);
+   }
 
    /* XXX this is a little ugly */
    if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
@@ -331,6 +358,59 @@ _mesa_lookup_program(GLcontext *ctx, GLuint id)
 }
 
 
+/**
+ * Reference counting for vertex/fragment programs
+ */
+void
+_mesa_reference_program(GLcontext *ctx,
+                        struct gl_program **ptr,
+                        struct gl_program *prog)
+{
+   assert(ptr);
+   if (*ptr && prog) {
+      /* sanity check */
+      ASSERT((*ptr)->Target == prog->Target);
+   }
+   if (*ptr == prog) {
+      return;  /* no change */
+   }
+   if (*ptr) {
+      GLboolean deleteFlag;
+
+      /*_glthread_LOCK_MUTEX((*ptr)->Mutex);*/
+#if 0
+      printf("Program %p %u 0x%x  Refcount-- to %d\n",
+             *ptr, (*ptr)->Id, (*ptr)->Target, (*ptr)->RefCount - 1);
+#endif
+      ASSERT((*ptr)->RefCount > 0);
+      (*ptr)->RefCount--;
+
+      deleteFlag = ((*ptr)->RefCount == 0);
+      /*_glthread_UNLOCK_MUTEX((*ptr)->Mutex);*/
+      
+      if (deleteFlag) {
+         ASSERT(ctx);
+         ctx->Driver.DeleteProgram(ctx, *ptr);
+      }
+
+      *ptr = NULL;
+   }
+
+   assert(!*ptr);
+   if (prog) {
+      /*_glthread_LOCK_MUTEX(prog->Mutex);*/
+      prog->RefCount++;
+#if 0
+      printf("Program %p %u 0x%x  Refcount++ to %d\n",
+             prog, prog->Id, prog->Target, prog->RefCount);
+#endif
+      /*_glthread_UNLOCK_MUTEX(prog->Mutex);*/
+   }
+
+   *ptr = prog;
+}
+
+
 /**
  * Return a copy of a program.
  * XXX Problem here if the program object is actually OO-derivation
@@ -341,21 +421,22 @@ _mesa_clone_program(GLcontext *ctx, const struct gl_program *prog)
 {
    struct gl_program *clone;
 
-   clone = _mesa_new_program(ctx, prog->Target, prog->Id);
+   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->RefCount = 1;
    clone->Format = prog->Format;
    clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions);
    if (!clone->Instructions) {
-      _mesa_delete_program(ctx, clone);
+      ctx->Driver.DeleteProgram(ctx, clone);
       return NULL;
    }
-   memcpy(clone->Instructions, prog->Instructions,
-          prog->NumInstructions * sizeof(struct prog_instruction));
+   _mesa_copy_instructions(clone->Instructions, prog->Instructions,
+                           prog->NumInstructions);
    clone->InputsRead = prog->InputsRead;
    clone->OutputsWritten = prog->OutputsWritten;
    memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed));
@@ -365,6 +446,8 @@ _mesa_clone_program(GLcontext *ctx, const struct gl_program *prog)
    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;
@@ -477,9 +560,9 @@ _mesa_BindProgram(GLenum target, GLuint id)
       /* Bind a default program */
       newProg = NULL;
       if (target == GL_VERTEX_PROGRAM_ARB) /* == GL_VERTEX_PROGRAM_NV */
-         newProg = ctx->Shared->DefaultVertexProgram;
+         newProg = &ctx->Shared->DefaultVertexProgram->Base;
       else
-         newProg = ctx->Shared->DefaultFragmentProgram;
+         newProg = &ctx->Shared->DefaultFragmentProgram->Base;
    }
    else {
       /* Bind a user program */
@@ -507,26 +590,16 @@ _mesa_BindProgram(GLenum target, GLuint id)
       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 */
    if (target == GL_VERTEX_PROGRAM_ARB) { /* == GL_VERTEX_PROGRAM_NV */
-      ctx->VertexProgram.Current = (struct gl_vertex_program *) newProg;
+      _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current,
+                               (struct gl_vertex_program *) newProg);
    }
    else if (target == GL_FRAGMENT_PROGRAM_NV ||
             target == GL_FRAGMENT_PROGRAM_ARB) {
-      ctx->FragmentProgram.Current = (struct gl_fragment_program *) newProg;
+      _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current,
+                               (struct gl_fragment_program *) newProg);
    }
-   newProg->RefCount++;
 
    /* Never null pointers */
    ASSERT(ctx->VertexProgram.Current);
@@ -584,10 +657,7 @@ _mesa_DeletePrograms(GLsizei n, const GLuint *ids)
             }
             /* The ID is immediately available for re-use now */
             _mesa_HashRemove(ctx->Shared->Programs, ids[i]);
-            prog->RefCount--;
-            if (prog->RefCount <= 0) {
-               ctx->Driver.DeleteProgram(ctx, prog);
-            }
+            _mesa_reference_program(ctx, &prog, NULL);
          }
       }
    }
@@ -627,233 +697,3 @@ _mesa_GenPrograms(GLsizei n, GLuint *ids)
       ids[i] = first + i;
    }
 }
-
-
-/**********************************************************************/
-/* GL_MESA_program_debug extension                                    */
-/**********************************************************************/
-
-
-/* XXX temporary */
-GLAPI void GLAPIENTRY
-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 */
-GLAPI void GLAPIENTRY
-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) {
-      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,
-                        "glGetProgramRegisterfvMESA(target)");
-            return;
-         }
-         if (!ctx->VertexProgram._Enabled) {
-            _mesa_error(ctx, GL_INVALID_OPERATION,
-                        "glGetProgramRegisterfvMESA");
-            return;
-         }
-         /* GL_NV_vertex_program */
-         if (reg[0] == 'R') {
-            /* Temp register */
-            GLint i = _mesa_atoi(reg + 1);
-            if (i >= (GLint)ctx->Const.VertexProgram.MaxTemps) {
-               _mesa_error(ctx, GL_INVALID_VALUE,
-                           "glGetProgramRegisterfvMESA(registerName)");
-               return;
-            }
-#if 0 /* FIX ME */
-            ctx->Driver.GetVertexProgramRegister(ctx, PROGRAM_TEMPORARY, i, v);
-#endif
-         }
-         else if (reg[0] == 'v' && reg[1] == '[') {
-            /* Vertex Input attribute */
-            GLuint i;
-            for (i = 0; i < ctx->Const.VertexProgram.MaxAttribs; i++) {
-               const char *name = _mesa_nv_vertex_input_register_name(i);
-               char number[10];
-               _mesa_sprintf(number, "%d", i);
-               if (_mesa_strncmp(reg + 2, name, 4) == 0 ||
-                   _mesa_strncmp(reg + 2, number, _mesa_strlen(number)) == 0) {
-#if 0 /* FIX ME */
-                  ctx->Driver.GetVertexProgramRegister(ctx, PROGRAM_INPUT,
-                                                       i, v);
-#endif
-                  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;
-         }
-         if (!ctx->FragmentProgram._Enabled) {
-            _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;
-         }
-         if (!ctx->FragmentProgram._Enabled) {
-            _mesa_error(ctx, GL_INVALID_OPERATION,
-                        "glGetProgramRegisterfvMESA");
-            return;
-         }
-         if (reg[0] == 'R') {
-            /* Temp register */
-            GLint i = _mesa_atoi(reg + 1);
-            if (i >= (GLint)ctx->Const.FragmentProgram.MaxTemps) {
-               _mesa_error(ctx, GL_INVALID_VALUE,
-                           "glGetProgramRegisterfvMESA(registerName)");
-               return;
-            }
-            ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_TEMPORARY,
-                                                   i, v);
-         }
-         else if (reg[0] == 'f' && reg[1] == '[') {
-            /* Fragment input attribute */
-            GLuint i;
-            for (i = 0; i < ctx->Const.FragmentProgram.MaxAttribs; i++) {
-               const char *name = _mesa_nv_fragment_input_register_name(i);
-               if (_mesa_strncmp(reg + 2, name, 4) == 0) {
-                  ctx->Driver.GetFragmentProgramRegister(ctx,
-                                                         PROGRAM_INPUT, i, v);
-                  return;
-               }
-            }
-            _mesa_error(ctx, GL_INVALID_VALUE,
-                        "glGetProgramRegisterfvMESA(registerName)");
-            return;
-         }
-         else if (_mesa_strcmp(reg, "o[COLR]") == 0) {
-            /* Fragment output color */
-            ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_OUTPUT,
-                                                   FRAG_RESULT_COLR, v);
-         }
-         else if (_mesa_strcmp(reg, "o[COLH]") == 0) {
-            /* Fragment output color */
-            ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_OUTPUT,
-                                                   FRAG_RESULT_COLH, v);
-         }
-         else if (_mesa_strcmp(reg, "o[DEPR]") == 0) {
-            /* Fragment output depth */
-            ctx->Driver.GetFragmentProgramRegister(ctx, PROGRAM_OUTPUT,
-                                                   FRAG_RESULT_DEPR, v);
-         }
-         else {
-            /* try user-defined identifiers */
-            const GLfloat *value = _mesa_lookup_parameter_value(
-                       ctx->FragmentProgram.Current->Base.Parameters, -1, reg);
-            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;
-   }
-}