[Mesa-dev] [PATCHv3 11/16] mesa: add infrastructure for threaded shader compilation

Chia-I Wu olvaffe at gmail.com
Tue Aug 19 23:40:32 PDT 2014


Add _mesa_enable_glsl_threadpool to enable the thread pool for a context, and
add ctx->Const.DeferCompileShader and ctx->Const.DeferLinkProgram to
fine-control what gets threaded.

Setting DeferCompileShader to true will make _mesa_glsl_compile_shader be
executed in a worker thread.  The function is thread-safe so there is no
restriction on DeferCompileShader.

Setting DeferLinkProgram to true will make _mesa_glsl_link_shader be executed
in a worker thread.  The function is thread-safe only when certain driver
functions (as documented in struct gl_constants) are thread-safe.  It is
drivers' responsibility to fix those driver functions before setting
DeferLinkProgram.

When DeferLinkProgram is set, drivers are not supposed to inspect the context
in their LinkShader callbacks.  Instead, NotifyLinkShader is added.  Drivers
should inspect the context in NotifyLinkShader and save what they need for
LinkShader in gl_shader_program.

As a final note, most applications will not benefit from threaded shader
compilation because they check GL_COMPILE_STATUS/GL_LINK_STATUS immediately,
giving the worker threads no time to do their jobs.  A possible improvement is
to split LinkShader into two parts: the first part links and error checks
while the second part optimizes and generates the machine code.  With the
split, we can always defer the second part to the thread pool.

Signed-off-by: Chia-I Wu <olv at lunarg.com>
Reviewed-by: Brian Paul <brianp at vmware.com>
Reviewed-by: Ian Romanick <ian.d.romanick at intel.com>

v2:

 - replace "void *TaskData" by "struct gl_context *TaskContext"
 - use bool instead of GLboolean internally
 - add more comments to the newly added functions
---
 src/mesa/main/context.c     |  29 +++++++++++
 src/mesa/main/context.h     |   3 ++
 src/mesa/main/dd.h          |   8 +++
 src/mesa/main/mtypes.h      |  34 ++++++++++++
 src/mesa/main/pipelineobj.c |  18 +++++++
 src/mesa/main/shaderapi.c   | 124 +++++++++++++++++++++++++++++++++++++++-----
 src/mesa/main/shaderobj.c   |  84 +++++++++++++++++++++++++++---
 src/mesa/main/shaderobj.h   |  55 ++++++++++++++++++--
 8 files changed, 332 insertions(+), 23 deletions(-)

diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c
index 7a1b6f6..54d1248 100644
--- a/src/mesa/main/context.c
+++ b/src/mesa/main/context.c
@@ -112,6 +112,7 @@
 #include "points.h"
 #include "polygon.h"
 #include "queryobj.h"
+#include "shaderapi.h"
 #include "syncobj.h"
 #include "rastpos.h"
 #include "remap.h"
@@ -133,6 +134,7 @@
 #include "math/m_matrix.h"
 #include "main/dispatch.h" /* for _gloffset_COUNT */
 #include "util/simple_list.h"
+#include "util/threadpool.h"
 
 #ifdef USE_SPARC_ASM
 #include "sparc/sparc.h"
@@ -1187,6 +1189,27 @@ _mesa_create_context(gl_api api,
    }
 }
 
+void
+_mesa_enable_glsl_threadpool(struct gl_context *ctx, int max_threads)
+{
+   if (!ctx->ThreadPool)
+      ctx->ThreadPool = _mesa_threadpool_get_singleton(max_threads);
+}
+
+static void
+wait_shader_object_cb(GLuint id, void *data, void *userData)
+{
+   struct gl_context *ctx = (struct gl_context *) userData;
+   struct gl_shader *sh = (struct gl_shader *) data;
+
+   if (_mesa_validate_shader_target(ctx, sh->Type)) {
+      _mesa_wait_shaders(ctx, &sh, 1);
+   }
+   else {
+      struct gl_shader_program *shProg = (struct gl_shader_program *) data;
+      _mesa_wait_shader_program(ctx, shProg);
+   }
+}
 
 /**
  * Free the data associated with the given context.
@@ -1205,6 +1228,12 @@ _mesa_free_context_data( struct gl_context *ctx )
       _mesa_make_current(ctx, NULL, NULL);
    }
 
+   if (ctx->ThreadPool) {
+      _mesa_HashWalk(ctx->Shared->ShaderObjects, wait_shader_object_cb, ctx);
+      _mesa_threadpool_unref(ctx->ThreadPool);
+      ctx->ThreadPool = NULL;
+   }
+
    /* unreference WinSysDraw/Read buffers */
    _mesa_reference_framebuffer(&ctx->WinSysDrawBuffer, NULL);
    _mesa_reference_framebuffer(&ctx->WinSysReadBuffer, NULL);
diff --git a/src/mesa/main/context.h b/src/mesa/main/context.h
index d902ea7..e81d4f7 100644
--- a/src/mesa/main/context.h
+++ b/src/mesa/main/context.h
@@ -118,6 +118,9 @@ _mesa_create_context(gl_api api,
                      const struct dd_function_table *driverFunctions);
 
 extern void
+_mesa_enable_glsl_threadpool(struct gl_context *ctx, int max_threads);
+
+extern void
 _mesa_free_context_data( struct gl_context *ctx );
 
 extern void
diff --git a/src/mesa/main/dd.h b/src/mesa/main/dd.h
index c130b14..9310002 100644
--- a/src/mesa/main/dd.h
+++ b/src/mesa/main/dd.h
@@ -477,6 +477,14 @@ struct dd_function_table {
     */
    /*@{*/
    /**
+    * Called when a shader program is to be linked.
+    *
+    * This is optional and gives drivers an opportunity to inspect the context
+    * and prepare for LinkShader, which may be deferred to another thread.
+    */
+   void (*NotifyLinkShader)(struct gl_context *ctx,
+                            struct gl_shader_program *shader);
+   /**
     * Called when a shader program is linked.
     *
     * This gives drivers an opportunity to clone the IR and make their
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index f2c7013..a8f63eb 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -70,6 +70,8 @@ typedef GLuint64 GLbitfield64;
  */
 /*@{*/
 struct _mesa_HashTable;
+struct _mesa_threadpool;
+struct _mesa_threadpool_task;
 struct gl_attrib_node;
 struct gl_list_extensions;
 struct gl_meta_state;
@@ -2544,6 +2546,14 @@ struct gl_shader
        */
       unsigned LocalSize[3];
    } Comp;
+
+   /**
+    * Deferred task of glCompileShader.  We should extend the mutex, not only
+    * to protect the deferred task, but to protect the entire gl_shader.
+    */
+   mtx_t Mutex;
+   struct _mesa_threadpool_task *Task;
+   struct gl_context *TaskContext;
 };
 
 
@@ -2804,6 +2814,15 @@ struct gl_shader_program
     * #extension ARB_fragment_coord_conventions: enable
     */
    GLboolean ARB_fragment_coord_conventions_enable;
+
+   /**
+    * Deferred task of glLinkProgram.  We should extend the mutex, not only
+    * to protect the deferred task, but to protect the entire
+    * gl_shader_program.
+    */
+   mtx_t Mutex;
+   struct _mesa_threadpool_task *Task;
+   struct gl_context *TaskContext;
 };   
 
 
@@ -3530,6 +3549,18 @@ struct gl_constants
    GLboolean FakeSWMSAA;
 
    struct gl_shader_compiler_options ShaderCompilerOptions[MESA_SHADER_STAGES];
+
+   /*
+    * Defer certain operations to a thread pool.
+    *
+    * When DeferLinkProgram is set, these functions must be thread-safe
+    *
+    *   ctx->Driver.NewShader
+    *   ctx->Driver.DeleteShader
+    *   ctx->Driver.LinkShader
+    */
+   bool DeferCompileShader;
+   bool DeferLinkProgram;
 };
 
 
@@ -4306,6 +4337,9 @@ struct gl_context
     * Once this field becomes true, it is never reset to false.
     */
    GLboolean ShareGroupReset;
+
+   /* A thread pool for threaded shader compilation */
+   struct _mesa_threadpool *ThreadPool;
 };
 
 
diff --git a/src/mesa/main/pipelineobj.c b/src/mesa/main/pipelineobj.c
index 017d425..4e60b71 100644
--- a/src/mesa/main/pipelineobj.c
+++ b/src/mesa/main/pipelineobj.c
@@ -211,6 +211,24 @@ _mesa_reference_pipeline_object_(struct gl_context *ctx,
       }
       else {
          obj->RefCount++;
+
+         /*
+          * The current pipeline object (as set by glUseProgram or
+          * glBindProgramPipeline) cannot have unfinished tasks.
+          */
+         if (ptr == &ctx->_Shader) {
+            if (obj->ActiveProgram)
+               _mesa_wait_shader_program(ctx, obj->ActiveProgram);
+            if (obj->Name) {
+               int i;
+
+               for (i = 0; i < MESA_SHADER_STAGES; i++) {
+                  if (obj->CurrentProgram[i])
+                     _mesa_wait_shader_program(ctx, obj->CurrentProgram[i]);
+               }
+            }
+         }
+
          *ptr = obj;
       }
       mtx_unlock(&obj->Mutex);
diff --git a/src/mesa/main/shaderapi.c b/src/mesa/main/shaderapi.c
index 620cab3..d9b1a83 100644
--- a/src/mesa/main/shaderapi.c
+++ b/src/mesa/main/shaderapi.c
@@ -53,6 +53,7 @@
 #include "program/prog_parameter.h"
 #include "util/ralloc.h"
 #include "util/hash_table.h"
+#include "util/threadpool.h"
 #include <stdbool.h>
 #include "../glsl/glsl_parser_extras.h"
 #include "../glsl/ir.h"
@@ -210,7 +211,8 @@ _mesa_validate_shader_target(const struct gl_context *ctx, GLenum type)
 static GLboolean
 is_program(struct gl_context *ctx, GLuint name)
 {
-   struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, name);
+   struct gl_shader_program *shProg =
+      _mesa_lookup_shader_program_no_wait(ctx, name);
    return shProg ? GL_TRUE : GL_FALSE;
 }
 
@@ -218,7 +220,7 @@ is_program(struct gl_context *ctx, GLuint name)
 static GLboolean
 is_shader(struct gl_context *ctx, GLuint name)
 {
-   struct gl_shader *shader = _mesa_lookup_shader(ctx, name);
+   struct gl_shader *shader = _mesa_lookup_shader_no_wait(ctx, name);
    return shader ? GL_TRUE : GL_FALSE;
 }
 
@@ -239,7 +241,7 @@ attach_shader(struct gl_context *ctx, GLuint program, GLuint shader)
    if (!shProg)
       return;
 
-   sh = _mesa_lookup_shader_err(ctx, shader, "glAttachShader");
+   sh = _mesa_lookup_shader_err_no_wait(ctx, shader, "glAttachShader");
    if (!sh) {
       return;
    }
@@ -341,7 +343,9 @@ delete_shader_program(struct gl_context *ctx, GLuint name)
     */
    struct gl_shader_program *shProg;
 
-   shProg = _mesa_lookup_shader_program_err(ctx, name, "glDeleteProgram");
+   /* no waiting until _mesa_delete_shader_program() */
+   shProg = _mesa_lookup_shader_program_err_no_wait(ctx, name,
+                                                    "glDeleteProgram");
    if (!shProg)
       return;
 
@@ -359,7 +363,8 @@ delete_shader(struct gl_context *ctx, GLuint shader)
 {
    struct gl_shader *sh;
 
-   sh = _mesa_lookup_shader_err(ctx, shader, "glDeleteShader");
+   /* no waiting until _mesa_delete_shader() */
+   sh = _mesa_lookup_shader_err_no_wait(ctx, shader, "glDeleteShader");
    if (!sh)
       return;
 
@@ -812,6 +817,49 @@ shader_source(struct gl_context *ctx, GLuint shader, const GLchar *source)
 #endif
 }
 
+static bool
+can_queue_task(struct gl_context *ctx)
+{
+   if (!ctx->ThreadPool)
+      return false;
+
+   /* MESA_GLSL is set */
+   if (ctx->_Shader->Flags)
+      return false;
+
+   /* context requires synchronized compiler warnings and errors */
+   if (_mesa_get_debug_state_int(ctx, GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB))
+      return false;
+
+   return true;
+}
+
+/* note that this is called from a threadpool thread */
+static void
+deferred_compile_shader(void *data)
+{
+   struct gl_shader *sh = (struct gl_shader *) data;
+   struct gl_context *ctx = sh->TaskContext;
+
+   _mesa_glsl_compile_shader(ctx, sh, false, false);
+}
+
+static bool
+queue_compile_shader(struct gl_context *ctx, struct gl_shader *sh)
+{
+   if (!can_queue_task(ctx))
+      return false;
+   if (!ctx->Const.DeferCompileShader)
+      return false;
+
+   sh->TaskContext = ctx;
+   sh->Task = _mesa_threadpool_queue_task(ctx->ThreadPool,
+         deferred_compile_shader, (void *) sh);
+   if (!sh->Task)
+      sh->TaskContext = NULL;
+
+   return (sh->Task != NULL);
+}
 
 /**
  * Compile a shader.
@@ -844,10 +892,12 @@ compile_shader(struct gl_context *ctx, GLuint shaderObj)
          fflush(stderr);
       }
 
-      /* this call will set the shader->CompileStatus field to indicate if
-       * compilation was successful.
+      /* This call will set the shader->CompileStatus field to indicate if
+       * compilation was successful.  But if queueing succeeded, the field
+       * will be set at a later point.
        */
-      _mesa_glsl_compile_shader(ctx, sh, false, false);
+      if (!queue_compile_shader(ctx, sh))
+         _mesa_glsl_compile_shader(ctx, sh, false, false);
 
       if (ctx->_Shader->Flags & GLSL_LOG) {
          _mesa_write_shader_to_file(sh);
@@ -870,7 +920,7 @@ compile_shader(struct gl_context *ctx, GLuint shaderObj)
 
    }
 
-   if (!sh->CompileStatus) {
+   if (ctx->_Shader->Flags && !sh->CompileStatus) {
       if (ctx->_Shader->Flags & GLSL_DUMP_ON_ERROR) {
          fprintf(stderr, "GLSL source for %s shader %d:\n",
                  _mesa_shader_stage_to_string(sh->Stage), sh->Name);
@@ -887,6 +937,50 @@ compile_shader(struct gl_context *ctx, GLuint shaderObj)
 }
 
 
+/* note that this is called from a threadpool thread */
+static void
+deferred_link_program(void *data)
+{
+   struct gl_shader_program *shProg = (struct gl_shader_program *) data;
+   struct gl_context *ctx = shProg->TaskContext;
+
+   _mesa_wait_shaders(ctx, shProg->Shaders, shProg->NumShaders);
+   _mesa_glsl_link_shader(ctx, shProg);
+}
+
+static bool
+queue_link_program(struct gl_context *ctx, struct gl_shader_program *shProg)
+{
+   int i;
+
+   if (!can_queue_task(ctx))
+      return false;
+   if (!ctx->Const.DeferLinkProgram)
+      return false;
+
+   /*
+    * Rather than adding _mesa_wait_shader_program calls here and there, we
+    * simply disallow threaded linking when the program is current or active.
+    */
+   if (ctx->_Shader->ActiveProgram == shProg)
+      return false;
+   /* be careful for separate programs */
+   if (ctx->_Shader->Name != 0) {
+      for (i = 0; i < MESA_SHADER_STAGES; i++) {
+         if (ctx->_Shader->CurrentProgram[i] == shProg)
+            return false;
+      }
+   }
+
+   shProg->TaskContext = ctx;
+   shProg->Task = _mesa_threadpool_queue_task(ctx->ThreadPool,
+         deferred_link_program, (void *) shProg);
+   if (!shProg->Task)
+      shProg->TaskContext = NULL;
+
+   return (shProg->Task != NULL);
+}
+
 /**
  * Link a program's shaders.
  */
@@ -912,10 +1006,16 @@ link_program(struct gl_context *ctx, GLuint program)
 
    FLUSH_VERTICES(ctx, _NEW_PROGRAM);
 
-   _mesa_glsl_link_shader(ctx, shProg);
+   if (ctx->Driver.NotifyLinkShader)
+      ctx->Driver.NotifyLinkShader(ctx, shProg);
+
+   if (!queue_link_program(ctx, shProg)) {
+      _mesa_wait_shaders(ctx, shProg->Shaders, shProg->NumShaders);
+      _mesa_glsl_link_shader(ctx, shProg);
+   }
 
-   if (shProg->LinkStatus == GL_FALSE && 
-       (ctx->_Shader->Flags & GLSL_REPORT_ERRORS)) {
+   if ((ctx->_Shader->Flags & GLSL_REPORT_ERRORS) &&
+       shProg->LinkStatus == GL_FALSE) {
       _mesa_debug(ctx, "Error linking program %u:\n%s\n",
                   shProg->Name, shProg->InfoLog);
    }
diff --git a/src/mesa/main/shaderobj.c b/src/mesa/main/shaderobj.c
index 693e9a2..b1282a4 100644
--- a/src/mesa/main/shaderobj.c
+++ b/src/mesa/main/shaderobj.c
@@ -41,6 +41,7 @@
 #include "program/prog_parameter.h"
 #include "program/hash_table.h"
 #include "util/ralloc.h"
+#include "util/threadpool.h"
 
 /**********************************************************************/
 /*** Shader object functions                                        ***/
@@ -95,6 +96,7 @@ _mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
 void
 _mesa_init_shader(struct gl_context *ctx, struct gl_shader *shader)
 {
+   mtx_init(&shader->Mutex, mtx_plain);
    shader->RefCount = 1;
 }
 
@@ -125,18 +127,54 @@ _mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type)
 static void
 _mesa_delete_shader(struct gl_context *ctx, struct gl_shader *sh)
 {
+   _mesa_wait_shaders(ctx, &sh, 1);
+
    free((void *)sh->Source);
    free(sh->Label);
    _mesa_reference_program(ctx, &sh->Program, NULL);
+   mtx_destroy(&sh->Mutex);
    ralloc_free(sh);
 }
 
 
 /**
- * Lookup a GLSL shader object.
+ * Wait for the threaded compile tasks to complete.
+ */
+void
+_mesa_wait_shaders(struct gl_context *ctx,
+                   struct gl_shader **shaders,
+                   int num_shaders)
+{
+   int i;
+
+   for (i = 0; i < num_shaders; i++) {
+      struct gl_shader *sh = shaders[i];
+
+      mtx_lock(&sh->Mutex);
+
+      if (sh->Task) {
+         struct gl_context *task_ctx = sh->TaskContext;
+
+         if (!_mesa_threadpool_complete_task(task_ctx->ThreadPool, sh->Task))
+            sh->CompileStatus = GL_FALSE;
+
+         sh->Task = NULL;
+         sh->TaskContext = NULL;
+      }
+
+      mtx_unlock(&sh->Mutex);
+   }
+}
+
+
+/**
+ * Lookup a GLSL shader object.  A threadpool thread may still be working on
+ * the shader object and the caller needs to be careful.
+ *
+ * \see _mesa_lookup_shader()
  */
 struct gl_shader *
-_mesa_lookup_shader(struct gl_context *ctx, GLuint name)
+_mesa_lookup_shader_no_wait(struct gl_context *ctx, GLuint name)
 {
    if (name) {
       struct gl_shader *sh = (struct gl_shader *)
@@ -158,7 +196,8 @@ _mesa_lookup_shader(struct gl_context *ctx, GLuint name)
  * As above, but record an error if shader is not found.
  */
 struct gl_shader *
-_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char *caller)
+_mesa_lookup_shader_err_no_wait(struct gl_context *ctx, GLuint name,
+                                const char *caller)
 {
    if (!name) {
       _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
@@ -238,6 +277,8 @@ _mesa_reference_shader_program(struct gl_context *ctx,
 void
 _mesa_init_shader_program(struct gl_context *ctx, struct gl_shader_program *prog)
 {
+   mtx_init(&prog->Mutex, mtx_plain);
+
    prog->Type = GL_SHADER_PROGRAM_MESA;
    prog->RefCount = 1;
 
@@ -373,17 +414,46 @@ _mesa_free_shader_program_data(struct gl_context *ctx,
 static void
 _mesa_delete_shader_program(struct gl_context *ctx, struct gl_shader_program *shProg)
 {
+   _mesa_wait_shader_program(ctx, shProg);
+
    _mesa_free_shader_program_data(ctx, shProg);
+   mtx_destroy(&shProg->Mutex);
 
    ralloc_free(shProg);
 }
 
 
 /**
- * Lookup a GLSL program object.
+ * Wait for the threaded linking task to complete.
+ */
+void
+_mesa_wait_shader_program(struct gl_context *ctx,
+                          struct gl_shader_program *shProg)
+{
+   mtx_lock(&shProg->Mutex);
+
+   if (shProg->Task) {
+      struct gl_context *task_ctx = shProg->TaskContext;
+
+      if (!_mesa_threadpool_complete_task(task_ctx->ThreadPool,
+                                          shProg->Task))
+         shProg->LinkStatus = GL_FALSE;
+      shProg->Task = NULL;
+      shProg->TaskContext = NULL;
+   }
+
+   mtx_unlock(&shProg->Mutex);
+}
+
+
+/**
+ * Lookup a GLSL program object.  A threadpool thread may still be working on
+ * the program object and the caller needs to be careful.
+ *
+ * \see _mesa_lookup_shader_program()
  */
 struct gl_shader_program *
-_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name)
+_mesa_lookup_shader_program_no_wait(struct gl_context *ctx, GLuint name)
 {
    struct gl_shader_program *shProg;
    if (name) {
@@ -406,8 +476,8 @@ _mesa_lookup_shader_program(struct gl_context *ctx, GLuint name)
  * As above, but record an error if program is not found.
  */
 struct gl_shader_program *
-_mesa_lookup_shader_program_err(struct gl_context *ctx, GLuint name,
-                                const char *caller)
+_mesa_lookup_shader_program_err_no_wait(struct gl_context *ctx, GLuint name,
+                                        const char *caller)
 {
    if (!name) {
       _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
diff --git a/src/mesa/main/shaderobj.h b/src/mesa/main/shaderobj.h
index fae8be8..c2f8a0d 100644
--- a/src/mesa/main/shaderobj.h
+++ b/src/mesa/main/shaderobj.h
@@ -53,13 +53,35 @@ extern void
 _mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
                        struct gl_shader *sh);
 
+extern void
+_mesa_wait_shaders(struct gl_context *ctx,
+                   struct gl_shader **shaders,
+                   int num_shaders);
+
 extern struct gl_shader *
-_mesa_lookup_shader(struct gl_context *ctx, GLuint name);
+_mesa_lookup_shader_no_wait(struct gl_context *ctx, GLuint name);
 
 extern struct gl_shader *
-_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char *caller);
+_mesa_lookup_shader_err_no_wait(struct gl_context *ctx, GLuint name,
+                                const char *caller);
 
+static inline struct gl_shader *
+_mesa_lookup_shader(struct gl_context *ctx, GLuint name)
+{
+   struct gl_shader *sh = _mesa_lookup_shader_no_wait(ctx, name);
+   if (sh)
+      _mesa_wait_shaders(ctx, &sh, 1);
+   return sh;
+}
 
+static inline struct gl_shader *
+_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char *caller)
+{
+   struct gl_shader *sh = _mesa_lookup_shader_err_no_wait(ctx, name, caller);
+   if (sh)
+      _mesa_wait_shaders(ctx, &sh, 1);
+   return sh;
+}
 
 extern void
 _mesa_reference_shader_program(struct gl_context *ctx,
@@ -74,12 +96,37 @@ _mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type);
 extern void
 _mesa_init_shader_program(struct gl_context *ctx, struct gl_shader_program *prog);
 
+extern void
+_mesa_wait_shader_program(struct gl_context *ctx,
+                          struct gl_shader_program *shProg);
+
 extern struct gl_shader_program *
-_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name);
+_mesa_lookup_shader_program_no_wait(struct gl_context *ctx, GLuint name);
 
 extern struct gl_shader_program *
+_mesa_lookup_shader_program_err_no_wait(struct gl_context *ctx, GLuint name,
+                                        const char *caller);
+
+static inline struct gl_shader_program *
+_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name)
+{
+   struct gl_shader_program *shProg =
+      _mesa_lookup_shader_program_no_wait(ctx, name);
+   if (shProg)
+      _mesa_wait_shader_program(ctx, shProg);
+   return shProg;
+}
+
+static inline struct gl_shader_program *
 _mesa_lookup_shader_program_err(struct gl_context *ctx, GLuint name,
-                                const char *caller);
+                                const char *caller)
+{
+   struct gl_shader_program *shProg =
+      _mesa_lookup_shader_program_err_no_wait(ctx, name, caller);
+   if (shProg)
+      _mesa_wait_shader_program(ctx, shProg);
+   return shProg;
+}
 
 extern void
 _mesa_clear_shader_program_data(struct gl_context *ctx,
-- 
2.0.0.rc2



More information about the mesa-dev mailing list