Panda3D
 All Classes Functions Variables Enumerations
graphicsPipeSelection.cxx
00001 // Filename: graphicsPipeSelection.cxx
00002 // Created by:  drose (15Aug02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "graphicsPipeSelection.h"
00016 #include "lightMutexHolder.h"
00017 #include "string_utils.h"
00018 #include "filename.h"
00019 #include "load_dso.h"
00020 #include "config_display.h"
00021 #include "typeRegistry.h"
00022 #include "pset.h"
00023 #include "config_util.h"
00024 
00025 #include <algorithm>
00026 
00027 GraphicsPipeSelection *GraphicsPipeSelection::_global_ptr = NULL;
00028 
00029 ////////////////////////////////////////////////////////////////////
00030 //     Function: GraphicsPipeSelection::Constructor
00031 //       Access: Protected
00032 //  Description:
00033 ////////////////////////////////////////////////////////////////////
00034 GraphicsPipeSelection::
00035 GraphicsPipeSelection() : _lock("GraphicsPipeSelection") {
00036   // We declare these variables here instead of in config_display, in
00037   // case this constructor is running at static init time.
00038   ConfigVariableString load_display
00039     ("load-display", "*",
00040      PRC_DESC("Specify the name of the default graphics display library or "
00041               "GraphicsPipe to load.  It is the name of a shared library (or * for "
00042               "all libraries named in aux-display), optionally followed by the "
00043               "name of the particular GraphicsPipe class to create."));
00044   
00045   ConfigVariableList aux_display
00046     ("aux-display",
00047      PRC_DESC("Names each of the graphics display libraries that are available on "
00048               "a particular platform.  This variable may be repeated several "
00049               "times.  These libraries will be tried one at a time if the library "
00050               "specified by load_display cannot be loaded."));
00051 
00052   _default_display_module = load_display.get_word(0);
00053   _default_pipe_name = load_display.get_word(1);
00054 
00055   if (_default_display_module == "*") {
00056     // '*' or empty string is the key for all display modules.
00057     _default_display_module = string();
00058 
00059   } else if (!_default_display_module.empty()) {
00060     _display_modules.push_back(_default_display_module);
00061   }
00062 
00063   // Also get the set of modules named in the various aux-display
00064   // Config variables.  We'll want to know this when we call
00065   // load_modules() later.
00066   int num_aux = aux_display.get_num_unique_values();
00067   for (int i = 0; i < num_aux; i++) {
00068     string name = aux_display.get_unique_value(i);
00069     if (name != _default_display_module) {
00070       _display_modules.push_back(name);
00071     }
00072   }
00073 
00074   _default_module_loaded = false;
00075 }
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: GraphicsPipeSelection::Destructor
00079 //       Access: Protected
00080 //  Description:
00081 ////////////////////////////////////////////////////////////////////
00082 GraphicsPipeSelection::
00083 ~GraphicsPipeSelection() {
00084 }
00085 
00086 ////////////////////////////////////////////////////////////////////
00087 //     Function: GraphicsPipeSelection::get_num_pipe_types
00088 //       Access: Published
00089 //  Description: Returns the number of different types of
00090 //               GraphicsPipes that are available to create through
00091 //               this interface.
00092 ////////////////////////////////////////////////////////////////////
00093 int GraphicsPipeSelection::
00094 get_num_pipe_types() const {
00095   load_default_module();
00096 
00097   int result;
00098   {
00099     LightMutexHolder holder(_lock);
00100     result = _pipe_types.size();
00101   }
00102   return result;
00103 }
00104 
00105 ////////////////////////////////////////////////////////////////////
00106 //     Function: GraphicsPipeSelection::get_pipe_type
00107 //       Access: Published
00108 //  Description: Returns the nth type of GraphicsPipe available
00109 //               through this interface.
00110 ////////////////////////////////////////////////////////////////////
00111 TypeHandle GraphicsPipeSelection::
00112 get_pipe_type(int n) const {
00113   load_default_module();
00114 
00115   TypeHandle result;
00116   {
00117     LightMutexHolder holder(_lock);
00118     if (n >= 0 && n < (int)_pipe_types.size()) {
00119       result = _pipe_types[n]._type;
00120     }
00121   }
00122   return result;
00123 }
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: GraphicsPipeSelection::print_pipe_types
00127 //       Access: Published
00128 //  Description: Writes a list of the currently known GraphicsPipe
00129 //               types to nout, for the user's information.
00130 ////////////////////////////////////////////////////////////////////
00131 void GraphicsPipeSelection::
00132 print_pipe_types() const {
00133   load_default_module();
00134 
00135   LightMutexHolder holder(_lock);
00136   nout << "Known pipe types:" << endl;
00137   PipeTypes::const_iterator pi;
00138   for (pi = _pipe_types.begin(); pi != _pipe_types.end(); ++pi) {
00139     const PipeType &pipe_type = (*pi);
00140     nout << "  " << pipe_type._type << "\n";
00141   }
00142   if (_display_modules.empty()) {
00143     nout << "(all display modules loaded.)\n";
00144   } else {
00145     nout << "(" << _display_modules.size() 
00146          << " aux display modules not yet loaded.)\n";
00147   }
00148 }
00149 
00150 ////////////////////////////////////////////////////////////////////
00151 //     Function: GraphicsPipeSelection::make_pipe
00152 //       Access: Published
00153 //  Description: Creates a new GraphicsPipe of the indicated type (or
00154 //               a type more specific than the indicated type, if
00155 //               necessary) and returns it.  Returns NULL if the type
00156 //               cannot be matched.
00157 //
00158 //               If the type is not already defined, this will
00159 //               implicitly load the named module, or if module_name
00160 //               is empty, it will call load_aux_modules().
00161 ////////////////////////////////////////////////////////////////////
00162 PT(GraphicsPipe) GraphicsPipeSelection::
00163 make_pipe(const string &type_name, const string &module_name) {
00164   TypeRegistry *type_reg = TypeRegistry::ptr();
00165 
00166   // First, see if the type is already available.
00167   TypeHandle type = type_reg->find_type(type_name);
00168 
00169   // If it isn't, try the named module.
00170   if (type == TypeHandle::none()) {
00171     if (!module_name.empty()) {
00172       load_named_module(module_name);
00173       type = type_reg->find_type(type_name);
00174     }
00175   }
00176 
00177   // If that didn't help, try the default module.
00178   if (type == TypeHandle::none()) {
00179     load_default_module();
00180     type = type_reg->find_type(type_name);
00181   }
00182 
00183   // Still not enough, try all modules.
00184   if (type == TypeHandle::none()) {
00185     load_aux_modules();
00186     type = type_reg->find_type(type_name);
00187   }
00188 
00189   if (type == TypeHandle::none()) {
00190     return NULL;
00191   }
00192 
00193   return make_pipe(type);
00194 }
00195 
00196 ////////////////////////////////////////////////////////////////////
00197 //     Function: GraphicsPipeSelection::make_pipe
00198 //       Access: Published
00199 //  Description: Creates a new GraphicsPipe of the indicated type (or
00200 //               a type more specific than the indicated type, if
00201 //               necessary) and returns it.  Returns NULL if the type
00202 //               cannot be matched.
00203 ////////////////////////////////////////////////////////////////////
00204 PT(GraphicsPipe) GraphicsPipeSelection::
00205 make_pipe(TypeHandle type) {
00206   LightMutexHolder holder(_lock);
00207   PipeTypes::const_iterator ti;
00208 
00209   // First, look for an exact match of the requested type.
00210   for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
00211     const PipeType &ptype = (*ti);
00212     if (ptype._type == type) {
00213       // Here's an exact match.
00214       PT(GraphicsPipe) pipe = (*ptype._constructor)();
00215       if (pipe != (GraphicsPipe *)NULL) {
00216         return pipe;
00217       }
00218     }
00219   }
00220 
00221   // Now look for a more-specific type.
00222   for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
00223     const PipeType &ptype = (*ti);
00224     if (ptype._type.is_derived_from(type)) {
00225       // Here's an approximate match.
00226       PT(GraphicsPipe) pipe = (*ptype._constructor)();
00227       if (pipe != (GraphicsPipe *)NULL) {
00228         return pipe;
00229       }
00230     }
00231   }
00232 
00233   // Couldn't find any match; load the default module and try again.
00234   load_default_module();
00235   for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
00236     const PipeType &ptype = (*ti);
00237     if (ptype._type.is_derived_from(type)) {
00238       // Here's an approximate match.
00239       PT(GraphicsPipe) pipe = (*ptype._constructor)();
00240       if (pipe != (GraphicsPipe *)NULL) {
00241         return pipe;
00242       }
00243     }
00244   }
00245 
00246   // Couldn't find a matching pipe type.
00247   return NULL;
00248 }
00249 
00250 ////////////////////////////////////////////////////////////////////
00251 //     Function: GraphicsPipeSelection::make_module_pipe
00252 //       Access: Published
00253 //  Description: Returns a new GraphicsPipe of a type defined by the
00254 //               indicated module.  Returns NULL if the module is not
00255 //               found or does not properly recommend a GraphicsPipe.
00256 ////////////////////////////////////////////////////////////////////
00257 PT(GraphicsPipe) GraphicsPipeSelection::
00258 make_module_pipe(const string &module_name) {
00259   if (display_cat.is_debug()) {
00260     display_cat.debug()
00261       << "make_module_pipe(" << module_name << ")\n";
00262   }
00263 
00264   TypeHandle pipe_type = load_named_module(module_name);
00265   if (pipe_type == TypeHandle::none()) {
00266     return NULL;
00267   }
00268    
00269   return make_pipe(pipe_type);
00270 }
00271 
00272 ////////////////////////////////////////////////////////////////////
00273 //     Function: GraphicsPipeSelection::make_default_pipe
00274 //       Access: Published
00275 //  Description: Creates a new GraphicsPipe of some arbitrary type.
00276 //               The user may specify a preference using the Configrc
00277 //               file; otherwise, one will be chosen arbitrarily.
00278 ////////////////////////////////////////////////////////////////////
00279 PT(GraphicsPipe) GraphicsPipeSelection::
00280 make_default_pipe() {
00281   load_default_module();
00282 
00283   LightMutexHolder holder(_lock);
00284   PipeTypes::const_iterator ti;
00285 
00286   if (!_default_pipe_name.empty()) {
00287     // First, look for an exact match of the default type name from
00288     // the Configrc file (excepting case and hyphen/underscore).
00289     for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
00290       const PipeType &ptype = (*ti);
00291       if (cmp_nocase_uh(ptype._type.get_name(), _default_pipe_name) == 0) {
00292         // Here's an exact match.
00293         PT(GraphicsPipe) pipe = (*ptype._constructor)();
00294         if (pipe != (GraphicsPipe *)NULL) {
00295           return pipe;
00296         }
00297       }
00298     }
00299     
00300     // No match; look for a substring match.
00301     string preferred_name = downcase(_default_pipe_name);
00302     for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
00303       const PipeType &ptype = (*ti);
00304       string ptype_name = downcase(ptype._type.get_name());
00305       if (ptype_name.find(preferred_name) != string::npos) {
00306         // Here's a substring match.
00307         PT(GraphicsPipe) pipe = (*ptype._constructor)();
00308         if (pipe != (GraphicsPipe *)NULL) {
00309           return pipe;
00310         }
00311       }
00312     }
00313   }
00314     
00315   // Couldn't find a matching pipe type; choose the first one on the
00316   // list.
00317   for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
00318     const PipeType &ptype = (*ti);
00319     PT(GraphicsPipe) pipe = (*ptype._constructor)();
00320     if (pipe != (GraphicsPipe *)NULL) {
00321       return pipe;
00322     }
00323   }
00324 
00325   // Nothing.  Probably the list was empty.
00326   return NULL;
00327 }
00328 
00329 ////////////////////////////////////////////////////////////////////
00330 //     Function: GraphicsPipeSelection::load_aux_modules
00331 //       Access: Published
00332 //  Description: Loads all the modules named in the aux-display
00333 //               Configrc variable, making as many graphics pipes as
00334 //               possible available.
00335 ////////////////////////////////////////////////////////////////////
00336 void GraphicsPipeSelection::
00337 load_aux_modules() {
00338   DisplayModules::iterator di;
00339   for (di = _display_modules.begin(); di != _display_modules.end(); ++di) {
00340     load_named_module(*di);
00341   }
00342 
00343   _display_modules.clear();
00344   _default_module_loaded = true;
00345 }
00346 
00347 ////////////////////////////////////////////////////////////////////
00348 //     Function: GraphicsPipeSelection::add_pipe_type
00349 //       Access: Public
00350 //  Description: Adds a new kind of GraphicsPipe to the list of
00351 //               available pipes for creation.  Normally, this is
00352 //               called at static init type by the various shared
00353 //               libraries as they are linked in.  Returns true on
00354 //               success, false on failure.
00355 ////////////////////////////////////////////////////////////////////
00356 bool GraphicsPipeSelection::
00357 add_pipe_type(TypeHandle type, PipeConstructorFunc *func) {
00358   nassertr(func != NULL, false);
00359 
00360   if (!type.is_derived_from(GraphicsPipe::get_class_type())) {
00361     display_cat.warning()
00362       << "Attempt to register " << type << " as a GraphicsPipe type.\n";
00363     return false;
00364   }
00365   
00366   // First, make sure we don't already have a GraphicsPipe of this
00367   // type.
00368   LightMutexHolder holder(_lock);
00369   PipeTypes::const_iterator ti;
00370   for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
00371     const PipeType &ptype = (*ti);
00372     if (ptype._type == type) {
00373       display_cat.warning()
00374         << "Attempt to register GraphicsPipe type " << type
00375         << " more than once.\n";
00376       return false;
00377     }
00378   }
00379 
00380   if (display_cat.is_debug()) {
00381     display_cat.debug()
00382       << "Registering " << type << " as a GraphicsPipe type.\n";
00383   }
00384 
00385   // Ok, now add a new entry.
00386   _pipe_types.push_back(PipeType(type, func));
00387 
00388   return true;
00389 }
00390 
00391 
00392 ////////////////////////////////////////////////////////////////////
00393 //     Function: GraphicsPipeSelection::do_load_default_module
00394 //       Access: Private
00395 //  Description: Loads the particular display module listed in the
00396 //               load-display Configrc variable, which should default
00397 //               the default pipe time.  If this string is empty or
00398 //               "*", loads all modules named in aux-display.
00399 ////////////////////////////////////////////////////////////////////
00400 void GraphicsPipeSelection::
00401 do_load_default_module() {
00402   if (_default_display_module.empty()) {
00403     load_aux_modules();
00404     return;
00405   }
00406 
00407   load_named_module(_default_display_module);
00408 
00409   DisplayModules::iterator di =
00410     find(_display_modules.begin(), _display_modules.end(), 
00411          _default_display_module);
00412   if (di != _display_modules.end()) {
00413     _display_modules.erase(di);
00414   }
00415 
00416   _default_module_loaded = true;
00417 
00418   if (_pipe_types.empty()) {
00419     // If we still don't have any pipes after loading the default
00420     // module, automatically load the aux modules.
00421     load_aux_modules();
00422   }
00423 }
00424 
00425 ////////////////////////////////////////////////////////////////////
00426 //     Function: GraphicsPipeSelection::load_named_module
00427 //       Access: Private
00428 //  Description: Loads the indicated display module by looking for a
00429 //               matching .dll or .so file.  Returns the TypeHandle
00430 //               recommended by the module, or TypeHandle::none() on
00431 //               failure.
00432 ////////////////////////////////////////////////////////////////////
00433 TypeHandle GraphicsPipeSelection::
00434 load_named_module(const string &name) {
00435   LightMutexHolder holder(_loaded_modules_lock);
00436 
00437   LoadedModules::iterator mi = _loaded_modules.find(name);
00438   if (mi != _loaded_modules.end()) {
00439     // We have previously loaded this module.  Don't attempt to
00440     // re-load it.
00441     return (*mi).second._default_pipe_type;
00442   }
00443 
00444   // We have not yet loaded this module.  Load it now.
00445   Filename dlname = Filename::dso_filename("lib" + name + ".so");
00446   display_cat.info()
00447     << "loading display module: " << dlname.to_os_specific() << endl;
00448   void *handle = load_dso(get_plugin_path().get_value(), dlname);
00449   if (handle == (void *)NULL) {
00450     display_cat.warning()
00451       << "Unable to load: " << load_dso_error() << endl;
00452     return TypeHandle::none();
00453   }
00454 
00455   // Now get the module's recommended pipe type.  This requires
00456   // calling a specially-named function that should have been exported
00457   // from the module.
00458   string symbol_name = "get_pipe_type_" + name;
00459   void *dso_symbol = get_dso_symbol(handle, symbol_name);
00460   if (display_cat.is_debug()) {
00461     display_cat.debug()
00462       << "symbol of " << symbol_name << " = " << dso_symbol << "\n";
00463   }
00464 
00465   TypeHandle pipe_type = TypeHandle::none();
00466 
00467   if (dso_symbol == (void *)NULL) {
00468     // Couldn't find the module function.
00469     display_cat.warning()
00470       << "Unable to find " << symbol_name << " in " << dlname.get_basename()
00471       << "\n";
00472 
00473   } else {
00474     // We successfully loaded the module, and we found the
00475     // get_pipe_type_* recommendation function.  Call it to figure
00476     // out what pipe type we should expect.
00477     typedef int FuncType();
00478     int pipe_type_index = (*(FuncType *)dso_symbol)();
00479     if (display_cat.is_debug()) {
00480       display_cat.debug()
00481         << "pipe_type_index = " << pipe_type_index << "\n";
00482     }
00483     
00484     if (pipe_type_index != 0) {
00485       TypeRegistry *type_reg = TypeRegistry::ptr();
00486       pipe_type = type_reg->find_type_by_id(pipe_type_index);
00487       if (display_cat.is_debug()) {
00488         display_cat.debug()
00489           << "pipe_type = " << pipe_type << "\n";
00490       }
00491     }
00492   }
00493     
00494   if (pipe_type == TypeHandle::none()) {
00495     // The recommendation function returned a bogus type index, or the
00496     // function didn't work at all.  We can't safely unload the
00497     // module, though, because it may have assigned itself into the
00498     // GraphicsPipeSelection table.  So we carry on.
00499     display_cat.warning()
00500       << "No default pipe type available for " << dlname.get_basename()
00501       << "\n";
00502   }
00503 
00504   LoadedModule &module = _loaded_modules[name];
00505   module._module_name = name;
00506   module._module_handle = handle;
00507   module._default_pipe_type = pipe_type;
00508 
00509   return pipe_type;
00510 }
 All Classes Functions Variables Enumerations