Browse Source

Merge branch 'release/1.10.x'

rdb 4 years ago
parent
commit
8cbe36b352

+ 1 - 1
contrib/src/ai/aiBehaviors.h

@@ -151,7 +151,7 @@ PUBLISHED:
 
   void obstacle_avoidance(float feeler_length = 1.0);
 
-  void path_follow(float follow_wt);
+  void path_follow(float follow_wt = 1.0f);
   void add_to_path(LVecBase3 pos);
   void start_follow(std::string type = "normal");
 

+ 1 - 1
direct/src/motiontrail/cMotionTrail.cxx

@@ -331,7 +331,7 @@ update_motion_trail (PN_stdfloat current_time, LMatrix4 *transform) {
 
     frame_iterator = _frame_list.begin ( );
     motion_trail_frame = *frame_iterator;
-    if (*transform == motion_trail_frame._transform) {
+    if (motion_trail_frame._transform == UnalignedLMatrix4(*transform)) {
       // duplicate transform
       return;
     }

+ 5 - 5
dtool/src/dtoolbase/pallocator.T

@@ -20,7 +20,7 @@ pallocator_single(TypeHandle type_handle) noexcept :
 
 template<class Type>
 INLINE Type *pallocator_single<Type>::
-allocate(typename pallocator_single<Type>::size_type n, typename std::allocator<void>::const_pointer) {
+allocate(typename pallocator_single<Type>::size_type n, const void *) {
   TAU_PROFILE("pallocator_single:allocate()", " ", TAU_USER);
   // This doesn't support allocating arrays.
   assert(n == 1);
@@ -30,7 +30,7 @@ allocate(typename pallocator_single<Type>::size_type n, typename std::allocator<
 
 template<class Type>
 INLINE void pallocator_single<Type>::
-deallocate(typename pallocator_single<Type>::pointer p, typename pallocator_single<Type>::size_type) {
+deallocate(Type *p, typename pallocator_single<Type>::size_type) {
   TAU_PROFILE("pallocator_single:deallocate()", " ", TAU_USER);
   StaticDeletedChain<Type>::deallocate(p, _type_handle);
 }
@@ -44,13 +44,13 @@ pallocator_array(TypeHandle type_handle) noexcept :
 
 template<class Type>
 INLINE Type *pallocator_array<Type>::
-allocate(typename pallocator_array<Type>::size_type n, typename std::allocator<void>::const_pointer) {
-  return (typename pallocator_array<Type>::pointer)
+allocate(typename pallocator_array<Type>::size_type n, const void *) {
+  return (Type *)
     ASSUME_ALIGNED(_type_handle.allocate_array(n * sizeof(Type)), MEMORY_HOOK_ALIGNMENT);
 }
 
 template<class Type>
 INLINE void pallocator_array<Type>::
-deallocate(typename pallocator_array<Type>::pointer p, typename pallocator_array<Type>::size_type) {
+deallocate(Type *p, typename pallocator_array<Type>::size_type) {
   _type_handle.deallocate_array((void *)p);
 }

+ 12 - 12
dtool/src/dtoolbase/pallocator.h

@@ -46,10 +46,10 @@ class pallocator_single : public std::allocator<Type> {
 public:
   // Nowadays we cannot implicitly inherit typedefs from base classes in a
   // template class; we must explicitly copy them here.
-  typedef typename std::allocator<Type>::pointer pointer;
-  typedef typename std::allocator<Type>::reference reference;
-  typedef typename std::allocator<Type>::const_pointer const_pointer;
-  typedef typename std::allocator<Type>::const_reference const_reference;
+  typedef Type *pointer;
+  typedef Type &reference;
+  typedef const Type *const_pointer;
+  typedef const Type &const_reference;
   typedef typename std::allocator<Type>::size_type size_type;
 
   INLINE pallocator_single(TypeHandle type_handle) noexcept;
@@ -59,9 +59,9 @@ public:
   INLINE pallocator_single(const pallocator_single<U> &copy) noexcept :
     _type_handle(copy._type_handle) { }
 
-  INLINE Type *allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
+  INLINE Type *allocate(size_type n, const void *hint = 0)
     RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);
-  INLINE void deallocate(pointer p, size_type n);
+  INLINE void deallocate(Type *p, size_type n);
 
   template<class U> struct rebind {
     typedef pallocator_single<U> other;
@@ -75,10 +75,10 @@ class pallocator_array : public std::allocator<Type> {
 public:
   // Nowadays we cannot implicitly inherit typedefs from base classes in a
   // template class; we must explicitly copy them here.
-  typedef typename std::allocator<Type>::pointer pointer;
-  typedef typename std::allocator<Type>::reference reference;
-  typedef typename std::allocator<Type>::const_pointer const_pointer;
-  typedef typename std::allocator<Type>::const_reference const_reference;
+  typedef Type *pointer;
+  typedef Type &reference;
+  typedef const Type *const_pointer;
+  typedef const Type &const_reference;
   typedef typename std::allocator<Type>::size_type size_type;
 
   INLINE pallocator_array(TypeHandle type_handle = TypeHandle::none()) noexcept;
@@ -88,9 +88,9 @@ public:
   INLINE pallocator_array(const pallocator_array<U> &copy) noexcept :
     _type_handle(copy._type_handle) { }
 
-  INLINE Type *allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
+  INLINE Type *allocate(size_type n, const void *hint = 0)
     RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);
-  INLINE void deallocate(pointer p, size_type n);
+  INLINE void deallocate(Type *p, size_type n);
 
   template<class U> struct rebind {
     typedef pallocator_array<U> other;

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

@@ -4306,18 +4306,19 @@ int get_type_sort(CPPType *type) {
              TypeManager::is_struct(type)) {
     answer = 20;
     int deepest = 0;
-    TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)), false);
-    InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
-    const InterrogateType &itype = idb->get_type(type_index);
 
-    if (itype.is_class() || itype.is_struct()) {
-      int num_derivations = itype.number_of_derivations();
-      for (int di = 0; di < num_derivations; di++) {
-        TypeIndex d_type_Index = itype.get_derivation(di);
-        const InterrogateType &d_itype = idb->get_type(d_type_Index);
-        int this_one = get_type_sort(d_itype._cpptype);
-        if (this_one > deepest) {
-          deepest = this_one;
+    // Sort such that more derived classes come first.
+    type = TypeManager::unwrap(TypeManager::resolve_type(type));
+    if (type != nullptr) {
+      CPPStructType *struct_type = type->as_struct_type();
+      if (struct_type != nullptr) {
+        for (const CPPStructType::Base &base : struct_type->_derivation) {
+          if (base._base != nullptr) {
+            int this_one = get_type_sort(base._base);
+            if (this_one > deepest) {
+              deepest = this_one;
+            }
+          }
         }
       }
     }

+ 5 - 0
dtool/src/interrogatedb/py_compat.h

@@ -137,6 +137,11 @@ typedef long Py_hash_t;
 #  endif
 #endif
 
+/* Python 3.4 */
+#if PY_VERSION_HEX < 0x03040000
+#define PyGILState_Check() (PyGILState_GetThisThreadState() == _PyThreadState_Current)
+#endif
+
 /* Python 3.6 */
 
 #if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_CallNoArg)

+ 3 - 2
panda/src/event/asyncFuture.h

@@ -84,13 +84,14 @@ PUBLISHED:
   BLOCKING void wait();
   BLOCKING void wait(double timeout);
 
+  EXTENSION(void set_result(PyObject *));
+
+public:
   INLINE void set_result(std::nullptr_t);
   INLINE void set_result(TypedObject *result);
   INLINE void set_result(TypedReferenceCount *result);
   INLINE void set_result(TypedWritableReferenceCount *result);
   INLINE void set_result(const EventParameter &result);
-
-public:
   void set_result(TypedObject *ptr, ReferenceCount *ref_ptr);
 
   INLINE TypedObject *get_result() const;

+ 77 - 4
panda/src/event/asyncFuture_ext.cxx

@@ -15,14 +15,20 @@
 #include "asyncTaskSequence.h"
 #include "eventParameter.h"
 #include "paramValue.h"
+#include "paramPyObject.h"
 #include "pythonTask.h"
+#include "asyncTaskManager.h"
+#include "config_event.h"
 
 #ifdef HAVE_PYTHON
 
 #ifndef CPPPARSER
 extern struct Dtool_PyTypedObject Dtool_AsyncFuture;
+extern struct Dtool_PyTypedObject Dtool_EventParameter;
 extern struct Dtool_PyTypedObject Dtool_ParamValueBase;
 extern struct Dtool_PyTypedObject Dtool_TypedObject;
+extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
+extern struct Dtool_PyTypedObject Dtool_TypedWritableReferenceCount;
 #endif
 
 /**
@@ -90,20 +96,24 @@ static PyObject *get_done_result(const AsyncFuture *future) {
         // EventStoreInt and Double are not exposed to Python for some reason.
         if (type == EventStoreInt::get_class_type()) {
           return Dtool_WrapValue(((EventStoreInt *)ptr)->get_value());
-        } else if (type == EventStoreDouble::get_class_type()) {
+        }
+        else if (type == EventStoreDouble::get_class_type()) {
           return Dtool_WrapValue(((EventStoreDouble *)ptr)->get_value());
         }
+        else if (type == ParamPyObject::get_class_type()) {
+          return ((ParamPyObject *)ptr)->get_value();
+        }
 
         ParamValueBase *value = (ParamValueBase *)ptr;
         PyObject *wrap = DTool_CreatePyInstanceTyped
           ((void *)value, Dtool_ParamValueBase, false, false, type.get_index());
         if (wrap != nullptr) {
           PyObject *value = PyObject_GetAttrString(wrap, "value");
+          Py_DECREF(wrap);
           if (value != nullptr) {
             return value;
           }
           PyErr_Clear();
-          Py_DECREF(wrap);
         }
       }
 
@@ -154,6 +164,58 @@ __await__(PyObject *self) {
   return Dtool_NewGenerator(self, &gen_next);
 }
 
+/**
+ * Sets this future's result.  Can only be called if done() returns false.
+ */
+void Extension<AsyncFuture>::
+set_result(PyObject *result) {
+  if (result == Py_None) {
+    _this->set_result(nullptr);
+    return;
+  }
+  else if (DtoolInstance_Check(result)) {
+    void *ptr;
+    if ((ptr = DtoolInstance_UPCAST(result, Dtool_EventParameter))) {
+      _this->set_result(*(const EventParameter *)ptr);
+      return;
+    }
+    if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedWritableReferenceCount))) {
+      _this->set_result((TypedWritableReferenceCount *)ptr);
+      return;
+    }
+    if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedReferenceCount))) {
+      _this->set_result((TypedReferenceCount *)ptr);
+      return;
+    }
+    if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedObject))) {
+      _this->set_result((TypedObject *)ptr);
+      return;
+    }
+  }
+  else if (PyUnicode_Check(result)) {
+    Py_ssize_t result_len;
+    wchar_t *result_str = PyUnicode_AsWideCharString(result, &result_len);
+    _this->set_result(new EventStoreWstring(std::wstring(result_str, result_len)));
+    PyMem_Free(result_str);
+    return;
+  }
+  else if (PyLongOrInt_Check(result)) {
+    long result_val = PyLongOrInt_AS_LONG(result);
+    if (result_val >= INT_MIN && result_val <= INT_MAX) {
+      _this->set_result(new EventStoreInt((int)result_val));
+      return;
+    }
+  }
+  else if (PyNumber_Check(result)) {
+    _this->set_result(new EventStoreDouble(PyFloat_AsDouble(result)));
+    return;
+  }
+
+  // If we don't recognize the type, store it as a generic PyObject pointer.
+  ParamPyObject::init_type();
+  _this->set_result(new ParamPyObject(result));
+}
+
 /**
  * Returns the result of this future, unless it was cancelled, in which case
  * it returns CancelledError.
@@ -285,8 +347,19 @@ gather(PyObject *args) {
       }
     } else if (PyCoro_CheckExact(item)) {
       // We allow passing in a coroutine instead of a future.  This causes it
-      // to be scheduled as a task.
-      futures.push_back(new PythonTask(item));
+      // to be scheduled as a task on the current task manager.
+      PT(AsyncTask) task = new PythonTask(item);
+      Thread *current_thread = Thread::get_current_thread();
+      AsyncTask *current_task = (AsyncTask *)current_thread->get_current_task();
+      if (current_task != nullptr) {
+        task->set_task_chain(current_task->get_task_chain());
+        current_task->get_manager()->add(task);
+      }
+      else {
+        event_cat.warning()
+          << "gather() with coroutine not called from within a task; not scheduling with task manager.\n";
+      }
+      futures.push_back(task);
       continue;
     }
     return Dtool_Raise_ArgTypeError(item, i, "gather", "coroutine, task or future");

+ 1 - 0
panda/src/event/asyncFuture_ext.h

@@ -29,6 +29,7 @@ public:
   static PyObject *__await__(PyObject *self);
   static PyObject *__iter__(PyObject *self) { return __await__(self); }
 
+  void set_result(PyObject *result);
   PyObject *result(PyObject *timeout = Py_None) const;
 
   PyObject *add_done_callback(PyObject *self, PyObject *fn);

+ 0 - 2
panda/src/event/pythonTask.cxx

@@ -84,7 +84,6 @@ PythonTask(PyObject *func_or_coro, const std::string &name) :
  */
 PythonTask::
 ~PythonTask() {
-#ifndef NDEBUG
   // If the coroutine threw an exception, and there was no opportunity to
   // handle it, let the user know.
   if (_exception != nullptr && !_retrieved_exception) {
@@ -97,7 +96,6 @@ PythonTask::
     _exc_value = nullptr;
     _exc_traceback = nullptr;
   }
-#endif
 
   Py_XDECREF(_function);
   Py_DECREF(_args);

+ 31 - 0
panda/src/glstuff/glShaderContext_src.cxx

@@ -3054,6 +3054,37 @@ glsl_report_program_errors(GLuint program, bool fatal) {
     if (strcmp(info_log, "Success.\n") != 0 &&
         strcmp(info_log, "No errors.\n") != 0 &&
         strcmp(info_log, "Validation successful.\n") != 0) {
+
+#ifdef __APPLE__
+      // Filter out these unhelpful warnings that Apple always generates.
+      while (true) {
+        if (info_log[0] == '\n') {
+          ++info_log;
+          continue;
+        }
+        if (info_log[0] == '\0') {
+          // We reached the end without finding anything interesting.
+          return;
+        }
+        int linelen = 0;
+        if ((sscanf(info_log, "WARNING: Could not find vertex shader attribute %*s to match BindAttributeLocation request.%*[\n]%n", &linelen) == 0 && linelen > 0) ||
+            (sscanf(info_log, "WARNING: Could not find fragment shader output %*s to match FragDataBinding request.%*[\n]%n", &linelen) == 0 && linelen > 0)) {
+          info_log += linelen;
+          continue;
+        }
+        else {
+          break;
+        }
+
+        info_log = strchr(info_log, '\n');
+        if (info_log == nullptr) {
+          // We reached the end without finding anything interesting.
+          return;
+        }
+        ++info_log;
+      }
+#endif
+
       if (!fatal) {
         GLCAT.warning()
           << "Shader " << _shader->get_filename() << " produced the "

+ 3 - 1
panda/src/pipeline/pythonThread.cxx

@@ -258,7 +258,9 @@ call_python_func(PyObject *function, PyObject *args) {
 
       PyGILState_Release(gstate);
 
-      PyErr_Restore(exc, val, tb);
+      if (PyGILState_Check()) {
+        PyErr_Restore(exc, val, tb);
+      }
     } else {
       // No exception.  Restore the thread state normally.
       PyGILState_Release(gstate);

+ 1 - 0
panda/src/putil/p3putil_ext_composite.cxx

@@ -1,5 +1,6 @@
 #include "bamReader_ext.cxx"
 #include "bitArray_ext.cxx"
+#include "paramPyObject.cxx"
 #include "pythonCallbackObject.cxx"
 #include "sparseArray_ext.cxx"
 #include "typedWritable_ext.cxx"

+ 31 - 0
panda/src/putil/paramPyObject.I

@@ -0,0 +1,31 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file paramPyObject.I
+ * @author rdb
+ * @date 2021-03-01
+ */
+
+#include "paramPyObject.h"
+
+/**
+ * Increments the reference count.  Assumes the GIL is held.
+ */
+INLINE ParamPyObject::
+ParamPyObject(PyObject *value) : _value(value) {
+  Py_INCREF(value);
+}
+
+/**
+ * Returns a new reference to the stored value.
+ */
+INLINE PyObject *ParamPyObject::
+get_value() const {
+  Py_INCREF(_value);
+  return _value;
+}

+ 42 - 0
panda/src/putil/paramPyObject.cxx

@@ -0,0 +1,42 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file paramPyObject.cxx
+ * @author rdb
+ * @date 2021-03-01
+ */
+
+#include "paramPyObject.h"
+
+TypeHandle ParamPyObject::_type_handle;
+
+/**
+ * Decrements the reference count.
+ */
+ParamPyObject::
+~ParamPyObject() {
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+#endif
+
+  Py_DECREF(_value);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_Release(gstate);
+#endif
+}
+
+/**
+ *
+ */
+void ParamPyObject::
+output(std::ostream &out) const {
+  out << "<" << Py_TYPE(_value)->tp_name
+      << " object at " << (void *)_value << ">";
+}

+ 61 - 0
panda/src/putil/paramPyObject.h

@@ -0,0 +1,61 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file paramPyObject.h
+ * @author rdb
+ * @date 2021-03-01
+ */
+
+#ifndef PARAMPYOBJECT_H
+#define PARAMPYOBJECT_H
+
+#include "pandabase.h"
+#include "paramValue.h"
+
+#ifdef HAVE_PYTHON
+
+#include "py_panda.h"
+
+/**
+ * A class object for storing an arbitrary Python object.
+ */
+class ParamPyObject final : public ParamValueBase {
+public:
+  INLINE ParamPyObject(PyObject *value);
+  virtual ~ParamPyObject();
+
+  INLINE PyObject *get_value() const;
+
+  void output(std::ostream &out) const override;
+
+public:
+  PyObject *_value;
+
+public:
+  virtual TypeHandle get_type() const override {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() override {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParamValueBase::init_type();
+    register_type(_type_handle, "ParamPyObject",
+                  ParamValueBase::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "paramPyObject.I"
+
+#endif  // HAVE_PYTHON
+
+#endif

+ 65 - 0
tests/event/test_futures.py

@@ -163,6 +163,71 @@ def test_coro_exception():
         task.result()
 
 
+def test_future_result():
+    # Cancelled
+    fut = core.AsyncFuture()
+    assert not fut.done()
+    fut.cancel()
+    with pytest.raises(CancelledError):
+        fut.result()
+
+    # None
+    fut = core.AsyncFuture()
+    fut.set_result(None)
+    assert fut.done()
+    assert fut.result() is None
+
+    # Store int
+    fut = core.AsyncFuture()
+    fut.set_result(123)
+    assert fut.result() == 123
+
+    # Store string
+    fut = core.AsyncFuture()
+    fut.set_result("test\000\u1234")
+    assert fut.result() == "test\000\u1234"
+
+    # Store TypedWritableReferenceCount
+    tex = core.Texture()
+    rc = tex.get_ref_count()
+    fut = core.AsyncFuture()
+    fut.set_result(tex)
+    assert tex.get_ref_count() == rc + 1
+    assert fut.result() == tex
+    assert tex.get_ref_count() == rc + 1
+    assert fut.result() == tex
+    assert tex.get_ref_count() == rc + 1
+    fut = None
+    assert tex.get_ref_count() == rc
+
+    # Store EventParameter (gets unwrapped)
+    ep = core.EventParameter(0.5)
+    fut = core.AsyncFuture()
+    fut.set_result(ep)
+    assert fut.result() == 0.5
+    assert fut.result() == 0.5
+
+    # Store TypedObject
+    dg = core.Datagram(b"test")
+    fut = core.AsyncFuture()
+    fut.set_result(dg)
+    assert fut.result() == dg
+    assert fut.result() == dg
+
+    # Store arbitrary Python object
+    obj = object()
+    rc = sys.getrefcount(obj)
+    fut = core.AsyncFuture()
+    fut.set_result(obj)
+    assert sys.getrefcount(obj) == rc + 1
+    assert fut.result() is obj
+    assert sys.getrefcount(obj) == rc + 1
+    assert fut.result() is obj
+    assert sys.getrefcount(obj) == rc + 1
+    fut = None
+    assert sys.getrefcount(obj) == rc
+
+
 def test_future_gather():
     fut1 = core.AsyncFuture()
     fut2 = core.AsyncFuture()