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