Merge branch 'release/1.10.x'

This commit is contained in:
rdb 2021-03-01 19:19:34 +01:00
commit 8cbe36b352
17 changed files with 351 additions and 39 deletions

View File

@ -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");

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}
}
}
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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 "

View File

@ -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);

View File

@ -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"

View File

@ -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;
}

View File

@ -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 << ">";
}

View File

@ -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

View File

@ -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()