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  */
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  */
121 print_pipe_types() const {
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  std::string error = load_dso_error();
401  display_cat.warning()
402  << "Unable to load " << dlname.to_os_specific() << ": " << error << std::endl;
403  return TypeHandle::none();
404  }
405 
406  // Now get the module's recommended pipe type. This requires calling a
407  // specially-named function that should have been exported from the module.
408  string symbol_name = "get_pipe_type_" + name;
409  void *dso_symbol = get_dso_symbol(handle, symbol_name);
410  if (display_cat.is_debug()) {
411  display_cat.debug()
412  << "symbol of " << symbol_name << " = " << dso_symbol << "\n";
413  }
414 
415  TypeHandle pipe_type = TypeHandle::none();
416 
417  if (dso_symbol == nullptr) {
418  // Couldn't find the module function.
419  display_cat.warning()
420  << "Unable to find " << symbol_name << " in " << dlname.to_os_specific()
421  << "\n";
422 
423  } else {
424  // We successfully loaded the module, and we found the get_pipe_type_*
425  // recommendation function. Call it to figure out what pipe type we
426  // should expect.
427  typedef int FuncType();
428  int pipe_type_index = (*(FuncType *)dso_symbol)();
429  if (display_cat.is_debug()) {
430  display_cat.debug()
431  << "pipe_type_index = " << pipe_type_index << "\n";
432  }
433 
434  if (pipe_type_index != 0) {
435  TypeRegistry *type_reg = TypeRegistry::ptr();
436  pipe_type = type_reg->find_type_by_id(pipe_type_index);
437  if (display_cat.is_debug()) {
438  display_cat.debug()
439  << "pipe_type = " << pipe_type << "\n";
440  }
441  }
442  }
443 
444  if (pipe_type == TypeHandle::none()) {
445  // The recommendation function returned a bogus type index, or the
446  // function didn't work at all. We can't safely unload the module,
447  // though, because it may have assigned itself into the
448  // GraphicsPipeSelection table. So we carry on.
449  display_cat.warning()
450  << "No default pipe type available for " << dlname.to_os_specific()
451  << "\n";
452  }
453 
454  LoadedModule &module = _loaded_modules[name];
455  module._module_name = name;
456  module._module_handle = handle;
457  module._default_pipe_type = pipe_type;
458 
459  return pipe_type;
460 }
This class is similar to ConfigVariable, but it reports its value as a list of strings.
This is a convenience class to specialize ConfigVariable as a string type.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
This maintains a list of GraphicsPipes by type that are available for creation.
get_pipe_type
Returns the nth type of GraphicsPipe available through this interface.
void print_pipe_types() const
Writes a list of the currently known GraphicsPipe types to nout, for the user's information.
void load_aux_modules()
Loads all the modules named in the aux-display Configrc variable, making as many graphics pipes as po...
get_num_pipe_types
Returns the number of different types of GraphicsPipes that are available to create through this inte...
bool add_pipe_type(TypeHandle type, PipeConstructorFunc *func)
Adds a new kind of GraphicsPipe to the list of available pipes for creation.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Similar to MutexHolder, but for a light mutex.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
Definition: typeRegistry.h:36
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...
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
Definition: typeRegistry.I:30
TypeHandle find_type(const std::string &name) const
Looks for a previously-registered type of the given name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.