Panda3D
graphicsPipeSelection.cxx
1 // Filename: graphicsPipeSelection.cxx
2 // Created by: drose (15Aug02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "graphicsPipeSelection.h"
16 #include "lightMutexHolder.h"
17 #include "string_utils.h"
18 #include "filename.h"
19 #include "load_dso.h"
20 #include "config_display.h"
21 #include "typeRegistry.h"
22 #include "pset.h"
23 #include "config_util.h"
24 
25 #include <algorithm>
26 
27 GraphicsPipeSelection *GraphicsPipeSelection::_global_ptr = NULL;
28 
29 ////////////////////////////////////////////////////////////////////
30 // Function: GraphicsPipeSelection::Constructor
31 // Access: Protected
32 // Description:
33 ////////////////////////////////////////////////////////////////////
34 GraphicsPipeSelection::
35 GraphicsPipeSelection() : _lock("GraphicsPipeSelection") {
36  // We declare these variables here instead of in config_display, in
37  // case this constructor is running at static init time.
38  ConfigVariableString load_display
39  ("load-display", "*",
40  PRC_DESC("Specify the name of the default graphics display library or "
41  "GraphicsPipe to load. It is the name of a shared library (or * for "
42  "all libraries named in aux-display), optionally followed by the "
43  "name of the particular GraphicsPipe class to create."));
44 
45  ConfigVariableList aux_display
46  ("aux-display",
47  PRC_DESC("Names each of the graphics display libraries that are available on "
48  "a particular platform. This variable may be repeated several "
49  "times. These libraries will be tried one at a time if the library "
50  "specified by load_display cannot be loaded."));
51 
52  _default_display_module = load_display.get_word(0);
53  _default_pipe_name = load_display.get_word(1);
54 
55  if (_default_display_module == "*") {
56  // '*' or empty string is the key for all display modules.
57  _default_display_module = string();
58 
59  } else if (!_default_display_module.empty()) {
60  _display_modules.push_back(_default_display_module);
61  }
62 
63  // Also get the set of modules named in the various aux-display
64  // Config variables. We'll want to know this when we call
65  // load_modules() later.
66  int num_aux = aux_display.get_num_unique_values();
67  for (int i = 0; i < num_aux; i++) {
68  string name = aux_display.get_unique_value(i);
69  if (name != _default_display_module) {
70  _display_modules.push_back(name);
71  }
72  }
73 
74  _default_module_loaded = false;
75 }
76 
77 ////////////////////////////////////////////////////////////////////
78 // Function: GraphicsPipeSelection::Destructor
79 // Access: Protected
80 // Description:
81 ////////////////////////////////////////////////////////////////////
82 GraphicsPipeSelection::
83 ~GraphicsPipeSelection() {
84 }
85 
86 ////////////////////////////////////////////////////////////////////
87 // Function: GraphicsPipeSelection::get_num_pipe_types
88 // Access: Published
89 // Description: Returns the number of different types of
90 // GraphicsPipes that are available to create through
91 // this interface.
92 ////////////////////////////////////////////////////////////////////
95  load_default_module();
96 
97  int result;
98  {
99  LightMutexHolder holder(_lock);
100  result = _pipe_types.size();
101  }
102  return result;
103 }
104 
105 ////////////////////////////////////////////////////////////////////
106 // Function: GraphicsPipeSelection::get_pipe_type
107 // Access: Published
108 // Description: Returns the nth type of GraphicsPipe available
109 // through this interface.
110 ////////////////////////////////////////////////////////////////////
112 get_pipe_type(int n) const {
113  load_default_module();
114 
115  TypeHandle result;
116  {
117  LightMutexHolder holder(_lock);
118  if (n >= 0 && n < (int)_pipe_types.size()) {
119  result = _pipe_types[n]._type;
120  }
121  }
122  return result;
123 }
124 
125 ////////////////////////////////////////////////////////////////////
126 // Function: GraphicsPipeSelection::print_pipe_types
127 // Access: Published
128 // Description: Writes a list of the currently known GraphicsPipe
129 // types to nout, for the user's information.
130 ////////////////////////////////////////////////////////////////////
133  load_default_module();
134 
135  LightMutexHolder holder(_lock);
136  nout << "Known pipe types:" << endl;
137  PipeTypes::const_iterator pi;
138  for (pi = _pipe_types.begin(); pi != _pipe_types.end(); ++pi) {
139  const PipeType &pipe_type = (*pi);
140  nout << " " << pipe_type._type << "\n";
141  }
142  if (_display_modules.empty()) {
143  nout << "(all display modules loaded.)\n";
144  } else {
145  nout << "(" << _display_modules.size()
146  << " aux display modules not yet loaded.)\n";
147  }
148 }
149 
150 ////////////////////////////////////////////////////////////////////
151 // Function: GraphicsPipeSelection::make_pipe
152 // Access: Published
153 // Description: Creates a new GraphicsPipe of the indicated type (or
154 // a type more specific than the indicated type, if
155 // necessary) and returns it. Returns NULL if the type
156 // cannot be matched.
157 //
158 // If the type is not already defined, this will
159 // implicitly load the named module, or if module_name
160 // is empty, it will call load_aux_modules().
161 ////////////////////////////////////////////////////////////////////
162 PT(GraphicsPipe) GraphicsPipeSelection::
163 make_pipe(const string &type_name, const string &module_name) {
164  TypeRegistry *type_reg = TypeRegistry::ptr();
165 
166  // First, see if the type is already available.
167  TypeHandle type = type_reg->find_type(type_name);
168 
169  // If it isn't, try the named module.
170  if (type == TypeHandle::none()) {
171  if (!module_name.empty()) {
172  load_named_module(module_name);
173  type = type_reg->find_type(type_name);
174  }
175  }
176 
177  // If that didn't help, try the default module.
178  if (type == TypeHandle::none()) {
179  load_default_module();
180  type = type_reg->find_type(type_name);
181  }
182 
183  // Still not enough, try all modules.
184  if (type == TypeHandle::none()) {
186  type = type_reg->find_type(type_name);
187  }
188 
189  if (type == TypeHandle::none()) {
190  return NULL;
191  }
192 
193  return make_pipe(type);
194 }
195 
196 ////////////////////////////////////////////////////////////////////
197 // Function: GraphicsPipeSelection::make_pipe
198 // Access: Published
199 // Description: Creates a new GraphicsPipe of the indicated type (or
200 // a type more specific than the indicated type, if
201 // necessary) and returns it. Returns NULL if the type
202 // cannot be matched.
203 ////////////////////////////////////////////////////////////////////
204 PT(GraphicsPipe) GraphicsPipeSelection::
205 make_pipe(TypeHandle type) {
206  LightMutexHolder holder(_lock);
207  PipeTypes::const_iterator ti;
208 
209  // First, look for an exact match of the requested type.
210  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
211  const PipeType &ptype = (*ti);
212  if (ptype._type == type) {
213  // Here's an exact match.
214  PT(GraphicsPipe) pipe = (*ptype._constructor)();
215  if (pipe != (GraphicsPipe *)NULL) {
216  return pipe;
217  }
218  }
219  }
220 
221  // Now look for a more-specific type.
222  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
223  const PipeType &ptype = (*ti);
224  if (ptype._type.is_derived_from(type)) {
225  // Here's an approximate match.
226  PT(GraphicsPipe) pipe = (*ptype._constructor)();
227  if (pipe != (GraphicsPipe *)NULL) {
228  return pipe;
229  }
230  }
231  }
232 
233  // Couldn't find any match; load the default module and try again.
234  load_default_module();
235  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
236  const PipeType &ptype = (*ti);
237  if (ptype._type.is_derived_from(type)) {
238  // Here's an approximate match.
239  PT(GraphicsPipe) pipe = (*ptype._constructor)();
240  if (pipe != (GraphicsPipe *)NULL) {
241  return pipe;
242  }
243  }
244  }
245 
246  // Couldn't find a matching pipe type.
247  return NULL;
248 }
249 
250 ////////////////////////////////////////////////////////////////////
251 // Function: GraphicsPipeSelection::make_module_pipe
252 // Access: Published
253 // Description: Returns a new GraphicsPipe of a type defined by the
254 // indicated module. Returns NULL if the module is not
255 // found or does not properly recommend a GraphicsPipe.
256 ////////////////////////////////////////////////////////////////////
257 PT(GraphicsPipe) GraphicsPipeSelection::
258 make_module_pipe(const string &module_name) {
259  if (display_cat.is_debug()) {
260  display_cat.debug()
261  << "make_module_pipe(" << module_name << ")\n";
262  }
263 
264  TypeHandle pipe_type = load_named_module(module_name);
265  if (pipe_type == TypeHandle::none()) {
266  return NULL;
267  }
268 
269  return make_pipe(pipe_type);
270 }
271 
272 ////////////////////////////////////////////////////////////////////
273 // Function: GraphicsPipeSelection::make_default_pipe
274 // Access: Published
275 // Description: Creates a new GraphicsPipe of some arbitrary type.
276 // The user may specify a preference using the Configrc
277 // file; otherwise, one will be chosen arbitrarily.
278 ////////////////////////////////////////////////////////////////////
279 PT(GraphicsPipe) GraphicsPipeSelection::
280 make_default_pipe() {
281  load_default_module();
282 
283  LightMutexHolder holder(_lock);
284  PipeTypes::const_iterator ti;
285 
286  if (!_default_pipe_name.empty()) {
287  // First, look for an exact match of the default type name from
288  // the Configrc file (excepting case and hyphen/underscore).
289  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
290  const PipeType &ptype = (*ti);
291  if (cmp_nocase_uh(ptype._type.get_name(), _default_pipe_name) == 0) {
292  // Here's an exact match.
293  PT(GraphicsPipe) pipe = (*ptype._constructor)();
294  if (pipe != (GraphicsPipe *)NULL) {
295  return pipe;
296  }
297  }
298  }
299 
300  // No match; look for a substring match.
301  string preferred_name = downcase(_default_pipe_name);
302  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
303  const PipeType &ptype = (*ti);
304  string ptype_name = downcase(ptype._type.get_name());
305  if (ptype_name.find(preferred_name) != string::npos) {
306  // Here's a substring match.
307  PT(GraphicsPipe) pipe = (*ptype._constructor)();
308  if (pipe != (GraphicsPipe *)NULL) {
309  return pipe;
310  }
311  }
312  }
313  }
314 
315  // Couldn't find a matching pipe type; choose the first one on the
316  // list.
317  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
318  const PipeType &ptype = (*ti);
319  PT(GraphicsPipe) pipe = (*ptype._constructor)();
320  if (pipe != (GraphicsPipe *)NULL) {
321  return pipe;
322  }
323  }
324 
325  // Nothing. Probably the list was empty.
326  return NULL;
327 }
328 
329 ////////////////////////////////////////////////////////////////////
330 // Function: GraphicsPipeSelection::load_aux_modules
331 // Access: Published
332 // Description: Loads all the modules named in the aux-display
333 // Configrc variable, making as many graphics pipes as
334 // possible available.
335 ////////////////////////////////////////////////////////////////////
338  DisplayModules::iterator di;
339  for (di = _display_modules.begin(); di != _display_modules.end(); ++di) {
340  load_named_module(*di);
341  }
342 
343  _display_modules.clear();
344  _default_module_loaded = true;
345 }
346 
347 ////////////////////////////////////////////////////////////////////
348 // Function: GraphicsPipeSelection::add_pipe_type
349 // Access: Public
350 // Description: Adds a new kind of GraphicsPipe to the list of
351 // available pipes for creation. Normally, this is
352 // called at static init type by the various shared
353 // libraries as they are linked in. Returns true on
354 // success, false on failure.
355 ////////////////////////////////////////////////////////////////////
357 add_pipe_type(TypeHandle type, PipeConstructorFunc *func) {
358  nassertr(func != NULL, false);
359 
360  if (!type.is_derived_from(GraphicsPipe::get_class_type())) {
361  display_cat->warning()
362  << "Attempt to register " << type << " as a GraphicsPipe type.\n";
363  return false;
364  }
365 
366  // First, make sure we don't already have a GraphicsPipe of this
367  // type.
368  LightMutexHolder holder(_lock);
369  PipeTypes::const_iterator ti;
370  for (ti = _pipe_types.begin(); ti != _pipe_types.end(); ++ti) {
371  const PipeType &ptype = (*ti);
372  if (ptype._type == type) {
373  display_cat->warning()
374  << "Attempt to register GraphicsPipe type " << type
375  << " more than once.\n";
376  return false;
377  }
378  }
379 
380  if (display_cat->is_debug()) {
381  display_cat->debug()
382  << "Registering " << type << " as a GraphicsPipe type.\n";
383  }
384 
385  // Ok, now add a new entry.
386  _pipe_types.push_back(PipeType(type, func));
387 
388  return true;
389 }
390 
391 
392 ////////////////////////////////////////////////////////////////////
393 // Function: GraphicsPipeSelection::do_load_default_module
394 // Access: Private
395 // Description: Loads the particular display module listed in the
396 // load-display Configrc variable, which should default
397 // the default pipe time. If this string is empty or
398 // "*", loads all modules named in aux-display.
399 ////////////////////////////////////////////////////////////////////
400 void GraphicsPipeSelection::
401 do_load_default_module() {
402  if (_default_display_module.empty()) {
404  return;
405  }
406 
407  load_named_module(_default_display_module);
408 
409  DisplayModules::iterator di =
410  find(_display_modules.begin(), _display_modules.end(),
411  _default_display_module);
412  if (di != _display_modules.end()) {
413  _display_modules.erase(di);
414  }
415 
416  _default_module_loaded = true;
417 
418  if (_pipe_types.empty()) {
419  // If we still don't have any pipes after loading the default
420  // module, automatically load the aux modules.
422  }
423 }
424 
425 ////////////////////////////////////////////////////////////////////
426 // Function: GraphicsPipeSelection::load_named_module
427 // Access: Private
428 // Description: Loads the indicated display module by looking for a
429 // matching .dll or .so file. Returns the TypeHandle
430 // recommended by the module, or TypeHandle::none() on
431 // failure.
432 ////////////////////////////////////////////////////////////////////
433 TypeHandle GraphicsPipeSelection::
434 load_named_module(const string &name) {
435  LightMutexHolder holder(_loaded_modules_lock);
436 
437  LoadedModules::iterator mi = _loaded_modules.find(name);
438  if (mi != _loaded_modules.end()) {
439  // We have previously loaded this module. Don't attempt to
440  // re-load it.
441  return (*mi).second._default_pipe_type;
442  }
443 
444  // We have not yet loaded this module. Load it now.
445  Filename dlname = Filename::dso_filename("lib" + name + ".so");
446  display_cat.info()
447  << "loading display module: " << dlname.to_os_specific() << endl;
448  void *handle = load_dso(get_plugin_path().get_value(), dlname);
449  if (handle == (void *)NULL) {
450  display_cat.warning()
451  << "Unable to load: " << load_dso_error() << endl;
452  return TypeHandle::none();
453  }
454 
455  // Now get the module's recommended pipe type. This requires
456  // calling a specially-named function that should have been exported
457  // from the module.
458  string symbol_name = "get_pipe_type_" + name;
459  void *dso_symbol = get_dso_symbol(handle, symbol_name);
460  if (display_cat.is_debug()) {
461  display_cat.debug()
462  << "symbol of " << symbol_name << " = " << dso_symbol << "\n";
463  }
464 
465  TypeHandle pipe_type = TypeHandle::none();
466 
467  if (dso_symbol == (void *)NULL) {
468  // Couldn't find the module function.
469  display_cat.warning()
470  << "Unable to find " << symbol_name << " in " << dlname.get_basename()
471  << "\n";
472 
473  } else {
474  // We successfully loaded the module, and we found the
475  // get_pipe_type_* recommendation function. Call it to figure
476  // out what pipe type we should expect.
477  typedef int FuncType();
478  int pipe_type_index = (*(FuncType *)dso_symbol)();
479  if (display_cat.is_debug()) {
480  display_cat.debug()
481  << "pipe_type_index = " << pipe_type_index << "\n";
482  }
483 
484  if (pipe_type_index != 0) {
485  TypeRegistry *type_reg = TypeRegistry::ptr();
486  pipe_type = type_reg->find_type_by_id(pipe_type_index);
487  if (display_cat.is_debug()) {
488  display_cat.debug()
489  << "pipe_type = " << pipe_type << "\n";
490  }
491  }
492  }
493 
494  if (pipe_type == TypeHandle::none()) {
495  // The recommendation function returned a bogus type index, or the
496  // function didn't work at all. We can't safely unload the
497  // module, though, because it may have assigned itself into the
498  // GraphicsPipeSelection table. So we carry on.
499  display_cat.warning()
500  << "No default pipe type available for " << dlname.get_basename()
501  << "\n";
502  }
503 
504  LoadedModule &module = _loaded_modules[name];
505  module._module_name = name;
506  module._module_handle = handle;
507  module._default_pipe_type = pipe_type;
508 
509  return pipe_type;
510 }
string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:436
static TypeHandle none()
Returns a special zero-valued TypeHandle that is used to indicate no type.
Definition: typeHandle.I:274
TypeHandle find_type(const string &name) const
Looks for a previously-registered type of the given name.
TypeHandle find_type_by_id(int id) const
Looks for a previously-registered type with the given id number (as returned by TypeHandle::get_index...
bool is_derived_from(TypeHandle parent, TypedObject *object=(TypedObject *) NULL) const
Returns true if this type is derived from the indicated type, false otherwise.
Definition: typeHandle.I:152
void print_pipe_types() const
Writes a list of the currently known GraphicsPipe types to nout, for the user&#39;s information.
This class is similar to ConfigVariable, but it reports its value as a list of strings.
TypeHandle get_pipe_type(int n) const
Returns the nth type of GraphicsPipe available through this interface.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This is a convenience class to specialize ConfigVariable as a string type.
This maintains a list of GraphicsPipes by type that are available for creation.
Similar to MutexHolder, but for a light mutex.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
int get_num_pipe_types() const
Returns the number of different types of GraphicsPipes that are available to create through this inte...
void load_aux_modules()
Loads all the modules named in the aux-display Configrc variable, making as many graphics pipes as po...
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
Definition: typeRegistry.h:39
bool add_pipe_type(TypeHandle type, PipeConstructorFunc *func)
Adds a new kind of GraphicsPipe to the list of available pipes for creation.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1196