glsl: implement compiling/linking of separate compilation units
authorBrian Paul <brianp@vmware.com>
Thu, 2 Apr 2009 01:50:28 +0000 (19:50 -0600)
committerBrian Paul <brianp@vmware.com>
Thu, 2 Apr 2009 01:54:35 +0000 (19:54 -0600)
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
src/mesa/shader/slang/slang_codegen.c
src/mesa/shader/slang/slang_codegen.h
src/mesa/shader/slang/slang_compile.c
src/mesa/shader/slang/slang_emit.c
src/mesa/shader/slang/slang_link.c

index a5d3be3543d4faf00ee6ddce230529b71b7085a9..e77dd1d226a9884d9a04beb07d36d631a2d4ebb1 100644 (file)
@@ -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;
index a7cfc45e6f00315014d89e045340f2b16da67b4a..6d693c9027aaed591534e0587aafaf52b9e32145 100644 (file)
@@ -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);
index e812c1f7ea595bd0d601d74f4a9c3824d46462da..d80013ad34119a88bf7701169022020dcde6f196 100644 (file)
@@ -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;
 
 
index fb7128841c4441f8c1b973aaeaf6b516c96d1386..6348f799aa2c40a2896b337d8a9350fa2fb49d55 100644 (file)
@@ -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);
index 1b1edb44609673ee3e355acf2ba441427f50aaf6..8493c490fbc358841b34680a4aeb69013044dd2a 100644 (file)
@@ -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);
index f98434892b64acc1ddce23caf27990104c1da2c5..e2daf72e7de2a0dcc894408fd911d9f1ad8ea138 100644 (file)
@@ -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