32 using std::ostringstream;
35 BamCache *BamCache::_global_ptr =
nullptr;
49 PRC_DESC(
"The full path to a directory, local to this computer, in which " 50 "model and texture files will be cached on load. If a directory " 51 "name is specified here, files may be loaded from the cache " 52 "instead of from their actual pathnames, which may save load time, " 53 "especially if you are loading egg files instead of bam files, " 54 "or if you are loading models from a shared network drive. " 55 "If this is the empty string, no cache will be used."));
58 (
"model-cache-flush", 30,
59 PRC_DESC(
"This is the amount of time, in seconds, between automatic " 60 "flushes of the model-cache index."));
63 (
"model-cache-models",
true,
64 PRC_DESC(
"If this is set to true, models will be cached in the " 65 "model cache, as bam files."));
68 (
"model-cache-textures",
true,
69 PRC_DESC(
"If this is set to true, textures will also be cached in the " 70 "model cache, as txo files."));
73 (
"model-cache-compressed-textures",
false,
74 PRC_DESC(
"If this is set to true, compressed textures will be cached " 75 "in the model cache, in their compressed form as downloaded " 76 "by the GSG. This may be set in conjunction with " 77 "model-cache-textures, or it may be independent."));
80 (
"model-cache-compiled-shaders",
false,
81 PRC_DESC(
"If this is set to true, compiled shaders will be cached " 82 "in the model cache, in their binary form as downloaded " 86 (
"model-cache-max-kbytes", 10485760,
87 PRC_DESC(
"This is the maximum size of the model cache, in kilobytes."));
89 _cache_models = model_cache_models;
90 _cache_textures = model_cache_textures;
91 _cache_compressed_textures = model_cache_compressed_textures;
92 _cache_compiled_shaders = model_cache_compiled_shaders;
94 _flush_time = model_cache_flush;
95 _max_kbytes = model_cache_max_kbytes;
97 if (!model_cache_dir.empty()) {
98 set_root(model_cache_dir);
135 _index_stale_since = 0;
159 lookup(
const Filename &source_filename,
const string &cache_extension) {
165 Filename source_pathname(source_filename);
168 Filename rel_pathname(source_pathname);
179 return find_and_read_record(source_pathname, cache_filename);
191 nassertr(!record->_cache_pathname.empty(),
false);
192 nassertr(record->has_data(),
false);
202 Filename rel_pathname(record->_cache_pathname);
204 nassertr(rel_pathname.
is_local(),
false);
207 record->_recorded_time = time(
nullptr);
209 Filename cache_pathname = Filename::binary_filename(record->_cache_pathname);
214 Thread *current_thread = Thread::get_current_thread();
215 string extension = current_thread->
get_unique_id() + string(
".tmp");
216 Filename temp_pathname = cache_pathname;
221 if (!dout.
open(temp_pathname)) {
223 <<
"Could not write cache file: " << temp_pathname <<
"\n";
225 emergency_read_only();
231 <<
"Unable to write to " << temp_pathname <<
"\n";
238 if (!writer.
init()) {
240 <<
"Unable to write Bam header to " << temp_pathname <<
"\n";
247 if (record->
get_data()->is_of_type(texture_type)) {
257 if (record->
get_data()->is_of_type(node_type)) {
263 <<
"Unable to write object to " << temp_pathname <<
"\n";
270 <<
"Unable to write object data to " << temp_pathname <<
"\n";
284 if (!vfs->
rename_file(temp_pathname, cache_pathname) && vfs->
exists(temp_pathname)) {
286 if (!vfs->
rename_file(temp_pathname, cache_pathname)) {
288 <<
"Unable to rename " << temp_pathname <<
" to " 289 << cache_pathname <<
"\n";
295 add_to_index(record);
306 emergency_read_only() {
308 "Could not write to the Bam Cache. Disabling future attempts.\n";
318 #if defined(HAVE_THREADS) || defined(DEBUG_THREADS) 326 if (_index_stale_since != 0) {
327 int elapsed = (int)time(
nullptr) - (int)_index_stale_since;
328 if (elapsed > _flush_time) {
333 #if defined(HAVE_THREADS) || defined(DEBUG_THREADS) 344 if (_index_stale_since == 0) {
356 if (!do_write_index(temp_pathname, _index)) {
357 emergency_read_only();
365 string old_index = _index_ref_contents;
374 _index_pathname = temp_pathname;
375 _index_ref_contents = new_index;
376 _index_stale_since = 0;
384 _index_ref_contents = orig_index;
395 _index->write(out, indent_level);
404 if (!read_index_pathname(_index_pathname, _index_ref_contents)) {
412 if (new_index !=
nullptr) {
413 merge_index(new_index);
419 Filename old_index_pathname = _index_pathname;
420 if (!read_index_pathname(_index_pathname, _index_ref_contents)) {
426 if (old_index_pathname == _index_pathname) {
442 read_index_pathname(
Filename &index_pathname,
string &index_ref_contents)
const {
444 index_ref_contents.clear();
450 string trimmed =
trim(index_ref_contents);
451 if (trimmed.empty()) {
468 if (_index_stale_since == 0) {
476 old_index->release_records();
477 new_index->release_records();
480 BamCacheIndex::Records::const_iterator ai = old_index->_records.begin();
481 BamCacheIndex::Records::const_iterator bi = new_index->_records.begin();
483 while (ai != old_index->_records.end() &&
484 bi != new_index->_records.end()) {
485 if ((*ai).first < (*bi).first) {
488 Filename cache_pathname(_root, record->get_cache_filename());
489 if (cache_pathname.exists()) {
491 _index->_records.insert(_index->_records.end(), BamCacheIndex::Records::value_type(record->get_source_pathname(), record));
495 }
else if ((*bi).first < (*ai).first) {
498 Filename cache_pathname(_root, record->get_cache_filename());
499 if (cache_pathname.exists()) {
501 _index->_records.insert(_index->_records.end(), BamCacheIndex::Records::value_type(record->get_source_pathname(), record));
509 if (*a_record == *b_record) {
512 _index->_records.insert(_index->_records.end(), BamCacheIndex::Records::value_type(a_record->get_source_pathname(), a_record));
518 Filename cache_pathname(_root, a_record->get_cache_filename());
520 if (cache_pathname.exists()) {
521 PT(
BamCacheRecord) record = do_read_record(cache_pathname,
false);
522 if (record !=
nullptr) {
523 _index->_records.insert(_index->_records.end(), BamCacheIndex::Records::value_type(record->get_source_pathname(), record));
533 while (ai != old_index->_records.end()) {
536 Filename cache_pathname(_root, record->get_cache_filename());
537 if (cache_pathname.exists()) {
539 _index->_records.insert(_index->_records.end(), BamCacheIndex::Records::value_type(record->get_source_pathname(), record));
544 while (bi != new_index->_records.end()) {
547 Filename cache_pathname(_root, record->get_cache_filename());
548 if (cache_pathname.exists()) {
550 _index->_records.insert(_index->_records.end(), BamCacheIndex::Records::value_type(record->get_source_pathname(), record));
555 _index->process_new_records();
566 if (contents ==
nullptr) {
568 <<
"Unable to read directory " << _root <<
", caching disabled.\n";
576 int num_files = contents->get_num_files();
577 for (
int ci = 0; ci < num_files; ++ci) {
579 Filename filename = file->get_filename();
585 if (record ==
nullptr) {
587 if (util_cat.is_debug()) {
589 <<
"Deleting invalid " << pathname <<
"\n";
594 record->_record_access_time = record->_recorded_time;
596 bool inserted = _index->_records.insert(BamCacheIndex::Records::value_type(record->get_source_pathname(), record)).second;
599 <<
"Multiple cache files defining " << record->get_source_pathname() <<
"\n";
605 _index->process_new_records();
607 _index_stale_since = time(
nullptr);
620 if (_index->add_record(new_record)) {
630 remove_from_index(
const Filename &source_pathname) {
631 if (_index->remove_record(source_pathname)) {
642 if (_index->_cache_size == 0) {
647 if (_index->_cache_size / 1024 > _max_kbytes) {
648 while (_index->_cache_size / 1024 > _max_kbytes) {
650 if (record ==
nullptr) {
655 Filename cache_pathname(_root, record->get_cache_filename());
656 if (util_cat.is_debug()) {
658 <<
"Deleting " << cache_pathname
659 <<
" to keep cache size below " << _max_kbytes <<
"K\n";
672 do_read_index(
const Filename &index_pathname) {
673 if (index_pathname.empty()) {
678 if (!din.
open(index_pathname)) {
680 <<
"Could not read index file: " << index_pathname <<
"\n";
687 << index_pathname <<
" is not an index file.\n";
691 if (head != _bam_header) {
693 << index_pathname <<
" is not an index file.\n";
698 if (!reader.init()) {
704 if (
object ==
nullptr) {
706 <<
"Cache index " << index_pathname <<
" is empty.\n";
709 }
else if (!object->
is_of_type(BamCacheIndex::get_class_type())) {
711 <<
"Cache index " << index_pathname <<
" contains a " 712 <<
object->
get_type() <<
", not a BamCacheIndex.\n";
717 if (!reader.resolve()) {
719 <<
"Unable to fully resolve cache index file.\n";
733 if (!dout.
open(index_pathname)) {
735 <<
"Could not write index file: " << index_pathname <<
"\n";
742 <<
"Unable to write to " << index_pathname <<
"\n";
749 if (!writer.init()) {
754 if (!writer.write_object(index)) {
769 find_and_read_record(
const Filename &source_pathname,
774 read_record(source_pathname, cache_filename, pass);
775 if (record !=
nullptr) {
776 add_to_index(record);
788 read_record(
const Filename &source_pathname,
792 Filename cache_pathname(_root, cache_filename);
795 strm << cache_pathname.get_basename_wo_extension() <<
"_" << pass;
796 cache_pathname.set_basename_wo_extension(strm.str());
799 if (!cache_pathname.exists()) {
801 if (util_cat.is_debug()) {
803 <<
"Declaring new cache file " << cache_pathname <<
" for " << source_pathname <<
"\n";
807 record->_cache_pathname = cache_pathname;
811 if (util_cat.is_debug()) {
813 <<
"Reading cache file " << cache_pathname <<
" for " << source_pathname <<
"\n";
817 if (record ==
nullptr) {
819 if (util_cat.is_debug()) {
821 <<
"Deleting invalid cache file " << cache_pathname <<
"\n";
824 remove_from_index(source_pathname);
828 record->_cache_pathname = cache_pathname;
832 if (record->get_source_pathname() != source_pathname) {
834 if (util_cat.is_debug()) {
836 <<
"Cache file " << cache_pathname <<
" references " 837 << record->get_source_pathname() <<
", not " 838 << source_pathname <<
"\n";
843 if (!record->has_data()) {
848 record->_cache_pathname = cache_pathname;
856 do_read_record(
const Filename &cache_pathname,
bool read_data) {
858 if (!din.
open(cache_pathname)) {
859 if (util_cat.is_debug()) {
861 <<
"Could not read cache file: " << cache_pathname <<
"\n";
868 if (util_cat.is_debug()) {
870 << cache_pathname <<
" is not a cache file.\n";
875 if (head != _bam_header) {
876 if (util_cat.is_debug()) {
878 << cache_pathname <<
" is not a cache file.\n";
884 if (!reader.init()) {
889 if (
object ==
nullptr) {
890 if (util_cat.is_debug()) {
892 << cache_pathname <<
" is empty.\n";
896 }
else if (!object->
is_of_type(BamCacheRecord::get_class_type())) {
897 if (util_cat.is_debug()) {
899 <<
"Cache file " << cache_pathname <<
" contains a " 900 <<
object->
get_type() <<
", not a BamCacheRecord.\n";
906 if (!reader.resolve()) {
907 if (util_cat.is_debug()) {
909 <<
"Unable to fully resolve cache record in " << cache_pathname <<
"\n";
924 if (reader.read_object(ptr, ref_ptr)) {
925 if (!reader.resolve()) {
926 if (util_cat.is_debug()) {
928 <<
"Unable to fully resolve cached object in " << cache_pathname <<
"\n";
942 record->_record_size = vfile->get_file_size(&in);
945 record->_record_access_time = time(
nullptr);
955 hash_filename(
const string &filename) {
959 hv.hash_string(filename);
964 #else // HAVE_OPENSSL 966 unsigned int hash = 0;
967 for (string::const_iterator si = filename.begin();
968 si != filename.end();
970 hash = (hash * 9109) + (
unsigned int)(*si);
974 strm << std::hex << std::setw(8) << std::setfill(
'0') << hash;
977 #endif // HAVE_OPENSSL 987 if (_global_ptr->_root.empty()) {
bool try_lock()
Alias for try_acquire() to match C++11 semantics.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual std::streampos get_file_pos()
Returns the current file position within the data stream, if any, or 0 if the file position is not me...
void set_extension(const std::string &s)
Replaces the file extension.
set_data
Stores a new data object on the record.
bool rename_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to move or rename the indicated file or directory.
set_root_node
Sets the root node of the part of the scene graph we are currently writing out.
set_active
Changes the state of the active flag.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
This is a convenience class to specialize ConfigVariable as a Filename type.
A hierarchy of directories and files that appears to be one continuous file system,...
This is a convenience class to specialize ConfigVariable as a boolean type.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_binary()
Indicates that the filename represents a binary file.
Base class for objects that can be written to and read from Bam files.
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
bool atomic_read_contents(const Filename &filename, std::string &contents) const
See Filename::atomic_read_contents().
void flush_index()
Ensures the index is written to disk.
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
bool make_directory_full(const Filename &filename)
Attempts to create a directory within the file system.
The abstract base class for a file or directory within the VirtualFileSystem.
void output_hex(std::ostream &out) const
Outputs the HashVal as a 32-digit hexadecimal number.
TypeHandle find_type(const std::string &name) const
Looks for a previously-registered type of the given name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool open(const FileReference *file)
Opens the indicated filename for writing.
PT(BamCacheRecord) BamCache
Looks up a file in the cache.
void list_index(std::ostream &out, int indent_level=0) const
Writes the contents of the index to standard output.
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
This represents the in-memory index that records the list of files stored in the BamCache.
void set_file_texture_mode(BamTextureMode file_texture_mode)
Changes the BamTextureMode preference for the Bam file currently being written.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
A list of VirtualFiles, as returned by VirtualFile::scan_directory().
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
bool is_directory(const Filename &filename) const
Convenience function; returns true if the named file exists and is a directory.
The name of a file, such as a texture file or an Egg file.
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
virtual bool delete_file()
Attempts to delete this file or directory.
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
void unlock()
Alias for release() to match C++11 semantics.
Similar to MutexHolder, but for a reentrant mutex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_local() const
Returns true if the filename is local, e.g.
std::string get_extension() const
Returns the file extension.
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash,...
std::string get_basename() const
Returns the basename part of the filename.
set_root
Changes the current root pathname of the cache.
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
This class can be used to write a binary file that consists of an arbitrary header followed by a numb...
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname),...
A base class for all things that want to be reference-counted.
void clear_dependent_files()
Empties the list of files that contribute to the data in this record.
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
A thread; that is, a lightweight process.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a convenience class to specialize ConfigVariable as an integer type.
get_unique_id
Returns a string that is guaranteed to be unique to this thread, across all processes on the machine,...
static Filename temporary(const std::string &dirname, const std::string &prefix, const std::string &suffix=std::string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Filename get_cwd() const
Returns the current directory name.
bool dependents_unchanged() const
Returns true if all of the dependent files are still the same as when the cache was recorded,...
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
TypeHandle is the identifier used to differentiate C++ class types.
void close()
Closes the file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
bool atomic_compare_and_exchange_contents(const Filename &filename, std::string &orig_contents, const std::string &old_contents, const std::string &new_contents)
See Filename::atomic_compare_and_exchange_contents().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void consider_flush_index()
Flushes the index if enough time has elapsed since the index was last flushed.