Mesa (master): glsl: implement compiling/ linking of separate compilation units

Brian Paul brianp at kemper.freedesktop.org
Thu Apr 2 01:56:07 UTC 2009


Module: Mesa
Branch: master
Commit: 49fb750a6884c3f647f46270ffce8652f664f908
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=49fb750a6884c3f647f46270ffce8652f664f908

Author: Brian Paul <brianp at vmware.com>
Date:   Wed Apr  1 19:50:28 2009 -0600

glsl: implement compiling/linking of separate compilation units

A shader program may consist of multiple shaders (source code units).
If we find there are unresolved functions after compiling the unit that
defines main(), we'll concatenate all the respective vertex or fragment
shaders then recompile.

This isn't foolproof but should work in most cases.

---

 src/mesa/main/mtypes.h                |    1 +
 src/mesa/shader/slang/slang_codegen.c |   19 ++++-
 src/mesa/shader/slang/slang_codegen.h |    1 +
 src/mesa/shader/slang/slang_compile.c |    2 +
 src/mesa/shader/slang/slang_emit.c    |    7 ++-
 src/mesa/shader/slang/slang_link.c    |  132 ++++++++++++++++++++++++++++----
 6 files changed, 139 insertions(+), 23 deletions(-)

diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index a5d3be3..e77dd1d 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -1974,6 +1974,7 @@ struct gl_shader
    GLboolean DeletePending;
    GLboolean CompileStatus;
    GLboolean Main;  /**< shader defines main() */
+   GLboolean UnresolvedRefs;
    const GLchar *Source;  /**< Source code string */
    struct gl_program *Program;  /**< Post-compile assembly code */
    GLchar *InfoLog;
diff --git a/src/mesa/shader/slang/slang_codegen.c b/src/mesa/shader/slang/slang_codegen.c
index a7cfc45..6d693c9 100644
--- a/src/mesa/shader/slang/slang_codegen.c
+++ b/src/mesa/shader/slang/slang_codegen.c
@@ -2199,12 +2199,13 @@ _slang_gen_function_call_name(slang_assemble_ctx *A, const char *name,
                            name);
       return NULL;
    }
+
    if (!fun->body) {
-      slang_info_log_error(A->log,
-                           "Function '%s' prototyped but not defined.  "
-                           "Separate compilation units not supported.",
-                           name);
-      return NULL;
+      /* The function body may be in another compilation unit.
+       * We'll try concatenating the shaders and recompile at link time.
+       */
+      A->UnresolvedRefs = GL_TRUE;
+      return new_node1(IR_NOP, NULL);
    }
 
    /* type checking to be sure function's return type matches 'dest' type */
@@ -4648,6 +4649,14 @@ _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun)
    printf("************* End codegen function ************\n\n");
 #endif
 
+   if (A->UnresolvedRefs) {
+      /* Can't codegen at this time.
+       * At link time we'll concatenate all the vertex shaders and/or all
+       * the fragment shaders and try recompiling.
+       */
+      return GL_TRUE;
+   }
+
    /* Emit program instructions */
    success = _slang_emit_code(n, A->vartable, A->program, A->pragmas, GL_TRUE, A->log);
    _slang_free_ir_tree(n);
diff --git a/src/mesa/shader/slang/slang_codegen.h b/src/mesa/shader/slang/slang_codegen.h
index e812c1f..d80013a 100644
--- a/src/mesa/shader/slang/slang_codegen.h
+++ b/src/mesa/shader/slang/slang_codegen.h
@@ -43,6 +43,7 @@ typedef struct slang_assemble_ctx_
    struct slang_ir_node_ *CurLoop;
    struct slang_function_ *CurFunction;
    GLuint UnrollLoop;
+   GLboolean UnresolvedRefs;
 } slang_assemble_ctx;
 
 
diff --git a/src/mesa/shader/slang/slang_compile.c b/src/mesa/shader/slang/slang_compile.c
index fb71288..6348f79 100644
--- a/src/mesa/shader/slang/slang_compile.c
+++ b/src/mesa/shader/slang/slang_compile.c
@@ -2435,6 +2435,8 @@ parse_code_unit(slang_parse_ctx * C, slang_code_unit * unit,
       _slang_codegen_function(&A, mainFunc);
 
       shader->Main = GL_TRUE; /* this shader defines main() */
+
+      shader->UnresolvedRefs = A.UnresolvedRefs;
    }
 
    _slang_pop_var_table(o.vartable);
diff --git a/src/mesa/shader/slang/slang_emit.c b/src/mesa/shader/slang/slang_emit.c
index 1b1edb4..8493c49 100644
--- a/src/mesa/shader/slang/slang_emit.c
+++ b/src/mesa/shader/slang/slang_emit.c
@@ -62,6 +62,8 @@ typedef struct
 
    GLuint MaxInstructions;  /**< size of prog->Instructions[] buffer */
 
+   GLboolean UnresolvedFunctions;
+
    /* code-gen options */
    GLboolean EmitHighLevelInstructions;
    GLboolean EmitCondCodes;
@@ -872,6 +874,7 @@ emit_compare(slang_emit_info *emitInfo, slang_ir_node *n)
    emit(emitInfo, n->Children[1]);
 
    if (n->Children[0]->Store->Size != n->Children[1]->Store->Size) {
+      /* XXX this error should have been caught in slang_codegen.c */
       slang_info_log_error(emitInfo->log, "invalid operands to == or !=");
       n->Store = NULL;
       return NULL;
@@ -1356,7 +1359,8 @@ emit_copy(slang_emit_info *emitInfo, slang_ir_node *n)
    inst = emit(emitInfo, n->Children[1]);
 
    if (!n->Children[1]->Store || n->Children[1]->Store->Index < 0) {
-      if (!emitInfo->log->text) {
+      if (!emitInfo->log->text && !emitInfo->UnresolvedFunctions) {
+         /* XXX this error should have been caught in slang_codegen.c */
          slang_info_log_error(emitInfo->log, "invalid assignment");
       }
       return NULL;
@@ -2155,6 +2159,7 @@ emit_var_ref(slang_emit_info *emitInfo, slang_ir_node *n)
       if (index < 0) {
          /* error */
          char s[100];
+         /* XXX isn't this really an out of memory/resources error? */
          _mesa_snprintf(s, sizeof(s), "Undefined variable '%s'",
                         (char *) n->Var->a_name);
          slang_info_log_error(emitInfo->log, s);
diff --git a/src/mesa/shader/slang/slang_link.c b/src/mesa/shader/slang/slang_link.c
index f984348..e2daf72 100644
--- a/src/mesa/shader/slang/slang_link.c
+++ b/src/mesa/shader/slang/slang_link.c
@@ -534,6 +534,106 @@ _slang_update_inputs_outputs(struct gl_program *prog)
 }
 
 
+
+
+
+/**
+ * Return a new shader whose source code is the concatenation of
+ * all the shader sources of the given type.
+ */
+static struct gl_shader *
+concat_shaders(struct gl_shader_program *shProg, GLenum shaderType)
+{
+   struct gl_shader *newShader;
+   const struct gl_shader *firstShader = NULL;
+   GLuint shaderLengths[100];
+   GLchar *source;
+   GLuint totalLen = 0, len = 0;
+   GLuint i;
+
+   /* compute total size of new shader source code */
+   for (i = 0; i < shProg->NumShaders; i++) {
+      const struct gl_shader *shader = shProg->Shaders[i];
+      if (shader->Type == shaderType) {
+         shaderLengths[i] = _mesa_strlen(shader->Source);
+         totalLen += shaderLengths[i];
+         if (!firstShader)
+            firstShader = shader;
+      }
+   }
+
+   source = (GLchar *) _mesa_malloc(totalLen + 1);
+   if (!source)
+      return NULL;
+
+   /* concatenate shaders */
+   for (i = 0; i < shProg->NumShaders; i++) {
+      const struct gl_shader *shader = shProg->Shaders[i];
+      if (shader->Type == shaderType) {
+         _mesa_memcpy(source + len, shader->Source, shaderLengths[i]);
+         len += shaderLengths[i];
+      }
+   }
+   source[len] = '\0';
+   /*
+   _mesa_printf("---NEW CONCATENATED SHADER---:\n%s\n------------\n", source);
+   */
+
+   newShader = CALLOC_STRUCT(gl_shader);
+   newShader->Type = shaderType;
+   newShader->Source = source;
+   newShader->Pragmas = firstShader->Pragmas;
+
+   return newShader;
+}
+
+
+/**
+ * Search the shader program's list of shaders to find the one that
+ * defines main().
+ * This will involve shader concatenation and recompilation if needed.
+ */
+static struct gl_shader *
+get_main_shader(GLcontext *ctx,
+                struct gl_shader_program *shProg, GLenum type)
+{
+   struct gl_shader *shader = NULL;
+   GLuint i;
+
+   /*
+    * Look for a shader that defines main() and has no unresolved references.
+    */
+   for (i = 0; i < shProg->NumShaders; i++) {
+      shader = shProg->Shaders[i];
+      if (shader->Type == type &&
+          shader->Main &&
+          !shader->UnresolvedRefs) {
+         /* All set! */
+         return shader;
+      }
+   }
+
+   /*
+    * There must have been unresolved references during the original
+    * compilation.  Try concatenating all the shaders of the given type
+    * and recompile that.
+    */
+   shader = concat_shaders(shProg, type);
+
+   _slang_compile(ctx, shader);
+
+   /* Finally, check if recompiling failed */
+   if (!shader->CompileStatus ||
+       !shader->Main ||
+       shader->UnresolvedRefs) {
+      link_error(shProg, "Unresolved symbols");
+      return NULL;
+   }
+
+   return shader;
+}
+
+
 /**
  * Shader linker.  Currently:
  *
@@ -557,6 +657,9 @@ _slang_link(GLcontext *ctx,
 
    _mesa_clear_shader_program_data(ctx, shProg);
 
+   /* Initialize LinkStatus to "success".  Will be cleared if error. */
+   shProg->LinkStatus = GL_TRUE;
+
    /* check that all programs compiled successfully */
    for (i = 0; i < shProg->NumShaders; i++) {
       if (!shProg->Shaders[i]->CompileStatus) {
@@ -568,24 +671,19 @@ _slang_link(GLcontext *ctx,
    shProg->Uniforms = _mesa_new_uniform_list();
    shProg->Varying = _mesa_new_parameter_list();
 
-   /**
-    * Find attached vertex, fragment shaders defining main()
+   /*
+    * Find the vertex and fragment shaders which define main()
     */
-   vertProg = NULL;
-   fragProg = NULL;
-   for (i = 0; i < shProg->NumShaders; i++) {
-      struct gl_shader *shader = shProg->Shaders[i];
-      if (shader->Type == GL_VERTEX_SHADER) {
-         if (shader->Main)
-            vertProg = vertex_program(shader->Program);
-      }
-      else if (shader->Type == GL_FRAGMENT_SHADER) {
-         if (shader->Main)
-            fragProg = fragment_program(shader->Program);
-      }
-      else {
-         _mesa_problem(ctx, "unexpected shader target in slang_link()");
-      }
+   {
+      struct gl_shader *vertShader, *fragShader;
+      vertShader = get_main_shader(ctx, shProg, GL_VERTEX_SHADER);
+      fragShader = get_main_shader(ctx, shProg, GL_FRAGMENT_SHADER);
+      if (vertShader)
+         vertProg = vertex_program(vertShader->Program);
+      if (fragShader)
+         fragProg = fragment_program(fragShader->Program);
+      if (!shProg->LinkStatus)
+         return;
    }
 
 #if FEATURE_es2_glsl




More information about the mesa-commit mailing list