Browse Source

shaderpipeline: More shader input work, reflect structs & arrays

rdb 5 years ago
parent
commit
e94eb2cdaa

+ 148 - 10
panda/src/gobj/shader.cxx

@@ -2434,13 +2434,23 @@ link() {
           remap[var.get_location()] = it->second->get_location();
         }
       } else if (var.has_location()) {
-        if (used_locations.get_bit(var.get_location())) {
-          // This location is already used.
+        // Check whether the locations occupied by this variable are already in
+        // use by another stage.
+        int num_locations = var.type->get_num_parameter_locations();
+        if (used_locations.has_any_of(var.get_location(), num_locations)) {
+          // This location is already used.  Find another free location.
           int location = used_locations.get_lowest_off_bit();
-          used_locations.set_bit(location);
+          while (num_locations > 1 && used_locations.has_any_of(location, num_locations)) {
+            // This free space isn't big enough to fit all the needed locations.
+            int next_bit = used_locations.get_next_higher_different_bit(location);
+            nassertr(next_bit > location, false);
+            location = used_locations.get_next_higher_different_bit(next_bit);
+            nassertr(location > next_bit, false);
+          }
+          used_locations.set_range(location, num_locations);
           remap[var.get_location()] = location;
         } else {
-          used_locations.set_bit(var.get_location());
+          used_locations.set_range(var.get_location(), num_locations);
         }
       }
     }
@@ -2580,15 +2590,143 @@ bind_parameter(const InternalName *name, const ::ShaderType *type, int location)
 
   // Check if it has a p3d_ prefix - if so, assign special meaning.
   if (pieces[0] == "p3d" && _language == SL_GLSL) {
-    if (pieces[1] == "ModelViewProjectionMatrix") {
+    // Check for matrix inputs.
+    bool transpose = false;
+    bool inverse = false;
+    string matrix_name = pieces[1];
+
+    // Check for and chop off any "Transpose" or "Inverse" suffix.
+    if (matrix_name.size() > 6 + 9 &&
+        matrix_name.compare(matrix_name.size() - 9, 9, "Transpose") == 0) {
+      transpose = true;
+      matrix_name = matrix_name.substr(0, matrix_name.size() - 9);
+    }
+    if (matrix_name.size() > 6 + 7 &&
+        matrix_name.compare(matrix_name.size() - 7, 7, "Inverse") == 0) {
+      inverse = true;
+      matrix_name = matrix_name.substr(0, matrix_name.size() - 7);
+    }
+
+    // Now if the suffix that is left over is "Matrix", we know that it is
+    // supposed to be a matrix input.
+    if (matrix_name.size() > 6 &&
+        matrix_name.compare(matrix_name.size() - 6, 6, "Matrix") == 0) {
+
       ShaderMatSpec bind;
       bind._id = arg_id;
       bind._func = SMF_compose;
-      bind._piece = SMP_whole;
       bind._arg[0] = nullptr;
       bind._arg[1] = nullptr;
-      bind._part[0] = SMO_model_to_apiview;
-      bind._part[1] = SMO_apiview_to_apiclip;
+
+      const ::ShaderType::Matrix *matrix = type->as_matrix();
+      if (matrix != nullptr) {
+        if (matrix->get_num_rows() >= 4) {
+          bind._piece = transpose ? SMP_transpose : SMP_whole;
+        } else {
+          bind._piece = transpose ? SMP_upper3x3 : SMP_transpose3x3;
+        }
+      } else {
+        shader_cat.error()
+          << "Matrix input " << name_str << " should be mat3 or mat4\n";
+        return false;
+      }
+
+      if (matrix_name == "ModelViewProjectionMatrix") {
+        if (inverse) {
+          bind._part[0] = SMO_apiclip_to_apiview;
+          bind._part[1] = SMO_apiview_to_model;
+        } else {
+          bind._part[0] = SMO_model_to_apiview;
+          bind._part[1] = SMO_apiview_to_apiclip;
+        }
+
+      } else if (matrix_name == "ModelViewMatrix") {
+        bind._func = SMF_first;
+        bind._part[0] = inverse ? SMO_apiview_to_model
+                                : SMO_model_to_apiview;
+        bind._part[1] = SMO_identity;
+
+      } else if (matrix_name == "ProjectionMatrix") {
+        bind._func = SMF_first;
+        bind._part[0] = inverse ? SMO_apiclip_to_apiview
+                                : SMO_apiview_to_apiclip;
+        bind._part[1] = SMO_identity;
+
+      } else if (matrix_name == "NormalMatrix") {
+        // This is really the upper 3x3 of the ModelViewMatrixInverseTranspose.
+        bind._func = SMF_first;
+        bind._part[0] = inverse ? SMO_model_to_apiview
+                                : SMO_apiview_to_model;
+        bind._part[1] = SMO_identity;
+
+        if (matrix->get_num_rows() != 3 || matrix->get_num_columns() != 3) {
+          shader_cat.warning()
+            << "p3d_NormalMatrix input should be mat3!\n";
+          return false;
+        }
+
+      } else if (matrix_name == "ModelMatrix") {
+        if (inverse) {
+          bind._part[0] = SMO_world_to_view;
+          bind._part[1] = SMO_view_to_model;
+        } else {
+          bind._part[0] = SMO_model_to_view;
+          bind._part[1] = SMO_view_to_world;
+        }
+
+      } else if (matrix_name == "ViewMatrix") {
+        if (inverse) {
+          bind._part[0] = SMO_apiview_to_view;
+          bind._part[1] = SMO_view_to_world;
+        } else {
+          bind._part[0] = SMO_world_to_view;
+          bind._part[1] = SMO_view_to_apiview;
+        }
+
+      } else if (matrix_name == "ViewProjectionMatrix") {
+        if (inverse) {
+          bind._part[0] = SMO_apiclip_to_view;
+          bind._part[1] = SMO_view_to_world;
+        } else {
+          bind._part[0] = SMO_world_to_view;
+          bind._part[1] = SMO_view_to_apiclip;
+        }
+
+      } else if (matrix_name == "TextureMatrix") {
+        // We may support 2-D texmats later, but let's make sure that people
+        // don't think they can just use a mat3 to get the 2-D version.
+        if (matrix->get_num_rows() != 4 || matrix->get_num_columns() != 4) {
+          shader_cat.error() << "p3d_TextureMatrix should be mat4[]!\n";
+          return false;
+        }
+
+        bind._func = SMF_first;
+        bind._part[0] = inverse ? SMO_inv_texmat_i
+                                : SMO_texmat_i;
+        bind._part[1] = SMO_identity;
+
+        // Add it once for each index.
+//        for (bind._index = 0; bind._index < param_size; ++bind._index) {
+//          // It was discovered in #846, that GLSL 4.10 and lower don't seem to
+//          // guarantee that matrices occupy successive locations, and on macOS
+//          // they indeed occupy four locations per element.
+//          // As a big fat hack, we multiply by four on macOS, because this is
+//          // hard to fix on the 1.10 branch.  We'll have a proper fix on the
+//          // master branch.
+//#ifdef __APPLE__
+//          bind._id._seqno = p + bind._index * 4;
+//#else
+//          bind._id._seqno = p + bind._index;
+//#endif
+//          cp_add_mat_spec(bind);
+//        }
+        return true;
+
+      } else {
+        shader_cat.error() << "Unrecognized uniform matrix name '" << matrix_name << "'!\n";
+        return false;
+      }
+
       cp_add_mat_spec(bind);
       return true;
     }
@@ -2599,8 +2737,8 @@ bind_parameter(const InternalName *name, const ::ShaderType *type, int location)
       bind._name = 0;
       bind._desired_type = Texture::TT_2d_texture;
 
-      string tail;
-      bind._stage = string_to_int(name_str.substr(7), tail);
+      std::string tail;
+      bind._stage = string_to_int(pieces[1].substr(7), tail);
       if (!tail.empty()) {
         shader_cat.error()
           << "Error parsing shader input name: unexpected '"

+ 47 - 4
panda/src/gobj/shaderType.cxx

@@ -144,7 +144,14 @@ compare_to_impl(const ShaderType &other) const {
  */
 void ShaderType::Struct::
 output(std::ostream &out) const {
-  out << "struct";
+  out << "struct { ";
+  for (const Member &member : _members) {
+    if (member.type != nullptr) {
+      out << *member.type << ' ';
+    }
+    out << *member.name << "; ";
+  }
+  out << '}';
 }
 
 /**
@@ -154,9 +161,36 @@ output(std::ostream &out) const {
 int ShaderType::Struct::
 compare_to_impl(const ShaderType &other) const {
   const Struct &other_struct = (const Struct &)other;
-  //FIXME
-  return (_members.size() > other_struct._members.size())
-       - (_members.size() < other_struct._members.size());
+  if (_members.size() != other_struct._members.size()) {
+    return (_members.size() > other_struct._members.size())
+         - (_members.size() < other_struct._members.size());
+  }
+
+  for (size_t i = 0; i < _members.size(); ++i) {
+    if (_members[i].type != other_struct._members[i].type) {
+      return (_members[i].type > other_struct._members[i].type)
+           - (_members[i].type < other_struct._members[i].type);
+    }
+    if (_members[i].name != other_struct._members[i].name) {
+      return (_members[i].name > other_struct._members[i].name)
+           - (_members[i].name < other_struct._members[i].name);
+    }
+  }
+
+  return 0;
+}
+
+/**
+ * Returns the number of uniform locations taken up by uniform variables having
+ * this type.
+ */
+int ShaderType::Struct::
+get_num_parameter_locations() const {
+  int total = 0;
+  for (const Member &member : _members) {
+    total += member.type->get_num_parameter_locations();
+  }
+  return total;
 }
 
 /**
@@ -181,6 +215,15 @@ compare_to_impl(const ShaderType &other) const {
        - (_num_elements < other_array._num_elements);
 }
 
+/**
+ * Returns the number of uniform locations taken up by uniform variables having
+ * this type.
+ */
+int ShaderType::Array::
+get_num_parameter_locations() const {
+  return _element_type->get_num_parameter_locations() * _num_elements;
+}
+
 /**
  *
  */

+ 6 - 0
panda/src/gobj/shaderType.h

@@ -34,6 +34,8 @@ public:
 
   virtual void output(std::ostream &out) const=0;
 
+  virtual int get_num_parameter_locations() const { return 1; }
+
 private:
   typedef pset<const ShaderType *, indirect_compare_to<const ShaderType *> > Registry;
   static Registry *_registered_types;
@@ -202,6 +204,8 @@ public:
   virtual void output(std::ostream &out) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
 
+  virtual int get_num_parameter_locations() const override;
+
   const Struct *as_struct() const override { return this; }
 
 PUBLISHED:
@@ -242,6 +246,8 @@ public:
   virtual void output(std::ostream &out) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
 
+  virtual int get_num_parameter_locations() const override;
+
   const Array *as_array() const override { return this; }
 
 PUBLISHED:

+ 83 - 7
panda/src/shaderpipeline/shaderModuleSpirV.cxx

@@ -236,7 +236,11 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
     break;
 
   case SpvOpName:
-    defs[args[0]]._name = std::string((const char *)&args[1]);
+    defs[args[0]].set_name((const char *)&args[1]);
+    break;
+
+  case SpvOpMemberName:
+    defs[args[0]].set_member_name(args[1], (const char *)&args[2]);
     break;
 
   case SpvOpTypeVoid:
@@ -374,6 +378,30 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
     }
     break;
 
+  case SpvOpTypeArray:
+    if (defs[args[1]]._type != nullptr) {
+      defs[args[0]].set_type(ShaderType::register_type(
+        ShaderType::Array(defs[args[1]]._type, defs[args[2]]._constant)));
+    }
+    break;
+
+  case SpvOpTypeStruct:
+    {
+      ShaderType::Struct type;
+      for (size_t i = 0; i < nargs - 1; ++i) {
+        type.add_member(
+          defs[args[i + 1]]._type,
+          InternalName::make(defs[args[0]]._member_names[i])
+        );
+      }
+      defs[args[0]].set_type(ShaderType::register_type(std::move(type)));
+    }
+    break;
+
+  case SpvOpConstant:
+    defs[args[1]].set_constant(defs[args[0]]._type, args + 2, nargs - 2);
+    break;
+
   case SpvOpVariable:
     {
       const Definition &ptr = defs[args[0]];
@@ -434,7 +462,7 @@ assign_locations(Definitions &defs) {
         output_locations.set_bit(def._location);
       }
       else if (def._storage_class == SpvStorageClassUniformConstant) {
-        uniform_locations.set_bit(def._location);
+        uniform_locations.set_range(def._location, def._type ? def._type->get_num_parameter_locations() : 1);
       }
     }
   }
@@ -484,7 +512,8 @@ assign_locations(Definitions &defs) {
       int location;
       if (def._storage_class == SpvStorageClassInput) {
         if (get_stage() == Stage::vertex && !input_locations.get_bit(0)) {
-          if (def._name == "vertex" || def._name == "p3d_Vertex") {
+          if (def._name == "vertex" || def._name == "p3d_Vertex" ||
+              def._name == "vtx_position") {
             // Prefer assigning the vertex column to location 0.
             location = 0;
           } else if (!input_locations.get_bit(1)) {
@@ -512,12 +541,26 @@ assign_locations(Definitions &defs) {
         }
       }
       else if (def._storage_class == SpvStorageClassUniformConstant) {
+        int num_locations = def._type->get_num_parameter_locations();
         location = uniform_locations.get_lowest_off_bit();
+        while (num_locations > 1 && uniform_locations.has_any_of(location, num_locations)) {
+          // Not enough bits free, try the next open range.
+          int next_bit = uniform_locations.get_next_higher_different_bit(location);
+          assert(next_bit > location);
+          location = uniform_locations.get_next_higher_different_bit(next_bit);
+          assert(location >= 0);
+        }
         uniform_locations.set_bit(location);
 
         if (shader_cat.is_debug()) {
-          shader_cat.debug()
-            << "Assigning " << def._name << " to uniform location " << location << "\n";
+          if (num_locations == 1) {
+            shader_cat.debug()
+              << "Assigning " << def._name << " to uniform location " << location << "\n";
+          } else {
+            shader_cat.debug()
+              << "Assigning " << def._name << " to uniform locations " << location
+              << ".." << (location + num_locations - 1) << "\n";
+          }
         }
       }
       else {
@@ -619,6 +662,25 @@ strip() {
   }
 }
 
+/**
+ * Called when an OpName is encountered in the SPIR-V instruction stream.
+ */
+void ShaderModuleSpirV::Definition::
+set_name(const char *name) {
+  _name.assign(name);
+}
+
+/**
+ * Called when an OpMemberName is encountered in the SPIR-V instruction stream.
+ */
+void ShaderModuleSpirV::Definition::
+set_member_name(uint32_t i, const char *name) {
+  if (i >= _member_names.size()) {
+    _member_names.resize(i + 1);
+  }
+  _member_names[i].assign(name);
+}
+
 /**
  * Called when an OpType is encountered in the SPIR-V instruction stream.
  */
@@ -656,9 +718,9 @@ set_variable(const ShaderType *type, SpvStorageClass storage_class) {
   _type = type;
   _storage_class = storage_class;
 
-  if (shader_cat.is_debug()) {
+  if (shader_cat.is_debug() && storage_class == SpvStorageClassUniformConstant) {
     shader_cat.debug()
-      << "Defined variable " << _name;
+      << "Defined uniform " << _name;
 
     if (_location >= 0) {
       shader_cat.debug(false) << " (location " << _location << ")";
@@ -696,3 +758,17 @@ set_variable(const ShaderType *type, SpvStorageClass storage_class) {
     break;
   }
 }
+
+/**
+ * Called when an OpConstant is encountered in the SPIR-V instruction stream.
+ */
+void ShaderModuleSpirV::Definition::
+set_constant(const ShaderType *type, const uint32_t *words, uint32_t nwords) {
+  _dtype = DT_constant;
+  _type = type;
+  if (nwords > 0) {
+    _constant = words[0];
+  } else {
+    _constant = 0;
+  }
+}

+ 7 - 0
panda/src/shaderpipeline/shaderModuleSpirV.h

@@ -51,6 +51,7 @@ protected:
     DT_type,
     DT_type_pointer,
     DT_variable,
+    DT_constant,
   };
 
   /**
@@ -63,13 +64,19 @@ protected:
     const ShaderType *_type = nullptr;
     int _location = -1;
     SpvBuiltIn _builtin = SpvBuiltInMax;
+    uint32_t _constant = 0;
+    vector_string _member_names;
 
     // Only defined for DT_variable.
     SpvStorageClass _storage_class;
 
+    void set_name(const char *name);
+    void set_member_name(uint32_t i, const char *name);
+
     void set_type(const ShaderType *type);
     void set_type_pointer(SpvStorageClass storage_class, const ShaderType *type);
     void set_variable(const ShaderType *type, SpvStorageClass storage_class);
+    void set_constant(const ShaderType *type, const uint32_t *words, uint32_t nwords);
   };
   typedef pvector<Definition> Definitions;