Panda3D
|
00001 // Filename: texturePool.cxx 00002 // Created by: drose (26Apr00) 00003 // Updated by: fperazzi, PandaSE(29Apr10) (added ns_load_2d_texture_array) 00004 // 00005 //////////////////////////////////////////////////////////////////// 00006 // 00007 // PANDA 3D SOFTWARE 00008 // Copyright (c) Carnegie Mellon University. All rights reserved. 00009 // 00010 // All use of this software is subject to the terms of the revised BSD 00011 // license. You should have received a copy of this license along 00012 // with this source code in a file named "LICENSE." 00013 // 00014 //////////////////////////////////////////////////////////////////// 00015 00016 #include "texturePool.h" 00017 #include "config_gobj.h" 00018 #include "config_util.h" 00019 #include "config_express.h" 00020 #include "string_utils.h" 00021 #include "virtualFileSystem.h" 00022 #include "bamCache.h" 00023 #include "bamCacheRecord.h" 00024 #include "pnmFileTypeRegistry.h" 00025 #include "texturePoolFilter.h" 00026 #include "configVariableList.h" 00027 #include "load_dso.h" 00028 #include "mutexHolder.h" 00029 00030 TexturePool *TexturePool::_global_ptr; 00031 00032 //////////////////////////////////////////////////////////////////// 00033 // Function: TexturePool::write 00034 // Access: Published, Static 00035 // Description: Lists the contents of the texture pool to the 00036 // indicated output stream. 00037 // For debugging. 00038 //////////////////////////////////////////////////////////////////// 00039 void TexturePool:: 00040 write(ostream &out) { 00041 get_global_ptr()->ns_list_contents(out); 00042 } 00043 00044 //////////////////////////////////////////////////////////////////// 00045 // Function: TexturePool::register_texture_type 00046 // Access: Public 00047 // Description: Records a factory function that makes a Texture 00048 // object of the appropriate type for one or more 00049 // particular filename extensions. The string 00050 // extensions may be a string that contains 00051 // space-separated list of extensions, case-insensitive. 00052 //////////////////////////////////////////////////////////////////// 00053 void TexturePool:: 00054 register_texture_type(MakeTextureFunc *func, const string &extensions) { 00055 MutexHolder holder(_lock); 00056 00057 vector_string words; 00058 extract_words(downcase(extensions), words); 00059 00060 vector_string::const_iterator wi; 00061 for (wi = words.begin(); wi != words.end(); ++wi) { 00062 _type_registry[*wi] = func; 00063 } 00064 } 00065 00066 //////////////////////////////////////////////////////////////////// 00067 // Function: TexturePool::register_filter 00068 // Access: Public 00069 // Description: Records a TexturePoolFilter object that may operate 00070 // on texture images as they are loaded from disk. 00071 //////////////////////////////////////////////////////////////////// 00072 void TexturePool:: 00073 register_filter(TexturePoolFilter *filter) { 00074 MutexHolder holder(_lock); 00075 00076 gobj_cat.info() 00077 << "Registering Texture filter " << *filter << "\n"; 00078 _filter_registry.push_back(filter); 00079 } 00080 00081 //////////////////////////////////////////////////////////////////// 00082 // Function: TexturePool::get_texture_type 00083 // Access: Public 00084 // Description: Returns the factory function to construct a new 00085 // texture of the type appropriate for the indicated 00086 // filename extension, if any, or NULL if the extension 00087 // is not one of the extensions for a texture file. 00088 //////////////////////////////////////////////////////////////////// 00089 TexturePool::MakeTextureFunc *TexturePool:: 00090 get_texture_type(const string &extension) const { 00091 MutexHolder holder(_lock); 00092 00093 string c = downcase(extension); 00094 TypeRegistry::const_iterator ti; 00095 ti = _type_registry.find(c); 00096 if (ti != _type_registry.end()) { 00097 return (*ti).second; 00098 } 00099 00100 // Check the PNM type registry. 00101 PNMFileTypeRegistry *pnm_reg = PNMFileTypeRegistry::get_global_ptr(); 00102 PNMFileType *type = pnm_reg->get_type_from_extension(c); 00103 if (type != (PNMFileType *)NULL) { 00104 // This is a known image type; create an ordinary Texture. 00105 ((TexturePool *)this)->_type_registry[c] = Texture::make_texture; 00106 return Texture::make_texture; 00107 } 00108 00109 // This is an unknown texture type. 00110 return NULL; 00111 } 00112 00113 //////////////////////////////////////////////////////////////////// 00114 // Function: TexturePool::write_texture_types 00115 // Access: Public 00116 // Description: Outputs a list of the available texture types to the 00117 // indicated output stream. This is mostly the list of 00118 // available image types, with maybe a few additional 00119 // ones for video textures. 00120 //////////////////////////////////////////////////////////////////// 00121 void TexturePool:: 00122 write_texture_types(ostream &out, int indent_level) const { 00123 MutexHolder holder(_lock); 00124 00125 PNMFileTypeRegistry *pnm_reg = PNMFileTypeRegistry::get_global_ptr(); 00126 pnm_reg->write(out, indent_level); 00127 00128 // Also output any of the additional texture types, that aren't 00129 // strictly images (these are typically video textures). 00130 TypeRegistry::const_iterator ti; 00131 for (ti = _type_registry.begin(); ti != _type_registry.end(); ++ti) { 00132 string extension = (*ti).first; 00133 MakeTextureFunc *func = (*ti).second; 00134 00135 if (pnm_reg->get_type_from_extension(extension) == NULL) { 00136 PT(Texture) tex = func(); 00137 string name = tex->get_type().get_name(); 00138 indent(out, indent_level) << name; 00139 indent(out, max(30 - (int)name.length(), 0)) 00140 << " ." << extension << "\n"; 00141 } 00142 } 00143 } 00144 00145 //////////////////////////////////////////////////////////////////// 00146 // Function: TexturePool::get_global_ptr 00147 // Access: Public, Static 00148 // Description: Initializes and/or returns the global pointer to the 00149 // one TexturePool object in the system. 00150 //////////////////////////////////////////////////////////////////// 00151 TexturePool *TexturePool:: 00152 get_global_ptr() { 00153 if (_global_ptr == (TexturePool *)NULL) { 00154 _global_ptr = new TexturePool; 00155 00156 // We have to call this here, not in the constructor, so that the 00157 // _global_ptr is safely assigned by the time the filters begin to 00158 // load. 00159 _global_ptr->load_filters(); 00160 } 00161 return _global_ptr; 00162 } 00163 00164 //////////////////////////////////////////////////////////////////// 00165 // Function: TexturePool::Constructor 00166 // Access: Private 00167 // Description: The constructor is not intended to be called 00168 // directly; there's only supposed to be one TexturePool 00169 // in the universe and it constructs itself. 00170 //////////////////////////////////////////////////////////////////// 00171 TexturePool:: 00172 TexturePool() { 00173 ConfigVariableFilename fake_texture_image 00174 ("fake-texture-image", "", 00175 PRC_DESC("Set this to enable a speedy-load mode in which you don't care " 00176 "what the world looks like, you just want it to load in minimal " 00177 "time. This causes all texture loads via the TexturePool to use " 00178 "the same texture file, which will presumably only be loaded " 00179 "once.")); 00180 _fake_texture_image = fake_texture_image; 00181 } 00182 00183 //////////////////////////////////////////////////////////////////// 00184 // Function: TexturePool::ns_has_texture 00185 // Access: Private 00186 // Description: The nonstatic implementation of has_texture(). 00187 //////////////////////////////////////////////////////////////////// 00188 bool TexturePool:: 00189 ns_has_texture(const Filename &orig_filename) { 00190 MutexHolder holder(_lock); 00191 00192 Filename filename; 00193 resolve_filename(filename, orig_filename, false, LoaderOptions()); 00194 00195 Textures::const_iterator ti; 00196 ti = _textures.find(filename); 00197 if (ti != _textures.end()) { 00198 // This texture was previously loaded. 00199 return true; 00200 } 00201 00202 return false; 00203 } 00204 00205 //////////////////////////////////////////////////////////////////// 00206 // Function: TexturePool::ns_load_texture 00207 // Access: Private 00208 // Description: The nonstatic implementation of load_texture(). 00209 //////////////////////////////////////////////////////////////////// 00210 Texture *TexturePool:: 00211 ns_load_texture(const Filename &orig_filename, int primary_file_num_channels, 00212 bool read_mipmaps, const LoaderOptions &options) { 00213 Filename filename; 00214 00215 { 00216 MutexHolder holder(_lock); 00217 resolve_filename(filename, orig_filename, read_mipmaps, options); 00218 Textures::const_iterator ti; 00219 ti = _textures.find(filename); 00220 if (ti != _textures.end()) { 00221 // This texture was previously loaded. 00222 Texture *tex = (*ti).second; 00223 nassertr(!tex->get_fullpath().empty(), tex); 00224 return tex; 00225 } 00226 } 00227 00228 // The texture was not found in the pool. 00229 PT(Texture) tex; 00230 PT(BamCacheRecord) record; 00231 bool store_record = false; 00232 00233 // Can one of our texture filters supply the texture? 00234 tex = pre_load(orig_filename, Filename(), primary_file_num_channels, 0, 00235 read_mipmaps, options); 00236 00237 BamCache *cache = BamCache::get_global_ptr(); 00238 bool compressed_cache_record = false; 00239 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00240 options); 00241 00242 if (tex == (Texture *)NULL) { 00243 // The texture was neither in the pool, nor found in the on-disk 00244 // cache; it needs to be loaded from its source image(s). 00245 gobj_cat.info() 00246 << "Loading texture " << filename << "\n"; 00247 00248 string ext = downcase(filename.get_extension()); 00249 if (ext == "txo" || ext == "bam") { 00250 // Assume this is a txo file, which might conceivably contain a 00251 // movie file or some other subclass of Texture. In that case, 00252 // use make_from_txo() to load it instead of read(). 00253 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00254 00255 filename.set_binary(); 00256 PT(VirtualFile) file = vfs->get_file(filename); 00257 if (file == (VirtualFile *)NULL) { 00258 // No such file. 00259 gobj_cat.error() 00260 << "Could not find " << filename << "\n"; 00261 return false; 00262 } 00263 00264 if (gobj_cat.is_debug()) { 00265 gobj_cat.debug() 00266 << "Reading texture object " << filename << "\n"; 00267 } 00268 00269 istream *in = file->open_read_file(true); 00270 tex = Texture::make_from_txo(*in, filename); 00271 vfs->close_read_file(in); 00272 00273 if (tex == (Texture *)NULL) { 00274 return false; 00275 } 00276 tex->set_fullpath(filename); 00277 tex->clear_alpha_fullpath(); 00278 tex->set_keep_ram_image(false); 00279 00280 } else { 00281 // Read it the conventional way. 00282 tex = ns_make_texture(ext); 00283 if (!tex->read(filename, Filename(), primary_file_num_channels, 0, 00284 0, 0, false, read_mipmaps, record, options)) { 00285 // This texture was not found or could not be read. 00286 report_texture_unreadable(filename); 00287 return NULL; 00288 } 00289 } 00290 00291 if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) { 00292 tex->generate_simple_ram_image(); 00293 } 00294 00295 store_record = (record != (BamCacheRecord *)NULL); 00296 } 00297 00298 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00299 #ifndef HAVE_SQUISH 00300 bool needs_driver_compression = true; 00301 #else 00302 bool needs_driver_compression = driver_compress_textures; 00303 #endif // HAVE_SQUISH 00304 if (needs_driver_compression) { 00305 // We don't want to save the uncompressed version; we'll save the 00306 // compressed version when it becomes available. 00307 store_record = false; 00308 if (!compressed_cache_record) { 00309 tex->set_post_load_store_cache(true); 00310 } 00311 } 00312 00313 } else if (!cache->get_cache_textures()) { 00314 // We don't want to save this texture. 00315 store_record = false; 00316 } 00317 00318 // Set the original filename, before we searched along the path. 00319 nassertr(tex != (Texture *)NULL, NULL); 00320 tex->set_filename(orig_filename); 00321 tex->set_fullpath(filename); 00322 tex->_texture_pool_key = filename; 00323 00324 { 00325 MutexHolder holder(_lock); 00326 00327 // Now look again--someone may have just loaded this texture in 00328 // another thread. 00329 Textures::const_iterator ti; 00330 ti = _textures.find(filename); 00331 if (ti != _textures.end()) { 00332 // This texture was previously loaded. 00333 Texture *tex = (*ti).second; 00334 nassertr(!tex->get_fullpath().empty(), tex); 00335 return tex; 00336 } 00337 00338 _textures[filename] = tex; 00339 } 00340 00341 if (store_record && tex->is_cacheable()) { 00342 // Store the on-disk cache record for next time. 00343 record->set_data(tex, tex); 00344 cache->store(record); 00345 } 00346 00347 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) { 00348 // And now drop the RAM until we need it. 00349 tex->clear_ram_image(); 00350 } 00351 00352 nassertr(!tex->get_fullpath().empty(), tex); 00353 00354 // Finally, apply any post-loading texture filters. 00355 tex = post_load(tex); 00356 00357 return tex; 00358 } 00359 00360 //////////////////////////////////////////////////////////////////// 00361 // Function: TexturePool::ns_load_texture 00362 // Access: Private 00363 // Description: The nonstatic implementation of load_texture(). 00364 //////////////////////////////////////////////////////////////////// 00365 Texture *TexturePool:: 00366 ns_load_texture(const Filename &orig_filename, 00367 const Filename &orig_alpha_filename, 00368 int primary_file_num_channels, 00369 int alpha_file_channel, 00370 bool read_mipmaps, const LoaderOptions &options) { 00371 if (!_fake_texture_image.empty()) { 00372 return ns_load_texture(_fake_texture_image, primary_file_num_channels, 00373 read_mipmaps, options); 00374 } 00375 00376 Filename filename; 00377 Filename alpha_filename; 00378 00379 { 00380 MutexHolder holder(_lock); 00381 resolve_filename(filename, orig_filename, read_mipmaps, options); 00382 resolve_filename(alpha_filename, orig_alpha_filename, read_mipmaps, options); 00383 00384 Textures::const_iterator ti; 00385 ti = _textures.find(filename); 00386 if (ti != _textures.end()) { 00387 // This texture was previously loaded. 00388 Texture *tex = (*ti).second; 00389 nassertr(!tex->get_fullpath().empty(), tex); 00390 return tex; 00391 } 00392 } 00393 00394 PT(Texture) tex; 00395 PT(BamCacheRecord) record; 00396 bool store_record = false; 00397 00398 // Can one of our texture filters supply the texture? 00399 tex = pre_load(orig_filename, alpha_filename, primary_file_num_channels, 00400 alpha_file_channel, read_mipmaps, options); 00401 00402 BamCache *cache = BamCache::get_global_ptr(); 00403 bool compressed_cache_record = false; 00404 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00405 options); 00406 00407 if (tex == (Texture *)NULL) { 00408 // The texture was neither in the pool, nor found in the on-disk 00409 // cache; it needs to be loaded from its source image(s). 00410 gobj_cat.info() 00411 << "Loading texture " << filename << " and alpha component " 00412 << alpha_filename << endl; 00413 tex = ns_make_texture(filename.get_extension()); 00414 if (!tex->read(filename, alpha_filename, primary_file_num_channels, 00415 alpha_file_channel, 0, 0, false, read_mipmaps, NULL, 00416 options)) { 00417 // This texture was not found or could not be read. 00418 report_texture_unreadable(filename); 00419 return NULL; 00420 } 00421 00422 if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) { 00423 tex->generate_simple_ram_image(); 00424 } 00425 00426 store_record = (record != (BamCacheRecord *)NULL); 00427 } 00428 00429 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00430 #ifndef HAVE_SQUISH 00431 bool needs_driver_compression = true; 00432 #else 00433 bool needs_driver_compression = driver_compress_textures; 00434 #endif // HAVE_SQUISH 00435 if (needs_driver_compression) { 00436 // We don't want to save the uncompressed version; we'll save the 00437 // compressed version when it becomes available. 00438 store_record = false; 00439 if (!compressed_cache_record) { 00440 tex->set_post_load_store_cache(true); 00441 } 00442 } 00443 00444 } else if (!cache->get_cache_textures()) { 00445 // We don't want to save this texture. 00446 store_record = false; 00447 } 00448 00449 // Set the original filenames, before we searched along the path. 00450 nassertr(tex != (Texture *)NULL, NULL); 00451 tex->set_filename(orig_filename); 00452 tex->set_fullpath(filename); 00453 tex->set_alpha_filename(orig_alpha_filename); 00454 tex->set_alpha_fullpath(alpha_filename); 00455 tex->_texture_pool_key = filename; 00456 00457 { 00458 MutexHolder holder(_lock); 00459 00460 // Now look again. 00461 Textures::const_iterator ti; 00462 ti = _textures.find(filename); 00463 if (ti != _textures.end()) { 00464 // This texture was previously loaded. 00465 Texture *tex = (*ti).second; 00466 nassertr(!tex->get_fullpath().empty(), tex); 00467 return tex; 00468 } 00469 00470 _textures[filename] = tex; 00471 } 00472 00473 if (store_record && tex->is_cacheable()) { 00474 // Store the on-disk cache record for next time. 00475 record->set_data(tex, tex); 00476 cache->store(record); 00477 } 00478 00479 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) { 00480 // And now drop the RAM until we need it. 00481 tex->clear_ram_image(); 00482 } 00483 00484 nassertr(!tex->get_fullpath().empty(), tex); 00485 00486 // Finally, apply any post-loading texture filters. 00487 tex = post_load(tex); 00488 00489 return tex; 00490 } 00491 00492 //////////////////////////////////////////////////////////////////// 00493 // Function: TexturePool::ns_load_3d_texture 00494 // Access: Private 00495 // Description: The nonstatic implementation of load_3d_texture(). 00496 //////////////////////////////////////////////////////////////////// 00497 Texture *TexturePool:: 00498 ns_load_3d_texture(const Filename &filename_pattern, 00499 bool read_mipmaps, const LoaderOptions &options) { 00500 Filename orig_filename(filename_pattern); 00501 orig_filename.set_pattern(true); 00502 00503 Filename filename; 00504 { 00505 MutexHolder holder(_lock); 00506 resolve_filename(filename, orig_filename, read_mipmaps, options); 00507 00508 Textures::const_iterator ti; 00509 ti = _textures.find(filename); 00510 if (ti != _textures.end()) { 00511 if ((*ti).second->get_texture_type() == Texture::TT_3d_texture) { 00512 // This texture was previously loaded, as a 3d texture 00513 return (*ti).second; 00514 } 00515 } 00516 } 00517 00518 PT(Texture) tex; 00519 PT(BamCacheRecord) record; 00520 bool store_record = false; 00521 00522 BamCache *cache = BamCache::get_global_ptr(); 00523 bool compressed_cache_record = false; 00524 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00525 options); 00526 00527 if (tex == (Texture *)NULL || 00528 tex->get_texture_type() != Texture::TT_3d_texture) { 00529 // The texture was neither in the pool, nor found in the on-disk 00530 // cache; it needs to be loaded from its source image(s). 00531 gobj_cat.info() 00532 << "Loading 3-d texture " << filename << "\n"; 00533 tex = ns_make_texture(filename.get_extension()); 00534 tex->setup_3d_texture(); 00535 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) { 00536 // This texture was not found or could not be read. 00537 report_texture_unreadable(filename); 00538 return NULL; 00539 } 00540 store_record = (record != (BamCacheRecord *)NULL); 00541 } 00542 00543 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00544 #ifndef HAVE_SQUISH 00545 bool needs_driver_compression = true; 00546 #else 00547 bool needs_driver_compression = driver_compress_textures; 00548 #endif // HAVE_SQUISH 00549 if (needs_driver_compression) { 00550 // We don't want to save the uncompressed version; we'll save the 00551 // compressed version when it becomes available. 00552 store_record = false; 00553 if (!compressed_cache_record) { 00554 tex->set_post_load_store_cache(true); 00555 } 00556 } 00557 00558 } else if (!cache->get_cache_textures()) { 00559 // We don't want to save this texture. 00560 store_record = false; 00561 } 00562 00563 // Set the original filename, before we searched along the path. 00564 nassertr(tex != (Texture *)NULL, false); 00565 tex->set_filename(filename_pattern); 00566 tex->set_fullpath(filename); 00567 tex->_texture_pool_key = filename; 00568 00569 { 00570 MutexHolder holder(_lock); 00571 00572 // Now look again. 00573 Textures::const_iterator ti; 00574 ti = _textures.find(filename); 00575 if (ti != _textures.end()) { 00576 if ((*ti).second->get_texture_type() == Texture::TT_3d_texture) { 00577 // This texture was previously loaded, as a 3d texture 00578 return (*ti).second; 00579 } 00580 } 00581 00582 _textures[filename] = tex; 00583 } 00584 00585 if (store_record && tex->is_cacheable()) { 00586 // Store the on-disk cache record for next time. 00587 record->set_data(tex, tex); 00588 cache->store(record); 00589 } 00590 00591 nassertr(!tex->get_fullpath().empty(), tex); 00592 return tex; 00593 } 00594 00595 //////////////////////////////////////////////////////////////////// 00596 // Function: TexturePool::ns_load_2d_texture_array 00597 // Access: Private 00598 // Description: The nonstatic implementation of load_2d_texture_array(). 00599 //////////////////////////////////////////////////////////////////// 00600 Texture *TexturePool:: 00601 ns_load_2d_texture_array(const Filename &filename_pattern, 00602 bool read_mipmaps, const LoaderOptions &options) { 00603 Filename orig_filename(filename_pattern); 00604 orig_filename.set_pattern(true); 00605 00606 Filename filename; 00607 Filename unique_filename; //differentiate 3d-textures from 2d-texture arrays 00608 { 00609 MutexHolder holder(_lock); 00610 resolve_filename(filename, orig_filename, read_mipmaps, options); 00611 // Differentiate from preloaded 3d textures 00612 unique_filename = filename + ".2DARRAY"; 00613 00614 Textures::const_iterator ti; 00615 ti = _textures.find(unique_filename); 00616 if (ti != _textures.end()) { 00617 if ((*ti).second->get_texture_type() == Texture::TT_2d_texture_array) { 00618 // This texture was previously loaded, as a 2d texture array 00619 return (*ti).second; 00620 } 00621 } 00622 } 00623 00624 PT(Texture) tex; 00625 PT(BamCacheRecord) record; 00626 bool store_record = false; 00627 00628 BamCache *cache = BamCache::get_global_ptr(); 00629 bool compressed_cache_record = false; 00630 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00631 options); 00632 00633 if (tex == (Texture *)NULL || 00634 tex->get_texture_type() != Texture::TT_2d_texture_array) { 00635 // The texture was neither in the pool, nor found in the on-disk 00636 // cache; it needs to be loaded from its source image(s). 00637 gobj_cat.info() 00638 << "Loading 2-d texture array " << filename << "\n"; 00639 tex = ns_make_texture(filename.get_extension()); 00640 tex->setup_2d_texture_array(); 00641 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) { 00642 // This texture was not found or could not be read. 00643 report_texture_unreadable(filename); 00644 return NULL; 00645 } 00646 store_record = (record != (BamCacheRecord *)NULL); 00647 } 00648 00649 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00650 #ifndef HAVE_SQUISH 00651 bool needs_driver_compression = true; 00652 #else 00653 bool needs_driver_compression = driver_compress_textures; 00654 #endif // HAVE_SQUISH 00655 if (needs_driver_compression) { 00656 // We don't want to save the uncompressed version; we'll save the 00657 // compressed version when it becomes available. 00658 store_record = false; 00659 if (!compressed_cache_record) { 00660 tex->set_post_load_store_cache(true); 00661 } 00662 } 00663 00664 } else if (!cache->get_cache_textures()) { 00665 // We don't want to save this texture. 00666 store_record = false; 00667 } 00668 00669 // Set the original filename, before we searched along the path. 00670 nassertr(tex != (Texture *)NULL, false); 00671 tex->set_filename(filename_pattern); 00672 tex->set_fullpath(filename); 00673 tex->_texture_pool_key = filename; 00674 00675 { 00676 MutexHolder holder(_lock); 00677 00678 // Now look again. 00679 Textures::const_iterator ti; 00680 ti = _textures.find(unique_filename); 00681 if (ti != _textures.end()) { 00682 if ((*ti).second->get_texture_type() == Texture::TT_2d_texture_array) { 00683 // This texture was previously loaded, as a 2d texture array 00684 return (*ti).second; 00685 } 00686 } 00687 00688 _textures[unique_filename] = tex; 00689 } 00690 00691 if (store_record && tex->is_cacheable()) { 00692 // Store the on-disk cache record for next time. 00693 record->set_data(tex, tex); 00694 cache->store(record); 00695 } 00696 00697 nassertr(!tex->get_fullpath().empty(), tex); 00698 return tex; 00699 } 00700 00701 //////////////////////////////////////////////////////////////////// 00702 // Function: TexturePool::ns_load_cube_map 00703 // Access: Private 00704 // Description: The nonstatic implementation of load_cube_map(). 00705 //////////////////////////////////////////////////////////////////// 00706 Texture *TexturePool:: 00707 ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps, 00708 const LoaderOptions &options) { 00709 Filename orig_filename(filename_pattern); 00710 orig_filename.set_pattern(true); 00711 00712 Filename filename; 00713 { 00714 MutexHolder holder(_lock); 00715 resolve_filename(filename, orig_filename, read_mipmaps, options); 00716 00717 Textures::const_iterator ti; 00718 ti = _textures.find(filename); 00719 if (ti != _textures.end()) { 00720 // This texture was previously loaded. 00721 return (*ti).second; 00722 } 00723 } 00724 00725 PT(Texture) tex; 00726 PT(BamCacheRecord) record; 00727 bool store_record = false; 00728 00729 BamCache *cache = BamCache::get_global_ptr(); 00730 bool compressed_cache_record = false; 00731 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00732 options); 00733 00734 if (tex == (Texture *)NULL || 00735 tex->get_texture_type() != Texture::TT_cube_map) { 00736 // The texture was neither in the pool, nor found in the on-disk 00737 // cache; it needs to be loaded from its source image(s). 00738 gobj_cat.info() 00739 << "Loading cube map texture " << filename << "\n"; 00740 tex = ns_make_texture(filename.get_extension()); 00741 tex->setup_cube_map(); 00742 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) { 00743 // This texture was not found or could not be read. 00744 report_texture_unreadable(filename); 00745 return NULL; 00746 } 00747 store_record = (record != (BamCacheRecord *)NULL); 00748 } 00749 00750 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00751 #ifndef HAVE_SQUISH 00752 bool needs_driver_compression = true; 00753 #else 00754 bool needs_driver_compression = driver_compress_textures; 00755 #endif // HAVE_SQUISH 00756 if (needs_driver_compression) { 00757 // We don't want to save the uncompressed version; we'll save the 00758 // compressed version when it becomes available. 00759 store_record = false; 00760 if (!compressed_cache_record) { 00761 tex->set_post_load_store_cache(true); 00762 } 00763 } 00764 00765 } else if (!cache->get_cache_textures()) { 00766 // We don't want to save this texture. 00767 store_record = false; 00768 } 00769 00770 // Set the original filename, before we searched along the path. 00771 nassertr(tex != (Texture *)NULL, false); 00772 tex->set_filename(filename_pattern); 00773 tex->set_fullpath(filename); 00774 tex->_texture_pool_key = filename; 00775 00776 { 00777 MutexHolder holder(_lock); 00778 00779 // Now look again. 00780 Textures::const_iterator ti; 00781 ti = _textures.find(filename); 00782 if (ti != _textures.end()) { 00783 // This texture was previously loaded. 00784 return (*ti).second; 00785 } 00786 00787 _textures[filename] = tex; 00788 } 00789 00790 if (store_record && tex->is_cacheable()) { 00791 // Store the on-disk cache record for next time. 00792 record->set_data(tex, tex); 00793 cache->store(record); 00794 } 00795 00796 nassertr(!tex->get_fullpath().empty(), tex); 00797 return tex; 00798 } 00799 00800 //////////////////////////////////////////////////////////////////// 00801 // Function: TexturePool::ns_get_normalization_cube_map 00802 // Access: Private 00803 // Description: The nonstatic implementation of get_normalization_cube_map(). 00804 //////////////////////////////////////////////////////////////////// 00805 Texture *TexturePool:: 00806 ns_get_normalization_cube_map(int size) { 00807 MutexHolder holder(_lock); 00808 00809 if (_normalization_cube_map == (Texture *)NULL) { 00810 _normalization_cube_map = new Texture("normalization_cube_map"); 00811 } 00812 if (_normalization_cube_map->get_x_size() < size || 00813 _normalization_cube_map->get_texture_type() != Texture::TT_cube_map) { 00814 _normalization_cube_map->generate_normalization_cube_map(size); 00815 } 00816 00817 return _normalization_cube_map; 00818 } 00819 00820 //////////////////////////////////////////////////////////////////// 00821 // Function: TexturePool::ns_get_alpha_scale_map 00822 // Access: Private 00823 // Description: The nonstatic implementation of get_alpha_scale_map(). 00824 //////////////////////////////////////////////////////////////////// 00825 Texture *TexturePool:: 00826 ns_get_alpha_scale_map() { 00827 MutexHolder holder(_lock); 00828 00829 if (_alpha_scale_map == (Texture *)NULL) { 00830 _alpha_scale_map = new Texture("alpha_scale_map"); 00831 _alpha_scale_map->generate_alpha_scale_map(); 00832 } 00833 00834 return _alpha_scale_map; 00835 } 00836 00837 //////////////////////////////////////////////////////////////////// 00838 // Function: TexturePool::ns_add_texture 00839 // Access: Private 00840 // Description: The nonstatic implementation of add_texture(). 00841 //////////////////////////////////////////////////////////////////// 00842 void TexturePool:: 00843 ns_add_texture(Texture *tex) { 00844 PT(Texture) keep = tex; 00845 MutexHolder holder(_lock); 00846 00847 if (!tex->_texture_pool_key.empty()) { 00848 ns_release_texture(tex); 00849 } 00850 string filename = tex->get_fullpath(); 00851 if (filename.empty()) { 00852 gobj_cat.error() << "Attempt to call add_texture() on an unnamed texture.\n"; 00853 } 00854 00855 // We blow away whatever texture was there previously, if any. 00856 tex->_texture_pool_key = filename; 00857 _textures[filename] = tex; 00858 nassertv(!tex->get_fullpath().empty()); 00859 } 00860 00861 //////////////////////////////////////////////////////////////////// 00862 // Function: TexturePool::ns_release_texture 00863 // Access: Private 00864 // Description: The nonstatic implementation of release_texture(). 00865 //////////////////////////////////////////////////////////////////// 00866 void TexturePool:: 00867 ns_release_texture(Texture *tex) { 00868 MutexHolder holder(_lock); 00869 00870 if (!tex->_texture_pool_key.empty()) { 00871 Textures::iterator ti; 00872 ti = _textures.find(tex->_texture_pool_key); 00873 if (ti != _textures.end() && (*ti).second == tex) { 00874 _textures.erase(ti); 00875 } 00876 tex->_texture_pool_key = string(); 00877 } 00878 00879 // Blow away the cache of resolved relative filenames. 00880 _relpath_lookup.clear(); 00881 } 00882 00883 //////////////////////////////////////////////////////////////////// 00884 // Function: TexturePool::ns_release_all_textures 00885 // Access: Private 00886 // Description: The nonstatic implementation of release_all_textures(). 00887 //////////////////////////////////////////////////////////////////// 00888 void TexturePool:: 00889 ns_release_all_textures() { 00890 MutexHolder holder(_lock); 00891 00892 Textures::iterator ti; 00893 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00894 Texture *tex = (*ti).second; 00895 tex->_texture_pool_key = string(); 00896 } 00897 00898 _textures.clear(); 00899 _normalization_cube_map = NULL; 00900 00901 // Blow away the cache of resolved relative filenames. 00902 _relpath_lookup.clear(); 00903 } 00904 00905 //////////////////////////////////////////////////////////////////// 00906 // Function: TexturePool::ns_garbage_collect 00907 // Access: Private 00908 // Description: The nonstatic implementation of garbage_collect(). 00909 //////////////////////////////////////////////////////////////////// 00910 int TexturePool:: 00911 ns_garbage_collect() { 00912 MutexHolder holder(_lock); 00913 00914 int num_released = 0; 00915 Textures new_set; 00916 00917 Textures::iterator ti; 00918 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00919 Texture *tex = (*ti).second; 00920 if (tex->get_ref_count() == 1) { 00921 if (gobj_cat.is_debug()) { 00922 gobj_cat.debug() 00923 << "Releasing " << (*ti).first << "\n"; 00924 } 00925 ++num_released; 00926 tex->_texture_pool_key = string(); 00927 } else { 00928 new_set.insert(new_set.end(), *ti); 00929 } 00930 } 00931 00932 _textures.swap(new_set); 00933 00934 if (_normalization_cube_map != (Texture *)NULL && 00935 _normalization_cube_map->get_ref_count() == 1) { 00936 if (gobj_cat.is_debug()) { 00937 gobj_cat.debug() 00938 << "Releasing normalization cube map\n"; 00939 } 00940 ++num_released; 00941 _normalization_cube_map = NULL; 00942 } 00943 00944 return num_released; 00945 } 00946 00947 //////////////////////////////////////////////////////////////////// 00948 // Function: TexturePool::ns_list_contents 00949 // Access: Private 00950 // Description: The nonstatic implementation of list_contents(). 00951 //////////////////////////////////////////////////////////////////// 00952 void TexturePool:: 00953 ns_list_contents(ostream &out) const { 00954 MutexHolder holder(_lock); 00955 00956 int total_size; 00957 int total_ram_size; 00958 Textures::const_iterator ti; 00959 00960 out << "texture pool contents:\n"; 00961 00962 total_size = 0; 00963 total_ram_size = 0; 00964 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00965 Texture *tex = (*ti).second; 00966 out << (*ti).first << "\n"; 00967 out << " (count = " << tex->get_ref_count() 00968 << ", ram = " << tex->get_ram_image_size() 00969 << ", size = " << tex->get_ram_page_size() 00970 << ", w = " << tex->get_x_size() 00971 << ", h = " << tex->get_y_size() 00972 << ")\n"; 00973 nassertv(tex->_texture_pool_key == (*ti).first); 00974 total_ram_size += tex->get_ram_image_size(); 00975 total_size += tex->get_ram_page_size(); 00976 } 00977 00978 out << "total number of textures: " << _textures.size() << "\n"; 00979 out << "texture pool ram : " << total_ram_size << "\n"; 00980 out << "texture pool size: " << total_size << "\n"; 00981 out << "texture pool size - texture pool ram: " << total_size - total_ram_size << "\n"; 00982 } 00983 00984 //////////////////////////////////////////////////////////////////// 00985 // Function: TexturePool::ns_find_texture 00986 // Access: Private 00987 // Description: The nonstatic implementation of find_texture(). 00988 //////////////////////////////////////////////////////////////////// 00989 Texture *TexturePool:: 00990 ns_find_texture(const string &name) const { 00991 MutexHolder holder(_lock); 00992 GlobPattern glob(name); 00993 00994 Textures::const_iterator ti; 00995 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00996 Texture *tex = (*ti).second; 00997 if (glob.matches(tex->get_name())) { 00998 return tex; 00999 } 01000 } 01001 01002 return NULL; 01003 } 01004 01005 //////////////////////////////////////////////////////////////////// 01006 // Function: TexturePool::ns_find_all_textures 01007 // Access: Private 01008 // Description: The nonstatic implementation of find_all_textures(). 01009 //////////////////////////////////////////////////////////////////// 01010 TextureCollection TexturePool:: 01011 ns_find_all_textures(const string &name) const { 01012 MutexHolder holder(_lock); 01013 TextureCollection result; 01014 GlobPattern glob(name); 01015 01016 Textures::const_iterator ti; 01017 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 01018 Texture *tex = (*ti).second; 01019 if (glob.matches(tex->get_name())) { 01020 result.add_texture(tex); 01021 } 01022 } 01023 01024 return result; 01025 } 01026 01027 //////////////////////////////////////////////////////////////////// 01028 // Function: TexturePool::ns_make_texture 01029 // Access: Public 01030 // Description: Creates a new Texture object of the appropriate type 01031 // for the indicated filename extension, according to 01032 // the types that have been registered via 01033 // register_texture_type(). 01034 //////////////////////////////////////////////////////////////////// 01035 PT(Texture) TexturePool:: 01036 ns_make_texture(const string &extension) const { 01037 MakeTextureFunc *func = get_texture_type(extension); 01038 if (func != NULL) { 01039 return func(); 01040 } 01041 01042 // We don't know what kind of file type this is; return an ordinary 01043 // Texture in case it's an image file with no extension. 01044 return new Texture; 01045 } 01046 01047 //////////////////////////////////////////////////////////////////// 01048 // Function: TexturePool::resolve_filename 01049 // Access: Private 01050 // Description: Searches for the indicated filename along the 01051 // model path. If the filename was previously 01052 // searched for, doesn't search again, as an 01053 // optimization. Assumes _lock is held. 01054 //////////////////////////////////////////////////////////////////// 01055 void TexturePool:: 01056 resolve_filename(Filename &new_filename, const Filename &orig_filename, 01057 bool read_mipmaps, const LoaderOptions &options) { 01058 if (!_fake_texture_image.empty()) { 01059 new_filename = _fake_texture_image; 01060 return; 01061 } 01062 01063 RelpathLookup::iterator rpi = _relpath_lookup.find(orig_filename); 01064 if (rpi != _relpath_lookup.end()) { 01065 new_filename = (*rpi).second; 01066 return; 01067 } 01068 01069 new_filename = orig_filename; 01070 if (read_mipmaps || (options.get_texture_flags() & LoaderOptions::TF_multiview)) { 01071 new_filename.set_pattern(true); 01072 } 01073 01074 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 01075 vfs->resolve_filename(new_filename, get_model_path()); 01076 01077 _relpath_lookup[orig_filename] = new_filename; 01078 } 01079 01080 //////////////////////////////////////////////////////////////////// 01081 // Function: TexturePool::try_load_cache 01082 // Access: Private 01083 // Description: Attempts to load the texture from the cache record. 01084 //////////////////////////////////////////////////////////////////// 01085 void TexturePool:: 01086 try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename, 01087 PT(BamCacheRecord) &record, bool &compressed_cache_record, 01088 const LoaderOptions &options) { 01089 if (tex == (Texture *)NULL) { 01090 // The texture was not supplied by a texture filter. See if it 01091 // can be found in the on-disk cache, if it is active. 01092 if ((cache->get_cache_textures() || cache->get_cache_compressed_textures()) && !textures_header_only) { 01093 record = cache->lookup(filename, "txo"); 01094 if (record != (BamCacheRecord *)NULL) { 01095 if (record->has_data()) { 01096 tex = DCAST(Texture, record->get_data()); 01097 compressed_cache_record = (tex->get_ram_image_compression() != Texture::CM_off); 01098 int x_size = tex->get_orig_file_x_size(); 01099 int y_size = tex->get_orig_file_y_size(); 01100 tex->adjust_this_size(x_size, y_size, filename.get_basename(), true); 01101 01102 if (!cache->get_cache_textures() && !compressed_cache_record) { 01103 // We're not supposed to be caching uncompressed textures. 01104 if (gobj_cat.is_debug()) { 01105 gobj_cat.debug() 01106 << "Not caching uncompressed texture " << *tex << "\n"; 01107 } 01108 tex = NULL; 01109 record = NULL; 01110 01111 } else if (x_size != tex->get_x_size() || 01112 y_size != tex->get_y_size()) { 01113 // The cached texture no longer matches our expected size 01114 // (the resizing config variables must have changed). 01115 // We'll have to reload the texture from its original file 01116 // so we can rebuild the cache. 01117 if (gobj_cat.is_debug()) { 01118 gobj_cat.debug() 01119 << "Cached texture " << *tex << " has size " 01120 << tex->get_x_size() << " x " << tex->get_y_size() 01121 << " instead of " << x_size << " x " << y_size 01122 << "; dropping cache.\n"; 01123 } 01124 tex = NULL; 01125 01126 } else if (!tex->has_compression() && tex->get_ram_image_compression() != Texture::CM_off) { 01127 // This texture shouldn't be compressed, but it is. Go 01128 // reload it. 01129 if (gobj_cat.is_debug()) { 01130 gobj_cat.debug() 01131 << "Cached texture " << *tex 01132 << " is compressed in cache; dropping cache.\n"; 01133 } 01134 tex = NULL; 01135 01136 } else { 01137 gobj_cat.info() 01138 << "Texture " << filename << " found in disk cache.\n"; 01139 if ((options.get_texture_flags() & LoaderOptions::TF_preload_simple) && 01140 !tex->has_simple_ram_image()) { 01141 tex->generate_simple_ram_image(); 01142 } 01143 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) { 01144 // But drop the RAM until we need it. 01145 tex->clear_ram_image(); 01146 01147 } else { 01148 bool was_compressed = (tex->get_ram_image_compression() != Texture::CM_off); 01149 if (tex->consider_auto_process_ram_image(tex->uses_mipmaps(), true)) { 01150 bool is_compressed = (tex->get_ram_image_compression() != Texture::CM_off); 01151 if (!was_compressed && is_compressed && 01152 cache->get_cache_compressed_textures()) { 01153 // We've re-compressed the image after loading it 01154 // from the cache. To keep the cache current, 01155 // rewrite it to the cache now, in its newly 01156 // compressed form. 01157 record->set_data(tex, tex); 01158 cache->store(record); 01159 compressed_cache_record = true; 01160 } 01161 } 01162 } 01163 tex->set_keep_ram_image(false); 01164 } 01165 } else { 01166 if (!cache->get_cache_textures()) { 01167 // This texture has no actual record, and therefore no 01168 // compressed record (yet). And we're not supposed to be 01169 // caching uncompressed textures. 01170 if (gobj_cat.is_debug()) { 01171 gobj_cat.debug() 01172 << "Not caching uncompressed texture\n"; 01173 } 01174 record = NULL; 01175 } 01176 } 01177 } 01178 } 01179 } 01180 } 01181 01182 //////////////////////////////////////////////////////////////////// 01183 // Function: TexturePool::report_texture_unreadable 01184 // Access: Private 01185 // Description: Prints a suitable error message when a texture could 01186 // not be loaded. 01187 //////////////////////////////////////////////////////////////////// 01188 void TexturePool:: 01189 report_texture_unreadable(const Filename &filename) const { 01190 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 01191 bool has_hash = (filename.get_fullpath().find('#') != string::npos); 01192 if (!has_hash && !vfs->exists(filename)) { 01193 if (filename.is_local()) { 01194 // The file doesn't exist, and it wasn't 01195 // fully-qualified--therefore, it wasn't found along either 01196 // search path. 01197 gobj_cat.error() 01198 << "Unable to find texture \"" << filename << "\"" 01199 << " on model-path " << get_model_path() <<"\n"; 01200 } else { 01201 // A fully-specified filename is not searched along the path, so 01202 // don't mislead the user with the error message. 01203 gobj_cat.error() 01204 << "Texture \"" << filename << "\" does not exist.\n"; 01205 } 01206 01207 } else { 01208 // The file exists, but it couldn't be read for some reason. 01209 if (!has_hash) { 01210 gobj_cat.error() 01211 << "Texture \"" << filename << "\" exists but cannot be read.\n"; 01212 } else { 01213 // If the filename contains a hash, we'll be noncommittal about 01214 // whether it exists or not. 01215 gobj_cat.error() 01216 << "Texture \"" << filename << "\" cannot be read.\n"; 01217 } 01218 01219 // Maybe the filename extension is unknown. 01220 MakeTextureFunc *func = get_texture_type(filename.get_extension()); 01221 if (func == (MakeTextureFunc *)NULL) { 01222 gobj_cat.error() 01223 << "Texture extension \"" << filename.get_extension() 01224 << "\" is unknown. Supported texture types:\n"; 01225 write_texture_types(gobj_cat.error(false), 2); 01226 } 01227 } 01228 } 01229 01230 //////////////////////////////////////////////////////////////////// 01231 // Function: TexturePool::pre_load 01232 // Access: Private 01233 // Description: Invokes pre_load() on all registered filters until 01234 // one returns non-NULL; returns NULL if there are no 01235 // registered filters or if all registered filters 01236 // returned NULL. 01237 //////////////////////////////////////////////////////////////////// 01238 PT(Texture) TexturePool:: 01239 pre_load(const Filename &orig_filename, const Filename &orig_alpha_filename, 01240 int primary_file_num_channels, int alpha_file_channel, 01241 bool read_mipmaps, const LoaderOptions &options) { 01242 PT(Texture) tex; 01243 01244 MutexHolder holder(_lock); 01245 01246 FilterRegistry::iterator fi; 01247 for (fi = _filter_registry.begin(); 01248 fi != _filter_registry.end(); 01249 ++fi) { 01250 tex = (*fi)->pre_load(orig_filename, orig_alpha_filename, 01251 primary_file_num_channels, alpha_file_channel, 01252 read_mipmaps, options); 01253 if (tex != (Texture *)NULL) { 01254 return tex; 01255 } 01256 } 01257 01258 return tex; 01259 } 01260 01261 //////////////////////////////////////////////////////////////////// 01262 // Function: TexturePool::post_load 01263 // Access: Public, Virtual 01264 // Description: Invokes post_load() on all registered filters. 01265 //////////////////////////////////////////////////////////////////// 01266 PT(Texture) TexturePool:: 01267 post_load(Texture *tex) { 01268 PT(Texture) result = tex; 01269 01270 MutexHolder holder(_lock); 01271 01272 FilterRegistry::iterator fi; 01273 for (fi = _filter_registry.begin(); 01274 fi != _filter_registry.end(); 01275 ++fi) { 01276 result = (*fi)->post_load(result); 01277 } 01278 01279 return result; 01280 } 01281 01282 01283 //////////////////////////////////////////////////////////////////// 01284 // Function: TexturePool::load_filters 01285 // Access: Private 01286 // Description: Loads up all of the dll's named by the texture-filter 01287 // Config.prc variable. 01288 //////////////////////////////////////////////////////////////////// 01289 void TexturePool:: 01290 load_filters() { 01291 ConfigVariableList texture_filter 01292 ("texture-filter", 01293 PRC_DESC("Names one or more external libraries that should be loaded for the " 01294 "purposes of performing texture filtering. This variable may be repeated several " 01295 "times. As in load-display, the actual library filename is derived by " 01296 "prefixing 'lib' to the specified name.")); 01297 01298 int num_aux = texture_filter.get_num_unique_values(); 01299 for (int i = 0; i < num_aux; i++) { 01300 string name = texture_filter.get_unique_value(i); 01301 01302 Filename dlname = Filename::dso_filename("lib" + name + ".so"); 01303 gobj_cat->info() 01304 << "loading texture filter: " << dlname.to_os_specific() << endl; 01305 void *tmp = load_dso(get_plugin_path().get_value(), dlname); 01306 if (tmp == (void *)NULL) { 01307 gobj_cat.info() 01308 << "Unable to load: " << load_dso_error() << endl; 01309 } 01310 } 01311 }