mesa/get: fix enum16 big-endian getting.
[mesa.git] / src / mesa / main / shaderapi.c
index 99eb2dc00e2979bc3753e97b2e8e3a7f11464437..19d7a3ab44003261b2bc601481fec61f093cbded 100644 (file)
  *
  * Implementation of GLSL-related API functions.
  * The glUniform* functions are in uniforms.c
- *
- *
- * XXX things to do:
- * 1. Check that the right error code is generated for all _mesa_error() calls.
- * 2. Insert FLUSH_VERTICES calls in various places
  */
 
 
 #include <errno.h>
 #include <stdbool.h>
 #include <c99_alloca.h>
+
 #include "main/glheader.h"
 #include "main/context.h"
 #include "main/enums.h"
@@ -53,6 +49,7 @@
 #include "main/state.h"
 #include "main/transformfeedback.h"
 #include "main/uniforms.h"
+#include "compiler/glsl/builtin_functions.h"
 #include "compiler/glsl/glsl_parser_extras.h"
 #include "compiler/glsl/ir.h"
 #include "compiler/glsl/ir_uniform.h"
@@ -65,6 +62,8 @@
 #include "util/mesa-sha1.h"
 #include "util/crc32.h"
 #include "util/os_file.h"
+#include "util/simple_list.h"
+#include "util/u_string.h"
 
 /**
  * Return mask of GLSL_x flags by examining the MESA_GLSL env var.
@@ -837,8 +836,10 @@ get_programiv(struct gl_context *ctx, GLuint program, GLenum pname,
       }
       return;
    case GL_GEOMETRY_SHADER_INVOCATIONS:
-      if (!has_gs || !ctx->Extensions.ARB_gpu_shader5)
+      if (!has_gs ||
+          (_mesa_is_desktop_gl(ctx) && !ctx->Extensions.ARB_gpu_shader5)) {
          break;
+      }
       if (check_gs_query(ctx, shProg)) {
          *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
             Program->info.gs.invocations;
@@ -1160,6 +1161,14 @@ set_shader_source(struct gl_shader *sh, const GLchar *source)
 #endif
 }
 
+static void
+ensure_builtin_types(struct gl_context *ctx)
+{
+   if (!ctx->shader_builtin_ref) {
+      _mesa_glsl_builtin_functions_init_or_ref();
+      ctx->shader_builtin_ref = true;
+   }
+}
 
 /**
  * Compile a shader.
@@ -1194,6 +1203,8 @@ _mesa_compile_shader(struct gl_context *ctx, struct gl_shader *sh)
          _mesa_log("%s\n", sh->Source);
       }
 
+      ensure_builtin_types(ctx);
+
       /* this call will set the shader->CompileStatus field to indicate if
        * compilation was successful.
        */
@@ -1239,6 +1250,29 @@ _mesa_compile_shader(struct gl_context *ctx, struct gl_shader *sh)
 }
 
 
+struct update_programs_in_pipeline_params
+{
+   struct gl_context *ctx;
+   struct gl_shader_program *shProg;
+};
+
+static void
+update_programs_in_pipeline(GLuint key, void *data, void *userData)
+{
+   struct update_programs_in_pipeline_params *params =
+      (struct update_programs_in_pipeline_params *) userData;
+   struct gl_pipeline_object *obj = (struct gl_pipeline_object *) data;
+
+   for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
+      if (obj->CurrentProgram[stage] &&
+          obj->CurrentProgram[stage]->Id == params->shProg->Name) {
+         struct gl_program *prog = params->shProg->_LinkedShaders[stage]->Program;
+         _mesa_use_program(params->ctx, stage, params->shProg, prog, obj);
+      }
+   }
+}
+
+
 /**
  * Link a program's shaders.
  */
@@ -1269,7 +1303,9 @@ link_program(struct gl_context *ctx, struct gl_shader_program *shProg,
              ctx->_Shader->CurrentProgram[stage]->Id == shProg->Name) {
             programs_in_use |= 1 << stage;
          }
-   }
+      }
+
+   ensure_builtin_types(ctx);
 
    FLUSH_VERTICES(ctx, 0);
    _mesa_glsl_link_shader(ctx, shProg);
@@ -1284,7 +1320,7 @@ link_program(struct gl_context *ctx, struct gl_shader_program *shProg,
     *     the state of any program pipeline for all stages where the program
     *     is attached."
     */
-   if (shProg->data->LinkStatus && programs_in_use) {
+   if (shProg->data->LinkStatus) {
       while (programs_in_use) {
          const int stage = u_bit_scan(&programs_in_use);
 
@@ -1294,6 +1330,15 @@ link_program(struct gl_context *ctx, struct gl_shader_program *shProg,
 
          _mesa_use_program(ctx, stage, shProg, prog, ctx->_Shader);
       }
+
+      if (ctx->Pipeline.Objects) {
+         struct update_programs_in_pipeline_params params = {
+            .ctx = ctx,
+            .shProg = shProg
+         };
+         _mesa_HashWalk(ctx->Pipeline.Objects, update_programs_in_pipeline,
+                        &params);
+      }
    }
 
    /* Capture .shader_test files. */
@@ -2250,7 +2295,12 @@ _mesa_GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype,
 void GLAPIENTRY
 _mesa_ReleaseShaderCompiler(void)
 {
-   _mesa_destroy_shader_compiler_caches();
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (ctx->shader_builtin_ref) {
+      _mesa_glsl_builtin_functions_decref();
+      ctx->shader_builtin_ref = false;
+   }
 }
 
 
@@ -2530,6 +2580,7 @@ _mesa_use_program(struct gl_context *ctx, gl_shader_stage stage,
                                      &shTarget->ReferencedPrograms[stage],
                                      shProg);
       _mesa_reference_program(ctx, target, prog);
+      _mesa_update_allow_draw_out_of_order(ctx);
       if (stage == MESA_SHADER_VERTEX)
          _mesa_update_vertex_processing_mode(ctx);
       return;
@@ -2643,6 +2694,7 @@ void GLAPIENTRY
 _mesa_PatchParameteri_no_error(GLenum pname, GLint value)
 {
    GET_CURRENT_CONTEXT(ctx);
+   FLUSH_VERTICES(ctx, 0);
    ctx->TessCtrlProgram.patch_vertices = value;
 }
 
@@ -2667,6 +2719,7 @@ _mesa_PatchParameteri(GLenum pname, GLint value)
       return;
    }
 
+   FLUSH_VERTICES(ctx, 0);
    ctx->TessCtrlProgram.patch_vertices = value;
 }
 
@@ -3123,6 +3176,533 @@ _mesa_GetProgramStageiv(GLuint program, GLenum shadertype,
    }
 }
 
+/* This is simple list entry that will be used to hold a list of string
+ * tokens of a parsed shader include path.
+ */
+struct sh_incl_path_entry
+{
+   struct sh_incl_path_entry *next;
+   struct sh_incl_path_entry *prev;
+
+   char *path;
+};
+
+/* Nodes of the shader include tree */
+struct sh_incl_path_ht_entry
+{
+   struct hash_table *path;
+   char *shader_source;
+};
+
+struct shader_includes {
+   /* Array to hold include paths given to glCompileShaderIncludeARB() */
+   struct sh_incl_path_entry **include_paths;
+   size_t num_include_paths;
+   size_t relative_path_cursor;
+
+   /* Root hash table holding the shader include tree */
+   struct hash_table *shader_include_tree;
+};
+
+void
+_mesa_init_shader_includes(struct gl_shared_state *shared)
+{
+   shared->ShaderIncludes = calloc(1, sizeof(struct shader_includes));
+   shared->ShaderIncludes->shader_include_tree =
+      _mesa_hash_table_create(NULL, _mesa_hash_string,
+                              _mesa_key_string_equal);
+}
+
+size_t
+_mesa_get_shader_include_cursor(struct gl_shared_state *shared)
+{
+   return shared->ShaderIncludes->relative_path_cursor;
+}
+
+void
+_mesa_set_shader_include_cursor(struct gl_shared_state *shared, size_t cursor)
+{
+   shared->ShaderIncludes->relative_path_cursor = cursor;
+}
+
+static void
+destroy_shader_include(struct hash_entry *entry)
+{
+   struct sh_incl_path_ht_entry *sh_incl_ht_entry =
+      (struct sh_incl_path_ht_entry *) entry->data;
+
+   _mesa_hash_table_destroy(sh_incl_ht_entry->path, destroy_shader_include);
+   free(sh_incl_ht_entry->shader_source);
+   free(sh_incl_ht_entry);
+}
+
+void
+_mesa_destroy_shader_includes(struct gl_shared_state *shared)
+{
+   _mesa_hash_table_destroy(shared->ShaderIncludes->shader_include_tree,
+                            destroy_shader_include);
+   free(shared->ShaderIncludes);
+}
+
+static bool
+valid_path_format(const char *str, bool relative_path)
+{
+   int i = 0;
+
+   if (!str[i] || (!relative_path && str[i] != '/'))
+      return false;
+
+   i++;
+
+   while (str[i]) {
+      const char c = str[i++];
+      if (('A' <= c && c <= 'Z') ||
+          ('a' <= c && c <= 'z') ||
+          ('0' <= c && c <= '9'))
+         continue;
+
+      if (c == '/') {
+         if (str[i - 2] == '/')
+            return false;
+
+         continue;
+      }
+
+      if (strchr("^. _+*%[](){}|&~=!:;,?-", c) == NULL)
+         return false;
+  }
+
+  if (str[i - 1] == '/')
+     return false;
+
+  return true;
+}
+
+
+static bool
+validate_and_tokenise_sh_incl(struct gl_context *ctx,
+                              void *mem_ctx,
+                              struct sh_incl_path_entry **path_list,
+                              char *full_path, bool error_check)
+{
+   bool relative_path = ctx->Shared->ShaderIncludes->num_include_paths;
+
+   if (!valid_path_format(full_path, relative_path)) {
+      if (error_check) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glNamedStringARB(invalid name %s)", full_path);
+      }
+      return false;
+   }
+
+   char *save_ptr = NULL;
+   char *path_str = strtok_r(full_path, "/", &save_ptr);
+
+   *path_list = rzalloc(mem_ctx, struct sh_incl_path_entry);
+
+   make_empty_list(*path_list);
+
+   while (path_str != NULL) {
+      if (strlen(path_str) == 0) {
+         if (error_check) {
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glNamedStringARB(invalid name %s)", full_path);
+         }
+
+         return false;
+      }
+
+      if (strcmp(path_str, ".") == 0) {
+         /* Do nothing */
+      } else if (strcmp(path_str, "..") == 0) {
+         struct sh_incl_path_entry *last = last_elem(*path_list);
+         remove_from_list(last);
+      } else {
+         struct sh_incl_path_entry *path =
+            rzalloc(mem_ctx, struct sh_incl_path_entry);
+
+         path->path = strdup(path_str);
+         insert_at_tail(*path_list, path);
+      }
+
+      path_str = strtok_r(NULL, "/", &save_ptr);
+   }
+
+   return true;
+}
+
+static struct sh_incl_path_ht_entry *
+lookup_shader_include(struct gl_context *ctx, char *path,
+                      bool error_check)
+{
+   void *mem_ctx = ralloc_context(NULL);
+   struct sh_incl_path_entry *path_list;
+
+   if (!validate_and_tokenise_sh_incl(ctx, mem_ctx, &path_list, path,
+                                      error_check)) {
+      ralloc_free(mem_ctx);
+      return NULL;
+   }
+
+   struct sh_incl_path_ht_entry *sh_incl_ht_entry = NULL;
+   struct hash_table *path_ht =
+      ctx->Shared->ShaderIncludes->shader_include_tree;
+
+   size_t count = ctx->Shared->ShaderIncludes->num_include_paths;
+   bool relative_path = path[0] != '/';
+
+   size_t i = ctx->Shared->ShaderIncludes->relative_path_cursor;
+   bool use_cursor = ctx->Shared->ShaderIncludes->relative_path_cursor;
+
+   do {
+      struct sh_incl_path_entry *entry;
+
+      if (relative_path) {
+next_relative_path:
+         {
+            struct sh_incl_path_entry *rel_path_list =
+               ctx->Shared->ShaderIncludes->include_paths[i];
+            foreach(entry, rel_path_list) {
+               struct hash_entry *ht_entry =
+                  _mesa_hash_table_search(path_ht, entry->path);
+
+               if (!ht_entry) {
+                  /* Reset search path and skip to the next include path */
+                  path_ht = ctx->Shared->ShaderIncludes->shader_include_tree;
+                  sh_incl_ht_entry = NULL;
+                  if (use_cursor) {
+                     i = 0;
+                     use_cursor = false;
+
+                     goto next_relative_path;
+                  }
+                  i++;
+                  if (i < count)
+                     goto next_relative_path;
+                  else
+                     break;
+               } else {
+                  sh_incl_ht_entry =
+                    (struct sh_incl_path_ht_entry *) ht_entry->data;
+               }
+
+               path_ht = sh_incl_ht_entry->path;
+            }
+         }
+      }
+
+      foreach(entry, path_list) {
+         struct hash_entry *ht_entry =
+            _mesa_hash_table_search(path_ht, entry->path);
+
+         if (!ht_entry) {
+            /* Reset search path and skip to the next include path */
+            path_ht = ctx->Shared->ShaderIncludes->shader_include_tree;
+            sh_incl_ht_entry = NULL;
+            if (use_cursor) {
+               i = 0;
+               use_cursor = false;
+
+               break;
+            }
+            i++;
+            break;
+         } else {
+
+            sh_incl_ht_entry =
+               (struct sh_incl_path_ht_entry *) ht_entry->data;
+         }
+
+         path_ht = sh_incl_ht_entry->path;
+      }
+
+      if (i < count &&
+          (sh_incl_ht_entry == NULL || !sh_incl_ht_entry->shader_source))
+         continue;
+
+      /* If we get here then we have found a matching path or exahusted our
+       * relative search paths.
+       */
+      ctx->Shared->ShaderIncludes->relative_path_cursor = i;
+      break;
+   } while (i < count);
+
+   ralloc_free(mem_ctx);
+
+   return sh_incl_ht_entry;
+}
+
+const char *
+_mesa_lookup_shader_include(struct gl_context *ctx, char *path,
+                            bool error_check)
+{
+   struct sh_incl_path_ht_entry *shader_include =
+      lookup_shader_include(ctx, path, error_check);
+
+   return shader_include ? shader_include->shader_source : NULL;
+}
+
+static char *
+copy_string(struct gl_context *ctx, const char *str, int str_len,
+            const char *caller)
+{
+   if (!str) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(NULL string)", caller);
+      return NULL;
+   }
+
+   char *cp;
+   if (str_len == -1)
+      cp = strdup(str);
+   else {
+      cp = calloc(sizeof(char), str_len + 1);
+      memcpy(cp, str, str_len);
+   }
+
+   return cp;
+}
+
+GLvoid GLAPIENTRY
+_mesa_NamedStringARB(GLenum type, GLint namelen, const GLchar *name,
+                     GLint stringlen, const GLchar *string)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   const char *caller = "glNamedStringARB";
+
+   if (type != GL_SHADER_INCLUDE_ARB) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid type)", caller);
+      return;
+   }
+
+   char *name_cp = copy_string(ctx, name, namelen, caller);
+   char *string_cp = copy_string(ctx, string, stringlen, caller);
+   if (!name_cp || !string_cp) {
+      free(string_cp);
+      free(name_cp);
+      return;
+   }
+
+   void *mem_ctx = ralloc_context(NULL);
+   struct sh_incl_path_entry *path_list;
+
+   if (!validate_and_tokenise_sh_incl(ctx, mem_ctx, &path_list, name_cp,
+                                      true)) {
+      free(string_cp);
+      free(name_cp);
+      ralloc_free(mem_ctx);
+      return;
+   }
+
+   mtx_lock(&ctx->Shared->ShaderIncludeMutex);
+
+   struct hash_table *path_ht =
+      ctx->Shared->ShaderIncludes->shader_include_tree;
+
+   struct sh_incl_path_entry *entry;
+   foreach(entry, path_list) {
+      struct hash_entry *ht_entry =
+         _mesa_hash_table_search(path_ht, entry->path);
+
+      struct sh_incl_path_ht_entry *sh_incl_ht_entry;
+      if (!ht_entry) {
+         sh_incl_ht_entry = calloc(1, sizeof(struct sh_incl_path_ht_entry));
+         sh_incl_ht_entry->path =
+            _mesa_hash_table_create(NULL, _mesa_hash_string,
+                                    _mesa_key_string_equal);
+         _mesa_hash_table_insert(path_ht, entry->path, sh_incl_ht_entry);
+      } else {
+         sh_incl_ht_entry = (struct sh_incl_path_ht_entry *) ht_entry->data;
+      }
+
+      path_ht = sh_incl_ht_entry->path;
+
+      if (last_elem(path_list) == entry) {
+         free(sh_incl_ht_entry->shader_source);
+         sh_incl_ht_entry->shader_source = string_cp;
+      }
+   }
+
+   mtx_unlock(&ctx->Shared->ShaderIncludeMutex);
+
+   free(name_cp);
+   ralloc_free(mem_ctx);
+}
+
+GLvoid GLAPIENTRY
+_mesa_DeleteNamedStringARB(GLint namelen, const GLchar *name)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   const char *caller = "glDeleteNamedStringARB";
+
+   char *name_cp = copy_string(ctx, name, namelen, caller);
+   if (!name_cp)
+      return;
+
+   struct sh_incl_path_ht_entry *shader_include =
+      lookup_shader_include(ctx, name_cp, true);
+
+   if (!shader_include) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(no string associated with path %s)", caller, name_cp);
+      free(name_cp);
+      return;
+   }
+
+   mtx_lock(&ctx->Shared->ShaderIncludeMutex);
+
+   free(shader_include->shader_source);
+   shader_include->shader_source = NULL;
+
+   mtx_unlock(&ctx->Shared->ShaderIncludeMutex);
+
+   free(name_cp);
+}
+
+GLvoid GLAPIENTRY
+_mesa_CompileShaderIncludeARB(GLuint shader, GLsizei count,
+                              const GLchar* const *path, const GLint *length)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   const char *caller = "glCompileShaderIncludeARB";
+
+   if (count > 0 && path == NULL) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(count > 0 && path == NULL)",
+                  caller);
+      return;
+   }
+
+   void *mem_ctx = ralloc_context(NULL);
+
+   mtx_lock(&ctx->Shared->ShaderIncludeMutex);
+
+   ctx->Shared->ShaderIncludes->include_paths =
+      ralloc_array_size(mem_ctx, sizeof(struct sh_incl_path_entry *), count);
+
+   for (size_t i = 0; i < count; i++) {
+      char *path_cp = copy_string(ctx, path[i], length ? length[i] : -1,
+                                  caller);
+      if (!path_cp) {
+         goto exit;
+      }
+
+      struct sh_incl_path_entry *path_list;
+
+      if (!validate_and_tokenise_sh_incl(ctx, mem_ctx, &path_list, path_cp,
+                                         true)) {
+         free(path_cp);
+         goto exit;
+      }
+
+      ctx->Shared->ShaderIncludes->include_paths[i] = path_list;
+
+      free(path_cp);
+   }
+
+   /* We must set this *after* all calls to validate_and_tokenise_sh_incl()
+    * are done as we use this to decide if we need to check the start of the
+    * path for a '/'
+    */
+   ctx->Shared->ShaderIncludes->num_include_paths = count;
+
+   struct gl_shader *sh = _mesa_lookup_shader(ctx, shader);
+   if (!sh) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(shader)", caller);
+      goto exit;
+   }
+
+   _mesa_compile_shader(ctx, sh);
+
+exit:
+   ctx->Shared->ShaderIncludes->num_include_paths = 0;
+   ctx->Shared->ShaderIncludes->relative_path_cursor = 0;
+   ctx->Shared->ShaderIncludes->include_paths = NULL;
+
+   mtx_unlock(&ctx->Shared->ShaderIncludeMutex);
+
+   ralloc_free(mem_ctx);
+}
+
+GLboolean GLAPIENTRY
+_mesa_IsNamedStringARB(GLint namelen, const GLchar *name)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (!name)
+      return false;
+
+   char *name_cp = copy_string(ctx, name, namelen, "");
+
+   const char *source = _mesa_lookup_shader_include(ctx, name_cp, false);
+   free(name_cp);
+
+   if (!source)
+      return false;
+
+   return true;
+}
+
+GLvoid GLAPIENTRY
+_mesa_GetNamedStringARB(GLint namelen, const GLchar *name, GLsizei bufSize,
+                        GLint *stringlen, GLchar *string)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   const char *caller = "glGetNamedStringARB";
+
+   char *name_cp = copy_string(ctx, name, namelen, caller);
+   if (!name_cp)
+      return;
+
+   const char *source = _mesa_lookup_shader_include(ctx, name_cp, true);
+   if (!source) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(no string associated with path %s)", caller, name_cp);
+      free(name_cp);
+      return;
+   }
+
+   size_t size = MIN2(strlen(source), bufSize - 1);
+   memcpy(string, source, size);
+   string[size] = '\0';
+
+   *stringlen = size;
+
+   free(name_cp);
+}
+
+GLvoid GLAPIENTRY
+_mesa_GetNamedStringivARB(GLint namelen, const GLchar *name,
+                          GLenum pname, GLint *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   const char *caller = "glGetNamedStringivARB";
+
+   char *name_cp = copy_string(ctx, name, namelen, caller);
+   if (!name_cp)
+      return;
+
+   const char *source = _mesa_lookup_shader_include(ctx, name_cp, true);
+   if (!source) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(no string associated with path %s)", caller, name_cp);
+      free(name_cp);
+      return;
+   }
+
+   switch (pname) {
+   case GL_NAMED_STRING_LENGTH_ARB:
+      *params = strlen(source) + 1;
+      break;
+   case GL_NAMED_STRING_TYPE_ARB:
+      *params = GL_SHADER_INCLUDE_ARB;
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname)", caller);
+      break;
+   }
+
+   free(name_cp);
+}
+
 static int
 find_compat_subroutine(struct gl_program *p, const struct glsl_type *type)
 {