mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
AdaptiveLru
This commit is contained in:
parent
645b20849b
commit
684810cbdd
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
229
panda/src/gobj/adaptiveLru.I
Normal file
229
panda/src/gobj/adaptiveLru.I
Normal 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;
|
||||
}
|
||||
}
|
676
panda/src/gobj/adaptiveLru.cxx
Normal file
676
panda/src/gobj/adaptiveLru.cxx
Normal 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 ©) :
|
||||
_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 ©) {
|
||||
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
panda/src/gobj/adaptiveLru.h
Normal file
207
panda/src/gobj/adaptiveLru.h
Normal 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 ©);
|
||||
void operator = (const AdaptiveLruPage ©);
|
||||
|
||||
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
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "adaptiveLru.cxx"
|
||||
#include "bufferContext.cxx"
|
||||
#include "bufferContextChain.cxx"
|
||||
#include "bufferResidencyTracker.cxx"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 ©) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user