Browse Source

Add set_shader_inputs for efficiently setting multiple shader inputs

rdb 8 years ago
parent
commit
cfe810ace7

+ 10 - 1
dtool/src/interrogate/functionRemap.cxx

@@ -791,6 +791,15 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
     _args_type = InterfaceMaker::AT_single_arg;
   } else {
     _args_type = InterfaceMaker::AT_varargs;
+
+    // If the arguments are named "args" and "kwargs", we will be directly
+    // passing the argument tuples to the function.
+    if (_parameters.size() == first_param + 2 &&
+        _parameters[first_param]._name == "args" &&
+        (_parameters[first_param + 1]._name == "kwargs" ||
+          _parameters[first_param + 1]._name == "kwds")) {
+      _flags |= F_explicit_args;
+    }
   }
 
   switch (_type) {
@@ -890,7 +899,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
       if (_args_type == InterfaceMaker::AT_varargs) {
         // Every other method can take keyword arguments, if they take more
         // than one argument.
-        _args_type = InterfaceMaker::AT_keyword_args;
+        _args_type |= InterfaceMaker::AT_keyword_args;
       }
     }
     break;

+ 1 - 0
dtool/src/interrogate/functionRemap.h

@@ -100,6 +100,7 @@ public:
     F_coerce_constructor = 0x1000,
     F_divide_float       = 0x2000,
     F_hash               = 0x4000,
+    F_explicit_args      = 0x8000,
   };
 
   typedef vector<Parameter> Parameters;

+ 32 - 11
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -3453,7 +3453,13 @@ write_function_for_name(ostream &out, Object *obj,
     max_required_args = collapse_default_remaps(map_sets, max_required_args);
   }
 
-  if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) {
+  if (remap->_flags & FunctionRemap::F_explicit_args) {
+    // We have a remap that wants to handle the wrapper itself.
+    string expected_params;
+    write_function_instance(out, remap, 0, 0, expected_params, 2, true, true,
+                            args_type, return_flags);
+
+  } else if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) {
     switch (args_type) {
     case AT_keyword_args:
       indent(out, 2) << "int parameter_count = (int)PyTuple_Size(args);\n";
@@ -4536,19 +4542,23 @@ write_function_instance(ostream &out, FunctionRemap *remap,
   expected_params += methodNameFromCppName(remap, "", false);
   expected_params += "(";
 
-  int num_params = max_num_args;
-  if (remap->_has_this) {
-    num_params += 1;
-  }
-  if (num_params > (int)remap->_parameters.size()) {
-    // Limit to how many parameters this remap actually has.
-    num_params = (int)remap->_parameters.size();
-    max_num_args = num_params;
+  int num_params = 0;
+
+  if ((remap->_flags & FunctionRemap::F_explicit_args) == 0) {
+    num_params = max_num_args;
     if (remap->_has_this) {
-      --max_num_args;
+      num_params += 1;
     }
+    if (num_params > (int)remap->_parameters.size()) {
+      // Limit to how many parameters this remap actually has.
+      num_params = (int)remap->_parameters.size();
+      max_num_args = num_params;
+      if (remap->_has_this) {
+        --max_num_args;
+      }
+    }
+    nassertv(num_params <= (int)remap->_parameters.size());
   }
-  nassertv(num_params <= (int)remap->_parameters.size());
 
   bool only_pyobjects = true;
 
@@ -4584,6 +4594,17 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     }
   }
 
+  if (remap->_flags & FunctionRemap::F_explicit_args) {
+    // The function handles the arguments by itself.
+    expected_params += "*args";
+    pexprs.push_back("args");
+    if (args_type == AT_keyword_args) {
+      expected_params += ", **kwargs";
+      pexprs.push_back("kwds");
+    }
+    num_params = 0;
+  }
+
   // Now convert (the rest of the) actual arguments, one by one.
   for (; pn < num_params; ++pn) {
     ParameterRemap *param = remap->_parameters[pn]._remap;

+ 2 - 0
panda/src/pgraph/nodePath.h

@@ -659,6 +659,8 @@ PUBLISHED:
   INLINE void set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2=0,
                                PN_stdfloat n3=0, PN_stdfloat n4=0, int priority=0);
 
+  EXTENSION(void set_shader_inputs(PyObject *args, PyObject *kwargs));
+
   void clear_shader_input(CPT_InternalName id);
   void set_instance_count(int instance_count);
 

+ 216 - 0
panda/src/pgraph/nodePath_ext.cxx

@@ -13,6 +13,7 @@
 
 #include "nodePath_ext.h"
 #include "typedWritable_ext.h"
+#include "shaderAttrib.h"
 
 #ifdef HAVE_PYTHON
 
@@ -24,6 +25,35 @@ extern struct Dtool_PyTypedObject Dtool_LPoint3d;
 #else
 extern struct Dtool_PyTypedObject Dtool_LPoint3f;
 #endif
+extern struct Dtool_PyTypedObject Dtool_Texture;
+extern struct Dtool_PyTypedObject Dtool_NodePath;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_float;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_double;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_int;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3f;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLMatrix4d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LMatrix3d;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_UnalignedLVecBase4i;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase3i;
+extern struct Dtool_PyTypedObject Dtool_PointerToArray_LVecBase2i;
+extern struct Dtool_PyTypedObject Dtool_LVecBase4f;
+extern struct Dtool_PyTypedObject Dtool_LVecBase3f;
+extern struct Dtool_PyTypedObject Dtool_LVecBase2f;
+extern struct Dtool_PyTypedObject Dtool_LVecBase4d;
+extern struct Dtool_PyTypedObject Dtool_LVecBase3d;
+extern struct Dtool_PyTypedObject Dtool_LVecBase2d;
+extern struct Dtool_PyTypedObject Dtool_LVecBase4i;
+extern struct Dtool_PyTypedObject Dtool_LVecBase3i;
+extern struct Dtool_PyTypedObject Dtool_LVecBase2i;
+extern struct Dtool_PyTypedObject Dtool_ShaderBuffer;
+extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
 #endif  // CPPPARSER
 
 /**
@@ -211,6 +241,192 @@ py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &da
   return NodePath::decode_from_bam_stream(data, reader);
 }
 
+/**
+ * Sets multiple shader inputs at the same time.  This can be significantly
+ * more efficient if many inputs need to be set at the same time.
+ */
+void Extension<NodePath>::
+set_shader_inputs(PyObject *args, PyObject *kwargs) {
+  if (PyObject_Size(args) > 0) {
+    Dtool_Raise_TypeError("NodePath.set_shader_inputs takes only keyword arguments");
+    return;
+  }
+
+  PT(PandaNode) node = _this->node();
+  CPT(RenderAttrib) prev_attrib = node->get_attrib(ShaderAttrib::get_class_slot());
+  PT(ShaderAttrib) attrib;
+  if (prev_attrib == nullptr) {
+    attrib = new ShaderAttrib();
+  } else {
+    attrib = new ShaderAttrib(*(const ShaderAttrib *)prev_attrib.p());
+  }
+
+  PyObject *key, *value;
+  Py_ssize_t pos = 0;
+
+  while (PyDict_Next(kwargs, &pos, &key, &value)) {
+    char *buffer;
+    Py_ssize_t length;
+#if PY_MAJOR_VERSION >= 3
+    buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length);
+    if (buffer == nullptr) {
+#else
+    if (PyString_AsStringAndSize(key, &buffer, &length) == -1) {
+#endif
+      Dtool_Raise_TypeError("NodePath.set_shader_inputs accepts only string keywords");
+      return;
+    }
+
+    CPT_InternalName name(string(buffer, length));
+    ShaderInput *input = nullptr;
+
+    if (PyTuple_CheckExact(value)) {
+      // A tuple is interpreted as a vector.
+      Py_ssize_t size = PyTuple_GET_SIZE(value);
+      if (size > 4) {
+        Dtool_Raise_TypeError("NodePath.set_shader_inputs tuple input should not have more than 4 scalars");
+        return;
+      }
+      // If any of them is a float, we are storing it as a float vector.
+      bool is_float = false;
+      for (Py_ssize_t i = 0; i < size; ++i) {
+        if (PyFloat_CheckExact(PyTuple_GET_ITEM(value, i))) {
+          is_float = true;
+          break;
+        }
+      }
+      if (is_float) {
+        LVecBase4 vec(0);
+        for (Py_ssize_t i = 0; i < size; ++i) {
+          vec[i] = (PN_stdfloat)PyFloat_AsDouble(PyTuple_GET_ITEM(value, i));
+        }
+        input = new ShaderInput(name, vec);
+      } else {
+        LVecBase4i vec(0);
+        for (Py_ssize_t i = 0; i < size; ++i) {
+          vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i));
+        }
+        input = new ShaderInput(name, vec);
+      }
+
+    } else if (DtoolCanThisBeAPandaInstance(value)) {
+      Dtool_PyInstDef *inst = (Dtool_PyInstDef *)value;
+      void *ptr;
+
+      if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_Texture))) {
+        input = new ShaderInput(name, (Texture *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) {
+        input = new ShaderInput(name, *(const NodePath *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) {
+        input = new ShaderInput(name, *(const PTA_float *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) {
+        input = new ShaderInput(name, *(const PTA_double *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) {
+        input = new ShaderInput(name, *(const PTA_int *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase4f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase3f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase2f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) {
+        input = new ShaderInput(name, *(const PTA_LMatrix4f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) {
+        input = new ShaderInput(name, *(const PTA_LMatrix3f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase4d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase3d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase2d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) {
+        input = new ShaderInput(name, *(const PTA_LMatrix4d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) {
+        input = new ShaderInput(name, *(const PTA_LMatrix3d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase4i *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase3i *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) {
+        input = new ShaderInput(name, *(const PTA_LVecBase2i *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) {
+        input = new ShaderInput(name, *(const LVecBase4f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) {
+        input = new ShaderInput(name, *(const LVecBase3f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) {
+        input = new ShaderInput(name, *(const LVecBase2f *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) {
+        input = new ShaderInput(name, *(const LVecBase4d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) {
+        input = new ShaderInput(name, *(const LVecBase3d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) {
+        input = new ShaderInput(name, *(const LVecBase2d *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) {
+        input = new ShaderInput(name, *(const LVecBase4i *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) {
+        input = new ShaderInput(name, *(const LVecBase3i *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) {
+        input = new ShaderInput(name, *(const LVecBase2i *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) {
+        input = new ShaderInput(name, (ShaderBuffer *)ptr);
+
+      } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) {
+        input = new ShaderInput(name, (ParamValueBase *)ptr);
+
+      } else {
+        Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
+        return;
+      }
+
+    } else if (PyFloat_Check(value)) {
+      input = new ShaderInput(name, LVecBase4(PyFloat_AS_DOUBLE(value), 0, 0, 0));
+
+#if PY_MAJOR_VERSION < 3
+    } else if (PyInt_Check(value)) {
+      input = new ShaderInput(name, LVecBase4i((int)PyInt_AS_LONG(value), 0, 0, 0));
+#endif
+
+    } else if (PyLong_Check(value)) {
+      input = new ShaderInput(name, LVecBase4i((int)PyLong_AsLong(value), 0, 0, 0));
+
+    } else {
+      Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
+      return;
+    }
+
+    attrib->_inputs[move(name)] = input;
+  }
+
+  node->set_attrib(ShaderAttrib::return_new(attrib));
+}
+
 /**
  * Returns the tight bounds as a 2-tuple of LPoint3 objects.  This is a
  * convenience function for Python users, among which the use of

+ 2 - 0
panda/src/pgraph/nodePath_ext.h

@@ -49,6 +49,8 @@ public:
   // This is defined to implement cycle detection in Python tags.
   INLINE int __traverse__(visitproc visit, void *arg);
 
+  void set_shader_inputs(PyObject *args, PyObject *kwargs);
+
   PyObject *get_tight_bounds(const NodePath &other = NodePath()) const;
 };
 

+ 3 - 0
panda/src/pgraph/shaderAttrib.h

@@ -31,6 +31,7 @@
 #include "pta_LVecBase4.h"
 #include "pta_LVecBase3.h"
 #include "pta_LVecBase2.h"
+#include "extension.h"
 
 /**
  *
@@ -147,6 +148,8 @@ private:
   typedef pmap<CPT_InternalName, CPT(ShaderInput)> Inputs;
   Inputs _inputs;
 
+  friend class Extension<NodePath>;
+
 PUBLISHED:
   static int get_class_slot() {
     return _attrib_slot;