From 2c28f1a62f6e1dd3bc4965ca7296e2d95b1bd576 Mon Sep 17 00:00:00 2001 From: aignacio_sf <> Date: Fri, 23 Dec 2005 01:49:19 +0000 Subject: [PATCH] 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. --- panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx | 227 +++- panda/src/dxgsg9/dxGraphicsStateGuardian9.h | 23 +- panda/src/dxgsg9/dxIndexBufferContext9.cxx | 146 +- panda/src/dxgsg9/dxIndexBufferContext9.h | 4 + panda/src/dxgsg9/dxTextureContext9.cxx | 29 +- panda/src/dxgsg9/dxTextureContext9.h | 1 + panda/src/dxgsg9/dxVertexBufferContext9.cxx | 141 +- panda/src/dxgsg9/dxVertexBufferContext9.h | 4 + panda/src/dxgsg9/dxgsg9_composite1.cxx | 1 + panda/src/dxgsg9/dxgsg9base.h | 10 + panda/src/dxgsg9/lru.cxx | 1191 +++++++++++++++++ panda/src/dxgsg9/lru.h | 206 +++ 12 files changed, 1918 insertions(+), 65 deletions(-) create mode 100644 panda/src/dxgsg9/lru.cxx create mode 100644 panda/src/dxgsg9/lru.h diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index f5b59d89fa..08c558dbc5 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -64,6 +64,11 @@ #include #include + +#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); } } - diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h index fb8308c5ec..b665838a48 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h +++ b/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" diff --git a/panda/src/dxgsg9/dxIndexBufferContext9.cxx b/panda/src/dxgsg9/dxIndexBufferContext9.cxx index 07821cdf3c..3910ffbd68 100755 --- a/panda/src/dxgsg9/dxIndexBufferContext9.cxx +++ b/panda/src/dxgsg9/dxIndexBufferContext9.cxx @@ -23,6 +23,8 @@ #include "pStatTimer.h" #include +#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,17 +48,90 @@ 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::allocate_ibuffer +// Access: Public +// Description: Allocates index buffer memory. +//////////////////////////////////////////////////////////////////// +void DXIndexBufferContext9:: +allocate_ibuffer(DXScreenData &scrn) { + + D3DFORMAT index_type = + DXGraphicsStateGuardian9::get_index_type(get_data()->get_index_type()); + + int data_size; + DWORD usage; + D3DPOOL pool; + + 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 (DEBUG_INDEX_BUFFER && dxgsg9_cat.is_debug()) { + dxgsg9_cat.debug() + << "creating index buffer " << _ibuffer << ": " + << get_data()->get_num_vertices() << " indices (" + << get_data()->get_vertices()->get_array_format()->get_column(0)->get_numeric_type() + << ")\n"; + } + } +} + //////////////////////////////////////////////////////////////////// // Function: DXIndexBufferContext9::create_ibuffer // Access: Public @@ -63,34 +140,45 @@ DXIndexBufferContext9:: //////////////////////////////////////////////////////////////////// void DXIndexBufferContext9:: create_ibuffer(DXScreenData &scrn) { - if (_ibuffer != NULL) { - RELEASE(_ibuffer, dxgsg9, "index buffer", RELEASE_ONCE); - _ibuffer = NULL; + + 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); - D3DFORMAT index_type = - DXGraphicsStateGuardian9::get_index_type(get_data()->get_index_type()); + int data_size; - HRESULT hr = scrn._d3d_device->CreateIndexBuffer + data_size = get_data()->get_data_size_bytes(); -// (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); + this -> allocate_ibuffer(scrn); - if (FAILED(hr)) { - dxgsg9_cat.warning() - << "CreateIndexBuffer failed" << D3DERRORSTRING(hr); - _ibuffer = NULL; - } else { - if (dxgsg9_cat.is_debug()) { - dxgsg9_cat.debug() - << "creating index buffer " << _ibuffer << ": " - << get_data()->get_num_vertices() << " indices (" - << get_data()->get_vertices()->get_array_format()->get_column(0)->get_numeric_type() - << ")\n"; + 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; + } + } } } } @@ -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); diff --git a/panda/src/dxgsg9/dxIndexBufferContext9.h b/panda/src/dxgsg9/dxIndexBufferContext9.h index c085fb25e2..3655e3fb1d 100755 --- a/panda/src/dxgsg9/dxIndexBufferContext9.h +++ b/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() { diff --git a/panda/src/dxgsg9/dxTextureContext9.cxx b/panda/src/dxgsg9/dxTextureContext9.cxx index aa0ac8fd25..b08f3835d6 100755 --- a/panda/src/dxgsg9/dxTextureContext9.cxx +++ b/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()) { diff --git a/panda/src/dxgsg9/dxTextureContext9.h b/panda/src/dxgsg9/dxTextureContext9.h index 79a02aead5..154d24200c 100755 --- a/panda/src/dxgsg9/dxTextureContext9.h +++ b/panda/src/dxgsg9/dxTextureContext9.h @@ -59,6 +59,7 @@ private: IDirect3DVolumeTexture9 *_d3d_volume_texture; IDirect3DCubeTexture9 *_d3d_cube_texture; + int _managed; bool _has_mipmaps; public: diff --git a/panda/src/dxgsg9/dxVertexBufferContext9.cxx b/panda/src/dxgsg9/dxVertexBufferContext9.cxx index 95e7d47494..2eec27cf64 100755 --- a/panda/src/dxgsg9/dxVertexBufferContext9.cxx +++ b/panda/src/dxgsg9/dxVertexBufferContext9.cxx @@ -25,6 +25,8 @@ #include "config_dxgsg9.h" #include +#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,17 +174,87 @@ 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::allocate_vbuffer +// Access: Public +// Description: Allocates vertex buffer memory. +//////////////////////////////////////////////////////////////////// +void DXVertexBufferContext9:: +allocate_vbuffer(DXScreenData &scrn) { + + int data_size; + HRESULT hr; + DWORD usage; + D3DPOOL pool; + + data_size = get_data()->get_data_size_bytes(); + + _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 (DEBUG_VERTEX_BUFFER && dxgsg9_cat.is_debug()) { + dxgsg9_cat.debug() + << "created vertex buffer " << _vbuffer << ": " + << get_data()->get_num_rows() << " vertices " + << *get_data()->get_array_format() << "\n"; + } + } +} + //////////////////////////////////////////////////////////////////// // Function: DXVertexBufferContext9::create_vbuffer // Access: Public @@ -189,30 +263,45 @@ DXVertexBufferContext9:: //////////////////////////////////////////////////////////////////// void DXVertexBufferContext9:: create_vbuffer(DXScreenData &scrn) { - if (_vbuffer != NULL) { - RELEASE(_vbuffer, dxgsg9, "vertex buffer", RELEASE_ONCE); - _vbuffer = NULL; + + 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); - HRESULT hr = scrn._d3d_device->CreateVertexBuffer + int data_size; -// (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); + data_size = get_data()->get_data_size_bytes(); - if (FAILED(hr)) { - dxgsg9_cat.warning() - << "CreateVertexBuffer failed" << D3DERRORSTRING(hr); - _vbuffer = NULL; - } else { - if (dxgsg9_cat.is_debug()) { - dxgsg9_cat.debug() - << "created vertex buffer " << _vbuffer << ": " - << get_data()->get_num_rows() << " vertices " - << *get_data()->get_array_format() << "\n"; + 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; + } + } } } } @@ -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); diff --git a/panda/src/dxgsg9/dxVertexBufferContext9.h b/panda/src/dxgsg9/dxVertexBufferContext9.h index 94080a5587..eb717dae7b 100755 --- a/panda/src/dxgsg9/dxVertexBufferContext9.h +++ b/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() { diff --git a/panda/src/dxgsg9/dxgsg9_composite1.cxx b/panda/src/dxgsg9/dxgsg9_composite1.cxx index f1b5623ee2..de97ce06c5 100755 --- a/panda/src/dxgsg9/dxgsg9_composite1.cxx +++ b/panda/src/dxgsg9/dxgsg9_composite1.cxx @@ -8,4 +8,5 @@ #include "wdxGraphicsPipe9.cxx" #include "wdxGraphicsWindow9.cxx" #include "dxGraphicsDevice9.cxx" +#include "lru.cxx" diff --git a/panda/src/dxgsg9/dxgsg9base.h b/panda/src/dxgsg9/dxgsg9base.h index 667348f6f3..f21d067a29 100755 --- a/panda/src/dxgsg9/dxgsg9base.h +++ b/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; }; diff --git a/panda/src/dxgsg9/lru.cxx b/panda/src/dxgsg9/lru.cxx new file mode 100644 index 0000000000..356ec451c9 --- /dev/null +++ b/panda/src/dxgsg9/lru.cxx @@ -0,0 +1,1191 @@ + +//#include "stdafx.h" + +#include +#include +#include + +#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; + } +} diff --git a/panda/src/dxgsg9/lru.h b/panda/src/dxgsg9/lru.h new file mode 100644 index 0000000000..496e8172db --- /dev/null +++ b/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