Panda3D
Loading...
Searching...
No Matches
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
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
25using std::string;
26
27GraphicsPipeSelection *GraphicsPipeSelection::_global_ptr = nullptr;
28
29/**
30 *
31 */
32GraphicsPipeSelection::
33GraphicsPipeSelection() : _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 */
77GraphicsPipeSelection::
78~GraphicsPipeSelection() {
79}
80
81/**
82 * Returns the number of different types of GraphicsPipes that are available
83 * to create through this interface.
84 */
86get_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 */
101get_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 */
121print_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 */
145PT(GraphicsPipe) GraphicsPipeSelection::
146make_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 */
184PT(GraphicsPipe) GraphicsPipeSelection::
185make_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 */
231PT(GraphicsPipe) GraphicsPipeSelection::
232make_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 */
251PT(GraphicsPipe) GraphicsPipeSelection::
252make_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 */
317add_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 */
354void GraphicsPipeSelection::
355do_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 */
384TypeHandle GraphicsPipeSelection::
385load_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:44
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
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.
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.
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.
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.
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.