diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl index 36080f71985..b80863fe9a1 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_Generals.inl @@ -640,7 +640,6 @@ static PoolSizeRec PoolSizes[] = { "Render2DClass", 64, 32 }, { "SurfaceClass", 128, 32 }, { "FontCharsClassCharDataStruct", 1024, 32 }, - { "FontCharsBuffer", 16, 4 }, { "FVFInfoClass", 128, 32 }, { "TerrainTracksRenderObjClass", 128, 32 }, { "DynamicIBAccessClass", 32, 32 }, diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index f33873f3fa1..f9e446fc6aa 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -636,7 +636,6 @@ static PoolSizeRec PoolSizes[] = { "Render2DClass", 64, 32 }, { "SurfaceClass", 128, 32 }, { "FontCharsClassCharDataStruct", 1024, 32 }, - { "FontCharsBuffer", 16, 4 }, { "FVFInfoClass", 152, 64 }, { "TerrainTracksRenderObjClass", 128, 32 }, { "DynamicIBAccessClass", 32, 32 }, diff --git a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp index 29d1a22a82d..6e68c6fe341 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp @@ -617,21 +617,55 @@ Render2DSentenceClass::Allocate_New_Surface (const WCHAR *text, bool justCalcExt } // - // Calculate the width of the text + // Calculate the width of the text and of its widest glyph // int text_width = 0; + int max_char_width = 0; for (int index = 0; text[index] != 0; index ++) { text_width += Font->Get_Char_Spacing (text[index]); + const int char_width = Font->Get_Char_Width (text[index]); + max_char_width = max (max_char_width, char_width); } int char_height = Font->Get_Char_Height (); + // + // TheSuperHackers @fix The texture must hold at least one row of glyphs and the widest glyph + // of this text, otherwise the blit runs off the surface. The widest glyph the font can produce + // is no use here, because fonts such as Arial report one several times wider than their text. + // Fonts that fit search 64 to 256 px, which bounds the texture that every display string + // allocates for itself. Larger fonts use the smallest texture that fits. + // + constexpr const int TextureSizeMinPow2 = 6; // 64 px, smallest texture + constexpr const int TextureSizeSearchMaxPow2 = 8; // 256 px, largest texture searched for fonts that fit + constexpr const int TextureSizeMaxPow2 = 11; // 2048 px, largest texture for any font + + const int min_extent = max (char_height + 1, max_char_width + TEXTURE_OFFSET + 1); + int min_pow2 = TextureSizeMinPow2; + while (min_pow2 < TextureSizeMaxPow2 && (1 << min_pow2) < min_extent) { + min_pow2 ++; + } + + int max_pow2 = max (TextureSizeSearchMaxPow2, min_pow2); + if (max_pow2 > TextureSizeSearchMaxPow2) { + // + // The font does not fit the search range, so use the smallest texture that fits, limited to + // the largest texture the device supports. + // + const D3DCAPS8 &dx8caps = DX8Wrapper::Get_Current_Caps ()->Get_DX8_Caps (); + const int max_device_size = (int)min (dx8caps.MaxTextureWidth, dx8caps.MaxTextureHeight); + while (max_pow2 > TextureSizeMinPow2 && (1 << max_pow2) > max_device_size) { + max_pow2 --; + } + min_pow2 = min (min_pow2, max_pow2); + } + // // Find the best texture size for the remaining text // - CurrTextureSize = 256; + CurrTextureSize = 1 << min_pow2; int best_tex_mem_usage = 999999999; - for (int pow2 = 6; pow2 <= 8; pow2 ++) { + for (int pow2 = min_pow2; pow2 <= max_pow2; pow2 ++) { int size = 1 << pow2; int row_count = (text_width / size) + 1; @@ -844,6 +878,8 @@ void Render2DSentenceClass::Build_Sentence_Centered (const WCHAR *text, int *hkX } for(int i = 0; i <= charCount; i++) { + // TheSuperHackers @fix The text still to be placed, starting at ch, so a new surface is sized for ch as well. + const WCHAR *remaining_text = text; WCHAR ch = *text++; dontBlit = false; // @@ -851,6 +887,7 @@ void Render2DSentenceClass::Build_Sentence_Centered (const WCHAR *text, int *hkX // if(ParseHotKey && (ch == L'&') && (*text != 0) && (*text > L' ') && (*text != L'\n')) { + remaining_text = text; ch = *text++; dontBlit = true; } @@ -892,7 +929,7 @@ void Render2DSentenceClass::Build_Sentence_Centered (const WCHAR *text, int *hkX // Did the text extent completely off the texture? // if ((TextureOffset.J + char_height) >= CurrTextureSize) { - Allocate_New_Surface (text); + Allocate_New_Surface (remaining_text); } } } @@ -912,12 +949,14 @@ void Render2DSentenceClass::Build_Sentence_Centered (const WCHAR *text, int *hkX // // Check to ensure the text will fit on this texture // - WWASSERT (((TextureOffset.I + char_spacing) < CurrTextureSize) && ((TextureOffset.J + char_height) < CurrTextureSize)); + const bool fits_texture = ((TextureOffset.I + char_spacing) < CurrTextureSize) && ((TextureOffset.J + char_height) < CurrTextureSize); + WWASSERT (fits_texture); // // Blit the character to the surface + // TheSuperHackers @fix Skip a glyph that still does not fit. // - if(!dontBlit) + if(!dontBlit && fits_texture) Font->Blit_Char (ch, LockedPtr, LockedStride, TextureOffset.I, TextureOffset.J); if (dontBlit) { @@ -988,6 +1027,8 @@ Vector2 Render2DSentenceClass::Build_Sentence_Not_Centered (const WCHAR *text, i // Loop over all the characters in the string // while (text != nullptr) { + // TheSuperHackers @fix The text still to be placed, starting at ch, so a new surface is sized for ch as well. + const WCHAR *remaining_text = text; WCHAR ch = *text++; dontBlit = false; // @@ -1001,6 +1042,7 @@ Vector2 Render2DSentenceClass::Build_Sentence_Not_Centered (const WCHAR *text, i else hotKeyPosX = Cursor.X + TextureOffset.I -TextureStartX;//TextureOffset.I; + remaining_text = text; ch = *text++; dontBlit = true; } @@ -1081,7 +1123,7 @@ Vector2 Render2DSentenceClass::Build_Sentence_Not_Centered (const WCHAR *text, i // Did the text extent completely off the texture? // if ((TextureOffset.J + char_height) >= CurrTextureSize) { - Allocate_New_Surface (text, justCalcExtents); + Allocate_New_Surface (remaining_text, justCalcExtents); } } } @@ -1102,12 +1144,14 @@ Vector2 Render2DSentenceClass::Build_Sentence_Not_Centered (const WCHAR *text, i // // Check to ensure the text will fit on this texture // - WWASSERT (((TextureOffset.I + char_spacing) < CurrTextureSize) && ((TextureOffset.J + char_height) < CurrTextureSize)); + const bool fits_texture = ((TextureOffset.I + char_spacing) < CurrTextureSize) && ((TextureOffset.J + char_height) < CurrTextureSize); + WWASSERT (fits_texture); // // Blit the character to the surface + // TheSuperHackers @fix Skip a glyph that still does not fit. // - if (!justCalcExtents && !dontBlit ) + if (!justCalcExtents && !dontBlit && fits_texture) { Font->Blit_Char (ch, LockedPtr, LockedStride, TextureOffset.I, TextureOffset.J); } @@ -1170,6 +1214,10 @@ FontCharsClass::FontCharsClass () : CurrPixelOffset( 0 ), PointSize( 0 ), CharHeight( 0 ), + GlyphBitmapWidth( 0 ), + GlyphBitmapHeight( 0 ), + GlyphCellBytes( 0 ), + GlyphBlockBytes( 0 ), UnicodeCharArray( nullptr ), FirstUnicodeChar( 0xFFFF ), LastUnicodeChar( 0 ), @@ -1186,14 +1234,37 @@ FontCharsClass::FontCharsClass () : // //////////////////////////////////////////////////////////////////////////////////// FontCharsClass::~FontCharsClass () +{ + Free_Glyph_Cache(); + Free_GDI_Font(); +} + + +//////////////////////////////////////////////////////////////////////////////////// +// +// Free_Glyph_Cache +// Discards the cached glyphs but keeps the font itself, so that the pointers other +// objects hold to this font stay valid and glyphs are rebuilt on demand. +// +//////////////////////////////////////////////////////////////////////////////////// +void +FontCharsClass::Free_Glyph_Cache () { while ( BufferList.Count() ) { delete [] BufferList[0].Buffer; BufferList.Delete(0); } - Free_GDI_Font(); Free_Character_Arrays(); + + // + // The character arrays are gone, so the unicode range has to start over as well. + // The GDI font and the derived metrics are deliberately kept, so Store_GDI_Char + // can rebuild any glyph that is asked for again without recreating this object. + // + CurrPixelOffset = 0; + FirstUnicodeChar = 0xFFFF; + LastUnicodeChar = 0; } @@ -1284,15 +1355,17 @@ FontCharsClass::Blit_Char (WCHAR ch, uint16 *dest_ptr, int dest_stride, int x, i // Setup the src and destination pointers // int dest_inc = (dest_stride >> 1); - uint16 *src_ptr = data->Buffer; + const uint8 *src_ptr = data->Buffer; dest_ptr += (dest_inc * y) + x; // - // Simply copy the data from the src buffer to the destination + // Copy the data from the src buffer to the destination, rebuilding the A4R4G4B4 texel + // from the stored coverage value the same way Store_GDI_Char used to compose it. // for ( int row = 0; row < CharHeight; row ++ ) { for ( int col = 0; col < data->Width; col ++ ) { - uint16 curData = *src_ptr; + const uint8 coverage = *src_ptr; + uint16 curData = (coverage != 0 ? 0x0FFF : 0) | ((uint16)(coverage >> 4) << 12); if (col> 4) & 0xF); - *curr_buffer_p++ = pixel_color | (alpha_value << 12); + *curr_buffer_p++ = pixel_value; } } + // + // TheSuperHackers @fix Blit_Char always reads CharHeight rows, so any row GDI did not + // report must not be left at whatever the freshly allocated block happened to contain. + // + if (char_size.cy < CharHeight) { + ::memset (curr_buffer_p, 0, (CharHeight - char_size.cy) * char_size.cx); + } + // // Save information about this character in our list // FontCharsClassCharDataStruct *char_data = W3DNEW FontCharsClassCharDataStruct; char_data->Value = ch; char_data->Width = char_size.cx; - char_data->Buffer = BufferList[BufferList.Count () - 1].Buffer + CurrPixelOffset; + char_data->Buffer = glyph_buffer_p; // // Insert this character into our array @@ -1422,9 +1508,10 @@ FontCharsClass::Store_GDI_Char (WCHAR ch) } // - // Advance the character position + // Advance the character position. This matches both what Update_Current_Buffer reserved and + // what Blit_Char reads back; char_size.cx already includes PixelOverlap. // - CurrPixelOffset += ((char_size.cx+PixelOverlap) * CharHeight); + CurrPixelOffset += (char_size.cx * CharHeight); // // Return the index of the entry we just added @@ -1462,9 +1549,18 @@ FontCharsClass::Update_Current_Buffer (int char_width) // if (needs_new_buffer) { - // TheSuperHackers @fix arcticdolphin 07/09/2026 Length may exceed CHAR_BUFFER_LEN to fit this glyph. - const int length = max( (int)CHAR_BUFFER_LEN, char_len ); - BufferList.Add( FontCharsBuffer( length, W3DNEWARRAY uint16[length] ) ); + // + // TheSuperHackers @fix Ceil the block size to the char size to make it fit. + // TheSuperHackers @tweak Ramp the first blocks up to the full size, because a font whose + // working set is a handful of glyphs would otherwise pay for a whole block of them. + // Halving rather than one small first block is what keeps such a font from being pushed + // into a full sized second block. + // + const int shift = 2 - min( 2, BufferList.Count() ); + const int length = max( GlyphBlockBytes >> shift, GlyphCellBytes ); + WWASSERT( char_len <= length ); + + BufferList.Add( FontCharsBuffer( length, W3DNEWARRAY uint8[length] ) ); CurrPixelOffset = 0; } } @@ -1513,18 +1609,66 @@ FontCharsClass::Create_GDI_Font (const char *font_name) CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, font_name); + // + // Create a device context we can select the font and bitmap into + // + MemDC = ::CreateCompatibleDC (screen_dc); + + // + // TheSuperHackers @fix Select the font and read its metrics before creating the scratch + // bitmap below, because that bitmap is sized from them. The point size alone cannot give a + // safe size: a font is free to report a tmHeight or a tmMaxCharWidth larger than any guess + // made from it, and Store_GDI_Char copies as many rows and columns as GDI reports. + // + OldGDIFont = (HFONT)::SelectObject (MemDC, GDIFont); + + // + // Lookup the pixel height of the font + // + TEXTMETRIC text_metric = { 0 }; + ::GetTextMetrics (MemDC, &text_metric); + CharHeight = text_metric.tmHeight; + CharAscent = text_metric.tmAscent; + CharOverhang = text_metric.tmOverhang; + if (doingGenerals) { + CharOverhang = 0; + } + + // + // The scratch bitmap must hold the widest glyph, the overlap column that Store_GDI_Char + // appends to it and the one pixel it shifts 'W' by. + // + GlyphBitmapWidth = (int)text_metric.tmMaxCharWidth + max ((int)text_metric.tmOverhang, 0) + PixelOverlap + 1; + + // Sanity check. A font reporting absurd metrics renders clipped + // rather than allocating an absurd bitmap and absurd glyph buffers. + const int max_glyph_extent = PointSize * 4 + 8; + GlyphBitmapWidth = min (max (GlyphBitmapWidth, 1), max_glyph_extent); + CharHeight = min (max (CharHeight, 1), max_glyph_extent); + GlyphBitmapHeight = CharHeight; + + // + // TheSuperHackers @tweak Size the glyph cache blocks from the widest glyph this font can produce, + // so that a block always holds a whole number of glyphs and the space abandoned when one does not + // fit is at most one glyph. A block always fits at least one glyph, however large the font is. + // + GlyphCellBytes = GlyphBitmapWidth * GlyphBitmapHeight; + GlyphBlockBytes = GlyphCellBytes * GLYPH_BLOCK_TARGET_CELLS; + GlyphBlockBytes = min (max (GlyphBlockBytes, (int)GLYPH_BLOCK_MIN_BYTES), (int)GLYPH_BLOCK_MAX_BYTES); + GlyphBlockBytes = max (GlyphBlockBytes, GlyphCellBytes); + // // Set-up the fields of the BITMAPINFOHEADER // Note: Top-down DIBs use negative height in Win32. // BITMAPINFOHEADER bitmap_info = { 0 }; bitmap_info.biSize = sizeof (BITMAPINFOHEADER); - bitmap_info.biWidth = PointSize * 2; - bitmap_info.biHeight = -(PointSize * 2); + bitmap_info.biWidth = GlyphBitmapWidth; + bitmap_info.biHeight = -GlyphBitmapHeight; bitmap_info.biPlanes = 1; bitmap_info.biBitCount = 24; bitmap_info.biCompression = BI_RGB; - bitmap_info.biSizeImage = ((PointSize * PointSize * 4) * 3); + bitmap_info.biSizeImage = (((GlyphBitmapWidth * 3) + 3) & ~3) * GlyphBitmapHeight; bitmap_info.biXPelsPerMeter = 0; bitmap_info.biYPelsPerMeter = 0; bitmap_info.biClrUsed = 0; @@ -1540,36 +1684,18 @@ FontCharsClass::Create_GDI_Font (const char *font_name) nullptr, 0L); - // - // Create a device context we can select the font and bitmap into - // - MemDC = ::CreateCompatibleDC (screen_dc); - // // Release our temporary screen DC // ::ReleaseDC ((HWND)WW3D::Get_Window(), screen_dc); // - // Now select the BMP and font into the DC + // Now select the BMP into the DC // OldGDIBitmap = (HBITMAP)::SelectObject (MemDC, GDIBitmap); - OldGDIFont = (HFONT)::SelectObject (MemDC, GDIFont); ::SetBkColor (MemDC, RGB (0, 0, 0)); ::SetTextColor (MemDC, RGB (255, 255, 255)); - // - // Lookup the pixel height of the font - // - TEXTMETRIC text_metric = { 0 }; - ::GetTextMetrics (MemDC, &text_metric); - CharHeight = text_metric.tmHeight; - CharAscent = text_metric.tmAscent; - CharOverhang = text_metric.tmOverhang; - if (doingGenerals) { - CharOverhang = 0; - } - return GDIFont != nullptr && GDIBitmap != nullptr; } diff --git a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h index a6c19625cfb..28c28050cb7 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h +++ b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h @@ -57,22 +57,32 @@ class FontCharsClassCharDataStruct public: WCHAR Value; short Width; - uint16 * Buffer; + uint8 * Buffer; }; -enum { CHAR_BUFFER_LEN = 32768 }; +// TheSuperHackers @tweak Glyph blocks are sized from the font's own glyph cell so that both the +// relative waste and the allocation count stay bounded at any point size. GLYPH_BLOCK_MIN_BYTES +// holds as many glyphs as the original fixed 64KB block did at one texel (2 bytes) per pixel, +// which keeps Arial up to roughly 19 point at the original density. GLYPH_BLOCK_MAX_BYTES +// bounds the allocation count for very large fonts, and only engages for Arial above roughly 80 point. +enum +{ + GLYPH_BLOCK_MIN_BYTES = 32768, + GLYPH_BLOCK_MAX_BYTES = 524288, + GLYPH_BLOCK_TARGET_CELLS = 16 +}; class FontCharsBuffer { public: FontCharsBuffer() : Length( 0 ), Buffer( nullptr ) {} - FontCharsBuffer( int length, uint16 *buffer ) : Length( length ), Buffer( buffer ) {} + FontCharsBuffer( int length, uint8 *buffer ) : Length( length ), Buffer( buffer ) {} bool operator== (const FontCharsBuffer &src) const { return Length == src.Length && Buffer == src.Buffer; } bool operator!= (const FontCharsBuffer &src) const { return !(*this == src); } int Length; - uint16 * Buffer; + uint8 * Buffer; }; @@ -100,6 +110,8 @@ class FontCharsClass : public RefCountClass void Blit_Char( WCHAR ch, uint16 *dest_ptr, int dest_stride, int x, int y ); + void Free_Glyph_Cache(); + private: // @@ -124,6 +136,10 @@ class FontCharsClass : public RefCountClass int CharAscent; int CharOverhang; int PixelOverlap; + int GlyphBitmapWidth; // extents of the GDI scratch bitmap, derived from the font metrics + int GlyphBitmapHeight; + int GlyphCellBytes; // worst case bytes for one glyph of this font + int GlyphBlockBytes; // size the glyph blocks ramp up to int PointSize; StringClass GDIFontName; HFONT OldGDIFont; diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index 03d3bfe5bff..984ea234e6e 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -514,6 +514,12 @@ Bool W3DDisplay::setDisplayMode( UnsignedInt xres, UnsignedInt yres, UnsignedInt { Render2DClass::Set_Screen_Resolution(RectClass(0, 0, xres, yres)); Display::setDisplayMode(xres, yres, bitdepth, windowed); + + // TheSuperHackers @tweak Font point sizes are scaled from the resolution, so every glyph + // cached for the old resolution is now dead weight. Discard the current glyphs and start + // with a clean slate. + WW3DAssetManager::Get_Instance()->Free_All_FontChars_Glyph_Caches(); + return TRUE; } @@ -932,6 +938,10 @@ void W3DDisplay::reset() Display::reset(); + // TheSuperHackers @tweak Discard the current glyphs and start with a clean slate. + // This can reduce the memory overhead from glyphs that are no longer needed from here on. + WW3DAssetManager::Get_Instance()->Free_All_FontChars_Glyph_Caches(); + // Remove all render objects. if (m_3DScene != nullptr) diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp b/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp index a9dac652860..968a4385943 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp @@ -1491,6 +1491,17 @@ void WW3DAssetManager::Release_All_FontChars() } } + +/*********************************************************************************************** + * WW3DAssetManager::Free_All_FontChars_Glyph_Caches -- Discards all cached glyphs * + *=============================================================================================*/ +void WW3DAssetManager::Free_All_FontChars_Glyph_Caches() +{ + for ( int i = 0; i < FontCharsList.Count(); i++ ) { + FontCharsList[i]->Free_Glyph_Cache(); + } +} + /*********************************************************************************************** * WW3DAssetManager::Register_Prototype_Loader -- add a new loader to the system * * * diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h b/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h index 68ea6722627..6728d4e1c30 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h @@ -287,6 +287,11 @@ class WW3DAssetManager */ virtual FontCharsClass * Get_FontChars( const char * name, int point_size, bool is_bold = false ); + /* + ** Discard the cached glyphs of every font without destroying the fonts. + */ + virtual void Free_All_FontChars_Glyph_Caches(); + /* ** Access to HTrees, Used by Animatable3DObj's */ diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index 155e8ac43ec..ec0fd111d01 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -564,6 +564,12 @@ Bool W3DDisplay::setDisplayMode( UnsignedInt xres, UnsignedInt yres, UnsignedInt { Render2DClass::Set_Screen_Resolution(RectClass(0, 0, xres, yres)); Display::setDisplayMode(xres, yres, bitdepth, windowed); + + // TheSuperHackers @tweak Font point sizes are scaled from the resolution, so every glyph + // cached for the old resolution is now dead weight. Discard the current glyphs and start + // with a clean slate. + WW3DAssetManager::Get_Instance()->Free_All_FontChars_Glyph_Caches(); + return TRUE; } @@ -982,6 +988,10 @@ void W3DDisplay::reset() Display::reset(); + // TheSuperHackers @tweak Discard the current glyphs and start with a clean slate. + // This can reduce the memory overhead from glyphs that are no longer needed from here on. + WW3DAssetManager::Get_Instance()->Free_All_FontChars_Glyph_Caches(); + // Remove all render objects. if (m_3DScene != nullptr) diff --git a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp index ce6f67a0324..72197ef8b6a 100644 --- a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp +++ b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp @@ -1496,6 +1496,17 @@ void WW3DAssetManager::Release_All_FontChars() } } + +/*********************************************************************************************** + * WW3DAssetManager::Free_All_FontChars_Glyph_Caches -- Discards all cached glyphs * + *=============================================================================================*/ +void WW3DAssetManager::Free_All_FontChars_Glyph_Caches() +{ + for ( int i = 0; i < FontCharsList.Count(); i++ ) { + FontCharsList[i]->Free_Glyph_Cache(); + } +} + /*********************************************************************************************** * WW3DAssetManager::Register_Prototype_Loader -- add a new loader to the system * * * diff --git a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h index 757afefbd31..5b37664f3bc 100644 --- a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h +++ b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.h @@ -287,6 +287,11 @@ class WW3DAssetManager */ virtual FontCharsClass * Get_FontChars( const char * name, int point_size, bool is_bold = false ); + /* + ** Discard the cached glyphs of every font without destroying the fonts. + */ + virtual void Free_All_FontChars_Glyph_Caches(); + /* ** Access to HTrees, Used by Animatable3DObj's */