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