Merge branch 'release/1.10.x'

This commit is contained in:
rdb
2019-07-30 19:32:16 +02:00
11 changed files with 639 additions and 1 deletions

View File

@@ -77,6 +77,7 @@ class DirectOptionMenu(DirectButton):
state = 'normal')
# Make sure this is on top of all the other widgets
self.cancelFrame.setBin('gui-popup', 0)
self.cancelFrame.node().setBounds(OmniBoundingVolume())
self.cancelFrame.bind(DGG.B1PRESS, self.hidePopupMenu)
# Default action on press is to show popup menu
self.bind(DGG.B1PRESS, self.showPopupMenu)

View File

@@ -147,12 +147,28 @@ class Loader(DirectObject):
Loader.loaderIndex += 1
self.accept(self.hook, self.__gotAsyncObject)
if ConfigVariableBool('loader-support-entry-points', True):
self._loadPythonFileTypes()
def destroy(self):
self.ignore(self.hook)
self.loader.stopThreads()
del self.base
del self.loader
def _loadPythonFileTypes(self):
import importlib
try:
pkg_resources = importlib.import_module('pkg_resources')
except ImportError:
pkg_resources = None
if pkg_resources:
registry = LoaderFileTypeRegistry.getGlobalPtr()
for entry_point in pkg_resources.iter_entry_points('panda3d.loaders'):
registry.register_deferred_type(entry_point)
# model loading funcs
def loadModel(self, modelPath, loaderOptions = None, noCache = None,
allowInstance = False, okMissing = None,

View File

@@ -29,6 +29,20 @@ AsyncFuture::
// If this triggers, the future destroyed before it was cancelled, which is
// not valid. Unless we should simply call cancel() here?
nassertv(_waiting.empty());
// This is an attempt to work around what appears to be a compiler bug in
// MSVC when compiling with optimizations and having an EventStoreInt stored
// in this field. It crashes when we delete via the ReferenceCount base
// instead of via the TypedObject. I haven't been able to find out why;
// just that it doesn't happen with ParamString. ~rdb
ReferenceCount *result_ref = _result_ref.p();
if (result_ref != nullptr) {
_result_ref.cheat() = nullptr;
if (!result_ref->unref()) {
delete _result;
}
_result = nullptr;
}
}
/**

View File

@@ -890,6 +890,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
// Add it once for each index.
for (bind._index = 0; bind._index < param_size; ++bind._index) {
bind._id._seqno = p + bind._index;
_shader->_mat_spec.push_back(bind);
}
_shader->_mat_deps |= bind._dep[0];

View File

@@ -36,6 +36,9 @@ public:
void register_deferred_type(const std::string &extension, const std::string &library);
PUBLISHED:
EXTENSION(void register_type(PyObject *type));
EXTENSION(void register_deferred_type(PyObject *entry_point));
int get_num_types() const;
LoaderFileType *get_type(int n) const;
MAKE_SEQ(get_types, get_num_types, get_type);

View File

@@ -0,0 +1,67 @@
/**
* 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 loaderFileTypeRegistry_ext.cxx
* @author rdb
* @date 2019-07-30
*/
#include "loaderFileTypeRegistry_ext.h"
#ifdef HAVE_PYTHON
#include "pythonLoaderFileType.h"
/**
* Registers a loader file type that is implemented in Python.
*/
void Extension<LoaderFileTypeRegistry>::
register_type(PyObject *type) {
PythonLoaderFileType *loader = new PythonLoaderFileType();
if (loader->init(type)) {
_this->register_type(loader);
} else {
delete loader;
}
}
/**
* Registers a loader file type from a pkg_resources.EntryPoint object, which
* will be loaded when a file with the extension is encountered.
*/
void Extension<LoaderFileTypeRegistry>::
register_deferred_type(PyObject *entry_point) {
// The "name" attribute holds the extension.
PyObject *name = PyObject_GetAttrString(entry_point, "name");
if (name == nullptr) {
Dtool_Raise_TypeError("entry_point argument is missing name attribute");
return;
}
const char *name_str;
Py_ssize_t name_len;
#if PY_MAJOR_VERSION >= 3
name_str = PyUnicode_AsUTF8AndSize(name, &name_len);
#else
if (PyString_AsStringAndSize(name, (char **)&name_str, &name_len) == -1) {
name_str = nullptr;
}
#endif
Py_DECREF(name);
if (name_str == nullptr) {
Dtool_Raise_TypeError("entry_point.name is expected to be str");
return;
}
PythonLoaderFileType *loader = new PythonLoaderFileType(std::string(name_str, name_len), entry_point);
_this->register_type(loader);
}
#endif

View File

@@ -0,0 +1,38 @@
/**
* 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 loaderFileTypeRegistry_ext.h
* @author rdb
* @date 2019-07-30
*/
#ifndef LOADERFILETYPEREGISTRYEXT_H
#define LOADERFILETYPEREGISTRYEXT_H
#include "pandabase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "loaderFileTypeRegistry.h"
#include "py_panda.h"
/**
* This class defines the extension methods for LoaderFileTypeRegistry, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<LoaderFileTypeRegistry> : public ExtensionBase<LoaderFileTypeRegistry> {
public:
void register_type(PyObject *type);
void register_deferred_type(PyObject *entry_point);
};
#endif // HAVE_PYTHON
#endif

View File

@@ -1,6 +1,8 @@
#include "loaderFileTypeRegistry_ext.cxx"
#include "nodePath_ext.cxx"
#include "nodePathCollection_ext.cxx"
#include "pandaNode_ext.cxx"
#include "pythonLoaderFileType.cxx"
#include "renderState_ext.cxx"
#include "shaderAttrib_ext.cxx"
#include "shaderInput_ext.cxx"

View File

@@ -0,0 +1,409 @@
/**
* 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 pythonLoaderFileType.cxx
* @author rdb
* @date 2019-07-29
*/
#include "pythonLoaderFileType.h"
#ifdef HAVE_PYTHON
#include "modelRoot.h"
#include "pythonThread.h"
#include "py_panda.h"
#include "virtualFileSystem.h"
extern struct Dtool_PyTypedObject Dtool_BamCacheRecord;
extern struct Dtool_PyTypedObject Dtool_Filename;
extern struct Dtool_PyTypedObject Dtool_LoaderOptions;
extern struct Dtool_PyTypedObject Dtool_PandaNode;
extern struct Dtool_PyTypedObject Dtool_PythonLoaderFileType;
TypeHandle PythonLoaderFileType::_type_handle;
/**
* This constructor expects init() to be called manually.
*/
PythonLoaderFileType::
PythonLoaderFileType() {
init_type();
}
/**
* This constructor expects a single pkg_resources.EntryPoint argument for a
* deferred loader.
*/
PythonLoaderFileType::
PythonLoaderFileType(std::string extension, PyObject *entry_point) :
_extension(std::move(extension)),
_entry_point(entry_point) {
init_type();
Py_INCREF(entry_point);
}
/**
* Destructor.
*/
PythonLoaderFileType::
~PythonLoaderFileType() {
if (_entry_point != nullptr || _load_func != nullptr || _save_func != nullptr) {
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#endif
Py_CLEAR(_entry_point);
Py_CLEAR(_load_func);
Py_CLEAR(_save_func);
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_Release(gstate);
#endif
}
}
/**
* Initializes the fields from the given Python loader object.
*/
bool PythonLoaderFileType::
init(PyObject *loader) {
nassertr(loader != nullptr, false);
nassertr(_load_func == nullptr, false);
nassertr(_save_func == nullptr, false);
// Check the extensions member. If we already have a registered extension,
// it must occur in the list.
PyObject *extensions = PyObject_GetAttrString(loader, "extensions");
if (extensions != nullptr) {
PyObject *sequence = PySequence_Fast(extensions, "extensions must be a sequence");
PyObject **items = PySequence_Fast_ITEMS(sequence);
Py_ssize_t num_items = PySequence_Fast_GET_SIZE(sequence);
Py_DECREF(extensions);
bool found_extension = false;
for (Py_ssize_t i = 0; i < num_items; ++i) {
PyObject *extension = items[i];
const char *extension_str;
Py_ssize_t extension_len;
#if PY_MAJOR_VERSION >= 3
extension_str = PyUnicode_AsUTF8AndSize(extension, &extension_len);
#else
if (PyString_AsStringAndSize(extension, (char **)&extension_str, &extension_len) == -1) {
extension_str = nullptr;
}
#endif
if (extension_str == nullptr) {
Py_DECREF(sequence);
return false;
}
if (_extension.empty()) {
_extension.assign(extension_str, extension_len);
found_extension = true;
} else {
std::string new_extension(extension_str, extension_len);
if (_extension == new_extension) {
found_extension = true;
} else {
if (!_additional_extensions.empty()) {
_additional_extensions += ' ';
}
_additional_extensions += new_extension;
}
}
}
Py_DECREF(sequence);
if (!found_extension) {
PyObject *repr = PyObject_Repr(loader);
loader_cat.error()
<< "Registered extension '" << _extension
<< "' does not occur in extensions list of "
#if PY_MAJOR_VERSION >= 3
<< PyUnicode_AsUTF8(repr) << "\n";
#else
<< PyString_AsString(repr) << "\n";
#endif
Py_DECREF(repr);
return false;
}
} else {
return false;
}
PyObject *supports_compressed = PyObject_GetAttrString(loader, "supports_compressed");
if (supports_compressed != nullptr) {
if (supports_compressed == Py_True) {
_supports_compressed = true;
}
else if (supports_compressed == Py_False) {
_supports_compressed = false;
}
else {
Dtool_Raise_TypeError("supports_compressed must be a bool");
Py_DECREF(supports_compressed);
return false;
}
Py_DECREF(supports_compressed);
}
_load_func = PyObject_GetAttrString(loader, "load_file");
_save_func = PyObject_GetAttrString(loader, "save_file");
PyErr_Clear();
if (_load_func == nullptr && _save_func == nullptr) {
#if PY_MAJOR_VERSION >= 3
PyErr_Format(PyExc_TypeError,
"loader plug-in %R does not define load_file or save_file function",
loader);
#else
PyObject *repr = PyObject_Repr(loader);
PyErr_Format(PyExc_TypeError,
"loader plug-in %s does not define load_file or save_file function",
PyString_AsString(repr));
Py_DECREF(repr);
#endif
return false;
}
// We don't need this any more.
Py_CLEAR(_entry_point);
return true;
}
/**
* Ensures that the referenced Python module is loaded.
*/
bool PythonLoaderFileType::
ensure_loaded() const {
if (_load_func != nullptr || _save_func != nullptr) {
return true;
}
nassertr_always(_entry_point != nullptr, false);
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#endif
if (loader_cat.is_info()) {
PyObject *repr = PyObject_Repr(_entry_point);
loader_cat.info()
<< "loading file type module: "
#if PY_MAJOR_VERSION >= 3
<< PyUnicode_AsUTF8(repr) << "\n";
#else
<< PyString_AsString(repr) << "\n";
#endif
Py_DECREF(repr);
}
PyObject *result = PyObject_CallMethod(_entry_point, (char *)"load", nullptr);
bool success = false;
if (result != nullptr) {
success = ((PythonLoaderFileType *)this)->init(result);
} else {
PyErr_Clear();
PyObject *repr = PyObject_Repr(_entry_point);
loader_cat.error()
<< "unable to load "
#if PY_MAJOR_VERSION >= 3
<< PyUnicode_AsUTF8(repr) << "\n";
#else
<< PyString_AsString(repr) << "\n";
#endif
Py_DECREF(repr);
}
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_Release(gstate);
#endif
return success;
}
/**
*
*/
std::string PythonLoaderFileType::
get_name() const {
return "Python loader";
}
/**
*
*/
std::string PythonLoaderFileType::
get_extension() const {
return _extension;
}
/**
* Returns a space-separated list of extension, in addition to the one
* returned by get_extension(), that are recognized by this converter.
*/
std::string PythonLoaderFileType::
get_additional_extensions() const {
return _additional_extensions;
}
/**
* Returns true if this file type can transparently load compressed files
* (with a .pz or .gz extension), false otherwise.
*/
bool PythonLoaderFileType::
supports_compressed() const {
return ensure_loaded() && _supports_compressed;
}
/**
* Returns true if the file type can be used to load files, and load_file() is
* supported. Returns false if load_file() is unimplemented and will always
* fail.
*/
bool PythonLoaderFileType::
supports_load() const {
return ensure_loaded() && _load_func != nullptr;
}
/**
* Returns true if the file type can be used to save files, and save_file() is
* supported. Returns false if save_file() is unimplemented and will always
* fail.
*/
bool PythonLoaderFileType::
supports_save() const {
return ensure_loaded() && _save_func != nullptr;
}
/**
*
*/
PT(PandaNode) PythonLoaderFileType::
load_file(const Filename &path, const LoaderOptions &options,
BamCacheRecord *record) const {
// Let's check whether the file even exists before calling Python.
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
PT(VirtualFile) vfile = vfs->get_file(path);
if (vfile == nullptr) {
return nullptr;
}
if (!supports_load()) {
return nullptr;
}
if (record != nullptr) {
record->add_dependent_file(vfile);
}
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#endif
// Wrap the arguments.
PyObject *args = PyTuple_New(3);
PyTuple_SET_ITEM(args, 0, DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true));
PyTuple_SET_ITEM(args, 1, DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true));
if (record != nullptr) {
record->ref();
PyTuple_SET_ITEM(args, 2, DTool_CreatePyInstanceTyped((void *)record, Dtool_BamCacheRecord, true, false, record->get_type_index()));
} else {
PyTuple_SET_ITEM(args, 2, Py_None);
Py_INCREF(Py_None);
}
PT(PandaNode) node;
PyObject *result = PythonThread::call_python_func(_load_func, args);
if (result != nullptr) {
if (DtoolInstance_Check(result)) {
node = (PandaNode *)DtoolInstance_UPCAST(result, Dtool_PandaNode);
}
Py_DECREF(result);
}
Py_DECREF(args);
if (node == nullptr) {
PyObject *exc_type = _PyErr_OCCURRED();
if (!exc_type) {
loader_cat.error()
<< "load_file must return valid PandaNode or raise exception\n";
} else {
loader_cat.error()
<< "Loading " << path.get_basename()
<< " failed with " << ((PyTypeObject *)exc_type)->tp_name << " exception.\n";
PyErr_Clear();
}
}
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_Release(gstate);
#endif
if (node != nullptr && node->is_of_type(ModelRoot::get_class_type())) {
ModelRoot *model_root = DCAST(ModelRoot, node.p());
model_root->set_fullpath(path);
model_root->set_timestamp(vfile->get_timestamp());
}
return node;
}
/**
*
*/
bool PythonLoaderFileType::
save_file(const Filename &path, const LoaderOptions &options,
PandaNode *node) const {
if (!supports_save()) {
return false;
}
nassertr(node != nullptr, false);
node->ref();
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#endif
// Wrap the arguments.
PyObject *args = PyTuple_New(3);
PyTuple_SET_ITEM(args, 0, DTool_CreatePyInstance((void *)&path, Dtool_Filename, false, true));
PyTuple_SET_ITEM(args, 1, DTool_CreatePyInstance((void *)&options, Dtool_LoaderOptions, false, true));
PyTuple_SET_ITEM(args, 2, DTool_CreatePyInstanceTyped((void *)node, Dtool_PandaNode, true, false, node->get_type_index()));
PyObject *result = PythonThread::call_python_func(_load_func, args);
Py_DECREF(args);
if (result != nullptr) {
Py_DECREF(result);
} else {
PyErr_Clear();
loader_cat.error()
<< "save_file failed with an exception.\n";
}
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_Release(gstate);
#endif
return (result != nullptr);
}
#endif // HAVE_PYTHON

View File

@@ -0,0 +1,81 @@
/**
* 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 pythonLoaderFileType.h
* @author rdb
* @date 2019-07-29
*/
#ifndef PYTHONLOADERFILETYPE_H
#define PYTHONLOADERFILETYPE_H
#include "pandabase.h"
#ifdef HAVE_PYTHON
#include "loaderFileType.h"
/**
* This defines a Python-based loader plug-in. An instance of this can be
* constructed by inheritance and explicitly registered, or it can be created
* by passing in a pkg_resources.EntryPoint instance.
*/
class PythonLoaderFileType : public LoaderFileType {
public:
PythonLoaderFileType();
PythonLoaderFileType(std::string extension, PyObject *entry_point);
~PythonLoaderFileType();
bool init(PyObject *loader);
bool ensure_loaded() const;
virtual std::string get_name() const override;
virtual std::string get_extension() const override;
virtual std::string get_additional_extensions() const override;
virtual bool supports_compressed() const override;
virtual bool supports_load() const override;
virtual bool supports_save() const override;
virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options,
BamCacheRecord *record) const override;
virtual bool save_file(const Filename &path, const LoaderOptions &options,
PandaNode *node) const override;
private:
std::string _extension;
std::string _additional_extensions;
PyObject *_entry_point = nullptr;
PyObject *_load_func = nullptr;
PyObject *_save_func = nullptr;
bool _supports_compressed = false;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
LoaderFileType::init_type();
register_type(_type_handle, "PythonLoaderFileType",
LoaderFileType::get_class_type());
}
virtual TypeHandle get_type() const override {
return get_class_type();
}
virtual TypeHandle force_init_type() override {
init_type();
return get_class_type();
}
private:
static TypeHandle _type_handle;
};
#endif // HAVE_PYTHON
#endif

View File

@@ -16,6 +16,7 @@
#include "wglGraphicsBuffer.h"
#include "wglGraphicsPipe.h"
#include "string_utils.h"
#include <atomic>
TypeHandle wglGraphicsStateGuardian::_type_handle;
@@ -49,7 +50,6 @@ wglGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
_wglCreateContextAttribsARB = nullptr;
get_gamma_table();
atexit(atexit_function);
}
/**
@@ -886,6 +886,12 @@ static_set_gamma(bool restore, PN_stdfloat gamma) {
if (SetDeviceGammaRamp (hdc, ramp)) {
set = true;
// Register an atexit handler
static std::atomic_flag gamma_modified = ATOMIC_FLAG_INIT;
if (!gamma_modified.test_and_set()) {
atexit(atexit_function);
}
}
ReleaseDC (nullptr, hdc);