Преглед на файлове

Preliminary LRU.
Only manages vertex buffers and index buffers at this time.
Textures still need to added to LRU.
Code needs reformatting and cleanup, error handling, and messaging.
Config options need to be added.

aignacio_sf преди 20 години
родител
ревизия
2c28f1a62f

+ 216 - 11
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -64,6 +64,11 @@
 #include <d3dx9.h>
 #include <mmsystem.h>
 
+
+#define DEBUG_LRU false
+#define DEFAULT_ENABLE_LRU true
+
+
 TypeHandle DXGraphicsStateGuardian9::_type_handle;
 
 D3DMATRIX DXGraphicsStateGuardian9::_d3d_ident_mat;
@@ -117,6 +122,14 @@ DXGraphicsStateGuardian9(const FrameBufferProperties &properties) :
     Geom::GR_indexed_other |
     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;
+
+  _enable_lru = DEFAULT_ENABLE_LRU;
+
+  _lru = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -130,6 +143,12 @@ DXGraphicsStateGuardian9::
     _d3d_device->SetTexture(0, NULL);  // this frees reference to the old texture
   }
   free_nondx_resources();
+
+  if (_lru)
+  {
+    delete _lru;
+    _lru = 0;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -315,6 +334,11 @@ void DXGraphicsStateGuardian9::
 apply_vertex_buffer(VertexBufferContext *vbc) {
   DXVertexBufferContext9 *dvbc = DCAST(DXVertexBufferContext9, vbc);
 
+  if (_lru)
+  {
+    _lru -> access_page (dvbc -> _lru_page);
+  }
+
   if (dvbc->_vbuffer == NULL) {
     // Attempt to create a new vertex buffer.
     if (vertex_buffers &&
@@ -416,6 +440,11 @@ void DXGraphicsStateGuardian9::
 apply_index_buffer(IndexBufferContext *ibc) {
   DXIndexBufferContext9 *dibc = DCAST(DXIndexBufferContext9, ibc);
 
+  if (_lru)
+  {
+    _lru -> access_page (dibc -> _lru_page);
+  }
+
   if (dibc->_ibuffer == NULL) {
     // Attempt to create a new index buffer.
     dibc->create_ibuffer(*_screen);
@@ -679,6 +708,12 @@ prepare_lens() {
 ////////////////////////////////////////////////////////////////////
 bool DXGraphicsStateGuardian9::
 begin_frame() {
+
+  if (_lru)
+  {
+    _lru -> begin_frame ( );
+  }
+
   return GraphicsStateGuardian::begin_frame();
 }
 
@@ -766,6 +801,47 @@ end_scene() {
 void DXGraphicsStateGuardian9::
 end_frame() {
 
+  if (_lru)
+  {
+    int frames;
+    int maximum_updates;
+
+// LRU *****
+    maximum_updates = 10;
+    _lru -> partial_lru_update (maximum_updates);
+//    _lru -> update_entire_lru ( );
+
+    frames = 256;
+    if ((_lru -> _m.current_frame_identifier % frames) == 0)
+    {
+      if (dxgsg9_cat.is_debug())
+      {
+        dxgsg9_cat.debug() << "* LRU: total_pages " << _lru -> _m.total_pages << "\n";
+        dxgsg9_cat.debug() << "*  available_memory " << _lru -> _m.available_memory << "\n";
+        dxgsg9_cat.debug() << "*  total lifetime pages created " << _lru -> _m.identifier << "\n";
+        dxgsg9_cat.debug() << "*  total_lifetime_page_ins " << _lru -> _m.total_lifetime_page_ins << "\n";
+        dxgsg9_cat.debug() << "*  total_lifetime_page_outs " << _lru -> _m.total_lifetime_page_outs << "\n";
+        dxgsg9_cat.debug() << "*  total_page_access " << _lru -> _m.total_page_access << " avg page access " << ((float) _lru -> _m.total_page_access / (float) frames) << "\n";
+        dxgsg9_cat.debug() << "*  total_lru_pages_in_pool " << _lru -> _m.total_lru_pages_in_pool << "\n";
+        dxgsg9_cat.debug() << "*  total_lru_pages_in_free_pool " << _lru -> _m.total_lru_pages_in_free_pool << "\n";
+
+        _lru -> _m.total_page_access = 0;
+
+        _lru -> count_priority_level_pages ( );
+
+        int index;
+
+        for (index = 0; index < LPP_TotalPriorities; index++)
+        {
+          if (_lru -> _m.lru_page_count_array [index])
+          {
+            dxgsg9_cat.debug() << "*  priority " << index << " pages " << _lru -> _m.lru_page_count_array [index] << "\n";
+          }
+        }
+      }
+    }
+  }
+
 #if defined(DO_PSTATS)
   if (_texmgrmem_total_pcollector.is_active()) {
 #define TICKS_PER_GETTEXINFO (2.5*1000)   // 2.5 second interval
@@ -983,7 +1059,7 @@ draw_tristrips(const GeomTristrips *primitive) {
       } else {
         // Indexed, client arrays, one long triangle strip.
         D3DFORMAT index_type = get_index_type(primitive->get_index_type());
-  draw_indexed_primitive_up
+        draw_indexed_primitive_up
           (D3DPT_TRIANGLESTRIP,
            min_vertex, max_vertex,
            primitive->get_num_vertices() - 2,
@@ -1001,7 +1077,7 @@ draw_tristrips(const GeomTristrips *primitive) {
 
       } else {
         // Indexed, client arrays, one long triangle strip.
-  draw_primitive_up(D3DPT_TRIANGLESTRIP,
+        draw_primitive_up(D3DPT_TRIANGLESTRIP,
         primitive->get_num_vertices() - 2,
         primitive->get_first_vertex(),
         primitive->get_num_vertices(),
@@ -1039,7 +1115,7 @@ draw_tristrips(const GeomTristrips *primitive) {
           unsigned int max = maxs.get_data1i();
           _d3d_device->DrawIndexedPrimitive
             (D3DPT_TRIANGLESTRIP,
-       0,
+             0,
              min, max - min + 1,
              start, ends[i] - start - 2);
 
@@ -1058,7 +1134,7 @@ draw_tristrips(const GeomTristrips *primitive) {
           _vertices_tristrip_pcollector.add_level(ends[i] - start);
           unsigned int min = mins.get_data1i();
           unsigned int max = maxs.get_data1i();
-    draw_indexed_primitive_up
+          draw_indexed_primitive_up
             (D3DPT_TRIANGLESTRIP,
              min, max,
              ends[i] - start - 2,
@@ -1091,7 +1167,7 @@ draw_tristrips(const GeomTristrips *primitive) {
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
           _vertices_tristrip_pcollector.add_level(ends[i] - start);
-    draw_primitive_up(D3DPT_TRIANGLESTRIP, ends[i] - start - 2,
+          draw_primitive_up(D3DPT_TRIANGLESTRIP, ends[i] - start - 2,
           first_vertex + start,
           ends[i] - start,
           array_data, stride);
@@ -1158,7 +1234,7 @@ draw_trifans(const GeomTrifans *primitive) {
         _vertices_trifan_pcollector.add_level(ends[i] - start);
         unsigned int min = mins.get_data1i();
         unsigned int max = maxs.get_data1i();
-  draw_indexed_primitive_up
+        draw_indexed_primitive_up
           (D3DPT_TRIANGLEFAN,
            min, max,
            ends[i] - start - 2,
@@ -1191,7 +1267,7 @@ draw_trifans(const GeomTrifans *primitive) {
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
         _vertices_trifan_pcollector.add_level(ends[i] - start);
-  draw_primitive_up(D3DPT_TRIANGLEFAN,
+        draw_primitive_up(D3DPT_TRIANGLEFAN,
         ends[i] - start - 2,
         first_vertex,
         ends[i] - start,
@@ -1666,6 +1742,81 @@ framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr, const Rend
   return true;
 }
 
+
+bool vertex_buffer_page_in_function (LruPage *lru_page)
+{
+  DXGraphicsStateGuardian9 *gsg;
+  DXVertexBufferContext9 *vertex_buffer;
+
+  gsg = (DXGraphicsStateGuardian9 *) (lru_page -> _m.lru -> _m.context);
+  vertex_buffer = (DXVertexBufferContext9 *) lru_page -> _m.lru_page_type.pointer;
+
+  // allocate vertex buffer
+  vertex_buffer -> allocate_vbuffer (*(gsg->_screen));
+
+  // update vertex buffer
+  vertex_buffer -> upload_data ( );
+
+  if (DEBUG_LRU && dxgsg9_cat.is_debug())
+  {
+    dxgsg9_cat.debug() << "  *** page IN VB " << lru_page -> _m.identifier << " size " << lru_page -> _m.size << "\n";
+  }
+
+  return true;
+}
+
+bool vertex_buffer_page_out_function (LruPage *lru_page)
+{
+  DXVertexBufferContext9 *vertex_buffer;
+
+  vertex_buffer = (DXVertexBufferContext9 *) lru_page -> _m.lru_page_type.pointer;
+  vertex_buffer -> free_vbuffer ( );
+
+  if (DEBUG_LRU && dxgsg9_cat.is_debug())
+  {
+    dxgsg9_cat.debug() << "  *** page OUT VB " << lru_page -> _m.identifier << " size " << lru_page -> _m.size << "\n";
+  }
+
+  return true;
+}
+
+bool index_buffer_page_in_function (LruPage *lru_page)
+{
+  DXGraphicsStateGuardian9 *gsg;
+  DXIndexBufferContext9 *index_buffer;
+
+  gsg = (DXGraphicsStateGuardian9 *) (lru_page -> _m.lru -> _m.context);
+  index_buffer = (DXIndexBufferContext9 *) lru_page -> _m.lru_page_type.pointer;
+
+  // allocate vertex buffer
+  index_buffer -> allocate_ibuffer (*(gsg->_screen));
+
+  // update vertex buffer
+  index_buffer -> upload_data ( );
+
+  if (DEBUG_LRU && dxgsg9_cat.is_debug())
+  {
+    dxgsg9_cat.debug() << "  *** page IN IB " << lru_page -> _m.identifier << " size " << lru_page -> _m.size << "\n";
+  }
+
+  return true;
+}
+
+bool index_buffer_page_out_function (LruPage *lru_page)
+{
+  DXIndexBufferContext9 *index_buffer;
+
+  index_buffer = (DXIndexBufferContext9 *) lru_page -> _m.lru_page_type.pointer;
+  index_buffer -> free_ibuffer ( );
+
+  if (DEBUG_LRU && dxgsg9_cat.is_debug())
+  {
+    dxgsg9_cat.debug() << "  *** page OUT IB " << lru_page -> _m.identifier << " size " << lru_page -> _m.size << "\n";
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian9::reset
 //       Access: Public, Virtual
@@ -1736,6 +1887,60 @@ reset() {
   _supports_texture_saved_result = ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP) != 0);
   _supports_texture_dot3 = true;
 
+  _screen->_supports_dynamic_textures = ((d3d_caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0);
+
+  _screen->_managed_textures = _gsg_managed_textures;
+  _screen->_managed_vertex_buffers = _gsg_managed_vertex_buffers;
+  _screen->_managed_index_buffers = _gsg_managed_index_buffers;
+
+  UINT available_texture_memory;
+
+  available_texture_memory = _d3d_device->GetAvailableTextureMem ( );
+  if (dxgsg9_cat.is_debug()) {
+    dxgsg9_cat.debug() << "*** GetAvailableTextureMem = " <<  available_texture_memory << "\n";
+  }
+  _available_texture_memory = available_texture_memory;
+
+  if (_lru)
+  {
+    delete _lru;
+    _lru = 0;
+  }
+
+  if (_enable_lru)
+  {
+    if (available_texture_memory >= 256000000)
+    {
+//      _enable_lru = false;
+    }
+  }
+
+  if (_enable_lru)
+  {
+    int maximum_memory;
+    int maximum_pages;
+    Lru *lru;
+
+maximum_memory = available_texture_memory;
+
+// TEST LRU *****
+maximum_memory = 20000000;
+maximum_pages = 20000;
+
+    lru = new Lru (maximum_memory, maximum_pages);
+    if (lru)
+    {
+      lru -> _m.minimum_memory = 1000000;
+
+      lru -> register_lru_page_type (GPT_VertexBuffer, vertex_buffer_page_in_function, vertex_buffer_page_out_function);
+      lru -> register_lru_page_type (GPT_IndexBuffer, index_buffer_page_in_function, index_buffer_page_out_function);
+
+      lru -> _m.context = (void *) this;
+    }
+
+    _lru = lru;
+  }
+
   // check for render to texture support
   D3DDEVICE_CREATION_PARAMETERS creation_parameters;
 
@@ -3207,6 +3412,8 @@ set_context(DXScreenData *new_context) {
   _screen = new_context;
   _d3d_device = _screen->_d3d_device;   //copy this one field for speed of deref
   _swap_chain = _screen->_swap_chain;   //copy this one field for speed of deref
+
+  _screen->_dxgsg9 = this;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3487,9 +3694,7 @@ reset_d3d_device(D3DPRESENT_PARAMETERS *presentation_params,
     }
 
     // Calling this forces all of the textures and vbuffers to be
-    // regenerated, a prerequisite to calling Reset().  Actually, this
-    // shouldn't be necessary, because all of our textures and
-    // vbuffers are stored in the D3DPOOL_MANAGED memory class.
+    // regenerated, a prerequisite to calling Reset().
     release_all();
 
     // Just to be extra-conservative for now, we'll go ahead and
@@ -3498,6 +3703,7 @@ reset_d3d_device(D3DPRESENT_PARAMETERS *presentation_params,
     release_all_vertex_buffers();
     release_all_index_buffers();
 
+    // must be called before reset
     _prepared_objects->update(this);
 
     hr = _d3d_device->Reset(&_presentation_reset);
@@ -3942,4 +4148,3 @@ draw_indexed_primitive_up(D3DPRIMITIVETYPE primitive_type,
        index_data, index_type, safe_buffer_start - stride * min_index, stride);
   }
 }
-

+ 22 - 1
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -33,6 +33,16 @@
 #include "fog.h"
 #include "pointerToArray.h"
 
+#include "lru.h"
+
+enum GsgPageType
+{
+  GPT_Error,
+  GPT_Texture,
+  GPT_VertexBuffer,
+  GPT_IndexBuffer,
+};
+
 class Light;
 
 class DXVertexBufferContext9;
@@ -186,8 +196,9 @@ protected:
 
   INLINE static unsigned char *get_safe_buffer_start();
 
-protected:
+public:
   DXScreenData *_screen;
+protected:
   LPDIRECT3DDEVICE9 _d3d_device;  // same as _screen->_d3d_device, cached for spd
   IDirect3DSwapChain9 *_swap_chain;
   D3DPRESENT_PARAMETERS _presentation_reset;  // This is built during reset device
@@ -234,6 +245,14 @@ protected:
   static unsigned char *_temp_buffer;
   static unsigned char *_safe_buffer_start;
 
+  int _gsg_managed_textures;
+  int _gsg_managed_vertex_buffers;
+  int _gsg_managed_index_buffers;
+  int _enable_lru;
+  UINT _available_texture_memory;
+
+  Lru *_lru;
+
 public:
   virtual TypeHandle get_type() const {
     return get_class_type();
@@ -259,6 +278,8 @@ private:
   friend class wdxGraphicsWindowGroup9;
   friend class DXTextureContext9;
   friend class wdxGraphicsBuffer9;
+  friend class DXVertexBufferContext9;
+  friend class DXIndexBufferContext9;
 };
 
 #include "dxGraphicsStateGuardian9.I"

+ 115 - 21
panda/src/dxgsg9/dxIndexBufferContext9.cxx

@@ -23,6 +23,8 @@
 #include "pStatTimer.h"
 #include <d3dx9.h>
 
+#define DEBUG_INDEX_BUFFER false
+
 TypeHandle DXIndexBufferContext9::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -35,6 +37,8 @@ DXIndexBufferContext9(GeomPrimitive *data) :
   IndexBufferContext(data),
   _ibuffer(NULL)
 {
+  _managed = -1;
+  _lru_page = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -44,48 +48,81 @@ DXIndexBufferContext9(GeomPrimitive *data) :
 ////////////////////////////////////////////////////////////////////
 DXIndexBufferContext9::
 ~DXIndexBufferContext9() {
+
+  this -> free_ibuffer ( );
+
+  if (_lru_page)
+  {
+    _lru_page -> _m.lru -> remove_page (_lru_page);
+    _lru_page -> _m.lru -> free_page (_lru_page);
+    _lru_page = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXIndexBufferContext9::free_ibuffer
+//       Access: Public
+//  Description: Free index buffer.
+////////////////////////////////////////////////////////////////////
+void DXIndexBufferContext9::
+free_ibuffer(void) {
   if (_ibuffer != NULL) {
-    if (dxgsg9_cat.is_debug()) {
+    if (DEBUG_INDEX_BUFFER && dxgsg9_cat.is_debug()) {
       dxgsg9_cat.debug()
         << "deleting index buffer " << _ibuffer << "\n";
     }
 
-    RELEASE(_ibuffer, dxgsg9, "index buffer", RELEASE_ONCE);
+    if (DEBUG_INDEX_BUFFER)
+    {
+      RELEASE(_ibuffer, dxgsg9, "index buffer", RELEASE_ONCE);
+    }
+    else
+    {
+      _ibuffer -> Release ( );
+    }
+
     _ibuffer = NULL;
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DXIndexBufferContext9::create_ibuffer
+//     Function: DXIndexBufferContext9::allocate_ibuffer
 //       Access: Public
-//  Description: Creates a new index buffer (but does not upload data
-//               to it).
+//  Description: Allocates index buffer memory.
 ////////////////////////////////////////////////////////////////////
 void DXIndexBufferContext9::
-create_ibuffer(DXScreenData &scrn) {
-  if (_ibuffer != NULL) {
-    RELEASE(_ibuffer, dxgsg9, "index buffer", RELEASE_ONCE);
-    _ibuffer = NULL;
-  }
-
-  PStatTimer timer(GraphicsStateGuardian::_create_index_buffer_pcollector);
+allocate_ibuffer(DXScreenData &scrn) {
 
   D3DFORMAT index_type =
     DXGraphicsStateGuardian9::get_index_type(get_data()->get_index_type());
 
-  HRESULT hr = scrn._d3d_device->CreateIndexBuffer
+  int data_size;
+  DWORD usage;
+  D3DPOOL pool;
 
-//    (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY,
-//     index_type, D3DPOOL_MANAGED, &_ibuffer, NULL);
-    (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
-     index_type, D3DPOOL_DEFAULT, &_ibuffer, NULL);
+  data_size = get_data()->get_data_size_bytes();
+
+  _managed = scrn._managed_index_buffers;
+  if (_managed)
+  {
+    usage = D3DUSAGE_WRITEONLY;
+    pool = D3DPOOL_MANAGED;
+  }
+  else
+  {
+    usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
+    pool = D3DPOOL_DEFAULT;
+  }
+
+  HRESULT hr = scrn._d3d_device->CreateIndexBuffer
+    (data_size, usage, index_type, pool, &_ibuffer, NULL);
 
   if (FAILED(hr)) {
     dxgsg9_cat.warning()
       << "CreateIndexBuffer failed" << D3DERRORSTRING(hr);
     _ibuffer = NULL;
   } else {
-    if (dxgsg9_cat.is_debug()) {
+    if (DEBUG_INDEX_BUFFER && dxgsg9_cat.is_debug()) {
       dxgsg9_cat.debug()
         << "creating index buffer " << _ibuffer << ": "
         << get_data()->get_num_vertices() << " indices ("
@@ -95,6 +132,57 @@ create_ibuffer(DXScreenData &scrn) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DXIndexBufferContext9::create_ibuffer
+//       Access: Public
+//  Description: Creates a new index buffer (but does not upload data
+//               to it).
+////////////////////////////////////////////////////////////////////
+void DXIndexBufferContext9::
+create_ibuffer(DXScreenData &scrn) {
+
+  this -> free_ibuffer ( );
+
+  if (_lru_page)
+  {
+    _lru_page -> _m.lru -> remove_page (_lru_page);
+    _lru_page -> _m.lru -> free_page (_lru_page);
+    _lru_page = 0;
+  }
+
+  PStatTimer timer(GraphicsStateGuardian::_create_index_buffer_pcollector);
+
+  int data_size;
+
+  data_size = get_data()->get_data_size_bytes();
+
+  this -> allocate_ibuffer(scrn);
+
+  if (_ibuffer)
+  {
+    if (_managed == false)
+    {
+      Lru *lru;
+
+      lru = scrn._dxgsg9 -> _lru;
+      if (lru)
+      {
+        LruPage *lru_page;
+
+        lru_page = lru -> allocate_page (data_size);
+        if (lru_page)
+        {
+          lru_page -> _m.type = GPT_IndexBuffer;
+          lru_page -> _m.lru_page_type.pointer = this;
+
+          lru -> add_cached_page (LPP_New, lru_page);
+          _lru_page = lru_page;
+        }
+      }
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DXIndexBufferContext9::upload_data
 //       Access: Public
@@ -114,11 +202,17 @@ upload_data() {
       << " bytes into index buffer " << _ibuffer << "\n";
   }
 
+  HRESULT hr;
   BYTE *local_pointer;
 
-//  HRESULT hr = _ibuffer->Lock(0, data_size, (void **) &local_pointer, 0);
-  HRESULT hr = _ibuffer->Lock(0, data_size, (void **) &local_pointer, D3DLOCK_DISCARD);
-
+  if (_managed)
+  {
+    hr = _ibuffer->Lock(0, data_size, (void **) &local_pointer, 0);
+  }
+  else
+  {
+    hr = _ibuffer->Lock(0, data_size, (void **) &local_pointer, D3DLOCK_DISCARD);
+  }
   if (FAILED(hr)) {
     dxgsg9_cat.error()
       << "IndexBuffer::Lock failed" << D3DERRORSTRING(hr);

+ 4 - 0
panda/src/dxgsg9/dxIndexBufferContext9.h

@@ -33,10 +33,14 @@ public:
   DXIndexBufferContext9(GeomPrimitive *data);
   virtual ~DXIndexBufferContext9();
 
+  void free_ibuffer(void);
+  void allocate_ibuffer(DXScreenData &scrn);
   void create_ibuffer(DXScreenData &scrn);
   void upload_data();
 
   IDirect3DIndexBuffer9 *_ibuffer;
+  int _managed;
+  LruPage *_lru_page;
 
 public:
   static TypeHandle get_class_type() {

+ 25 - 4
panda/src/dxgsg9/dxTextureContext9.cxx

@@ -47,6 +47,7 @@ DXTextureContext9(Texture *tex) :
   _d3d_volume_texture = NULL;
   _d3d_cube_texture = NULL;
   _has_mipmaps = false;
+  _managed = -1;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -637,15 +638,35 @@ create_texture(DXScreenData &scrn) {
   D3DPOOL pool;
 
   if (_texture->get_render_to_texture ()) {
-    // REQUIRED
+    // REQUIRED PARAMETERS
+    _managed = false;
     pool = D3DPOOL_DEFAULT;
     usage = D3DUSAGE_RENDERTARGET;
     target_pixel_format = scrn._render_to_texture_d3d_format;
   }
   else {
-    pool = D3DPOOL_MANAGED;
-//    pool = D3DPOOL_DEFAULT;
-    usage = 0;
+    _managed = scrn._managed_textures;
+    if (_managed)
+    {
+      pool = D3DPOOL_MANAGED;
+      usage = 0;
+    }
+    else
+    {
+      if (scrn._supports_dynamic_textures)
+      {
+        pool = D3DPOOL_DEFAULT;
+        usage = D3DUSAGE_DYNAMIC;
+      }
+      else
+      {
+        // can't lock textures so go back to managed for now
+        // need to use UpdateTexture or UpdateSurface
+        _managed = true;
+        pool = D3DPOOL_MANAGED;
+        usage = 0;
+      }
+    }
   }
 
   switch (_texture->get_texture_type()) {

+ 1 - 0
panda/src/dxgsg9/dxTextureContext9.h

@@ -59,6 +59,7 @@ private:
   IDirect3DVolumeTexture9 *_d3d_volume_texture;
   IDirect3DCubeTexture9 *_d3d_cube_texture;
 
+  int _managed;
   bool _has_mipmaps;
 
 public:

+ 115 - 20
panda/src/dxgsg9/dxVertexBufferContext9.cxx

@@ -25,6 +25,8 @@
 #include "config_dxgsg9.h"
 #include <d3dx9.h>
 
+#define DEBUG_VERTEX_BUFFER false
+
 TypeHandle DXVertexBufferContext9::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -46,6 +48,8 @@ DXVertexBufferContext9(GeomVertexArrayData *data) :
   int num_columns = array_format->get_num_columns();
 
   _fvf = 0;
+  _managed = -1;
+  _lru_page = 0;
 
   if (n < num_columns &&
       array_format->get_column(n)->get_name() == InternalName::get_vertex()) {
@@ -170,45 +174,79 @@ DXVertexBufferContext9(GeomVertexArrayData *data) :
 ////////////////////////////////////////////////////////////////////
 DXVertexBufferContext9::
 ~DXVertexBufferContext9() {
+
+  free_vbuffer ( );
+
+  if (_lru_page)
+  {
+    _lru_page -> _m.lru -> remove_page (_lru_page);
+    _lru_page -> _m.lru -> free_page (_lru_page);
+    _lru_page = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXVertexBufferContext9::free_vbuffer
+//       Access: Public
+//  Description: Frees vertex buffer memory.
+////////////////////////////////////////////////////////////////////
+void DXVertexBufferContext9::
+free_vbuffer(void) {
+
   if (_vbuffer != NULL) {
-    if (dxgsg9_cat.is_debug()) {
+    if (DEBUG_VERTEX_BUFFER && dxgsg9_cat.is_debug()) {
       dxgsg9_cat.debug()
         << "deleting vertex buffer " << _vbuffer << "\n";
     }
 
-    RELEASE(_vbuffer, dxgsg9, "vertex buffer", RELEASE_ONCE);
+    if (DEBUG_VERTEX_BUFFER)
+    {
+      RELEASE(_vbuffer, dxgsg9, "vertex buffer", RELEASE_ONCE);
+    }
+    else
+    {
+      _vbuffer -> Release ( );
+    }
+
     _vbuffer = NULL;
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DXVertexBufferContext9::create_vbuffer
+//     Function: DXVertexBufferContext9::allocate_vbuffer
 //       Access: Public
-//  Description: Creates a new vertex buffer (but does not upload data
-//               to it).
+//  Description: Allocates vertex buffer memory.
 ////////////////////////////////////////////////////////////////////
 void DXVertexBufferContext9::
-create_vbuffer(DXScreenData &scrn) {
-  if (_vbuffer != NULL) {
-    RELEASE(_vbuffer, dxgsg9, "vertex buffer", RELEASE_ONCE);
-    _vbuffer = NULL;
-  }
+allocate_vbuffer(DXScreenData &scrn) {
 
-  PStatTimer timer(GraphicsStateGuardian::_create_vertex_buffer_pcollector);
+  int data_size;
+  HRESULT hr;
+  DWORD usage;
+  D3DPOOL pool;
 
-  HRESULT hr = scrn._d3d_device->CreateVertexBuffer
+  data_size = get_data()->get_data_size_bytes();
 
-//    (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY,
-//    _fvf, D3DPOOL_MANAGED, &_vbuffer, NULL);
-      (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
-      _fvf, D3DPOOL_DEFAULT, &_vbuffer, NULL);
+  _managed = scrn._managed_vertex_buffers;
+  if (_managed)
+  {
+    pool = D3DPOOL_MANAGED;
+    usage = D3DUSAGE_WRITEONLY;
+  }
+  else
+  {
+    pool = D3DPOOL_DEFAULT;
+    usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
+  }
 
+  hr = scrn._d3d_device->CreateVertexBuffer
+      (data_size, usage, _fvf, pool, &_vbuffer, NULL);
   if (FAILED(hr)) {
     dxgsg9_cat.warning()
       << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
     _vbuffer = NULL;
   } else {
-    if (dxgsg9_cat.is_debug()) {
+    if (DEBUG_VERTEX_BUFFER && dxgsg9_cat.is_debug()) {
       dxgsg9_cat.debug()
         << "created vertex buffer " << _vbuffer << ": "
         << get_data()->get_num_rows() << " vertices "
@@ -217,6 +255,57 @@ create_vbuffer(DXScreenData &scrn) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DXVertexBufferContext9::create_vbuffer
+//       Access: Public
+//  Description: Creates a new vertex buffer (but does not upload data
+//               to it).
+////////////////////////////////////////////////////////////////////
+void DXVertexBufferContext9::
+create_vbuffer(DXScreenData &scrn) {
+
+  free_vbuffer ( );
+
+  if (_lru_page)
+  {
+    _lru_page -> _m.lru -> remove_page (_lru_page);
+    _lru_page -> _m.lru -> free_page (_lru_page);
+    _lru_page = 0;
+  }
+
+  PStatTimer timer(GraphicsStateGuardian::_create_vertex_buffer_pcollector);
+
+  int data_size;
+
+  data_size = get_data()->get_data_size_bytes();
+
+  this -> allocate_vbuffer(scrn);
+
+  if (_vbuffer)
+  {
+    if (_managed == false)
+    {
+      Lru *lru;
+
+      lru = scrn._dxgsg9 -> _lru;
+      if (lru)
+      {
+        LruPage *lru_page;
+
+        lru_page = lru -> allocate_page (data_size);
+        if (lru_page)
+        {
+          lru_page -> _m.type = GPT_VertexBuffer;
+          lru_page -> _m.lru_page_type.pointer = this;
+
+          lru -> add_cached_page (LPP_New, lru_page);
+          _lru_page = lru_page;
+        }
+      }
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DXVertexBufferContext9::upload_data
 //       Access: Public
@@ -236,11 +325,17 @@ upload_data() {
       << " bytes into vertex buffer " << _vbuffer << "\n";
   }
 
+  HRESULT hr;
   BYTE *local_pointer;
 
-//  HRESULT hr = _vbuffer->Lock(0, data_size, (void **) &local_pointer, 0);
-  HRESULT hr = _vbuffer->Lock(0, data_size, (void **) &local_pointer, D3DLOCK_DISCARD);
-
+  if (_managed)
+  {
+    hr = _vbuffer->Lock(0, data_size, (void **) &local_pointer, 0);
+  }
+  else
+  {
+    hr = _vbuffer->Lock(0, data_size, (void **) &local_pointer, D3DLOCK_DISCARD);
+  }
   if (FAILED(hr)) {
     dxgsg9_cat.error()
       << "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);

+ 4 - 0
panda/src/dxgsg9/dxVertexBufferContext9.h

@@ -33,11 +33,15 @@ public:
   DXVertexBufferContext9(GeomVertexArrayData *data);
   virtual ~DXVertexBufferContext9();
 
+  void free_vbuffer(void);
+  void allocate_vbuffer(DXScreenData &scrn);
   void create_vbuffer(DXScreenData &scrn);
   void upload_data();
 
   IDirect3DVertexBuffer9 *_vbuffer;
   int _fvf;
+  int _managed;
+  LruPage *_lru_page;
 
 public:
   static TypeHandle get_class_type() {

+ 1 - 0
panda/src/dxgsg9/dxgsg9_composite1.cxx

@@ -8,4 +8,5 @@
 #include "wdxGraphicsPipe9.cxx"
 #include "wdxGraphicsWindow9.cxx"
 #include "dxGraphicsDevice9.cxx"
+#include "lru.cxx"
 

+ 10 - 0
panda/src/dxgsg9/dxgsg9base.h

@@ -187,6 +187,8 @@ typedef enum {
 #define RECT_XSIZE(REC) (REC.right-REC.left)
 #define RECT_YSIZE(REC) (REC.bottom-REC.top)
 
+class DXGraphicsStateGuardian9;
+
 struct DXScreenData {
   LPDIRECT3DDEVICE9 _d3d_device;
   IDirect3DSwapChain9 *_swap_chain;
@@ -209,6 +211,14 @@ struct DXScreenData {
   D3DPRESENT_PARAMETERS _presentation_params;  // not redundant with _display_mode since width/height must be 0 for windowed mode
   D3DADAPTER_IDENTIFIER9 _dx_device_id;
   D3DFORMAT _render_to_texture_d3d_format;
+
+  int _managed_textures;
+  int _managed_vertex_buffers;
+  int _managed_index_buffers;
+
+  bool _supports_dynamic_textures;
+
+  DXGraphicsStateGuardian9 *_dxgsg9;
 };
 
 

+ 1191 - 0
panda/src/dxgsg9/lru.cxx

@@ -0,0 +1,1191 @@
+
+//#include "stdafx.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+
+#include "lru.h"
+
+
+#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];
+
+  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)
+{
+  if (this)
+  {
+    int index;
+
+    memset (&this -> _m, 0, sizeof (LruVariables));
+
+    this -> _m.maximum_memory = maximum_memory;
+    this -> _m.maximum_pages = maximum_pages;
+    this -> _m.available_memory = maximum_memory;
+    this -> _m.current_frame_identifier = 1;
+    this -> _m.weight = 0.20f;
+
+    this -> set_maximum_frame_bandwidth_utilization (2000000.0f);
+
+    for (index = 0; index < MAXIMUM_LRU_PAGE_TYPES; index++)
+    {
+      this -> _m.page_in_function_array [index] = default_page_in_function;
+      this -> _m.page_out_function_array [index] = default_page_out_function;
+    }
+
+    if (maximum_pages > 0)
+    {
+      this -> _m.lru_page_pool = new LruPage * [maximum_pages];
+      this -> _m.lru_page_free_pool = new LruPage * [maximum_pages];
+      for (index = 0; index < maximum_pages; index++)
+      {
+        this -> _m.lru_page_pool [index] = new LruPage ( );
+      }
+    }
+  }
+}
+
+Lru::~Lru ( )
+{
+  int index;
+
+  for (index = 0; index < LPP_TotalPriorities; index++)
+  {
+    LruPage *lru_page;
+    LruPage *next_lru_page;
+
+    if (this -> _m.maximum_pages > 0)
+    {
+      if (this -> _m.lru_page_free_pool)
+      {
+        for (index = 0; index < this -> _m.maximum_pages; index++)
+        {
+          delete this -> _m.lru_page_pool [index];
+        }
+
+        delete this -> _m.lru_page_free_pool;
+      }
+      if (this -> _m.lru_page_pool)
+      {
+        delete this -> _m.lru_page_pool;
+      }
+    }
+    else
+    {
+      lru_page = this -> _m.lru_page_array [index];
+      while (lru_page)
+      {
+        next_lru_page = lru_page -> _m.next;
+
+        delete lru_page;
+
+        lru_page = next_lru_page;
+      }
+    }
+  }
+}
+
+LruPage::LruPage ( )
+{
+  if (this)
+  {
+    memset (&this -> _m, 0, sizeof (LruPageVariables));
+  }
+}
+
+LruPage::~LruPage ( )
+{
+
+}
+
+bool Lru::register_lru_page_type (int index, LruPageTypeFunction page_in_function, LruPageTypeFunction page_out_function)
+{
+  bool state;
+
+  state = false;
+  if (index >=0 && index < MAXIMUM_LRU_PAGE_TYPES)
+  {
+      this -> _m.page_in_function_array [index] = page_in_function;
+      this -> _m.page_out_function_array [index] = page_out_function;
+      state = true;
+  }
+
+  return state;
+}
+
+void LruPage::change_priority (int delta)
+{
+  this -> _m.priority_change += delta;
+}
+
+LruPage * Lru::allocate_page (int size)
+{
+  LruPage *lru_page;
+
+  lru_page = 0;
+  if (size <= this -> _m.maximum_memory)
+  {
+    if (this -> _m.maximum_pages)
+    {
+      if (this -> _m.total_lru_pages_in_free_pool > 0)
+      {
+        lru_page = this -> _m.lru_page_free_pool [this -> _m.total_lru_pages_in_free_pool - 1];
+        this -> _m.total_lru_pages_in_free_pool--;
+      }
+      else
+      {
+        if (this -> _m.total_lru_pages_in_pool < this -> _m.maximum_pages)
+        {
+          lru_page = this -> _m.lru_page_pool [this -> _m.total_lru_pages_in_pool];
+          this -> _m.total_lru_pages_in_pool++;
+        }
+        else
+        {
+
+// ERROR: could not allocate LruPage, maximum pages exceeded
+
+        }
+      }
+    }
+    else
+    {
+      lru_page = new LruPage;
+    }
+    if (lru_page)
+    {
+      lru_page -> _m.lru = this;
+      lru_page -> _m.size = size;
+      lru_page -> _m.first_frame_identifier = this -> _m.current_frame_identifier;
+      lru_page -> _m.last_frame_identifier = this -> _m.current_frame_identifier;
+
+      lru_page -> _m.identifier = this -> _m.identifier;
+
+      lru_page -> _m.average_frame_utilization = 1.0f;
+
+      this -> _m.total_pages++;
+      this -> _m.identifier++;
+    }
+  }
+  else
+  {
+
+// ERROR: requested page size is larger than maximum memory size
+
+  }
+
+  return lru_page;
+}
+
+void Lru::update_start_update_lru_page (LruPage *lru_page)
+{
+  if (lru_page)
+  {
+    if (this ->_m.start_update_lru_page == lru_page)
+    {
+      if (lru_page -> _m.next)
+      {
+        this -> _m.start_update_lru_page = lru_page -> _m.next;
+      }
+      else
+      {
+        if ((this -> _m.start_priority_index + 1) >= LPP_TotalPriorities)
+        {
+          this -> _m.start_priority_index = 0;
+        }
+        else
+        {
+          this -> _m.start_priority_index = this -> _m.start_priority_index + 1;
+        }
+
+        this -> _m.start_update_lru_page = 0;
+      }
+    }
+  }
+}
+
+void Lru::free_page (LruPage *lru_page)
+{
+  if (this -> _m.total_pages > 0)
+  {
+    if (lru_page)
+    {
+      this -> update_start_update_lru_page (lru_page);
+
+      this -> _m.available_memory += lru_page -> _m.size;
+
+      if (this -> _m.maximum_pages)
+      {
+        this -> _m.lru_page_free_pool [this -> _m.total_lru_pages_in_free_pool] = lru_page;
+        this -> _m.total_lru_pages_in_free_pool++;
+      }
+      else
+      {
+        delete lru_page;
+      }
+
+      this -> _m.total_pages--;
+    }
+  }
+  else
+  {
+
+// ERROR: tried to free a page when 0 pages allocated
+
+  }
+}
+
+void Lru::add_page (LruPagePriority priority, LruPage *lru_page)
+{
+  if (lru_page)
+  {
+    LruPage *first_lru_page;
+
+    lru_page -> _m.priority = priority;
+
+    first_lru_page = this -> _m.lru_page_array [lru_page -> _m.priority];
+    if (first_lru_page)
+    {
+      first_lru_page -> _m.previous = lru_page;
+      lru_page -> _m.next = first_lru_page;
+    }
+
+    this -> _m.lru_page_array [lru_page -> _m.priority] = lru_page;
+  }
+}
+
+void Lru::add_cached_page (LruPagePriority priority, LruPage *lru_page)
+{
+  if (lru_page)
+  {
+    lru_page -> _m.in_cache = true;
+
+    if (lru_page -> _m.size > this -> _m.available_memory)
+    {
+        int memory_required;
+
+        memory_required = lru_page -> _m.size - this -> _m.available_memory;
+
+        // unload page(s)
+        this -> page_out_lru (memory_required);
+    }
+
+    this -> _m.available_memory -= lru_page -> _m.size;
+
+    this -> add_page (priority, lru_page);
+  }
+}
+
+void Lru::remove_page (LruPage *lru_page)
+{
+  if (lru_page)
+  {
+    if (this -> _m.total_pages > 0)
+    {
+      if (lru_page)
+      {
+        this -> update_start_update_lru_page (lru_page);
+
+        if (lru_page -> _m.previous)
+        {
+          lru_page -> _m.previous -> _m.next = lru_page -> _m.next;
+          if (lru_page -> _m.next)
+          {
+            lru_page -> _m.next -> _m.previous = lru_page -> _m.previous;
+          }
+        }
+        else
+        {
+          this -> _m.lru_page_array [lru_page -> _m.priority] = lru_page -> _m.next;
+          if (lru_page -> _m.next)
+          {
+            lru_page -> _m.next -> _m.previous = 0;
+          }
+        }
+
+        lru_page -> _m.next = 0;
+        lru_page -> _m.previous = 0;
+      }
+    }
+    else
+    {
+
+// ERROR: tried to remove a page when 0 pages are allocated
+
+    }
+  }
+}
+
+void Lru::lock_page (LruPage *lru_page)
+{
+  lru_page -> _m.lock = true;
+}
+
+void Lru::unlock_page (LruPage *lru_page)
+{
+  lru_page -> _m.lock = false;
+}
+
+void Lru::access_page (LruPage *lru_page)
+{
+  if (lru_page)
+  {
+    if (lru_page -> _m.current_frame_identifier == this -> _m.current_frame_identifier)
+    {
+      lru_page -> _m.current_frame_usage++;
+    }
+    else
+    {
+      // first update this frame
+      lru_page -> _m.last_frame_identifier = lru_page -> _m.current_frame_identifier;
+      lru_page -> _m.current_frame_identifier = this -> _m.current_frame_identifier;
+      lru_page -> _m.last_frame_usage = lru_page -> _m.current_frame_usage;
+      lru_page -> _m.current_frame_usage = 1;
+      lru_page -> _m.total_frame_page_faults = 0;
+    }
+
+    // check if the page is out
+    if (lru_page -> _m.in_cache == false)
+    {
+      bool state;
+
+      state = true;
+
+      // check memory usage
+      if (lru_page -> _m.size > this -> _m.available_memory)
+      {
+          int memory_required;
+
+          memory_required = lru_page -> _m.size - this -> _m.available_memory;
+
+          // unload page(s)
+          state = this -> page_out_lru (memory_required);
+      }
+
+      // load the page in
+      if (state)
+      {
+        // PAGE IN CALLBACK
+        if (this -> _m.page_in_function_array [lru_page -> _m.type] (lru_page))
+        {
+          this -> _m.available_memory -= lru_page -> _m.size;
+          lru_page -> _m.in_cache = true;
+
+          // CHANGE THE PAGE PRIORITY FROM LPP_PageOut TO LPP_New
+          this -> remove_page (lru_page);
+          this -> add_page (LPP_New, lru_page);
+
+          this -> _m.total_lifetime_page_ins++;
+        }
+      }
+
+      lru_page -> _m.total_frame_page_faults++;
+      lru_page -> _m.total_page_faults++;
+    }
+
+    lru_page -> _m.total_usage++;
+    lru_page -> _m.update_total_usage++;
+
+    this -> _m.total_page_access++;
+  }
+}
+
+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;
+}
+
+void Lru::begin_frame ( )
+{
+  this -> _m.current_frame_identifier++;
+
+  this -> _m.total_page_ins_last_frame = this -> _m.total_page_ins;
+  this -> _m.total_page_outs = this -> _m.total_page_outs;
+
+  this -> _m.total_page_ins = 0;
+  this -> _m.total_page_outs = 0;
+}
+
+void Lru::update_page_priorities (void)
+{
+  int index;
+  LruPage *lru_page;
+
+  for (index = 0; index < this -> _m.total_lru_page_priority_changes; index++)
+  {
+    int priority;
+
+    lru_page = this -> _m.lru_page_priority_change_array [index];
+
+    this -> remove_page (lru_page);
+
+    priority = ((int) lru_page -> _m.priority + lru_page -> _m.priority_change);
+    if (priority < 0)
+    {
+      priority = 0;
+    }
+    if (priority >= LPP_TotalPriorities)
+    {
+      priority = LPP_TotalPriorities - 1;
+    }
+
+    this -> add_page ((LruPagePriority) priority, lru_page);
+    lru_page -> _m.priority_change = 0;
+  }
+  this -> _m.total_lru_page_priority_changes = 0;
+}
+
+void Lru::update_lru_page (LruPage *lru_page)
+{
+  if (false)
+  {
+    char string [256];
+
+    sprintf (string, "  UPDATE %d\n", lru_page -> _m.identifier);
+    OutputDebugString (string);
+  }
+
+  if (lru_page -> _m.lock == false && lru_page -> _m.in_cache)
+  {
+    int delta_priority;
+
+    delta_priority = 0;
+//    if (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 >= 1)
+      {
+        if (lru_page -> _m.update_frame_identifier)
+        {
+          int target_priority;
+          int integer_update_frames;
+          float update_frames;
+          float one_over_update_frames;
+          float update_average_frame_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);
+
+            target_priority = lru_page -> _m.priority;
+            if (lru_page -> _m.average_frame_utilization >= 1.0f)
+            {
+              int integer_average_frame_utilization;
+
+              integer_average_frame_utilization = (int) ((lru_page -> _m.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) (lru_page -> _m.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;
+            }
+
+            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 (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++;
+      }
+    }
+  }
+}
+
+void Lru::update_lru_page_old (LruPage *lru_page)
+{
+  if (false)
+  {
+    char string [256];
+
+    sprintf (string, "  UPDATE %d\n", lru_page -> _m.identifier);
+    OutputDebugString (string);
+  }
+
+  if (lru_page -> _m.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++;
+      }
+    }
+  }
+}
+
+void Lru::update_entire_lru ( )
+{
+  if (this -> _m.total_pages > 0)
+  {
+    int index;
+    LruPage *lru_page;
+
+    for (index = 0; index < LPP_TotalPriorities; index++)
+    {
+      LruPage *next_lru_page;
+
+      lru_page = this -> _m.lru_page_array [index];
+      while (lru_page)
+      {
+        next_lru_page = lru_page -> _m.next;
+
+        this -> update_lru_page (lru_page);
+
+        lru_page = next_lru_page;
+      }
+    }
+
+    this -> update_page_priorities ( );
+  }
+}
+
+void Lru::partial_lru_update_old (int approximate_maximum_updates)
+{
+  int total_page_updates;
+
+  total_page_updates = this -> _m.update_overflow;
+  if (total_page_updates > 0)
+  {
+    this -> _m.update_overflow -= approximate_maximum_updates;
+    if (this -> _m.update_overflow < 0)
+    {
+      this -> _m.update_overflow = 0;
+    }
+  }
+
+  if (this -> _m.total_pages > 0 && total_page_updates < approximate_maximum_updates)
+  {
+    int index;
+    int start_priority;
+    LruPage *lru_page;
+
+    start_priority = this -> _m.start_priority_index;
+
+    {
+      for (index = start_priority; index < LPP_TotalPriorities; index++)
+      {
+        LruPage *next_lru_page;
+
+        lru_page = this -> _m.lru_page_array [index];
+        while (lru_page)
+        {
+          next_lru_page = lru_page -> _m.next;
+
+          this -> update_lru_page (lru_page);
+
+          total_page_updates++;
+
+          lru_page = next_lru_page;
+        }
+
+        if (total_page_updates >= approximate_maximum_updates)
+        {
+          this -> _m.update_overflow = total_page_updates - approximate_maximum_updates;
+          if ((index + 1) < LPP_TotalPriorities)
+          {
+            this -> _m.start_priority_index = index + 1;
+          }
+          break;
+        }
+      }
+    }
+
+    if (total_page_updates < approximate_maximum_updates)
+    {
+      for (index = 0; index < start_priority; index++)
+      {
+        LruPage *next_lru_page;
+
+        lru_page = this -> _m.lru_page_array [index];
+        while (lru_page)
+        {
+          next_lru_page = lru_page -> _m.next;
+
+          this -> update_lru_page (lru_page);
+
+          total_page_updates++;
+
+          lru_page = next_lru_page;
+        }
+
+        if (total_page_updates >= approximate_maximum_updates)
+        {
+          this -> _m.update_overflow = total_page_updates - approximate_maximum_updates;
+          if ((index + 1) < LPP_TotalPriorities)
+          {
+            this -> _m.start_priority_index = index + 1;
+          }
+          break;
+        }
+      }
+    }
+
+    if (index >= LPP_TotalPriorities)
+    {
+      this -> _m.start_priority_index = 0;
+    }
+
+    this -> update_page_priorities ( );
+  }
+}
+
+void Lru::partial_lru_update (int maximum_updates)
+{
+  int total_page_updates;
+
+  total_page_updates = 0;
+  if (this -> _m.total_pages > 0)
+  {
+    int index;
+    int start_priority;
+    LruPage *lru_page;
+
+    start_priority = this -> _m.start_priority_index;
+
+    {
+      for (index = start_priority; index < LPP_TotalPriorities; index++)
+      {
+        LruPage *next_lru_page;
+
+        if (index == start_priority)
+        {
+          if (this -> _m.start_update_lru_page)
+          {
+            lru_page = this -> _m.start_update_lru_page;
+          }
+          else
+          {
+            lru_page = this -> _m.lru_page_array [index];
+          }
+        }
+        else
+        {
+          lru_page = this -> _m.lru_page_array [index];
+        }
+        while (lru_page)
+        {
+          next_lru_page = lru_page -> _m.next;
+
+          this -> update_lru_page (lru_page);
+
+          total_page_updates++;
+          if (total_page_updates >= maximum_updates)
+          {
+            if (next_lru_page)
+            {
+              this -> _m.start_priority_index = index;
+              this -> _m.start_update_lru_page = next_lru_page;
+            }
+            else
+            {
+              if ((index + 1) >= LPP_TotalPriorities)
+              {
+                this -> _m.start_priority_index = 0;
+              }
+              else
+              {
+                this -> _m.start_priority_index = index + 1;
+              }
+
+              this -> _m.start_update_lru_page = 0;
+            }
+
+            break;
+          }
+
+          lru_page = next_lru_page;
+        }
+
+        if (total_page_updates >= maximum_updates)
+        {
+          break;
+        }
+      }
+    }
+
+    if (total_page_updates < maximum_updates)
+    {
+      for (index = 0; index < start_priority; index++)
+      {
+        LruPage *next_lru_page;
+
+        lru_page = this -> _m.lru_page_array [index];
+        while (lru_page)
+        {
+          next_lru_page = lru_page -> _m.next;
+
+          this -> update_lru_page (lru_page);
+
+          total_page_updates++;
+          if (total_page_updates >= maximum_updates)
+          {
+            if (next_lru_page)
+            {
+              this -> _m.start_priority_index = index;
+              this -> _m.start_update_lru_page = next_lru_page;
+            }
+            else
+            {
+              if ((index + 1) >= LPP_TotalPriorities)
+              {
+                this -> _m.start_priority_index = 0;
+              }
+              else
+              {
+                this -> _m.start_priority_index = index + 1;
+              }
+
+              this -> _m.start_update_lru_page = 0;
+            }
+
+            break;
+          }
+
+          lru_page = next_lru_page;
+        }
+
+        if (total_page_updates >= maximum_updates)
+        {
+          break;
+        }
+      }
+    }
+
+    this -> update_page_priorities ( );
+  }
+}
+
+void Lru::unlock_all_pages (void)
+{
+  if (this -> _m.total_pages > 0)
+  {
+    int index;
+
+    for (index = 0; index < LPP_TotalPriorities; index++)
+    {
+      LruPage *lru_page;
+      LruPage *next_lru_page;
+
+      lru_page = this -> _m.lru_page_array [index];
+      while (lru_page)
+      {
+        next_lru_page = lru_page -> _m.next;
+
+        lru_page -> _m.lock = false;
+
+        lru_page = next_lru_page;
+      }
+    }
+  }
+}
+
+bool Lru::page_out_lru (int memory_required)
+{
+  bool state;
+  int attempts;
+
+  state = false;
+  attempts = 0;
+  if (this -> _m.total_pages > 0)
+  {
+    do
+    {
+      int index;
+
+      // page out lower priority pages first
+      for (index = LPP_PageOut - 1; index >= 0; index--)
+      {
+        LruPage *lru_page;
+        LruPage *next_lru_page;
+
+        lru_page = this -> _m.lru_page_array [index];
+        while (lru_page)
+        {
+          next_lru_page = lru_page -> _m.next;
+
+          if (lru_page -> _m.lock == false && lru_page -> _m.in_cache)
+          {
+            memory_required -= lru_page -> _m.size;
+            this -> _m.available_memory += lru_page -> _m.size;
+            lru_page -> _m.in_cache = false;
+
+            // PAGE OUT CALLBACK
+            {
+              this -> _m.page_out_function_array [lru_page -> _m.type] (lru_page);
+
+              this -> _m.total_lifetime_page_outs++;
+            }
+
+            // MOVE THE PAGE TO THE LPP_PageOut PRIORITY
+            this -> remove_page (lru_page);
+            this -> add_page (LPP_PageOut, lru_page);
+
+            if (memory_required <= 0)
+            {
+              break;
+            }
+          }
+
+          lru_page = next_lru_page;
+        }
+
+        if (memory_required <= 0)
+        {
+          break;
+        }
+      }
+
+      if (memory_required > 0)
+      {
+        // WARNING: pages could not be freed, all pages unlocked
+        this -> unlock_all_pages ( );
+        state = false;
+      }
+      else
+      {
+        state = true;
+      }
+
+      attempts++;
+    }
+    while (state == false && attempts < 2);
+  }
+
+  return state;
+}
+
+void Lru::count_priority_level_pages (void)
+{
+  int index;
+
+  for (index = 0; index < LPP_TotalPriorities; index++)
+  {
+    int total_pages;
+    LruPage *lru_page;
+    LruPage *next_lru_page;
+
+    total_pages = 0;
+    lru_page = this -> _m.lru_page_array [index];
+    while (lru_page)
+    {
+      next_lru_page = lru_page -> _m.next;
+
+      total_pages++;
+
+      lru_page = next_lru_page;
+    }
+
+    this -> _m.lru_page_count_array [index] = total_pages;
+  }
+}
+
+void test_ema (void)
+{
+  int index;
+  float usage;
+  float weight;
+  float average;
+
+  weight = 0.2f;
+  average = 1.0f;
+  for (index = 0; index < 50; index++)
+  {
+    if (index < 25)
+    {
+      usage = (float) (index & 0x01);
+    }
+    else
+    {
+      usage = 0.0f;
+    }
+    average = calculate_exponential_moving_average (usage, weight, average);
+
+    char string [256];
+    sprintf (string, "%d  %f\n", index, average);
+    OutputDebugString (string);
+  }
+}
+
+void test_lru (void)
+{
+  int maximum_memory;
+  int maximum_pages;
+  Lru *lru;
+
+  test_ema ( );
+
+  maximum_memory = 3000000;
+  maximum_pages = 1000;
+  lru = new Lru (maximum_memory, maximum_pages);
+  if (lru)
+  {
+    lru -> _m.minimum_memory = 1000000;
+
+    LruPage *lru_page_0;
+    LruPage *lru_page_1;
+    LruPage *lru_page_2;
+    LruPage *lru_page_3;
+    LruPage *lru_page_4;
+    LruPage *lru_page_5;
+
+    lru_page_0 = lru -> allocate_page (1000000);
+    if (lru_page_0)
+    {
+      lru -> add_page (LPP_PageOut, lru_page_0);
+    }
+
+    lru_page_1 = lru -> allocate_page (1000000);
+    if (lru_page_1)
+    {
+      lru -> add_page (LPP_PageOut, lru_page_1);
+    }
+
+    lru_page_2 = lru -> allocate_page (1000000);
+    if (lru_page_2)
+    {
+      lru -> add_page (LPP_PageOut, lru_page_2);
+    }
+
+    lru_page_3 = lru -> allocate_page (1000000);
+    if (lru_page_3)
+    {
+      lru -> add_page (LPP_PageOut, lru_page_3);
+    }
+
+    lru_page_4 = lru -> allocate_page (1000000);
+    if (lru_page_4)
+    {
+      lru -> add_page (LPP_PageOut, lru_page_4);
+    }
+
+    lru_page_5 = lru -> allocate_page (1000000);
+    if (lru_page_5)
+    {
+      lru -> add_page (LPP_PageOut, lru_page_5);
+    }
+
+    int index;
+    int total_frames;
+
+    total_frames = 300;
+    for (index = 0; index < total_frames; index++)
+    {
+      char string [256];
+
+      sprintf (string, "FRAME %d\n", index);
+      OutputDebugString (string);
+
+      lru -> begin_frame ( );
+
+      if (index <= 5)
+      {
+        lru -> access_page (lru_page_0);
+      }
+
+      lru -> access_page (lru_page_1);
+      lru -> access_page (lru_page_1);
+
+      if (index & 0x01)
+      {
+        lru -> access_page (lru_page_2);
+      }
+
+      if ((index % 10) == 0)
+      {
+        lru -> access_page (lru_page_3);
+      }
+
+      if (index >= 100)
+      {
+        lru -> access_page (lru_page_4);
+      }
+
+      if (index >= 200)
+      {
+        lru -> access_page (lru_page_5);
+      }
+
+      if (false)
+      {
+        lru -> update_entire_lru ( );
+      }
+      else
+      {
+        int approximate_maximum_updates;
+
+        approximate_maximum_updates = 3;
+        lru -> partial_lru_update (approximate_maximum_updates);
+      }
+    }
+
+    if (true)
+    {
+      lru -> remove_page (lru_page_2);
+      lru -> free_page (lru_page_2);
+
+      lru -> remove_page (lru_page_3);
+      lru -> free_page (lru_page_3);
+
+      lru -> remove_page (lru_page_1);
+      lru -> free_page (lru_page_1);
+    }
+
+    delete lru;
+  }
+}

+ 206 - 0
panda/src/dxgsg9/lru.h

@@ -0,0 +1,206 @@
+
+#ifndef LRU_H
+#define LRU_H
+
+
+#define MAXIMUM_LRU_PAGE_TYPES 8
+#define FRAME_MAXIMUM_PRIORITY_CHANGES 256
+
+
+class Lru;
+class LruPage;
+
+enum LruPagePriority
+{
+  LPP_Highest = 0,
+  LPP_High = 10,
+  LPP_New = 20,
+  LPP_Normal = 25,
+  LPP_Intermediate = 30,
+  LPP_Low = 40,
+  LPP_TotalPriorities = 50,
+
+  LPP_PageOut = LPP_TotalPriorities - 1
+};
+
+typedef union _LruPageType
+{
+  void *pointer;
+
+}
+LruPageType;
+
+typedef bool (*LruPageTypeFunction) (LruPage *lru_page);
+
+class LruPage
+{
+
+protected:
+
+  LruPage ( );
+  ~LruPage ( );
+  void change_priority (int delta);
+
+public:
+
+  typedef struct _LruPageVariables
+  {
+    LruPageType lru_page_type;
+
+    int size;
+    LruPagePriority priority;
+    int priority_change;
+
+    struct
+    {
+      unsigned int type : 4;
+      unsigned int lock : 1;
+      unsigned int in_cache : 1;
+      unsigned int in_memory : 1;
+      unsigned int on_disk : 1;
+    };
+
+    int first_frame_identifier;   // creation time
+    int last_frame_identifier;    // previous time page was used
+    int current_frame_identifier;
+    int update_frame_identifier;
+
+    int current_frame_usage;
+    int last_frame_usage;
+
+    int total_frame_page_faults;
+    int total_page_faults;
+
+    int total_usage;
+    int update_total_usage;
+
+    int identifier; // this is also the number of pages created during the lifetime of the LRU
+
+    float average_frame_utilization;
+
+    LruPage *previous;
+    LruPage *next;
+    Lru *lru;
+  }
+  LruPageVariables;
+
+  LruPageVariables _m;
+
+  friend class Lru;
+};
+
+
+class Lru
+{
+public:
+
+  Lru (int maximum_memory, int maximum_pages);
+  ~Lru ( );
+
+  bool register_lru_page_type (int index, LruPageTypeFunction page_in_function, LruPageTypeFunction page_out_function);
+
+  LruPage *allocate_page (int size);
+  void update_start_update_lru_page (LruPage *lru_page);
+
+  void free_page (LruPage *lru_page);
+
+  void add_page (LruPagePriority priority, LruPage *lru_page);
+  void add_cached_page (LruPagePriority priority, LruPage *lru_page);
+  void remove_page (LruPage *lru_page);
+
+  void lock_page (LruPage *lru_page);
+  void unlock_page (LruPage *lru_page);
+
+  void access_page (LruPage *lru_page);
+
+  void set_maximum_frame_bandwidth_utilization (float maximum_frame_bandwidth_utilization);
+
+  void begin_frame ( );
+
+  void update_entire_lru ( );
+  void partial_lru_update (int maximum_updates);
+  void partial_lru_update_old (int approximate_maximum_updates);
+
+  // set maximum number of page updates per frame
+  // pause/resume updates/current_frame_identifier
+
+  void unlock_all_pages (void);
+
+  void count_priority_level_pages (void);
+
+private:
+  bool page_out_lru (int memory_required);
+  void update_page_priorities (void);
+  void update_lru_page (LruPage *lru_page);
+  void update_lru_page_old (LruPage *lru_page);
+
+public:
+  typedef struct _LruVariables
+  {
+    // LruPagePriority lists
+    LruPage *lru_page_array [LPP_TotalPriorities];
+
+    int total_pages;
+    int available_memory;
+    int current_frame_identifier;
+
+    int maximum_memory;
+    int minimum_memory;    // target amount of memory to keep free if possible
+
+    int total_lifetime_page_ins;
+    int total_lifetime_page_outs;
+
+    int total_page_ins_last_frame;
+    int total_page_outs_last_frame;
+
+    int total_page_ins;
+    int total_page_outs;
+
+    int total_page_access;
+
+    int minimum_page_out_frames;           // number of frames required before page out
+    int maximum_page_updates_per_frame;    // unused pages
+
+    int start_priority_index;
+    int update_overflow;
+    LruPage *start_update_lru_page;
+
+    int identifier;
+
+    float weight;
+    float maximum_frame_bandwidth_utilization;
+
+    float frame_bandwidth_factor;
+
+    LruPageTypeFunction page_in_function_array [MAXIMUM_LRU_PAGE_TYPES];
+    LruPageTypeFunction page_out_function_array [MAXIMUM_LRU_PAGE_TYPES];
+
+    int total_lru_page_priority_changes;
+    LruPage *lru_page_priority_change_array [FRAME_MAXIMUM_PRIORITY_CHANGES];
+
+    void *context;
+
+    int lru_page_count_array [LPP_TotalPriorities];
+
+    int maximum_pages;
+    int total_lru_pages_in_pool;
+    int total_lru_pages_in_free_pool;
+    LruPage **lru_page_pool;
+    LruPage **lru_page_free_pool;
+  }
+  LruVariables;
+
+  LruVariables _m;
+
+  friend class LruPage;
+};
+
+float calculate_exponential_moving_average (float value, float weight, float average);
+bool default_page_in_function (LruPage *lru_page);
+bool default_page_out_function (LruPage *lru_page);
+
+void test_ema (void);
+void test_lru (void);
+
+
+#endif