Browse Source

AdaptiveLru

David Rose 17 years ago
parent
commit
684810cbdd

+ 11 - 124
panda/src/display/lru.cxx

@@ -14,7 +14,7 @@
 
 
 //#include "stdafx.h"
 //#include "stdafx.h"
 
 
-#define LRU_UNIT_TEST 0
+#define LRU_UNIT_TEST 1
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
@@ -22,6 +22,7 @@
 
 
 #include "lru.h"
 #include "lru.h"
 
 
+#define OutputDebugString(string) cerr << string
 
 
 static const int HIGH_PRIORITY_SCALE = 4;
 static const int HIGH_PRIORITY_SCALE = 4;
 static const int LOW_PRIORITY_RANGE = 25;
 static const int LOW_PRIORITY_RANGE = 25;
@@ -145,7 +146,7 @@ Lru::~Lru ( )
 LruPage::LruPage ( )
 LruPage::LruPage ( )
 {
 {
   if(this) {
   if(this) {
-    memset(&this->_m, 0, sizeof (LruPageVariables));
+    memset(&this->_m, 0, (char *)&this->_m.name - (char *)&this->_m);
     _m.name = "";   
     _m.name = "";   
   }
   }
 }
 }
@@ -331,6 +332,7 @@ void Lru::free_page (LruPage *lru_page)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Lru::add_page (LruPagePriority priority, LruPage *lru_page)
 void Lru::add_page (LruPagePriority priority, LruPage *lru_page)
 {
 {
+  cerr << "adding " << lru_page << " with " << priority << "\n";
   if(lru_page) {
   if(lru_page) {
     LruMutexHolder(this->_m.mutex);
     LruMutexHolder(this->_m.mutex);
 
 
@@ -358,6 +360,7 @@ void Lru::add_page (LruPagePriority priority, LruPage *lru_page)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Lru::add_cached_page (LruPagePriority priority, LruPage *lru_page)
 void Lru::add_cached_page (LruPagePriority priority, LruPage *lru_page)
 {
 {
+  cerr << "adding " << lru_page << " with " << priority << "\n";
   if(lru_page) {
   if(lru_page) {
     LruMutexHolder(this->_m.mutex);
     LruMutexHolder(this->_m.mutex);
 
 
@@ -385,6 +388,7 @@ void Lru::add_cached_page (LruPagePriority priority, LruPage *lru_page)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Lru::remove_page (LruPage *lru_page)
 void Lru::remove_page (LruPage *lru_page)
 {
 {
+  cerr << "removing " << lru_page << "\n";
   if(this) {
   if(this) {
     if(this->_m.total_pages > 0) {
     if(this->_m.total_pages > 0) {
       if(lru_page) {
       if(lru_page) {
@@ -595,7 +599,7 @@ void Lru::update_lru_page (LruPage *lru_page)
 {
 {
 
 
 #if LRU_UNIT_TEST
 #if LRU_UNIT_TEST
-  if(false) {
+  if(true) {
     char  string[256];
     char  string[256];
 
 
     sprintf(string, "  UPDATE %d\n", lru_page->_m.identifier);
     sprintf(string, "  UPDATE %d\n", lru_page->_m.identifier);
@@ -659,6 +663,7 @@ void Lru::update_lru_page (LruPage *lru_page)
           }
           }
 
 
           delta_priority = target_priority - lru_page->_m.priority;
           delta_priority = target_priority - lru_page->_m.priority;
+          cerr << "target_priority = " << target_priority << ", delta = " << delta_priority << ", util = " << lru_page->_m.average_frame_utilization << "\n";
           lru_page->change_priority(delta_priority);
           lru_page->change_priority(delta_priority);
         }
         }
       }
       }
@@ -689,127 +694,7 @@ void Lru::update_lru_page (LruPage *lru_page)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Lru::update_lru_page_old (LruPage *lru_page)
 void Lru::update_lru_page_old (LruPage *lru_page)
 {
 {
-
-#if LRU_UNIT_TEST
-  if(false) {
-    char  string[256];
-
-    sprintf(string, "  UPDATE %d\n", lru_page->_m.identifier);
-    OutputDebugString(string);
-  }
-#endif
-
-  if(lru_page->_m.v.lock == false) {
-    int  delta_priority;
-
-    delta_priority = 0;
-    if(false && lru_page->_m.total_usage > 0) {
-      int lifetime_frames;
-
-      lifetime_frames = this->_m.current_frame_identifier -
-        lru_page->_m.first_frame_identifier;
-      if(lifetime_frames >= 10) {
-        float one_over_update_frames;
-
-        if(lru_page->_m.update_frame_identifier) {
-          int target_priority;
-          int integer_update_frames;
-          float update_frames;
-          float update_average_frame_utilization;
-          float average_frame_bandwidth_utilization;
-
-          integer_update_frames = (this->_m.current_frame_identifier -
-            lru_page->_m.update_frame_identifier);
-          if(integer_update_frames > 0) {
-            update_frames = ( float ) integer_update_frames;
-            one_over_update_frames = 1.0f / update_frames;
-
-            update_average_frame_utilization =
-              (float) (lru_page->_m.update_total_usage) *
-              one_over_update_frames;
-
-            lru_page->_m.average_frame_utilization =
-              calculate_exponential_moving_average (
-                 update_average_frame_utilization, this->_m.weight,
-                 lru_page->_m.average_frame_utilization);
-
-            average_frame_bandwidth_utilization =
-              lru_page->_m.average_frame_utilization *
-              lru_page->_m.size;
-
-            target_priority = (int) (average_frame_bandwidth_utilization *
-              this->_m.frame_bandwidth_factor);
-
-            target_priority = (LPP_TotalPriorities - 1) - target_priority;
-            if(target_priority < 0) {
-              target_priority = 0;
-            }
-            if(target_priority >= LPP_TotalPriorities) {
-              target_priority = LPP_TotalPriorities - 1;
-            }
-
-            delta_priority = target_priority - lru_page->_m.priority;
-            lru_page->change_priority(delta_priority);
-          }
-        }
-
-        lru_page->_m.update_frame_identifier =
-          this->_m.current_frame_identifier;
-
-        lru_page->_m.update_total_usage = 0;
-      }
-    }
-
-    if(delta_priority == 0) {
-      if(this->_m.current_frame_identifier
-        == lru_page->_m.current_frame_identifier) {
-        // page used during this frame twice or more =>
-        // increase priority
-        if(lru_page->_m.current_frame_usage >= 2) {
-          if(lru_page->_m.priority >= LPP_High) {
-            lru_page->change_priority(-2);
-          }
-        }
-
-        if(lru_page->_m.total_frame_page_faults >= 1) {
-          // multiple page faults this frame => increase priority
-          if(lru_page->_m.total_frame_page_faults >= 2) {
-            if(lru_page->_m.priority >= LPP_High) {
-              lru_page->change_priority(-2);
-            }
-          }
-          else {
-            // single page faults this frame => increase priority
-            if(lru_page->_m.priority >= LPP_High) {
-              lru_page->change_priority(-1);
-            }
-          }
-        }
-      }
-      else {
-        // page not used this frame
-        int  last_access_delta;
-
-        last_access_delta
-        = this->_m.current_frame_identifier
-          - lru_page->_m.current_frame_identifier;
-        if(last_access_delta > 1) {
-          if(lru_page->_m.priority < LPP_Low) {
-            lru_page->change_priority(+1);
-          }
-        }
-      }
-    }
-
-    if(lru_page->_m.priority_change) {
-      if(this->_m.total_lru_page_priority_changes
-         < FRAME_MAXIMUM_PRIORITY_CHANGES) {
-        this->_m.lru_page_priority_change_array
-          [this->_m.total_lru_page_priority_changes]= lru_page;
-        this->_m.total_lru_page_priority_changes++;
-      }
-    }
-  }
+  nassertv(false);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1154,6 +1039,8 @@ void Lru::calculate_lru_statistics (void)
 float calculate_exponential_moving_average(float value,
 float calculate_exponential_moving_average(float value,
   float weight, float average)
   float weight, float average)
 {
 {
+  cerr << "exp(" << value << ", " << weight << ", " << average << " = "
+       << ((value - average) * weight) + average << "\n";
   return ((value - average) * weight) + average;
   return ((value - average) * weight) + average;
 }
 }
 
 

+ 2 - 0
panda/src/display/lru.h

@@ -263,7 +263,9 @@ float calculate_exponential_moving_average (float value, float weight, float ave
 bool default_page_in_function (LruPage *lru_page);
 bool default_page_in_function (LruPage *lru_page);
 bool default_page_out_function (LruPage *lru_page);
 bool default_page_out_function (LruPage *lru_page);
 
 
+BEGIN_PUBLISH
 void test_ema (void);
 void test_ema (void);
 void test_lru (void);
 void test_lru (void);
+END_PUBLISH
 
 
 #endif
 #endif

+ 3 - 0
panda/src/gobj/Sources.pp

@@ -11,6 +11,7 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
 
   #define SOURCES \
   #define SOURCES \
+    adaptiveLru.I adaptiveLru.h \
     bufferContext.I bufferContext.h \
     bufferContext.I bufferContext.h \
     bufferContextChain.I bufferContextChain.h \
     bufferContextChain.I bufferContextChain.h \
     bufferResidencyTracker.I bufferResidencyTracker.h \
     bufferResidencyTracker.I bufferResidencyTracker.h \
@@ -75,6 +76,7 @@
     videoTexture.I videoTexture.h
     videoTexture.I videoTexture.h
     
     
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
+    adaptiveLru.cxx \
     bufferContext.cxx \
     bufferContext.cxx \
     bufferContextChain.cxx \
     bufferContextChain.cxx \
     bufferResidencyTracker.cxx \
     bufferResidencyTracker.cxx \
@@ -138,6 +140,7 @@
     videoTexture.cxx
     videoTexture.cxx
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
+    adaptiveLru.I adaptiveLru.h \
     bufferContext.I bufferContext.h \
     bufferContext.I bufferContext.h \
     bufferContextChain.I bufferContextChain.h \
     bufferContextChain.I bufferContextChain.h \
     bufferResidencyTracker.I bufferResidencyTracker.h \
     bufferResidencyTracker.I bufferResidencyTracker.h \

+ 229 - 0
panda/src/gobj/adaptiveLru.I

@@ -0,0 +1,229 @@
+// Filename: adaptiveLru.I
+// Created by:  drose (03Sep08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::get_total_size
+//       Access: Published
+//  Description: Returns the total size of all objects currently
+//               active on the LRU.
+////////////////////////////////////////////////////////////////////
+INLINE size_t AdaptiveLru::
+get_total_size() const {
+  MutexHolder holder(_lock);
+  return _total_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::get_max_size
+//       Access: Published
+//  Description: Returns the max size of all objects that are allowed
+//               to be active on the LRU.
+////////////////////////////////////////////////////////////////////
+INLINE size_t AdaptiveLru::
+get_max_size() const {
+  MutexHolder holder(_lock);
+  return _max_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::set_max_size
+//       Access: Published
+//  Description: Changes the max size of all objects that are allowed
+//               to be active on the LRU.
+//
+//               If the size is (size_t)-1, there is no limit.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLru::
+set_max_size(size_t max_size) {
+  MutexHolder holder(_lock);
+  _max_size = max_size;
+  if (_total_size > _max_size) {
+    do_evict_to(_max_size, false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::consider_evict
+//       Access: Published
+//  Description: Evicts a sequence of objects if the queue is full.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLru::
+consider_evict() {
+  MutexHolder holder(_lock);
+  if (_total_size > _max_size) {
+    do_evict_to(_max_size, false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::evict_to
+//       Access: Published
+//  Description: Evicts a sequence of objects until the queue fits
+//               within the indicated target size, regardless of its
+//               normal max size.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLru::
+evict_to(size_t target_size) {
+  MutexHolder holder(_lock);
+  if (_total_size > target_size) {
+    do_evict_to(target_size, true);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::validate
+//       Access: Published
+//  Description: Checks that the LRU is internally self-consistent.
+//               Returns true if successful, false if there is some
+//               problem.
+////////////////////////////////////////////////////////////////////
+INLINE bool AdaptiveLru::
+validate() {
+  MutexHolder holder(_lock);
+  return do_validate();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::set_weight
+//       Access: Published
+//  Description: Specifies the weight value used to compute the
+//               exponential moving average.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLru::
+set_weight(float weight) {
+  _weight = weight;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::get_weight
+//       Access: Published
+//  Description: Returns the weight value used to compute the
+//               exponential moving average.
+////////////////////////////////////////////////////////////////////
+INLINE float AdaptiveLru::
+get_weight() const {
+  return _weight;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::set_max_updates_per_frame
+//       Access: Published
+//  Description: Specifies the maximum number of pages the AdaptiveLru
+//               will update each frame.  This is a performance
+//               optimization: keeping this number low limits the
+//               impact of the AdaptiveLru's adaptive algorithm.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLru::
+set_max_updates_per_frame(int max_updates_per_frame) {
+  _max_updates_per_frame = max_updates_per_frame;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::get_max_updates_per_frame
+//       Access: Published
+//  Description: Returns the maximum number of pages the AdaptiveLru
+//               will update each frame.
+////////////////////////////////////////////////////////////////////
+INLINE int AdaptiveLru::
+get_max_updates_per_frame() const {
+  return _max_updates_per_frame;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::calculate_exponential_moving_average
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE float AdaptiveLru::
+calculate_exponential_moving_average(float value, float average) const {
+  return ((value - average) * _weight) + average;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::enqueue_lru
+//       Access: Published
+//  Description: Returns the LRU that manages this page, or NULL if it
+//               is not currently managed by any LRU.
+////////////////////////////////////////////////////////////////////
+INLINE AdaptiveLru *AdaptiveLruPage::
+get_lru() const {
+  return _lru;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::dequeue_lru
+//       Access: Published
+//  Description: Removes the page from its AdaptiveLru.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLruPage::
+dequeue_lru() {
+  enqueue_lru(NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::mark_used_lru
+//       Access: Published
+//  Description: To be called when the page is used; this will move it
+//               to the tail of the AdaptiveLru queue it is already on.
+//
+//               This method is const because it's not technically
+//               modifying the contents of the page itself.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLruPage::
+mark_used_lru() const {
+  if (_lru != (AdaptiveLru *)NULL) {
+    ((AdaptiveLruPage *)this)->mark_used_lru(_lru);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::mark_used_lru
+//       Access: Published
+//  Description: To be called when the page is used; this will move it
+//               to the tail of the specified AdaptiveLru queue.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLruPage::
+mark_used_lru(AdaptiveLru *lru) {
+  enqueue_lru(lru);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::get_lru_size
+//       Access: Published
+//  Description: Returns the size of this page as reported to the LRU,
+//               presumably in bytes.
+////////////////////////////////////////////////////////////////////
+INLINE size_t AdaptiveLruPage::
+get_lru_size() const {
+  return _lru_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::set_lru_size
+//       Access: Published
+//  Description: Specifies the size of this page, presumably in bytes,
+//               although any unit is possible.
+////////////////////////////////////////////////////////////////////
+INLINE void AdaptiveLruPage::
+set_lru_size(size_t lru_size) {
+  if (_lru != (AdaptiveLru *)NULL) {
+    MutexHolder holder(_lru->_lock);
+    _lru->_total_size -= _lru_size;
+    _lru->_total_size += lru_size;
+    _lru_size = lru_size;
+  } else {
+    _lru_size = lru_size;
+  }
+}

+ 676 - 0
panda/src/gobj/adaptiveLru.cxx

@@ -0,0 +1,676 @@
+// Filename: adaptiveLru.cxx
+// Created by:  drose (03Sep08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "adaptiveLru.h"
+#include "config_gobj.h"
+#include "indent.h"
+
+static const int HIGH_PRIORITY_SCALE = 4;
+static const int LOW_PRIORITY_RANGE = 25;
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+AdaptiveLru::
+AdaptiveLru(const string &name, size_t max_size) : 
+  Namable(name)
+{
+  _total_size = 0;
+  _max_size = max_size;
+
+  _current_frame_identifier = 0;
+  _weight = adaptive_lru_weight;
+  _max_updates_per_frame = adaptive_lru_max_updates_per_frame;
+
+  // Initialize our list heads to empty.
+  _static_list._next = &_static_list;
+  _static_list._prev = &_static_list;
+
+  int index;
+  for (index = 0; index < LPP_TotalPriorities; ++index) {
+    _page_array[index]._next = &_page_array[index];
+    _page_array[index]._prev = &_page_array[index];
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+AdaptiveLru::
+~AdaptiveLru() {
+#ifndef NDEBUG
+  // We're shutting down.  Force-remove everything remaining, but
+  // don't explicitly evict it (that would force vertex buffers to
+  // write themselves to disk unnecessarily).
+
+  while (_static_list._next != &_static_list) {
+    nassertv(_static_list._next != (LinkedListNode *)NULL);
+    AdaptiveLruPage *page = (AdaptiveLruPage *)(AdaptiveLruPageStaticList *)_static_list._next;
+
+    page->_lru = NULL;
+    ((AdaptiveLruPageDynamicList *)page)->remove_from_list();
+    ((AdaptiveLruPageStaticList *)page)->remove_from_list();
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::do_partial_lru_update
+//       Access: Private
+//  Description: This only updates a number of pages up to the
+//               specified maximum_updates.  Assumes the lock is held.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+do_partial_lru_update(int num_updates) {
+  // Iterate sequentially through the static list of pages.  As we
+  // process each page, pop it and push it back on the tail.  Stop
+  // when we have processed num_updates, or come back to the starting
+  // one.
+
+  AdaptiveLruPageStaticList *start_node = (AdaptiveLruPageStaticList *)_static_list._next;
+  if (start_node == &_static_list) {
+    // List is empty.
+    return;
+  }
+
+  AdaptiveLruPageStaticList *node = start_node;
+  do {
+    nassertv(node != &_static_list);
+    AdaptiveLruPageStaticList *next = (AdaptiveLruPageStaticList *)node->_next;
+    if (--num_updates <= 0) {
+      return;
+    }
+
+    update_page((AdaptiveLruPage *)node);
+    node->remove_from_list();
+    node->insert_before(&_static_list);
+    node = next;
+  } while (node != start_node);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::update_page
+//       Access: Private
+//  Description: This updates the page's average utilization.
+//               Priority LPP_New is considered to be average usage
+//               of 1.0 (which means the page is used once per frame
+//               on average).  Priorities < LPP_New are for pages
+//               used more than once per frame and Priorities >
+//               LPP_New are for pages used less than once per frame.
+//
+//               Assumes the lock is held.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+update_page(AdaptiveLruPage *page) {
+  int target_priority = page->_priority;
+  int lifetime_frames = _current_frame_identifier - page->_first_frame_identifier;
+  if (lifetime_frames >= 1) {
+    if (page->_update_frame_identifier) {
+      int update_frames;
+      
+      update_frames = (_current_frame_identifier - page->_update_frame_identifier);
+      if (update_frames > 0) {
+        float update_average_frame_utilization =
+          (float) (page->_update_total_usage) / (float)update_frames;
+
+        page->_average_frame_utilization =
+          calculate_exponential_moving_average(update_average_frame_utilization, 
+                                               page->_average_frame_utilization);
+
+        target_priority = page->_priority;
+        if (page->_average_frame_utilization >= 1.0f) {
+          int integer_average_frame_utilization;
+          
+          integer_average_frame_utilization =
+            (int) ((page->_average_frame_utilization - 1.0f) *
+                   (float) HIGH_PRIORITY_SCALE);
+          if (integer_average_frame_utilization >= LPP_New) {
+            integer_average_frame_utilization = LPP_New;
+          }
+          integer_average_frame_utilization = LPP_New -
+            integer_average_frame_utilization;
+          target_priority = integer_average_frame_utilization;
+        } else {
+          int integer_average_frame_utilization;
+          
+          integer_average_frame_utilization = (int)
+            (page->_average_frame_utilization *
+             (float) LOW_PRIORITY_RANGE);
+          integer_average_frame_utilization = LOW_PRIORITY_RANGE -
+            integer_average_frame_utilization;
+          target_priority = LPP_New + integer_average_frame_utilization;
+        }
+      }
+    }
+
+    page->_update_frame_identifier = _current_frame_identifier;
+    page->_update_total_usage = 0;
+  }
+
+  if (target_priority != page->_priority) {
+    page->_priority = min(max(target_priority, 0), LPP_TotalPriorities - 1);
+    ((AdaptiveLruPageDynamicList *)page)->remove_from_list();
+    ((AdaptiveLruPageDynamicList *)page)->insert_before(&_page_array[page->_priority]);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::enqueue_lru
+//       Access: Published
+//  Description: Adds the page to the LRU for the first time, or marks
+//               it recently-accessed if it has already been added.
+//
+//               If lru is NULL, it means to remove this page from its
+//               LRU.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLruPage::
+enqueue_lru(AdaptiveLru *lru) {
+  if (lru != _lru && _lru != (AdaptiveLru *)NULL) {
+    // It was previously on a different LRU.  Remove it first.
+    _lru->do_remove_page(this);
+    _lru = NULL;
+  }
+
+  if (lru == _lru) {
+    if (_lru != (AdaptiveLru *)NULL) {
+      // It's already on this LRU.  Access it.
+      _lru->do_access_page(this);
+    }
+  } else {
+    nassertv(lru != (AdaptiveLru *)NULL);
+    // Add it to a new LRU.
+    _lru = lru;
+
+    _priority = AdaptiveLru::LPP_New;
+    _first_frame_identifier = _lru->_current_frame_identifier;
+    _last_frame_identifier = _lru->_current_frame_identifier;
+    _lru->do_add_page(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::count_active_size
+//       Access: Published
+//  Description: Returns the total size of the pages that were
+//               enqueued since the last call to begin_epoch().
+////////////////////////////////////////////////////////////////////
+size_t AdaptiveLru::
+count_active_size() const {
+  size_t counted_size = 0;
+
+  int minimum_frame_identifier = _current_frame_identifier - 1;
+
+  AdaptiveLruPageStaticList *node = (AdaptiveLruPageStaticList *)_static_list._next;
+  while (node != &_static_list) {
+    AdaptiveLruPage *page = (AdaptiveLruPage *)node;
+    if (page->_current_frame_identifier >= minimum_frame_identifier) {
+      counted_size += page->_lru_size;
+    }
+    node = (AdaptiveLruPageStaticList *)node->_next;
+  }
+
+  return counted_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::begin_epoch
+//       Access: Published
+//  Description: Marks the end of the previous epoch and the beginning
+//               of the next one.  This will evict any objects that
+//               are pending eviction, and also update any internal
+//               bookkeeping.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+begin_epoch() {
+  MutexHolder holder(_lock);
+  do_partial_lru_update(_max_updates_per_frame);
+  if (_total_size > _max_size) {
+    do_evict_to(_max_size, false);
+  }
+
+  ++_current_frame_identifier;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+output(ostream &out) const {
+  MutexHolder holder(_lock);
+  out << "AdaptiveLru " << get_name()
+      << ", " << _total_size << " of " << _max_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << ":\n";
+
+  // We write out the list backwards.  Things we write out first are
+  // the freshest in the LRU.  Things at the end of the list will be
+  // the next to be evicted.
+
+  MutexHolder holder(_lock);
+
+  int minimum_frame_identifier = _current_frame_identifier - 1;
+    
+  int index;
+  for (index = 0; index < LPP_TotalPriorities; ++index) {
+    AdaptiveLruPageDynamicList *node = (AdaptiveLruPageDynamicList *)_page_array[index]._prev;
+    if (node != &_page_array[index]) {
+      indent(out, indent_level + 2) << "Priority " << index << ":\n";
+      while (node != &_page_array[index]) {
+        AdaptiveLruPage *page = (AdaptiveLruPage *)node;
+        indent(out, indent_level + 4) << *page;
+
+        if (page->_current_frame_identifier >= minimum_frame_identifier) {
+          out << " (active)";
+        }
+        out << "\n";
+
+        node = (AdaptiveLruPageDynamicList *)node->_prev;
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::do_add_page
+//       Access: Private
+//  Description: Adds a new page the the LRU.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+do_add_page(AdaptiveLruPage *page) {
+  nassertv(page != (AdaptiveLruPage *)NULL && page->_lru == this);
+  MutexHolder holder(_lock);
+
+  _total_size += page->_lru_size;
+  ((AdaptiveLruPageDynamicList *)page)->insert_before(&_page_array[page->_priority]);
+  ((AdaptiveLruPageStaticList *)page)->insert_before(&_static_list);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::do_remove_page
+//       Access: Private
+//  Description: Removes a page from the LRU.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+do_remove_page(AdaptiveLruPage *page) {
+  nassertv(page != (AdaptiveLruPage *)NULL && page->_lru == this);
+  MutexHolder holder(_lock);
+
+  _total_size -= page->_lru_size;
+  ((AdaptiveLruPageDynamicList *)page)->remove_from_list();
+  ((AdaptiveLruPageStaticList *)page)->remove_from_list();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::do_access_page
+//       Access: Private
+//  Description: Marks a page accessed.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+do_access_page(AdaptiveLruPage *page) {
+  nassertv(page != (AdaptiveLruPage *)NULL && page->_lru == this);
+  MutexHolder holder(_lock);
+
+  if (page->_current_frame_identifier == _current_frame_identifier) {
+    // This is the second or more time this page is accessed this
+    // frame.
+    ++(page->_current_frame_usage);
+
+  } else {
+    // This page has not yet been accessed this frame.  Update it.
+    page->_last_frame_identifier = page->_current_frame_identifier;
+    page->_current_frame_identifier = _current_frame_identifier;
+    page->_last_frame_usage = page->_current_frame_usage;
+    page->_current_frame_usage = 1;
+  }
+
+  // Move it to the tail of its priority list.
+  ((AdaptiveLruPageDynamicList *)page)->remove_from_list();
+  ((AdaptiveLruPageDynamicList *)page)->insert_before(&_page_array[page->_priority]);
+
+  ++(page->_update_total_usage);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::do_evict_to
+//       Access: Private
+//  Description: Evicts pages until the LRU is within the indicated
+//               size.  Assumes the lock is already held.  If
+//               hard_evict is false, does not evict "active" pages
+//               that were added within this epoch.
+////////////////////////////////////////////////////////////////////
+void AdaptiveLru::
+do_evict_to(size_t target_size, bool hard_evict) {
+  int attempts;
+
+  attempts = 0;
+  do {
+    int minimum_frame_identifier = _current_frame_identifier - 1;
+    
+    // page out lower priority pages first
+    int index;
+    for (index = LPP_TotalPriorities - 1; index >= 0; index--) {
+
+      // Store the current end of the list.  If pages re-enqueue
+      // themselves during this traversal, we don't want to visit them
+      // twice.
+      AdaptiveLruPageDynamicList *end = (AdaptiveLruPageDynamicList *)_page_array[index]._prev;
+
+      AdaptiveLruPageDynamicList *node = (AdaptiveLruPageDynamicList *)_page_array[index]._next;
+
+      while (node != &_page_array[index]) {
+        AdaptiveLruPageDynamicList *next = (AdaptiveLruPageDynamicList *)node->_next;
+        AdaptiveLruPage *page = (AdaptiveLruPage *)node;
+
+        if (attempts == 0 && (page->_current_frame_identifier >= minimum_frame_identifier)) {
+          // avoid swapping out pages used in the current and last
+          // frame on the first attempt
+
+        } else {
+          // We must release the lock while we call evict_lru().
+          cerr << "evicting page, " << page->_current_frame_identifier
+               << " vs. " << minimum_frame_identifier << ", attempts = "
+               << attempts << "\n";
+          _lock.release();
+          ((AdaptiveLruPage *)node)->evict_lru();
+          _lock.lock();
+
+          if (_total_size <= target_size) {
+            // We've evicted enough to satisfy our target.
+            return;
+          }
+        }
+        if (node == end) {
+          // We've reached the former end of the list.  Stop here;
+          // everything after has been re-queued.
+          break;
+        }
+        node = next;
+      }
+    }
+    attempts++;
+  } while (hard_evict && attempts < 2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLru::do_validate
+//       Access: Private
+//  Description: Checks that the LRU is internally consistent.  Assume
+//               the lock is already held.
+////////////////////////////////////////////////////////////////////
+bool AdaptiveLru::
+do_validate() {
+  bool okflag = true;
+  pset<AdaptiveLruPage *> pages;
+
+  // First, walk through the dynamic pages.
+  size_t counted_size = 0;
+  int index;
+  for (index = 0; index < LPP_TotalPriorities; ++index) {
+    AdaptiveLruPageDynamicList *node = (AdaptiveLruPageDynamicList *)_page_array[index]._next;
+    while (node != &_page_array[index]) {
+      AdaptiveLruPage *page = (AdaptiveLruPage *)node;
+      counted_size += page->_lru_size;
+      if (page->_priority != index) {
+        nout << "page " << page << " has priority " << page->_priority
+             << " but is in queue " << index << "\n";
+        okflag = false;
+      }
+
+      bool inserted_ok = pages.insert(page).second;
+      if (!inserted_ok) {
+        nout << "page " << page << " appears more than once in the dynamic index\n";
+        okflag = false;
+      }
+      node = (AdaptiveLruPageDynamicList *)node->_next;
+    }
+  }
+
+  if (counted_size != _total_size) {
+    nout << "count " << counted_size << " bytes in dynamic index, but have " << _total_size << " on record\n";
+    okflag = false;
+  }
+
+  // Now, walk through the static pages.
+  counted_size = 0;
+  AdaptiveLruPageStaticList *node = (AdaptiveLruPageStaticList *)_static_list._next;
+  while (node != &_static_list) {
+    AdaptiveLruPage *page = (AdaptiveLruPage *)node;
+    counted_size += page->_lru_size;
+    
+    if (pages.find(page) == pages.end()) {
+      nout << "page " << page << " appears in dynamic index, but not in static index (or multiple times in static index)\n";
+      okflag = false;
+    } else {
+      pages.erase(page);
+    }
+    node = (AdaptiveLruPageStaticList *)node->_next;
+  }
+
+  if (counted_size != _total_size) {
+    nout << "count " << counted_size << " bytes in static index, but have " << _total_size << " on record\n";
+    okflag = false;
+  }
+
+  return okflag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+AdaptiveLruPage::
+AdaptiveLruPage(size_t lru_size) :
+  _lru(NULL),
+  _lru_size(lru_size),
+  _priority(0),
+  _first_frame_identifier(0),
+  _last_frame_identifier(0),
+  _current_frame_identifier(0),
+  _update_frame_identifier(0),
+  _current_frame_usage(0),
+  _last_frame_usage(0),
+  _total_usage(0),
+  _update_total_usage(0),
+  _average_frame_utilization(1.0f)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::Copy Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+AdaptiveLruPage::
+AdaptiveLruPage(const AdaptiveLruPage &copy) :
+  _lru(NULL),
+  _lru_size(copy._lru_size),
+  _priority(0),
+  _first_frame_identifier(0),
+  _last_frame_identifier(0),
+  _current_frame_identifier(0),
+  _update_frame_identifier(0),
+  _current_frame_usage(0),
+  _last_frame_usage(0),
+  _total_usage(0),
+  _update_total_usage(0),
+  _average_frame_utilization(1.0f)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::Copy Assignment Operator
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void AdaptiveLruPage::
+operator = (const AdaptiveLruPage &copy) {
+  set_lru_size(copy.get_lru_size());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+AdaptiveLruPage::
+~AdaptiveLruPage() {
+  if (_lru != NULL) {
+    dequeue_lru();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::evict_lru
+//       Access: Published, Virtual
+//  Description: Evicts the page from the LRU.  Called internally when
+//               the LRU determines that it is full.  May also be
+//               called externally when necessary to explicitly evict
+//               the page.
+//
+//               It is legal for this method to either evict the page
+//               as requested, do nothing (in which case the eviction
+//               will be requested again at the next epoch), or
+//               requeue itself on the tail of the queue (in which
+//               case the eviction will be requested again much
+//               later).
+////////////////////////////////////////////////////////////////////
+void AdaptiveLruPage::
+evict_lru() {
+  dequeue_lru();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void AdaptiveLruPage::
+output(ostream &out) const {
+  out << "page " << this << ", " << _lru_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AdaptiveLruPage::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void AdaptiveLruPage::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << "\n";
+}
+
+#if 0
+
+////////////////////////////////////////////////////////////////////
+//     Function: test_adaptive_lru
+//       Access:
+//  Description: Unit test function for Lru.
+////////////////////////////////////////////////////////////////////
+void
+test_adaptive_lru() {
+  int maximum_memory = 3000000;
+  AdaptiveLru *lru = new AdaptiveLru("test", maximum_memory);
+
+  AdaptiveLruPage *lru_page_0;
+  AdaptiveLruPage *lru_page_1;
+  AdaptiveLruPage *lru_page_2;
+  AdaptiveLruPage *lru_page_3;
+  AdaptiveLruPage *lru_page_4;
+  AdaptiveLruPage *lru_page_5;
+  
+  lru_page_0 = new AdaptiveLruPage(1000000);
+  cerr << "created lru_page_0: " << lru_page_0 << "\n";
+  lru_page_0->enqueue_lru(lru);
+  
+  lru_page_1 = new AdaptiveLruPage(1000000);
+  cerr << "created lru_page_1: " << lru_page_1 << "\n";
+  lru_page_1->enqueue_lru(lru);
+  
+  lru_page_2 = new AdaptiveLruPage(1000000);
+  cerr << "created lru_page_2: " << lru_page_2 << "\n";
+  lru_page_2->enqueue_lru(lru);
+  
+  lru_page_3 = new AdaptiveLruPage(1000000);
+  cerr << "created lru_page_3: " << lru_page_3 << "\n";
+  lru_page_3->enqueue_lru(lru);
+  
+  lru_page_4 = new AdaptiveLruPage(1000000);
+  cerr << "created lru_page_4: " << lru_page_4 << "\n";
+  lru_page_4->enqueue_lru(lru);
+  
+  lru_page_5 = new AdaptiveLruPage(1000000);
+  cerr << "created lru_page_5: " << lru_page_5 << "\n";
+  lru_page_5->enqueue_lru(lru);
+
+  int total_frames = 300;
+  int index;
+  for (index = 0;  index < total_frames;  index++) {
+    cerr << "FRAME " << index << "\n";
+
+    lru->begin_epoch();
+
+    if (index <= 5) {
+      lru_page_0->mark_used_lru(lru);
+    }
+
+    lru_page_1->mark_used_lru(lru);
+    lru_page_1->mark_used_lru(lru);
+    
+    if (index & 0x01) {
+      lru_page_2->mark_used_lru(lru);
+    }
+
+    if ((index % 10) == 0) {
+      lru_page_3->mark_used_lru(lru);
+    }
+
+    if (index >= 100) {
+      lru_page_4->mark_used_lru(lru);
+    }
+
+    if (index >= 200) {
+      lru_page_5->mark_used_lru(lru);
+    }
+
+    if (!lru->validate()) {
+      cerr << "Failed validation\n";
+      break;
+    }
+  }
+  
+  delete lru;
+  delete lru_page_0;
+  delete lru_page_1;
+  delete lru_page_2;
+  delete lru_page_3;
+  delete lru_page_4;
+  delete lru_page_5;
+}
+
+#endif  // test_adaptive_lru

+ 207 - 0
panda/src/gobj/adaptiveLru.h

@@ -0,0 +1,207 @@
+// Filename: adaptiveLru.h
+// Created by:  drose (03Sep08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef ADAPTIVELRU_H
+#define ADAPTIVELRU_H
+
+#include "pandabase.h"
+#include "linkedListNode.h"
+#include "namable.h"
+#include "pmutex.h"
+#include "mutexHolder.h"
+
+class AdaptiveLruPage;
+
+// See the comment in the head of AdaptiveLruPage, below, for an
+// explanation of these two silly little classes.
+class EXPCL_PANDA_GOBJ AdaptiveLruPageDynamicList : public LinkedListNode {
+public:
+  friend class AdaptiveLru;
+};
+
+class EXPCL_PANDA_GOBJ AdaptiveLruPageStaticList : public LinkedListNode {
+public:
+  friend class AdaptiveLru;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : AdaptiveLru
+// Description : A basic LRU-type algorithm, except that it is
+//               adaptive and attempts to avoid evicting pages that
+//               have been used more frequently (even if less
+//               recently) than other pages.
+//
+//               The interface is designed to be identical to that for
+//               SimpleLru, so that it may be used as a drop-in
+//               replacement.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ AdaptiveLru : public Namable {
+PUBLISHED:
+  AdaptiveLru(const string &name, size_t max_size);
+  ~AdaptiveLru();
+
+  INLINE size_t get_total_size() const;
+  INLINE size_t get_max_size() const;
+  INLINE void set_max_size(size_t max_size);
+  size_t count_active_size() const;
+
+  INLINE void consider_evict();
+  INLINE void evict_to(size_t target_size);
+  void begin_epoch();
+
+  INLINE bool validate();
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level) const;
+
+  // The following methods are specific to AdaptiveLru, and do not
+  // exist in the SimpleLru implementation.  In most cases, the
+  // defaults will be sufficient, so you do not need to mess with
+  // them.
+  INLINE void set_weight(float weight);
+  INLINE float get_weight() const;
+
+  INLINE void set_max_updates_per_frame(int max_updates_per_frame);
+  INLINE int get_max_updates_per_frame() const;
+
+private:
+  enum LruPagePriority {
+    LPP_Highest = 0,
+    LPP_High = 10,
+    LPP_New = 20,
+    LPP_Normal = 25,
+    LPP_Intermediate = 30,
+    LPP_Low = 40,
+    LPP_TotalPriorities = 50,
+  };
+
+  INLINE float calculate_exponential_moving_average(float value, float average) const;
+
+  void do_partial_lru_update(int num_updates);
+  void update_page(AdaptiveLruPage *page);
+
+  void do_add_page(AdaptiveLruPage *page);
+  void do_remove_page(AdaptiveLruPage *page);
+  void do_access_page(AdaptiveLruPage *page);
+
+  void do_evict_to(size_t target_size, bool hard_evict);
+  bool do_validate();
+
+  Mutex _lock;
+
+  size_t _total_size;
+  size_t _max_size;
+
+  int _current_frame_identifier;
+  double _weight;
+  int _max_updates_per_frame;
+
+  // This array of linked lists keeps all of the active pages, grouped
+  // by priority.  We reshuffle pages among these lists as they are
+  // accessed and as they change priority in update_page().
+  AdaptiveLruPageDynamicList _page_array[LPP_TotalPriorities];
+
+  // This linked list keeps all of the active pages, in arbitrary
+  // order.  This list exists solely to allow us to incrementally
+  // update pages without having to iterate through the complex lists
+  // above and worry about losing our place.  New pages are added to
+  // the tail.  We also move pages from the head to the tail of this
+  // list in do_partial_lru_update() as we process each page with
+  // update_page().  Pages do not move within this list other that
+  // that.
+  AdaptiveLruPageStaticList _static_list;
+
+  friend class AdaptiveLruPage;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : AdaptiveLruPage
+// Description : One atomic piece that may be managed by a AdaptiveLru
+//               chain.  To use this class, inherit from it and
+//               override evict_lru().
+//
+//               This class multiply inherits from two classes which
+//               in turn both inherit from LinkedListNode.  This is
+//               just a sneaky C++ trick to allow this class to
+//               inherit from LinkedListNode twice, so that pages can
+//               be stored on two different linked lists
+//               simultaneously.  The AdaptiveLru class depends on
+//               this; it maintains its pages in two different lists,
+//               one grouped by priority, and one in order by next
+//               partial update needs.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ AdaptiveLruPage : public AdaptiveLruPageDynamicList, public AdaptiveLruPageStaticList {
+PUBLISHED:
+  AdaptiveLruPage(size_t lru_size);
+  AdaptiveLruPage(const AdaptiveLruPage &copy);
+  void operator = (const AdaptiveLruPage &copy);
+
+  virtual ~AdaptiveLruPage();
+
+  INLINE AdaptiveLru *get_lru() const;
+
+  void enqueue_lru(AdaptiveLru *lru);
+  INLINE void dequeue_lru();
+
+  INLINE void mark_used_lru() const;
+  INLINE void mark_used_lru(AdaptiveLru *lru);
+
+  INLINE size_t get_lru_size() const;
+  INLINE void set_lru_size(size_t lru_size);
+
+  virtual void evict_lru();
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+private:
+  AdaptiveLru *_lru;
+
+  size_t _lru_size;
+  int _priority;
+
+  int _first_frame_identifier;  // creation time
+  int _last_frame_identifier;   // last time page used
+  int _current_frame_identifier;
+  int _update_frame_identifier;
+
+  int _current_frame_usage;
+  int _last_frame_usage;
+  int _total_usage;
+  int _update_total_usage;
+
+  float _average_frame_utilization;
+
+  friend class AdaptiveLru;
+};
+
+inline ostream &operator << (ostream &out, const AdaptiveLru &lru) {
+  lru.output(out);
+  return out;
+}
+
+inline ostream &operator << (ostream &out, const AdaptiveLruPage &page) {
+  page.output(out);
+  return out;
+}
+
+#if 0
+BEGIN_PUBLISH
+void test_adaptive_lru();
+END_PUBLISH
+#endif
+
+#include "adaptiveLru.I"
+
+#endif

+ 24 - 0
panda/src/gobj/bufferContext.I

@@ -38,6 +38,30 @@ get_modified() const {
   return _modified;
   return _modified;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BufferContext::get_active
+//       Access: Public
+//  Description: Returns the active flag associated with this object.
+//               An object is considered "active" if it was rendered
+//               in the current frame.
+////////////////////////////////////////////////////////////////////
+INLINE bool BufferContext::
+get_active() const {
+  return (_residency_state & BufferResidencyTracker::S_active) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BufferContext::get_resident
+//       Access: Public
+//  Description: Returns the resident flag associated with this
+//               object.  An object is considered "resident" if it
+//               appears to be resident in texture memory.
+////////////////////////////////////////////////////////////////////
+INLINE bool BufferContext::
+get_resident() const {
+  return (_residency_state & BufferResidencyTracker::S_resident) != 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BufferContext::set_active
 //     Function: BufferContext::set_active
 //       Access: Public
 //       Access: Public

+ 2 - 0
panda/src/gobj/bufferContext.h

@@ -46,6 +46,8 @@ public:
 PUBLISHED:
 PUBLISHED:
   INLINE size_t get_data_size_bytes() const;
   INLINE size_t get_data_size_bytes() const;
   INLINE UpdateSeq get_modified() const;
   INLINE UpdateSeq get_modified() const;
+  INLINE bool get_active() const;
+  INLINE bool get_resident() const;
 
 
 public:
 public:
   INLINE void set_active(bool flag);
   INLINE void set_active(bool flag);

+ 18 - 0
panda/src/gobj/bufferContextChain.cxx

@@ -14,6 +14,7 @@
 
 
 #include "bufferContextChain.h"
 #include "bufferContextChain.h"
 #include "bufferContext.h"
 #include "bufferContext.h"
+#include "indent.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BufferContextChain::get_first
 //     Function: BufferContextChain::get_first
@@ -57,3 +58,20 @@ take_from(BufferContextChain &other) {
 
 
   take_list_from(&other);
   take_list_from(&other);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: BufferContextChain::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BufferContextChain::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << _count << " objects, consuming " << _total_size << " bytes:\n";
+
+  LinkedListNode *llnode = _next;
+  while (llnode != this) {
+    ((BufferContext *)llnode)->write(out, indent_level + 2);
+    llnode = ((BufferContext *)llnode)->_next;
+  }
+}

+ 2 - 0
panda/src/gobj/bufferContextChain.h

@@ -43,6 +43,8 @@ public:
 
 
   void take_from(BufferContextChain &other);
   void take_from(BufferContextChain &other);
 
 
+  void write(ostream &out, int indent_level) const;
+
 private:
 private:
   INLINE void adjust_bytes(int delta);
   INLINE void adjust_bytes(int delta);
   size_t _total_size;
   size_t _total_size;

+ 29 - 0
panda/src/gobj/bufferResidencyTracker.cxx

@@ -15,6 +15,7 @@
 #include "bufferResidencyTracker.h"
 #include "bufferResidencyTracker.h"
 #include "bufferContext.h"
 #include "bufferContext.h"
 #include "clockObject.h"
 #include "clockObject.h"
+#include "indent.h"
 
 
 PStatCollector BufferResidencyTracker::_gmem_collector("Graphics memory");
 PStatCollector BufferResidencyTracker::_gmem_collector("Graphics memory");
 
 
@@ -71,6 +72,34 @@ end_frame(Thread *current_thread) {
   _active_resident_collector.set_level(_chains[S_active_resident].get_total_size());
   _active_resident_collector.set_level(_chains[S_active_resident].get_total_size());
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BufferResidencyTracker::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BufferResidencyTracker::
+write(ostream &out, int indent_level) const {
+  if (_chains[S_inactive_nonresident].get_count() != 0) {
+    indent(out, indent_level) << "Inactive nonresident:\n";
+    _chains[S_inactive_nonresident].write(out, indent_level + 2);
+  }
+
+  if (_chains[S_active_nonresident].get_count() != 0) {
+    indent(out, indent_level) << "Active nonresident:\n";
+    _chains[S_active_nonresident].write(out, indent_level + 2);
+  }
+
+  if (_chains[S_inactive_resident].get_count() != 0) {
+    indent(out, indent_level) << "Inactive resident:\n";
+    _chains[S_inactive_resident].write(out, indent_level + 2);
+  }
+
+  if (_chains[S_active_resident].get_count() != 0) {
+    indent(out, indent_level) << "Active resident:\n";
+    _chains[S_active_resident].write(out, indent_level + 2);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BufferResidencyTracker::move_inactive
 //     Function: BufferResidencyTracker::move_inactive
 //       Access: Private
 //       Access: Private

+ 2 - 0
panda/src/gobj/bufferResidencyTracker.h

@@ -45,6 +45,8 @@ public:
   INLINE BufferContextChain &get_inactive_resident();
   INLINE BufferContextChain &get_inactive_resident();
   INLINE BufferContextChain &get_active_resident();
   INLINE BufferContextChain &get_active_resident();
 
 
+  void write(ostream &out, int indent_level) const;
+
 private:
 private:
   void move_inactive(BufferContextChain &inactive, BufferContextChain &active);
   void move_inactive(BufferContextChain &inactive, BufferContextChain &active);
 
 

+ 12 - 0
panda/src/gobj/config_gobj.cxx

@@ -387,6 +387,18 @@ ConfigVariableInt graphics_memory_limit
           "Set this to -1 to have no limit other than the normal "
           "Set this to -1 to have no limit other than the normal "
           "hardware-imposed limit."));
           "hardware-imposed limit."));
 
 
+ConfigVariableDouble adaptive_lru_weight
+("adaptive-lru-weight", 0.2,
+ PRC_DESC("Specifies the weight factor used to compute the AdaptiveLru's "
+          "exponential moving average."));
+
+ConfigVariableInt adaptive_lru_max_updates_per_frame
+("adaptive-lru-max-updates-per-frame", 40,
+ PRC_DESC("The number of pages the AdaptiveLru class will update per "
+          "frame.  Do not set this too high or it will degrade "
+          "performance."));
+ 
+
 ConfigureFn(config_gobj) {
 ConfigureFn(config_gobj) {
   BufferContext::init_type();
   BufferContext::init_type();
   Geom::init_type();
   Geom::init_type();

+ 2 - 0
panda/src/gobj/config_gobj.h

@@ -93,6 +93,8 @@ extern EXPCL_PANDA_GOBJ ConfigVariableString vertex_save_file_prefix;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt vertex_data_small_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt vertex_data_small_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt vertex_data_page_threads;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt vertex_data_page_threads;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt graphics_memory_limit;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt graphics_memory_limit;
+extern EXPCL_PANDA_GOBJ ConfigVariableDouble adaptive_lru_weight;
+extern EXPCL_PANDA_GOBJ ConfigVariableInt adaptive_lru_max_updates_per_frame;
 
 
 #endif
 #endif
 
 

+ 1 - 0
panda/src/gobj/gobj_composite1.cxx

@@ -1,3 +1,4 @@
+#include "adaptiveLru.cxx"
 #include "bufferContext.cxx"
 #include "bufferContext.cxx"
 #include "bufferContextChain.cxx"
 #include "bufferContextChain.cxx"
 #include "bufferResidencyTracker.cxx"
 #include "bufferResidencyTracker.cxx"

+ 2 - 2
panda/src/gobj/indexBufferContext.I

@@ -21,7 +21,7 @@
 INLINE IndexBufferContext::
 INLINE IndexBufferContext::
 IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data) :
 IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data) :
   BufferContext(&pgo->_ibuffer_residency),
   BufferContext(&pgo->_ibuffer_residency),
-  SimpleLruPage(0),
+  AdaptiveLruPage(0),
   _data(data)
   _data(data)
 {
 {
 }
 }
@@ -82,7 +82,7 @@ was_modified(const GeomPrimitivePipelineReader *reader) const {
 INLINE void IndexBufferContext::
 INLINE void IndexBufferContext::
 update_data_size_bytes(size_t new_data_size_bytes) {
 update_data_size_bytes(size_t new_data_size_bytes) {
   BufferContext::update_data_size_bytes(new_data_size_bytes);
   BufferContext::update_data_size_bytes(new_data_size_bytes);
-  SimpleLruPage::set_lru_size(new_data_size_bytes);
+  AdaptiveLruPage::set_lru_size(new_data_size_bytes);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 20 - 0
panda/src/gobj/indexBufferContext.cxx

@@ -15,3 +15,23 @@
 #include "indexBufferContext.h"
 #include "indexBufferContext.h"
 
 
 TypeHandle IndexBufferContext::_type_handle;
 TypeHandle IndexBufferContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: IndexBufferContext::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void IndexBufferContext::
+output(ostream &out) const {
+  out << *get_data() << ", " << get_data_size_bytes();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IndexBufferContext::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void IndexBufferContext::
+write(ostream &out, int indent_level) const {
+  SavedContext::write(out, indent_level);
+}

+ 5 - 2
panda/src/gobj/indexBufferContext.h

@@ -20,7 +20,7 @@
 #include "bufferContext.h"
 #include "bufferContext.h"
 #include "geomPrimitive.h"
 #include "geomPrimitive.h"
 #include "preparedGraphicsObjects.h"
 #include "preparedGraphicsObjects.h"
-#include "simpleLru.h"
+#include "adaptiveLru.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : IndexBufferContext
 //       Class : IndexBufferContext
@@ -33,7 +33,7 @@
 //               allocate a vertex buffer for the array.  OpenGL can
 //               allocate a vertex buffer for the array.  OpenGL can
 //               create a buffer object.
 //               create a buffer object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_GOBJ IndexBufferContext : public BufferContext, public SimpleLruPage {
+class EXPCL_PANDA_GOBJ IndexBufferContext : public BufferContext, public AdaptiveLruPage {
 public:
 public:
   INLINE IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data);
   INLINE IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data);
 
 
@@ -49,6 +49,9 @@ public:
   INLINE void mark_loaded(const GeomPrimitivePipelineReader *reader);
   INLINE void mark_loaded(const GeomPrimitivePipelineReader *reader);
   INLINE void mark_unloaded();
   INLINE void mark_unloaded();
 
 
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
 private:
 private:
   // This cannot be a PT(GeomPrimitive), because the data and
   // This cannot be a PT(GeomPrimitive), because the data and
   // the GSG both own their IndexBufferContexts!  That would create a
   // the GSG both own their IndexBufferContexts!  That would create a

+ 30 - 0
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -100,6 +100,36 @@ PreparedGraphicsObjects::
   _released_index_buffers.clear();
   _released_index_buffers.clear();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::show_graphics_memory_lru
+//       Access: Public
+//  Description: Writes to the indicated ostream a report of how the
+//               various textures and vertex buffers are allocated in
+//               the LRU.
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+show_graphics_memory_lru(ostream &out) const {
+  _graphics_memory_lru.write(out, 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::show_residency_trackers
+//       Access: Public
+//  Description: Writes to the indicated ostream a report of how the
+//               various textures and vertex buffers are allocated in
+//               the LRU.
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+show_residency_trackers(ostream &out) const {
+  out << "Textures:\n";
+  _texture_residency.write(out, 2);
+
+  out << "\nVertex buffers:\n";
+  _vbuffer_residency.write(out, 2);
+
+  out << "\nIndex buffers:\n";
+  _ibuffer_residency.write(out, 2);
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::enqueue_texture
 //     Function: PreparedGraphicsObjects::enqueue_texture

+ 4 - 1
panda/src/gobj/preparedGraphicsObjects.h

@@ -27,6 +27,7 @@
 #include "pset.h"
 #include "pset.h"
 #include "reMutex.h"
 #include "reMutex.h"
 #include "bufferResidencyTracker.h"
 #include "bufferResidencyTracker.h"
+#include "adaptiveLru.h"
 
 
 class TextureContext;
 class TextureContext;
 class GeomContext;
 class GeomContext;
@@ -64,6 +65,8 @@ PUBLISHED:
 
 
   INLINE void set_graphics_memory_limit(size_t limit);
   INLINE void set_graphics_memory_limit(size_t limit);
   INLINE size_t get_graphics_memory_limit() const;
   INLINE size_t get_graphics_memory_limit() const;
+  void show_graphics_memory_lru(ostream &out) const;
+  void show_residency_trackers(ostream &out) const;
 
 
   INLINE void release_all();
   INLINE void release_all();
   INLINE int get_num_queued() const;
   INLINE int get_num_queued() const;
@@ -199,7 +202,7 @@ public:
   BufferResidencyTracker _vbuffer_residency;
   BufferResidencyTracker _vbuffer_residency;
   BufferResidencyTracker _ibuffer_residency;
   BufferResidencyTracker _ibuffer_residency;
 
 
-  SimpleLru _graphics_memory_lru;
+  AdaptiveLru _graphics_memory_lru;
 
 
 public:
 public:
   // This is only public as a temporary hack.  Don't mess with it
   // This is only public as a temporary hack.  Don't mess with it

+ 21 - 0
panda/src/gobj/savedContext.cxx

@@ -13,5 +13,26 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "savedContext.h"
 #include "savedContext.h"
+#include "indent.h"
 
 
 TypeHandle SavedContext::_type_handle;
 TypeHandle SavedContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: SavedContext::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SavedContext::
+output(ostream &out) const {
+  out << "SavedContext " << this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SavedContext::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SavedContext::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << "\n";
+}

+ 9 - 3
panda/src/gobj/savedContext.h

@@ -24,14 +24,15 @@
 // Description : This is the base class for all GSG-specific context
 // Description : This is the base class for all GSG-specific context
 //               objects, such as TextureContext and GeomContext.  It
 //               objects, such as TextureContext and GeomContext.  It
 //               exists mainly to provide some structural
 //               exists mainly to provide some structural
-//               organization.  At the moment, there are no methods
-//               common to all of these objects, but there might be
-//               one day.
+//               organization.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_GOBJ SavedContext : public TypedObject {
 class EXPCL_PANDA_GOBJ SavedContext : public TypedObject {
 public:
 public:
   INLINE SavedContext();
   INLINE SavedContext();
 
 
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
 PUBLISHED:
 PUBLISHED:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;
@@ -51,6 +52,11 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 };
 };
 
 
+inline ostream &operator << (ostream &out, const SavedContext &context) {
+  context.output(out);
+  return out;
+}
+
 #include "savedContext.I"
 #include "savedContext.I"
 
 
 #endif
 #endif

+ 14 - 1
panda/src/gobj/simpleLru.I

@@ -96,6 +96,19 @@ begin_epoch() {
   _active_marker->enqueue_lru(this);
   _active_marker->enqueue_lru(this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleLru::validate
+//       Access: Published
+//  Description: Checks that the LRU is internally self-consistent.
+//               Returns true if successful, false if there is some
+//               problem.
+////////////////////////////////////////////////////////////////////
+INLINE bool SimpleLru::
+validate() {
+  MutexHolder holder(_global_lock);
+  return do_validate();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SimpleLruPage::Constructor
 //     Function: SimpleLruPage::Constructor
 //       Access: Protected
 //       Access: Protected
@@ -131,7 +144,7 @@ operator = (const SimpleLruPage &copy) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SimpleLruPage::enqueue_lru
+//     Function: SimpleLruPage::get_lru
 //       Access: Published
 //       Access: Published
 //  Description: Returns the LRU that manages this page, or NULL if it
 //  Description: Returns the LRU that manages this page, or NULL if it
 //               is not currently managed by any LRU.
 //               is not currently managed by any LRU.

+ 72 - 7
panda/src/gobj/simpleLru.cxx

@@ -13,7 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "simpleLru.h"
 #include "simpleLru.h"
-#include "clockObject.h"
+#include "indent.h"
 
 
 // We define this as a reference to an allocated object, instead of as
 // We define this as a reference to an allocated object, instead of as
 // a concrete object, so that it won't get destructed when the program
 // a concrete object, so that it won't get destructed when the program
@@ -60,8 +60,11 @@ SimpleLru::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SimpleLruPage::enqueue_lru
 //     Function: SimpleLruPage::enqueue_lru
 //       Access: Published
 //       Access: Published
-//  Description: Adds the page to the tail of the SimpleLru.  When it
-//               reaches the head, it will be the next to be evicted.
+//  Description: Adds the page to the LRU for the first time, or marks
+//               it recently-accessed if it has already been added.
+//
+//               If lru is NULL, it means to remove this page from its
+//               LRU.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void SimpleLruPage::
 void SimpleLruPage::
 enqueue_lru(SimpleLru *lru) {
 enqueue_lru(SimpleLru *lru) {
@@ -113,6 +116,48 @@ count_active_size() const {
   return total;
   return total;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleLru::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SimpleLru::
+output(ostream &out) const {
+  MutexHolder holder(_global_lock);
+  out << "SimpleLru " << get_name()
+      << ", " << _total_size << " of " << _max_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleLru::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SimpleLru::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << ":\n";
+
+  // We write out the list backwards.  Things we write out first are
+  // the freshest in the LRU.  Things at the end of the list will be
+  // the next to be evicted.
+
+  MutexHolder holder(_global_lock);
+  LinkedListNode *node = _prev;
+  while (node != _active_marker && node != this) {
+    SimpleLruPage *page = (SimpleLruPage *)node;
+    indent(out, indent_level + 2) << *page << " (active)\n";
+    node = page->_prev;
+  }
+  if (node == _active_marker) {
+    node = _active_marker->_prev;
+    while (node != this) {
+      SimpleLruPage *page = (SimpleLruPage *)node;
+      indent(out, indent_level + 2) << *page << "\n";
+      node = page->_prev;
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SimpleLru::do_evict_to
 //     Function: SimpleLru::do_evict_to
 //       Access: Private
 //       Access: Private
@@ -157,13 +202,13 @@ do_evict_to(size_t target_size, bool hard_evict) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SimpleLru::do_validate_size
+//     Function: SimpleLru::do_validate
 //       Access: Private
 //       Access: Private
-//  Description: Checks that _total_size is consistent.  Assume the
-//               lock is already held.
+//  Description: Checks that the LRU is internally consistent.  Assume
+//               the lock is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool SimpleLru::
 bool SimpleLru::
-do_validate_size() {
+do_validate() {
   size_t total = 0;
   size_t total = 0;
 
 
   LinkedListNode *node = _next;
   LinkedListNode *node = _next;
@@ -206,3 +251,23 @@ void SimpleLruPage::
 evict_lru() {
 evict_lru() {
   dequeue_lru();
   dequeue_lru();
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleLruPage::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SimpleLruPage::
+output(ostream &out) const {
+  out << "page " << this << ", " << _lru_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SimpleLruPage::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SimpleLruPage::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << "\n";
+}

+ 20 - 1
panda/src/gobj/simpleLru.h

@@ -26,6 +26,7 @@ class SimpleLruPage;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : SimpleLru
 //       Class : SimpleLru
 // Description : An implementation of a very simple LRU algorithm.
 // Description : An implementation of a very simple LRU algorithm.
+//               Also see AdaptiveLru.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_GOBJ SimpleLru : public LinkedListNode, public Namable {
 class EXPCL_PANDA_GOBJ SimpleLru : public LinkedListNode, public Namable {
 PUBLISHED:
 PUBLISHED:
@@ -41,12 +42,17 @@ PUBLISHED:
   INLINE void evict_to(size_t target_size);
   INLINE void evict_to(size_t target_size);
   INLINE void begin_epoch();
   INLINE void begin_epoch();
 
 
+  INLINE bool validate();
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level) const;
+
 public:
 public:
   static Mutex &_global_lock;
   static Mutex &_global_lock;
 
 
 private:
 private:
   void do_evict_to(size_t target_size, bool hard_evict);
   void do_evict_to(size_t target_size, bool hard_evict);
-  bool do_validate_size();
+  bool do_validate();
 
 
   size_t _total_size;
   size_t _total_size;
   size_t _max_size;
   size_t _max_size;
@@ -82,6 +88,9 @@ PUBLISHED:
 
 
   virtual void evict_lru();
   virtual void evict_lru();
 
 
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
 private:
 private:
   SimpleLru *_lru;
   SimpleLru *_lru;
 
 
@@ -90,6 +99,16 @@ private:
   friend class SimpleLru;
   friend class SimpleLru;
 };
 };
 
 
+inline ostream &operator << (ostream &out, const SimpleLru &lru) {
+  lru.output(out);
+  return out;
+}
+
+inline ostream &operator << (ostream &out, const SimpleLruPage &page) {
+  page.output(out);
+  return out;
+}
+
 #include "simpleLru.I"
 #include "simpleLru.I"
 
 
 #endif
 #endif

+ 2 - 2
panda/src/gobj/textureContext.I

@@ -21,7 +21,7 @@
 INLINE TextureContext::
 INLINE TextureContext::
 TextureContext(PreparedGraphicsObjects *pgo, Texture *tex) :
 TextureContext(PreparedGraphicsObjects *pgo, Texture *tex) :
   BufferContext(&pgo->_texture_residency),
   BufferContext(&pgo->_texture_residency),
-  SimpleLruPage(0),
+  AdaptiveLruPage(0),
   _texture(tex)
   _texture(tex)
 {
 {
 }
 }
@@ -94,7 +94,7 @@ was_simple_image_modified() const {
 INLINE void TextureContext::
 INLINE void TextureContext::
 update_data_size_bytes(size_t new_data_size_bytes) {
 update_data_size_bytes(size_t new_data_size_bytes) {
   BufferContext::update_data_size_bytes(new_data_size_bytes);
   BufferContext::update_data_size_bytes(new_data_size_bytes);
-  SimpleLruPage::set_lru_size(new_data_size_bytes);
+  AdaptiveLruPage::set_lru_size(new_data_size_bytes);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 20 - 0
panda/src/gobj/textureContext.cxx

@@ -15,3 +15,23 @@
 #include "textureContext.h"
 #include "textureContext.h"
 
 
 TypeHandle TextureContext::_type_handle;
 TypeHandle TextureContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureContext::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TextureContext::
+output(ostream &out) const {
+  out << *get_texture() << ", " << get_data_size_bytes();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureContext::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TextureContext::
+write(ostream &out, int indent_level) const {
+  SavedContext::write(out, indent_level);
+}

+ 5 - 2
panda/src/gobj/textureContext.h

@@ -20,7 +20,7 @@
 #include "bufferContext.h"
 #include "bufferContext.h"
 #include "texture.h"
 #include "texture.h"
 #include "preparedGraphicsObjects.h"
 #include "preparedGraphicsObjects.h"
-#include "simpleLru.h"
+#include "adaptiveLru.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : TextureContext
 //       Class : TextureContext
@@ -35,7 +35,7 @@
 //               texture and store it here.  The texture stores all of
 //               texture and store it here.  The texture stores all of
 //               these handles internally.
 //               these handles internally.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_GOBJ TextureContext : public BufferContext, public SimpleLruPage {
+class EXPCL_PANDA_GOBJ TextureContext : public BufferContext, public AdaptiveLruPage {
 public:
 public:
   INLINE TextureContext(PreparedGraphicsObjects *pgo, Texture *tex);
   INLINE TextureContext(PreparedGraphicsObjects *pgo, Texture *tex);
 
 
@@ -53,6 +53,9 @@ public:
   INLINE void mark_simple_loaded();
   INLINE void mark_simple_loaded();
   INLINE void mark_unloaded();
   INLINE void mark_unloaded();
 
 
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
 private:
 private:
   // This cannot be a PT(Texture), because the texture and the GSG
   // This cannot be a PT(Texture), because the texture and the GSG
   // both own their TextureContexts!  That would create a circular
   // both own their TextureContexts!  That would create a circular

+ 2 - 2
panda/src/gobj/vertexBufferContext.I

@@ -21,7 +21,7 @@
 INLINE VertexBufferContext::
 INLINE VertexBufferContext::
 VertexBufferContext(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data) :
 VertexBufferContext(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data) :
   BufferContext(&pgo->_vbuffer_residency),
   BufferContext(&pgo->_vbuffer_residency),
-  SimpleLruPage(0),
+  AdaptiveLruPage(0),
   _data(data)
   _data(data)
 {
 {
 }
 }
@@ -82,7 +82,7 @@ was_modified(const GeomVertexArrayDataHandle *reader) const {
 INLINE void VertexBufferContext::
 INLINE void VertexBufferContext::
 update_data_size_bytes(size_t new_data_size_bytes) {
 update_data_size_bytes(size_t new_data_size_bytes) {
   BufferContext::update_data_size_bytes(new_data_size_bytes);
   BufferContext::update_data_size_bytes(new_data_size_bytes);
-  SimpleLruPage::set_lru_size(new_data_size_bytes);
+  AdaptiveLruPage::set_lru_size(new_data_size_bytes);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 20 - 0
panda/src/gobj/vertexBufferContext.cxx

@@ -15,3 +15,23 @@
 #include "vertexBufferContext.h"
 #include "vertexBufferContext.h"
 
 
 TypeHandle VertexBufferContext::_type_handle;
 TypeHandle VertexBufferContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexBufferContext::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexBufferContext::
+output(ostream &out) const {
+  out << *get_data() << ", " << get_data_size_bytes();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexBufferContext::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexBufferContext::
+write(ostream &out, int indent_level) const {
+  SavedContext::write(out, indent_level);
+}

+ 5 - 2
panda/src/gobj/vertexBufferContext.h

@@ -20,7 +20,7 @@
 #include "bufferContext.h"
 #include "bufferContext.h"
 #include "geomVertexArrayData.h"
 #include "geomVertexArrayData.h"
 #include "preparedGraphicsObjects.h"
 #include "preparedGraphicsObjects.h"
-#include "simpleLru.h"
+#include "adaptiveLru.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : VertexBufferContext
 //       Class : VertexBufferContext
@@ -33,7 +33,7 @@
 //               allocate a vertex buffer for the array.  OpenGL can
 //               allocate a vertex buffer for the array.  OpenGL can
 //               create a buffer object.
 //               create a buffer object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_GOBJ VertexBufferContext : public BufferContext, public SimpleLruPage {
+class EXPCL_PANDA_GOBJ VertexBufferContext : public BufferContext, public AdaptiveLruPage {
 public:
 public:
   INLINE VertexBufferContext(PreparedGraphicsObjects *pgo,
   INLINE VertexBufferContext(PreparedGraphicsObjects *pgo,
                              GeomVertexArrayData *data);
                              GeomVertexArrayData *data);
@@ -50,6 +50,9 @@ public:
   INLINE void mark_loaded(const GeomVertexArrayDataHandle *reader);
   INLINE void mark_loaded(const GeomVertexArrayDataHandle *reader);
   INLINE void mark_unloaded();
   INLINE void mark_unloaded();
 
 
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
 private:
 private:
   // This cannot be a PT(GeomVertexArrayData), because the data and
   // This cannot be a PT(GeomVertexArrayData), because the data and
   // the GSG both own their VertexBufferContexts!  That would create a
   // the GSG both own their VertexBufferContexts!  That would create a

+ 20 - 0
panda/src/gobj/vertexDataPage.cxx

@@ -217,6 +217,26 @@ flush_threads() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexDataPage::
+output(ostream &out) const {
+  SimpleAllocator::output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataPage::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexDataPage::
+write(ostream &out, int indent_level) const {
+  SimpleAllocator::write(out);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataPage::make_block
 //     Function: VertexDataPage::make_block
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 8 - 0
panda/src/gobj/vertexDataPage.h

@@ -76,6 +76,9 @@ PUBLISHED:
   static void stop_threads();
   static void stop_threads();
   static void flush_threads();
   static void flush_threads();
 
 
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
 public:
 public:
   INLINE unsigned char *get_page_data(bool force);
   INLINE unsigned char *get_page_data(bool force);
   INLINE bool operator < (const VertexDataPage &other) const;
   INLINE bool operator < (const VertexDataPage &other) const;
@@ -229,6 +232,11 @@ private:
   friend class VertexDataBook;
   friend class VertexDataBook;
 };
 };
 
 
+inline ostream &operator << (ostream &out, const VertexDataPage &page) {
+  page.output(out);
+  return out;
+}
+
 #include "vertexDataPage.I"
 #include "vertexDataPage.I"
 
 
 #endif
 #endif