Panda3D

texturePool.cxx

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