[Bf-blender-cvs] [a504058dee7] master: Metal: Optimize GLSL to MSL translation. Improve cached compilation

Jason Fielder noreply at git.blender.org
Tue Jan 31 11:10:59 CET 2023


Commit: a504058dee7684758456aaac24342ae24df3f3a1
Author: Jason Fielder
Date:   Tue Jan 31 11:06:16 2023 +0100
Branches: master
https://developer.blender.org/rBa504058dee7684758456aaac24342ae24df3f3a1

Metal: Optimize GLSL to MSL translation. Improve cached compilation

Reduces the GLSL to MSL translation stage of shader compilation from 120 ms to 5 ms for complex EEVEE materials. This manifests in faster overall compilations, and faster cache hits for secondary compilations, as the MSL variant is needed as a key.

Startup time is also improved for both first-run and second-run. Note that this change does not affect shader compilation times within the Metal API.

Also disables shader output to disk

Authored by Apple: Michael Parkin-White

Ref T96261
Depends on D16990

Reviewed By: fclem

Maniphest Tasks: T96261

Differential Revision: https://developer.blender.org/D17033

===================================================================

M	source/blender/gpu/metal/mtl_shader.hh
M	source/blender/gpu/metal/mtl_shader_generator.hh
M	source/blender/gpu/metal/mtl_shader_generator.mm

===================================================================

diff --git a/source/blender/gpu/metal/mtl_shader.hh b/source/blender/gpu/metal/mtl_shader.hh
index 47e492dd69c..59c5a810e5e 100644
--- a/source/blender/gpu/metal/mtl_shader.hh
+++ b/source/blender/gpu/metal/mtl_shader.hh
@@ -36,7 +36,7 @@ class MTLShaderInterface;
 class MTLContext;
 
 /* Debug control. */
-#define MTL_SHADER_DEBUG_EXPORT_SOURCE 1
+#define MTL_SHADER_DEBUG_EXPORT_SOURCE 0
 #define MTL_SHADER_TRANSLATION_DEBUG_OUTPUT 0
 
 /* Separate print used only during development and debugging. */
diff --git a/source/blender/gpu/metal/mtl_shader_generator.hh b/source/blender/gpu/metal/mtl_shader_generator.hh
index 39772baf09b..84a921a852b 100644
--- a/source/blender/gpu/metal/mtl_shader_generator.hh
+++ b/source/blender/gpu/metal/mtl_shader_generator.hh
@@ -382,7 +382,7 @@ class MSLGeneratorInterface {
   /* Transform feedback interface. */
   blender::Vector<MSLVertexOutputAttribute> vertex_output_varyings_tf;
   /* Clip Distances. */
-  blender::Vector<std::string> clip_distances;
+  blender::Vector<char> clip_distances;
   /* Shared Memory Blocks. */
   blender::Vector<MSLSharedMemoryBlock> shared_memory_blocks;
 
diff --git a/source/blender/gpu/metal/mtl_shader_generator.mm b/source/blender/gpu/metal/mtl_shader_generator.mm
index ad0280efa03..d802ee3bd46 100644
--- a/source/blender/gpu/metal/mtl_shader_generator.mm
+++ b/source/blender/gpu/metal/mtl_shader_generator.mm
@@ -177,6 +177,70 @@ static bool is_program_word(const char *chr, int *len)
   return true;
 }
 
+static int backwards_program_word_scan(const char *array_loc, const char *min)
+{
+  const char *start;
+  char last_char = ' ';
+  int numchars = 0;
+  for (start = array_loc - 1; (start >= min) && (*start != '\0'); start--) {
+    char ch = *start;
+    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') ||
+        ch == '_' || ch == '#') {
+      numchars++;
+      last_char = ch;
+    }
+    else {
+      break;
+    }
+  }
+
+  if (numchars > 0) {
+    /* cannot start with numbers, so we need to invalidate the word. */
+    if ((last_char >= '0' && last_char <= '9')) {
+      numchars = 0;
+    }
+  }
+  return numchars;
+}
+
+/* Extract clipping distance usage indices, and replace syntax with metal-compatible.
+ * We need to replace syntax gl_ClipDistance[N] with gl_ClipDistance_N such that it is compatible
+ * with the Metal shaders Vertex shader output struct. */
+static void extract_and_replace_clipping_distances(std::string &vertex_source,
+                                                   MSLGeneratorInterface &msl_iface)
+{
+  char *current_str_begin = &*vertex_source.begin();
+  char *current_str_end = &*vertex_source.end();
+
+  for (char *c = current_str_begin + 2; c < current_str_end - 18; c++) {
+    char *base_search = strstr(c, "gl_ClipDistance[");
+    if (base_search == nullptr) {
+      /* No clip distances found. */
+      return;
+    }
+    c = base_search + 16;
+
+    /* Ensure closing brace. */
+    if (*(c + 1) != ']') {
+      continue;
+    }
+
+    /* Extract ID betwen zero and 9. */
+    if ((*c >= '0') && (*c <= '9')) {
+      char clip_distance_id = ((*c) - '0');
+      auto found = std::find(
+          msl_iface.clip_distances.begin(), msl_iface.clip_distances.end(), clip_distance_id);
+      if (found == msl_iface.clip_distances.end()) {
+        msl_iface.clip_distances.append(clip_distance_id);
+      }
+
+      /* Replace syntax (array brace removal, and replacement with underscore). */
+      *(base_search + 15) = '_';
+      *(base_search + 17) = ' ';
+    }
+  }
+}
+
 /**
  * Replace function parameter patterns containing:
  * `out vec3 somevar` with `THD vec3&somevar`.
@@ -189,8 +253,12 @@ static void replace_outvars(std::string &str)
   char *current_str_end = &*str.end();
 
   for (char *c = current_str_begin + 2; c < current_str_end - 6; c++) {
-    char *start = c;
-    if (strncmp(c, "out ", 4) == 0) {
+    char *start = strstr(c, "out");
+    if (start == nullptr) {
+      return;
+    }
+    else {
+      c = start;
       if (strncmp(c - 2, "in", 2) == 0) {
         start = c - 2;
       }
@@ -241,19 +309,84 @@ static void replace_outvars(std::string &str)
   }
 }
 
+static void replace_matrix_constructors(std::string &str)
+{
+
+  /* Replace matrix constructors with GLSL-compatible constructors for Metal.
+   * Base matrix constructors e.g. mat3x3 do not have as many overload variants as GLSL.
+   * To add compatibility, we declare custom constuctors e.g. MAT3x3 in mtl_shader_defines.msl.
+   * If the GLSL syntax matches, we map mat3x3(..) -> MAT3x3(..) and implement a custom
+   * constructor. This supports both mat3(..) and mat3x3(..) style sytax.*/
+  char *current_str_begin = &*str.begin();
+  char *current_str_end = &*str.end();
+
+  for (char *c = current_str_begin; c < current_str_end - 10; c++) {
+    char *base_scan = strstr(c, "mat");
+    if (base_scan == nullptr) {
+      break;
+    }
+    /* Track end of constructor. */
+    char *constructor_end = nullptr;
+
+    /* check if next character is matrix dim. */
+    c = base_scan + 3;
+    if (!(*c == '2' || *c == '3' || *c == '4')) {
+      /* Not constructor, skip. */
+      continue;
+    }
+
+    /* Possible multiple dimensional matrix constructor. Verify if next char is a dim*/
+    c++;
+    if (*c == 'x') {
+      c++;
+      if (*c == '2' || *c == '3' || *c == '4') {
+        c++;
+      }
+      else {
+        /* Not matrix constructor, continue. */
+        continue;
+      }
+    }
+
+    /* Check for constructor opening brace. */
+    if (*c == '(') {
+      constructor_end = c;
+    }
+    else {
+      /* Not matrix constructor, continue. */
+      continue;
+    }
+
+    /* If is constructor, replace with MATN(..) syntax. */
+    if (constructor_end != nullptr) {
+      strncpy(base_scan, "MAT", 3);
+      continue;
+    }
+  }
+}
+
 static void replace_array_initializers_func(std::string &str)
 {
   char *current_str_begin = &*str.begin();
   char *current_str_end = &*str.end();
 
   for (char *c = current_str_begin; c < current_str_end - 6; c++) {
-    char *base_scan = c;
+
     int typelen = 0;
 
-    if (is_program_word(c, &typelen) && *(c + typelen) == '[') {
+    /* first find next array brace, then work backwards to find start of program word to check if
+     * valid array syntax. */
+    char *array_scan = strchr(c, '[');
+    if (array_scan == nullptr) {
+      return;
+    }
+    typelen = backwards_program_word_scan(array_scan - 1, current_str_begin);
+    char *base_type_name = array_scan - 1 - typelen;
+
+    if (typelen > 0) {
+      // if (is_program_word(c, &typelen) && *(c + typelen) == '[') {
 
-      char *array_len_start = c + typelen + 1;
-      c = array_len_start;
+      c = array_scan;
       char *closing_square_brace = strchr(c, ']');
       if (closing_square_brace != nullptr) {
         c = closing_square_brace;
@@ -267,7 +400,7 @@ static void replace_array_initializers_func(std::string &str)
             /* Resolve to MSL-compatible array formatting. */
             *first_bracket = '{';
             *closing_bracket = '}';
-            for (char *clear = base_scan; clear <= closing_square_brace; clear++) {
+            for (char *clear = base_type_name; clear <= closing_square_brace; clear++) {
               *clear = ' ';
             }
           }
@@ -277,6 +410,11 @@ static void replace_array_initializers_func(std::string &str)
         return;
       }
     }
+    else {
+      /* Not an array initializer, continue scanning. */
+      c = array_scan + 1;
+      continue;
+    }
   }
 }
 
@@ -329,7 +467,7 @@ static void extract_global_scope_constants(std::string &str, std::stringstream &
     /* Check For global const declarations */
     if (nested_bracket_depth == 0 && strncmp(c, "const ", 6) == 0 &&
         strncmp(c, "const constant ", 15) != 0) {
-      char *c_expr_end = strstr(c, ";");
+      char *c_expr_end = strchr(c, ';');
       if (c_expr_end != nullptr && balanced_braces(c, c_expr_end)) {
         MTL_LOG_INFO(
             "[PERFORMANCE WARNING] Global scope constant expression found - These get allocated "
@@ -347,7 +485,7 @@ static void extract_global_scope_constants(std::string &str, std::stringstream &
 #endif
 
 static bool extract_ssbo_pragma_info(const MTLShader *shader,
-                                     const MSLGeneratorInterface &,
+                                     const MSLGeneratorInterface &msl_iface,
                                      const std::string &in_vertex_src,
                                      MTLPrimitiveType &out_prim_tye,
                                      uint32_t &out_num_output_verts)
@@ -767,25 +905,12 @@ bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info)
         vertex_fetch_ssbo_num_output_verts);
   }
 
-  /*** Regex Commands ***/
-  /* Source cleanup and syntax replacement. */
-  static std::regex remove_excess_newlines("\\n+");
-  static std::regex replace_matrix_construct("mat([234](x[234])?)\\s*\\(");
-
-  /* Special condition - mat3 and array constructor replacement.
-   * Also replace excessive new lines to ensure cases are not missed.
-   * NOTE(Metal): May be able to skip excess-newline removal. */
-  shd_builder_->glsl_vertex_source_ = std::regex_replace(
-      shd_builder_->glsl_vertex_source_, remove_excess_newlines, "\n");
-  shd_builder_->glsl_vertex_source_ = std::regex_replace(
-      shd_builder_->glsl_vertex_source_, replace_matrix_construct, "MAT$1(");
+  /* Special condition - mat3 and array constructor replacement. */
+  replace_matrix_constructors(shd_builder_->glsl_vertex_source_);
   replace_array_initializers_func(shd_builder_->glsl_vertex_source_);
 
   if (!msl_iface.uses_transform_feedback) {
-    shd_builder_->glsl_fragment_source_ = std::regex_replace(
-        shd_builder_->glsl_fragment_source_, remove_excess_newlines, "\n");
-    shd_builder_->glsl_fragment_source_ = std::regex_replace(
-        shd_builder_->glsl_fragment_source_, replace_matrix_construct, "MAT$1(");
+    replace_matrix_constructors(shd_builder_->glsl_fragment_source_);
     replace_array_initializers_func(shd_builder_->glsl_fragment_source_);
   }
 
@@ -856,24 +981,7 @@ bool MTLShader:

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list