diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 2d774cdf0e..555ffde6e1 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -66,7 +66,7 @@ #define DEBUG_LRU false -#define DEFAULT_ENABLE_LRU !true +#define DEFAULT_ENABLE_LRU true TypeHandle DXGraphicsStateGuardian9::_type_handle; @@ -123,9 +123,9 @@ DXGraphicsStateGuardian9(const FrameBufferProperties &properties) : Geom::GR_triangle_strip | Geom::GR_triangle_fan | Geom::GR_flat_first_vertex; - _gsg_managed_textures = !false; - _gsg_managed_vertex_buffers = !false; - _gsg_managed_index_buffers = !false; + _gsg_managed_textures = false; + _gsg_managed_vertex_buffers = false; + _gsg_managed_index_buffers = false; _enable_lru = DEFAULT_ENABLE_LRU; @@ -819,8 +819,8 @@ end_frame() { if (false && dxgsg9_cat.is_debug()) { - dxgsg9_cat.debug() << "* start_priority_index " << _lru -> _m.start_priority_index << "\n"; - dxgsg9_cat.debug() << "* start_update_lru_page " << _lru -> _m.start_update_lru_page << "\n"; + dxgsg9_cat.debug() << "* start_priority_index " << _lru -> _m.start_priority_index << "\n"; + dxgsg9_cat.debug() << "* start_update_lru_page " << _lru -> _m.start_update_lru_page << "\n"; } frames = 256; @@ -856,6 +856,24 @@ end_frame() { dxgsg9_cat.debug() << "* priority " << index << " pages " << _lru -> _m.lru_page_count_array [index] << "\n"; } } + + _lru -> calculate_lru_statistics ( ); + + for (index = 0; index < _lru -> _m.maximum_page_types; index++) + { + PageTypeStatistics *page_type_statistics; + + page_type_statistics = &_lru -> _m.page_type_statistics_array [index]; + dxgsg9_cat.debug() << "\n" << + " page type " << index << + " total pages " << page_type_statistics -> total_pages << + " in " << page_type_statistics -> total_pages_in << + " size " << page_type_statistics -> total_memory_in << + " out " << page_type_statistics -> total_pages_out << + " size " << page_type_statistics -> total_memory_out << + "\n"; + + } } } } @@ -1970,6 +1988,7 @@ reset() { { int maximum_memory; int maximum_pages; + int maximum_page_types; Lru *lru; maximum_memory = available_texture_memory; @@ -1977,8 +1996,9 @@ maximum_memory = available_texture_memory; // TEST LRU ***** maximum_memory = 55000000; maximum_pages = 20000; +maximum_page_types = GPT_TotalPageTypes; - lru = new Lru (maximum_memory, maximum_pages); + lru = new Lru (maximum_memory, maximum_pages, maximum_page_types); if (lru) { lru -> _m.minimum_memory = 1000000; diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h index b665838a48..843c488b26 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h @@ -37,10 +37,11 @@ enum GsgPageType { - GPT_Error, GPT_Texture, GPT_VertexBuffer, GPT_IndexBuffer, + + GPT_TotalPageTypes }; class Light; diff --git a/panda/src/dxgsg9/lru.cxx b/panda/src/dxgsg9/lru.cxx index f5e8cc56b9..8d384460ff 100644 --- a/panda/src/dxgsg9/lru.cxx +++ b/panda/src/dxgsg9/lru.cxx @@ -1,11 +1,25 @@ +// Filename: lru.cxx +// Created by: aignacio (12Dec05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2006, Disney Enterprises, Inc. All rights +// reserved. +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// +//#include "stdafx.h" #define LRU_UNIT_TEST 0 -#if LRU_UNIT_TEST -#include "stdafx.h" -#endif - #include #include #include @@ -16,32 +30,35 @@ #define HIGH_PRIORITY_SCALE 4 #define LOW_PRIORITY_RANGE 25 -float calculate_exponential_moving_average (float value, float weight, float average) -{ - return ((value - average) * weight) + average; -} -bool default_page_in_function (LruPage *lru_page) -{ - char string [256]; +//////////////////////////////////////////////////////////////////// +// Class : Lru +// Description : Least Recently Used algorithm implementation: +// In the Lru, each "memory page" has an associated class LruPage. +// The Lru has a range of priorities from LPP_Highest to +// LPP_PagedOut. Each priority has a doubly linked list of LruPages. +// The algorithim uses an adaptive method based on the average +// utilization of each page per frame (or time slice). The +// average utilization is calculated with an exponetial moving +// average. This is superior to a standard average since a standard +// average becomes less and less adaptive the longer a page exists. +// The average utilization is used to set the priority of each page. +// A higher average utilization automatically raises the priority +// of a page and a lower average utilization automatically lowers +// the priority of a page. Therefore, pages with a higher average +// utilization have a higher chance of being kept in memory or +// cached and pages with a lower average utilization have a higher +// chance of being paged out. When a page is paged in and there +// is not enough memory available, then the lowest priority pages +// will be paged out first until there is enough memory available. +//////////////////////////////////////////////////////////////////// - sprintf (string, " PAGE IN %d\n", lru_page -> _m.identifier); - OutputDebugString (string); - - return true; -} -bool default_page_out_function (LruPage *lru_page) -{ - char string [256]; - - sprintf (string, " PAGE OUT %d\n", lru_page -> _m.identifier); - OutputDebugString (string); - - return true; -} - - -Lru::Lru (int maximum_memory, int maximum_pages) +//////////////////////////////////////////////////////////////////// +// Function: Lru::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Lru::Lru (int maximum_memory, int maximum_pages, int maximum_page_types) { if (this) { @@ -51,6 +68,7 @@ Lru::Lru (int maximum_memory, int maximum_pages) this -> _m.maximum_memory = maximum_memory; this -> _m.maximum_pages = maximum_pages; + this -> _m.maximum_page_types = maximum_page_types; this -> _m.available_memory = maximum_memory; this -> _m.current_frame_identifier = 1; this -> _m.weight = 0.20f; @@ -83,9 +101,19 @@ Lru::Lru (int maximum_memory, int maximum_pages) } } } + + if (maximum_page_types > 0) + { + this -> _m.page_type_statistics_array = new PageTypeStatistics [maximum_page_types]; + } } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// Lru::~Lru ( ) { int index; @@ -130,8 +158,19 @@ Lru::~Lru ( ) lru_page = next_lru_page; } } + + if (this -> _m.page_type_statistics_array) + { + delete this -> _m.page_type_statistics_array; + } } +//////////////////////////////////////////////////////////////////// +// Function: LruPage::Constructor +// Access: Protected +// Description: Internal function only. +// Call Lru::allocate_page instead. +//////////////////////////////////////////////////////////////////// LruPage::LruPage ( ) { if (this) @@ -140,11 +179,33 @@ LruPage::LruPage ( ) } } +//////////////////////////////////////////////////////////////////// +// Function: LruPage::Destructor +// Access: Protected +// Description: Internal function only. +// Call Lru::free_page instead. +//////////////////////////////////////////////////////////////////// LruPage::~LruPage ( ) { } +//////////////////////////////////////////////////////////////////// +// Function: LruPage::change_priority +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void LruPage::change_priority (int delta) +{ + this -> _m.priority_change += delta; +} + +//////////////////////////////////////////////////////////////////// +// Function: Lru::register_lru_page_type +// Access: Public +// Description: Registers a specific type of page and its +// required page in and out functions. +//////////////////////////////////////////////////////////////////// bool Lru::register_lru_page_type (int index, LruPageTypeFunction page_in_function, LruPageTypeFunction page_out_function) { bool state; @@ -160,11 +221,11 @@ bool Lru::register_lru_page_type (int index, LruPageTypeFunction page_in_functio return state; } -void LruPage::change_priority (int delta) -{ - this -> _m.priority_change += delta; -} - +//////////////////////////////////////////////////////////////////// +// Function: Lru::allocate_page +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// LruPage * Lru::allocate_page (int size) { LruPage *lru_page; @@ -180,6 +241,7 @@ LruPage * Lru::allocate_page (int size) this -> _m.total_lru_pages_in_free_pool--; memset (&lru_page -> _m, 0, sizeof (LruPage::LruPageVariables)); + lru_page -> _m.pre_allocated = true; } else { @@ -214,6 +276,12 @@ LruPage * Lru::allocate_page (int size) this -> _m.total_pages++; this -> _m.identifier++; } + else + { + +// ERROR: could not allocate LruPage + + } } else { @@ -225,6 +293,11 @@ LruPage * Lru::allocate_page (int size) return lru_page; } +//////////////////////////////////////////////////////////////////// +// Function: Lru::update_start_update_lru_page +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// void Lru::update_start_update_lru_page (LruPage *lru_page) { if (lru_page) @@ -252,6 +325,11 @@ void Lru::update_start_update_lru_page (LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::free_page +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// void Lru::free_page (LruPage *lru_page) { if (this -> _m.total_pages > 0) @@ -294,6 +372,11 @@ void Lru::free_page (LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::add_page +// Access: Public +// Description: Adds a page to the LRU based on the given priority. +//////////////////////////////////////////////////////////////////// void Lru::add_page (LruPagePriority priority, LruPage *lru_page) { if (lru_page) @@ -315,6 +398,12 @@ void Lru::add_page (LruPagePriority priority, LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::add_cached_page +// Access: Public +// Description: Adds a page that is already paged in to the LRU +// based on the given priority. +//////////////////////////////////////////////////////////////////// void Lru::add_cached_page (LruPagePriority priority, LruPage *lru_page) { if (lru_page) @@ -337,6 +426,11 @@ void Lru::add_cached_page (LruPagePriority priority, LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::remove_page +// Access: Public +// Description: Removes a page from the LRU. +//////////////////////////////////////////////////////////////////// void Lru::remove_page (LruPage *lru_page) { if (lru_page) @@ -379,16 +473,33 @@ void Lru::remove_page (LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::lock_page +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// void Lru::lock_page (LruPage *lru_page) { lru_page -> _m.lock = true; } +//////////////////////////////////////////////////////////////////// +// Function: Lru::unlock_page +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// void Lru::unlock_page (LruPage *lru_page) { lru_page -> _m.lock = false; } +//////////////////////////////////////////////////////////////////// +// Function: Lru::access_page +// Access: Public +// Description: This must always be called before accessing or +// using a page's memory since it pages in the page +// if it is currently paged out. +//////////////////////////////////////////////////////////////////// void Lru::access_page (LruPage *lru_page) { if (lru_page) @@ -453,12 +564,23 @@ void Lru::access_page (LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::set_maximum_frame_bandwidth_utilization +// Access: Public +// Description: This must be called before accessing or using a +// page since it pages in the page if it is paged out. +//////////////////////////////////////////////////////////////////// void Lru::set_maximum_frame_bandwidth_utilization (float maximum_frame_bandwidth_utilization) { this -> _m.maximum_frame_bandwidth_utilization = maximum_frame_bandwidth_utilization; this -> _m.frame_bandwidth_factor = (float) LPP_TotalPriorities / this -> _m.maximum_frame_bandwidth_utilization; } +//////////////////////////////////////////////////////////////////// +// Function: Lru::begin_frame +// Access: Public +// Description: This must be called before each frame. +//////////////////////////////////////////////////////////////////// void Lru::begin_frame ( ) { this -> _m.current_frame_identifier++; @@ -470,6 +592,12 @@ void Lru::begin_frame ( ) this -> _m.total_page_outs = 0; } +//////////////////////////////////////////////////////////////////// +// Function: Lru::update_page_priorities +// Access: Public +// Description: This updates the priority of a page that has a +// change in priority. +//////////////////////////////////////////////////////////////////// void Lru::update_page_priorities (void) { int index; @@ -499,8 +627,17 @@ void Lru::update_page_priorities (void) this -> _m.total_lru_page_priority_changes = 0; } +//////////////////////////////////////////////////////////////////// +// Function: Lru::update_lru_page +// Access: Public +// Description: This updates the page's average utilization and +// adds it to the array of pages with changed +// priorities if there was a change in priority. +//////////////////////////////////////////////////////////////////// void Lru::update_lru_page (LruPage *lru_page) { + +#if LRU_UNIT_TEST if (false) { char string [256]; @@ -508,6 +645,7 @@ void Lru::update_lru_page (LruPage *lru_page) sprintf (string, " UPDATE %d\n", lru_page -> _m.identifier); OutputDebugString (string); } +#endif if (lru_page -> _m.lock == false && lru_page -> _m.in_cache) { @@ -582,8 +720,18 @@ void Lru::update_lru_page (LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::update_lru_page_old +// Access: Public +// Description: This updates the page's average utilization and +// adds it to the array of pages with changed +// priorities if there was a change in priority. +// Old method. +//////////////////////////////////////////////////////////////////// void Lru::update_lru_page_old (LruPage *lru_page) { + +#if LRU_UNIT_TEST if (false) { char string [256]; @@ -591,6 +739,7 @@ void Lru::update_lru_page_old (LruPage *lru_page) sprintf (string, " UPDATE %d\n", lru_page -> _m.identifier); OutputDebugString (string); } +#endif if (lru_page -> _m.lock == false) { @@ -707,6 +856,13 @@ void Lru::update_lru_page_old (LruPage *lru_page) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::update_entire_lru +// Access: Public +// Description: This updates all the pages in the Lru. +// Lru::partial_lru_update should be called instead +// due to performance reasons. +//////////////////////////////////////////////////////////////////// void Lru::update_entire_lru ( ) { if (this -> _m.total_pages > 0) @@ -733,6 +889,12 @@ void Lru::update_entire_lru ( ) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::partial_lru_update +// Access: Public +// Description: This only updates a number of pages up to the +// specified maximum_updates. +//////////////////////////////////////////////////////////////////// void Lru::partial_lru_update (int maximum_updates) { int total_page_updates; @@ -865,6 +1027,11 @@ void Lru::partial_lru_update (int maximum_updates) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::unlock_all_pages +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// void Lru::unlock_all_pages (void) { if (this -> _m.total_pages > 0) @@ -889,6 +1056,13 @@ void Lru::unlock_all_pages (void) } } +//////////////////////////////////////////////////////////////////// +// Function: Lru::page_out_lru +// Access: Public +// Description: Pages out the lowest priority pages until the +// memory_required is satisfied. This will unlock +// all pages if needed. +//////////////////////////////////////////////////////////////////// bool Lru::page_out_lru (int memory_required) { bool state; @@ -962,6 +1136,12 @@ bool Lru::page_out_lru (int memory_required) return state; } +//////////////////////////////////////////////////////////////////// +// Function: Lru::count_priority_level_pages +// Access: Public +// Description: Debug function. Counts the number of pages for each +// priority level. +//////////////////////////////////////////////////////////////////// void Lru::count_priority_level_pages (void) { int index; @@ -987,8 +1167,100 @@ void Lru::count_priority_level_pages (void) } } +void Lru::calculate_lru_statistics (void) +{ + if (this -> _m.maximum_page_types > 0) + { + int index; + + memset (this -> _m.page_type_statistics_array, 0, sizeof (PageTypeStatistics) * this -> _m.maximum_page_types); + for (index = 0; index < LPP_TotalPriorities; index++) + { + LruPage *lru_page; + LruPage *next_lru_page; + PageTypeStatistics *page_type_statistics; + + lru_page = this -> _m.lru_page_array [index]; + while (lru_page) + { + volatile int type; + + next_lru_page = lru_page -> _m.next; + + type = lru_page -> _m.type; + page_type_statistics = &this -> _m.page_type_statistics_array [type]; + page_type_statistics -> total_pages++; + + if (lru_page -> _m.in_cache) + { + page_type_statistics -> total_pages_in++; + page_type_statistics -> total_memory_in += lru_page -> _m.size; + } + else + { + page_type_statistics -> total_pages_out++; + page_type_statistics -> total_memory_out += lru_page -> _m.size; + } + + lru_page = next_lru_page; + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: calculate_exponential_moving_average +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +float calculate_exponential_moving_average (float value, float weight, float average) +{ + return ((value - average) * weight) + average; +} + +//////////////////////////////////////////////////////////////////// +// Function: default_page_in_function +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool default_page_in_function (LruPage *lru_page) +{ + +#if LRU_UNIT_TEST + char string [256]; + + sprintf (string, " PAGE IN %d\n", lru_page -> _m.identifier); + OutputDebugString (string); +#endif + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: default_page_out_function +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool default_page_out_function (LruPage *lru_page) +{ + +#if LRU_UNIT_TEST + char string [256]; + + sprintf (string, " PAGE OUT %d\n", lru_page -> _m.identifier); + OutputDebugString (string); +#endif + + return true; +} + #if LRU_UNIT_TEST +//////////////////////////////////////////////////////////////////// +// Function: test_ema +// Access: +// Description: Unit test function for ema. +//////////////////////////////////////////////////////////////////// void test_ema (void) { int index; @@ -1016,17 +1288,24 @@ void test_ema (void) } } +//////////////////////////////////////////////////////////////////// +// Function: test_lru +// Access: +// Description: Unit test function for Lru. +//////////////////////////////////////////////////////////////////// void test_lru (void) { int maximum_memory; int maximum_pages; + int maximum_page_types; Lru *lru; test_ema ( ); maximum_memory = 3000000; maximum_pages = 3; - lru = new Lru (maximum_memory, maximum_pages); + maximum_page_types = 4; + lru = new Lru (maximum_memory, maximum_pages, maximum_page_types); if (lru) { lru -> _m.minimum_memory = 1000000; @@ -1121,10 +1400,10 @@ void test_lru (void) } else { - int approximate_maximum_updates; + int maximum_updates; - approximate_maximum_updates = 3; - lru -> partial_lru_update (approximate_maximum_updates); + maximum_updates = 3; + lru -> partial_lru_update (maximum_updates); } } diff --git a/panda/src/dxgsg9/lru.h b/panda/src/dxgsg9/lru.h index 3bae9ae6fd..06b17b49f0 100644 --- a/panda/src/dxgsg9/lru.h +++ b/panda/src/dxgsg9/lru.h @@ -1,3 +1,20 @@ +// Filename: lru.h +// Created by: aignacio (12Dec05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2006, Disney Enterprises, Inc. All rights +// reserved. +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// #ifndef LRU_H #define LRU_H @@ -30,6 +47,16 @@ typedef union _LruPageType } LruPageType; +typedef struct +{ + int total_pages; + int total_pages_in; + int total_pages_out; + int total_memory_in; + int total_memory_out; +} +PageTypeStatistics; + typedef bool (*LruPageTypeFunction) (LruPage *lru_page); class LruPage @@ -45,7 +72,7 @@ public: typedef struct _LruPageVariables { - LruPageType lru_page_type; + LruPageType lru_page_type; // pointer to memory type int size; LruPagePriority priority; @@ -53,7 +80,7 @@ public: struct { - unsigned int type : 4; + unsigned int type : 8; unsigned int lock : 1; unsigned int in_cache : 1; unsigned int in_memory : 1; @@ -77,7 +104,7 @@ public: int total_usage; int update_total_usage; - int identifier; // this is also the number of pages created during the lifetime of the LRU + int identifier; float average_frame_utilization; @@ -97,7 +124,7 @@ class Lru { public: - Lru (int maximum_memory, int maximum_pages); + Lru (int maximum_memory, int maximum_pages, int maximum_page_types); ~Lru ( ); bool register_lru_page_type (int index, LruPageTypeFunction page_in_function, LruPageTypeFunction page_out_function); @@ -130,6 +157,8 @@ public: void count_priority_level_pages (void); + void calculate_lru_statistics (void); + private: bool page_out_lru (int memory_required); void update_page_priorities (void); @@ -148,6 +177,7 @@ public: int maximum_memory; int minimum_memory; // target amount of memory to keep free if possible + int maximum_page_types; int total_lifetime_page_ins; int total_lifetime_page_outs; @@ -164,12 +194,11 @@ public: int maximum_page_updates_per_frame; // unused pages int start_priority_index; - int update_overflow; LruPage *start_update_lru_page; - int identifier; + int identifier; // this is also the number of pages created during the lifetime of the LRU - float weight; + float weight; // used for exponential moving average float maximum_frame_bandwidth_utilization; float frame_bandwidth_factor; @@ -189,6 +218,8 @@ public: int total_lru_pages_in_free_pool; LruPage **lru_page_pool; LruPage **lru_page_free_pool; + + PageTypeStatistics *page_type_statistics_array; } LruVariables; @@ -204,5 +235,4 @@ bool default_page_out_function (LruPage *lru_page); void test_ema (void); void test_lru (void); - #endif