AdaptiveLru

This commit is contained in:
David Rose 2008-09-04 21:26:38 +00:00
parent 645b20849b
commit 684810cbdd
33 changed files with 1499 additions and 149 deletions

View File

@ -14,7 +14,7 @@
//#include "stdafx.h"
#define LRU_UNIT_TEST 0
#define LRU_UNIT_TEST 1
#include <stdio.h>
#include <stdlib.h>
@ -22,6 +22,7 @@
#include "lru.h"
#define OutputDebugString(string) cerr << string
static const int HIGH_PRIORITY_SCALE = 4;
static const int LOW_PRIORITY_RANGE = 25;
@ -145,7 +146,7 @@ Lru::~Lru ( )
LruPage::LruPage ( )
{
if(this) {
memset(&this->_m, 0, sizeof (LruPageVariables));
memset(&this->_m, 0, (char *)&this->_m.name - (char *)&this->_m);
_m.name = "";
}
}
@ -331,6 +332,7 @@ void Lru::free_page (LruPage *lru_page)
////////////////////////////////////////////////////////////////////
void Lru::add_page (LruPagePriority priority, LruPage *lru_page)
{
cerr << "adding " << lru_page << " with " << priority << "\n";
if(lru_page) {
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)
{
cerr << "adding " << lru_page << " with " << priority << "\n";
if(lru_page) {
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)
{
cerr << "removing " << lru_page << "\n";
if(this) {
if(this->_m.total_pages > 0) {
if(lru_page) {
@ -595,7 +599,7 @@ void Lru::update_lru_page (LruPage *lru_page)
{
#if LRU_UNIT_TEST
if(false) {
if(true) {
char string[256];
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;
cerr << "target_priority = " << target_priority << ", delta = " << delta_priority << ", util = " << lru_page->_m.average_frame_utilization << "\n";
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)
{
#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 weight, float average)
{
cerr << "exp(" << value << ", " << weight << ", " << average << " = "
<< ((value - average) * weight) + average << "\n";
return ((value - average) * weight) + average;
}

View File

@ -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_out_function (LruPage *lru_page);
BEGIN_PUBLISH
void test_ema (void);
void test_lru (void);
END_PUBLISH
#endif

View File

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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -38,6 +38,30 @@ get_modified() const {
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
// Access: Public

View File

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

View File

@ -14,6 +14,7 @@
#include "bufferContextChain.h"
#include "bufferContext.h"
#include "indent.h"
////////////////////////////////////////////////////////////////////
// Function: BufferContextChain::get_first
@ -57,3 +58,20 @@ take_from(BufferContextChain &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;
}
}

View File

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

View File

@ -15,6 +15,7 @@
#include "bufferResidencyTracker.h"
#include "bufferContext.h"
#include "clockObject.h"
#include "indent.h"
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());
}
////////////////////////////////////////////////////////////////////
// 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
// Access: Private

View File

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

View File

@ -387,6 +387,18 @@ ConfigVariableInt graphics_memory_limit
"Set this to -1 to have no limit other than the normal "
"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) {
BufferContext::init_type();
Geom::init_type();

View File

@ -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_page_threads;
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

View File

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

View File

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

View File

@ -15,3 +15,23 @@
#include "indexBufferContext.h"
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);
}

View File

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

View File

@ -100,6 +100,36 @@ PreparedGraphicsObjects::
_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

View File

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

View File

@ -13,5 +13,26 @@
////////////////////////////////////////////////////////////////////
#include "savedContext.h"
#include "indent.h"
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";
}

View File

@ -24,14 +24,15 @@
// Description : This is the base class for all GSG-specific context
// objects, such as TextureContext and GeomContext. It
// 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 {
public:
INLINE SavedContext();
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level) const;
PUBLISHED:
static TypeHandle get_class_type() {
return _type_handle;
@ -51,6 +52,11 @@ private:
static TypeHandle _type_handle;
};
inline ostream &operator << (ostream &out, const SavedContext &context) {
context.output(out);
return out;
}
#include "savedContext.I"
#endif

View File

@ -96,6 +96,19 @@ begin_epoch() {
_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
// Access: Protected
@ -131,7 +144,7 @@ operator = (const SimpleLruPage &copy) {
}
////////////////////////////////////////////////////////////////////
// Function: SimpleLruPage::enqueue_lru
// Function: SimpleLruPage::get_lru
// Access: Published
// Description: Returns the LRU that manages this page, or NULL if it
// is not currently managed by any LRU.

View File

@ -13,7 +13,7 @@
////////////////////////////////////////////////////////////////////
#include "simpleLru.h"
#include "clockObject.h"
#include "indent.h"
// 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
@ -60,8 +60,11 @@ SimpleLru::
////////////////////////////////////////////////////////////////////
// Function: SimpleLruPage::enqueue_lru
// 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::
enqueue_lru(SimpleLru *lru) {
@ -113,6 +116,48 @@ count_active_size() const {
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
// 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
// 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::
do_validate_size() {
do_validate() {
size_t total = 0;
LinkedListNode *node = _next;
@ -206,3 +251,23 @@ void SimpleLruPage::
evict_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";
}

View File

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

View File

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

View File

@ -15,3 +15,23 @@
#include "textureContext.h"
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);
}

View File

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

View File

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

View File

@ -15,3 +15,23 @@
#include "vertexBufferContext.h"
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);
}

View File

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

View File

@ -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
// Access: Protected, Virtual

View File

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