461 lines
13 KiB
C++
461 lines
13 KiB
C++
/**
|
|
* 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 graphicsPipeSelection.cxx
|
|
* @author drose
|
|
* @date 2002-08-15
|
|
*/
|
|
|
|
#include "graphicsPipeSelection.h"
|
|
#include "lightMutexHolder.h"
|
|
#include "string_utils.h"
|
|
#include "filename.h"
|
|
#include "load_dso.h"
|
|
#include "config_display.h"
|
|
#include "typeRegistry.h"
|
|
#include "config_putil.h"
|
|
|
|
#include <algorithm>
|
|
|
|
using std::string;
|
|
|
|
GraphicsPipeSelection *GraphicsPipeSelection::_global_ptr = nullptr;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
GraphicsPipeSelection::
|
|
GraphicsPipeSelection() : _lock("GraphicsPipeSelection") {
|
|
// We declare these variables here instead of in config_display, in case
|
|
// this constructor is running at static init time.
|
|
ConfigVariableString load_display
|
|
("load-display", "*",
|
|
PRC_DESC("Specify the name of the default graphics display library or "
|
|
"GraphicsPipe to load. It is the name of a shared library (or * for "
|
|
"all libraries named in aux-display), optionally followed by the "
|
|
"name of the particular GraphicsPipe class to create."));
|
|
|
|
ConfigVariableList aux_display
|
|
("aux-display",
|
|
PRC_DESC("Names each of the graphics display libraries that are available on "
|
|
"a particular platform. This variable may be repeated several "
|
|
"times. These libraries will be tried one at a time if the library "
|
|
"specified by load_display cannot be loaded."));
|
|
|
|
_default_display_module = load_display.get_word(0);
|
|
_default_pipe_name = load_display.get_word(1);
|
|
|
|
if (_default_display_module == "*") {
|
|
// '*' or empty string is the key for all display modules.
|
|
_default_display_module = string();
|
|
|
|
} else if (!_default_display_module.empty()) {
|
|
_display_modules.push_back(_default_display_module);
|
|
}
|
|
|
|
// Also get the set of modules named in the various aux-display Config
|
|
// variables. We'll want to know this when we call load_modules() later.
|
|
size_t num_aux = aux_display.get_num_unique_values();
|
|
for (size_t i = 0; i < num_aux; ++i) {
|
|
string name = aux_display.get_unique_value(i);
|
|
if (name != _default_display_module) {
|
|
_display_modules.push_back(name);
|
|
}
|
|
}
|
|
|
|
_default_module_loaded = false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
GraphicsPipeSelection::
|
|
~GraphicsPipeSelection() {
|
|
}
|
|
|
|
/**
|
|
* Returns the number of different types of GraphicsPipes that are available
|
|
* to create through this interface.
|
|
*/
|
|
int GraphicsPipeSelection::
|
|
get_num_pipe_types() const {
|
|
load_default_module();
|
|
|
|
int result;
|
|
{
|
|
LightMutexHolder holder(_lock);
|
|
result = _pipe_types.size();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns the nth type of GraphicsPipe available through this interface.
|
|
*/
|
|
TypeHandle GraphicsPipeSelection::
|
|
get_pipe_type(int n) const {
|
|
load_default_module();
|
|
|
|
TypeHandle result;
|
|
{
|
|
LightMutexHolder holder(_lock);
|
|
if (n >= 0 && n < (int)_pipe_types.size()) {
|
|
result = _pipe_types[n]._type;
|
|
} else {
|
|
result = TypeHandle::none();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Writes a list of the currently known GraphicsPipe types to nout, for the
|
|
* user's information.
|
|
*/
|
|
void GraphicsPipeSelection::
|
|
print_pipe_types() const {
|
|
load_default_module();
|
|
|
|
LightMutexHolder holder(_lock);
|
|
nout << "Known pipe types:" << std::endl;
|
|
for (const PipeType &pipe_type : _pipe_types) {
|
|
nout << " " << pipe_type._type << "\n";
|
|
}
|
|
if (_display_modules.empty()) {
|
|
nout << "(all display modules loaded.)\n";
|
|
} else {
|
|
nout << "(" << _display_modules.size()
|
|
<< " aux display modules not yet loaded.)\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new GraphicsPipe of the indicated type (or a type more specific
|
|
* than the indicated type, if necessary) and returns it. Returns NULL if the
|
|
* type cannot be matched.
|
|
*
|
|
* If the type is not already defined, this will implicitly load the named
|
|
* module, or if module_name is empty, it will call load_aux_modules().
|
|
*/
|
|
PT(GraphicsPipe) GraphicsPipeSelection::
|
|
make_pipe(const string &type_name, const string &module_name) {
|
|
TypeRegistry *type_reg = TypeRegistry::ptr();
|
|
|
|
// First, see if the type is already available.
|
|
TypeHandle type = type_reg->find_type(type_name);
|
|
|
|
// If it isn't, try the named module.
|
|
if (type == TypeHandle::none()) {
|
|
if (!module_name.empty()) {
|
|
load_named_module(module_name);
|
|
type = type_reg->find_type(type_name);
|
|
}
|
|
}
|
|
|
|
// If that didn't help, try the default module.
|
|
if (type == TypeHandle::none()) {
|
|
load_default_module();
|
|
type = type_reg->find_type(type_name);
|
|
}
|
|
|
|
// Still not enough, try all modules.
|
|
if (type == TypeHandle::none()) {
|
|
load_aux_modules();
|
|
type = type_reg->find_type(type_name);
|
|
}
|
|
|
|
if (type == TypeHandle::none()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return make_pipe(type);
|
|
}
|
|
|
|
/**
|
|
* Creates a new GraphicsPipe of the indicated type (or a type more specific
|
|
* than the indicated type, if necessary) and returns it. Returns NULL if the
|
|
* type cannot be matched.
|
|
*/
|
|
PT(GraphicsPipe) GraphicsPipeSelection::
|
|
make_pipe(TypeHandle type) {
|
|
LightMutexHolder holder(_lock);
|
|
|
|
// First, look for an exact match of the requested type.
|
|
for (const PipeType &ptype : _pipe_types) {
|
|
if (ptype._type == type) {
|
|
// Here's an exact match.
|
|
PT(GraphicsPipe) pipe = (*ptype._constructor)();
|
|
if (pipe != nullptr) {
|
|
return pipe;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now look for a more-specific type.
|
|
for (const PipeType &ptype : _pipe_types) {
|
|
if (ptype._type.is_derived_from(type)) {
|
|
// Here's an approximate match.
|
|
PT(GraphicsPipe) pipe = (*ptype._constructor)();
|
|
if (pipe != nullptr) {
|
|
return pipe;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Couldn't find any match; load the default module and try again.
|
|
load_default_module();
|
|
for (const PipeType &ptype : _pipe_types) {
|
|
if (ptype._type.is_derived_from(type)) {
|
|
// Here's an approximate match.
|
|
PT(GraphicsPipe) pipe = (*ptype._constructor)();
|
|
if (pipe != nullptr) {
|
|
return pipe;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Couldn't find a matching pipe type.
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Returns a new GraphicsPipe of a type defined by the indicated module.
|
|
* Returns NULL if the module is not found or does not properly recommend a
|
|
* GraphicsPipe.
|
|
*/
|
|
PT(GraphicsPipe) GraphicsPipeSelection::
|
|
make_module_pipe(const string &module_name) {
|
|
if (display_cat.is_debug()) {
|
|
display_cat.debug()
|
|
<< "make_module_pipe(" << module_name << ")\n";
|
|
}
|
|
|
|
TypeHandle pipe_type = load_named_module(module_name);
|
|
if (pipe_type == TypeHandle::none()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return make_pipe(pipe_type);
|
|
}
|
|
|
|
/**
|
|
* Creates a new GraphicsPipe of some arbitrary type. The user may specify a
|
|
* preference using the Configrc file; otherwise, one will be chosen
|
|
* arbitrarily.
|
|
*/
|
|
PT(GraphicsPipe) GraphicsPipeSelection::
|
|
make_default_pipe() {
|
|
load_default_module();
|
|
|
|
LightMutexHolder holder(_lock);
|
|
|
|
if (!_default_pipe_name.empty()) {
|
|
// First, look for an exact match of the default type name from the
|
|
// Configrc file (excepting case and hyphenunderscore).
|
|
for (const PipeType &ptype : _pipe_types) {
|
|
if (cmp_nocase_uh(ptype._type.get_name(), _default_pipe_name) == 0) {
|
|
// Here's an exact match.
|
|
PT(GraphicsPipe) pipe = (*ptype._constructor)();
|
|
if (pipe != nullptr) {
|
|
return pipe;
|
|
}
|
|
}
|
|
}
|
|
|
|
// No match; look for a substring match.
|
|
string preferred_name = downcase(_default_pipe_name);
|
|
for (const PipeType &ptype : _pipe_types) {
|
|
string ptype_name = downcase(ptype._type.get_name());
|
|
if (ptype_name.find(preferred_name) != string::npos) {
|
|
// Here's a substring match.
|
|
PT(GraphicsPipe) pipe = (*ptype._constructor)();
|
|
if (pipe != nullptr) {
|
|
return pipe;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Couldn't find a matching pipe type; choose the first one on the list.
|
|
for (const PipeType &ptype : _pipe_types) {
|
|
PT(GraphicsPipe) pipe = (*ptype._constructor)();
|
|
if (pipe != nullptr) {
|
|
return pipe;
|
|
}
|
|
}
|
|
|
|
// Nothing. Probably the list was empty.
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Loads all the modules named in the aux-display Configrc variable, making as
|
|
* many graphics pipes as possible available.
|
|
*/
|
|
void GraphicsPipeSelection::
|
|
load_aux_modules() {
|
|
for (const string &module : _display_modules) {
|
|
load_named_module(module);
|
|
}
|
|
|
|
_display_modules.clear();
|
|
_default_module_loaded = true;
|
|
}
|
|
|
|
/**
|
|
* Adds a new kind of GraphicsPipe to the list of available pipes for
|
|
* creation. Normally, this is called at static init type by the various
|
|
* shared libraries as they are linked in. Returns true on success, false on
|
|
* failure.
|
|
*/
|
|
bool GraphicsPipeSelection::
|
|
add_pipe_type(TypeHandle type, PipeConstructorFunc *func) {
|
|
nassertr(func != nullptr, false);
|
|
|
|
if (!type.is_derived_from(GraphicsPipe::get_class_type())) {
|
|
display_cat->warning()
|
|
<< "Attempt to register " << type << " as a GraphicsPipe type.\n";
|
|
return false;
|
|
}
|
|
|
|
// First, make sure we don't already have a GraphicsPipe of this type.
|
|
LightMutexHolder holder(_lock);
|
|
for (const PipeType &ptype : _pipe_types) {
|
|
if (ptype._type == type) {
|
|
display_cat->warning()
|
|
<< "Attempt to register GraphicsPipe type " << type
|
|
<< " more than once.\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (display_cat->is_debug()) {
|
|
display_cat->debug()
|
|
<< "Registering " << type << " as a GraphicsPipe type.\n";
|
|
}
|
|
|
|
// Ok, now add a new entry.
|
|
_pipe_types.push_back(PipeType(type, func));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Loads the particular display module listed in the load-display Configrc
|
|
* variable, which should default the default pipe time. If this string is
|
|
* empty or "*", loads all modules named in aux-display.
|
|
*/
|
|
void GraphicsPipeSelection::
|
|
do_load_default_module() {
|
|
if (_default_display_module.empty()) {
|
|
load_aux_modules();
|
|
return;
|
|
}
|
|
|
|
load_named_module(_default_display_module);
|
|
|
|
DisplayModules::iterator di =
|
|
std::find(_display_modules.begin(), _display_modules.end(),
|
|
_default_display_module);
|
|
if (di != _display_modules.end()) {
|
|
_display_modules.erase(di);
|
|
}
|
|
|
|
_default_module_loaded = true;
|
|
|
|
if (_pipe_types.empty()) {
|
|
// If we still don't have any pipes after loading the default module,
|
|
// automatically load the aux modules.
|
|
load_aux_modules();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads the indicated display module by looking for a matching .dll or .so
|
|
* file. Returns the TypeHandle recommended by the module, or
|
|
* TypeHandle::none() on failure.
|
|
*/
|
|
TypeHandle GraphicsPipeSelection::
|
|
load_named_module(const string &name) {
|
|
LightMutexHolder holder(_loaded_modules_lock);
|
|
|
|
LoadedModules::iterator mi = _loaded_modules.find(name);
|
|
if (mi != _loaded_modules.end()) {
|
|
// We have previously loaded this module. Don't attempt to re-load it.
|
|
return (*mi).second._default_pipe_type;
|
|
}
|
|
|
|
// We have not yet loaded this module. Load it now.
|
|
Filename dlname = Filename::dso_filename("lib" + name + ".so");
|
|
display_cat.info()
|
|
<< "loading display module: " << dlname.to_os_specific() << std::endl;
|
|
void *handle = load_dso(get_plugin_path().get_value(), dlname);
|
|
if (handle == nullptr) {
|
|
std::string error = load_dso_error();
|
|
display_cat.warning()
|
|
<< "Unable to load " << dlname.get_basename() << ": " << error << std::endl;
|
|
return TypeHandle::none();
|
|
}
|
|
|
|
// Now get the module's recommended pipe type. This requires calling a
|
|
// specially-named function that should have been exported from the module.
|
|
string symbol_name = "get_pipe_type_" + name;
|
|
void *dso_symbol = get_dso_symbol(handle, symbol_name);
|
|
if (display_cat.is_debug()) {
|
|
display_cat.debug()
|
|
<< "symbol of " << symbol_name << " = " << dso_symbol << "\n";
|
|
}
|
|
|
|
TypeHandle pipe_type = TypeHandle::none();
|
|
|
|
if (dso_symbol == nullptr) {
|
|
// Couldn't find the module function.
|
|
display_cat.warning()
|
|
<< "Unable to find " << symbol_name << " in " << dlname.get_basename()
|
|
<< "\n";
|
|
|
|
} else {
|
|
// We successfully loaded the module, and we found the get_pipe_type_*
|
|
// recommendation function. Call it to figure out what pipe type we
|
|
// should expect.
|
|
typedef int FuncType();
|
|
int pipe_type_index = (*(FuncType *)dso_symbol)();
|
|
if (display_cat.is_debug()) {
|
|
display_cat.debug()
|
|
<< "pipe_type_index = " << pipe_type_index << "\n";
|
|
}
|
|
|
|
if (pipe_type_index != 0) {
|
|
TypeRegistry *type_reg = TypeRegistry::ptr();
|
|
pipe_type = type_reg->find_type_by_id(pipe_type_index);
|
|
if (display_cat.is_debug()) {
|
|
display_cat.debug()
|
|
<< "pipe_type = " << pipe_type << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pipe_type == TypeHandle::none()) {
|
|
// The recommendation function returned a bogus type index, or the
|
|
// function didn't work at all. We can't safely unload the module,
|
|
// though, because it may have assigned itself into the
|
|
// GraphicsPipeSelection table. So we carry on.
|
|
display_cat.warning()
|
|
<< "No default pipe type available for " << dlname.get_basename()
|
|
<< "\n";
|
|
}
|
|
|
|
LoadedModule &module = _loaded_modules[name];
|
|
module._module_name = name;
|
|
module._module_handle = handle;
|
|
module._default_pipe_type = pipe_type;
|
|
|
|
return pipe_type;
|
|
}
|