diff --git a/src/renderer/Image.h b/src/renderer/Image.h index a5ef3ab6c..8a277fee3 100644 --- a/src/renderer/Image.h +++ b/src/renderer/Image.h @@ -207,6 +207,11 @@ class idImage { textureUsage_t GetUsage() const { return usage; } bool IsDefaulted() const { return defaulted; } bool IsScratchImage() const { return scratchImage; } + bool IsFontAtlas() const { + return idStr::Icmpn( imgName, "_ttfatlas", 9 ) == 0 || + idStr::Icmpn( imgName, "_ttfconsole", 11 ) == 0 || + idStr::Icmp( imgName, "_consoleFont" ) == 0; + } void SetReferencedOutsideLevelLoad() { referencedOutsideLevelLoad = true; } void SetReferencedInsideLevelLoad() { levelLoadReferenced = true; } diff --git a/src/renderer/ImageManager.cpp b/src/renderer/ImageManager.cpp index e7e71edf3..313c60f83 100644 --- a/src/renderer/ImageManager.cpp +++ b/src/renderer/ImageManager.cpp @@ -997,7 +997,11 @@ void idImageManager::ReloadImages( bool all ) { R_SetDDSProbeCacheActive( false ); for ( int i = 0 ; i < globalImages->images.Num() ; i++ ) { - globalImages->images[ i ]->Reload( all ); + idImage *image = globalImages->images[ i ]; + if ( image == NULL || image->IsFontAtlas() ) { + continue; + } + image->Reload( all ); } } diff --git a/src/renderer/Image_load.cpp b/src/renderer/Image_load.cpp index d2cd59713..f9971ef4f 100644 --- a/src/renderer/Image_load.cpp +++ b/src/renderer/Image_load.cpp @@ -1750,6 +1750,14 @@ void idImage::Reload( bool force ) { return; } + // Procedural font atlases are CPU-generated FreeType pages rather than + // disk files or render targets. Never purge or reallocate them with NULL + // data during texture reload, or UI text will be destroyed when texture + // reduction/quality cvars change. + if ( IsFontAtlas() ) { + return; + } + // persistent images are runtime render targets, not file-backed if ( opts.isPersistant ) { common->DPrintf( "reallocating persistent %s.\n", GetName() ); diff --git a/src/renderer/OpenGL/gl_ContextSDL3.cpp b/src/renderer/OpenGL/gl_ContextSDL3.cpp index 2ff31f8e2..b91f33de8 100644 --- a/src/renderer/OpenGL/gl_ContextSDL3.cpp +++ b/src/renderer/OpenGL/gl_ContextSDL3.cpp @@ -96,6 +96,17 @@ static void SDL3_WindowParmsFromGlimpParms(const glimpParms_t &src, renderWindow dst.multiSamples = src.multiSamples; } +static void SDL3_SyncGLConfigWindowDimensions( const renderModuleWindowInfo_t &windowInfo ) { + if ( windowInfo.pixelWidth > 0 && windowInfo.pixelHeight > 0 ) { + glConfig.vidWidth = windowInfo.pixelWidth; + glConfig.vidHeight = windowInfo.pixelHeight; + } + glConfig.uiViewportX = windowInfo.uiViewportX; + glConfig.uiViewportY = windowInfo.uiViewportY; + glConfig.uiViewportWidth = windowInfo.uiViewportWidth; + glConfig.uiViewportHeight = windowInfo.uiViewportHeight; +} + static bool SDL3_EnsureGLContextCurrent(const char *operation) { if (!s_glWindow || !s_glContext) { return false; @@ -487,6 +498,7 @@ bool GLimp_Init(glimpParms_t parms) { s_glWindowServices->RefreshNativeWindowHandles(&windowInfo); s_glHDC = windowInfo.nativeDisplayHandle; + SDL3_SyncGLConfigWindowDimensions(windowInfo); SDL3_LoadWGLExtensions(); if (r_swapInterval.IsModified()) { r_swapInterval.ClearModified(); @@ -523,6 +535,7 @@ bool GLimp_SetScreenParms(glimpParms_t parms) { renderModuleWindowInfo_t windowInfo; windowServices->RefreshNativeWindowHandles(&windowInfo); s_glHDC = windowInfo.nativeDisplayHandle; + SDL3_SyncGLConfigWindowDimensions(windowInfo); r_swapInterval.SetModified(); if (r_swapInterval.IsModified()) { r_swapInterval.ClearModified(); @@ -572,14 +585,7 @@ void GLimp_SwapBuffers(void) { if (s_glWindow && s_glWindowServices != NULL && s_glWindowServices->RefreshNativeWindowHandles != NULL) { renderModuleWindowInfo_t windowInfo; s_glWindowServices->RefreshNativeWindowHandles(&windowInfo); - if (windowInfo.pixelWidth > 0 && windowInfo.pixelHeight > 0) { - glConfig.vidWidth = windowInfo.pixelWidth; - glConfig.vidHeight = windowInfo.pixelHeight; - } - glConfig.uiViewportX = windowInfo.uiViewportX; - glConfig.uiViewportY = windowInfo.uiViewportY; - glConfig.uiViewportWidth = windowInfo.uiViewportWidth; - glConfig.uiViewportHeight = windowInfo.uiViewportHeight; + SDL3_SyncGLConfigWindowDimensions(windowInfo); } if (SDL3_EnsureGLContextCurrent("swap buffers") && !s_glWindowServices->SwapGLWindow()) { diff --git a/src/renderer/RenderSystem_init.cpp b/src/renderer/RenderSystem_init.cpp index 4f2388696..06121bebd 100644 --- a/src/renderer/RenderSystem_init.cpp +++ b/src/renderer/RenderSystem_init.cpp @@ -4944,6 +4944,7 @@ static void R_PerformFullVidRestart( bool forceWindow ) { R_InitFreeType(); R_RefreshConsoleFontAtlas(); + R_TTFRestoreAtlasMaterials(); } static GLenum R_ClearPendingGLErrors( void ) { diff --git a/src/renderer/tr_fontTTF.cpp b/src/renderer/tr_fontTTF.cpp index d5617d415..3fe0fbe5a 100644 --- a/src/renderer/tr_fontTTF.cpp +++ b/src/renderer/tr_fontTTF.cpp @@ -1213,6 +1213,24 @@ bool R_RegisterTrueTypeFont( const char *fontName, fontInfoEx_t &font ) { } if ( built != 3 ) { + if ( built > 0 ) { + if ( font.fontInfoSmall.pointSize <= 0.0f && font.fontInfoMedium.pointSize > 0.0f ) { + font.fontInfoSmall = font.fontInfoMedium; + font.maxWidthSmall = font.maxWidthMedium; + font.maxHeightSmall = font.maxHeightMedium; + } + if ( font.fontInfoLarge.pointSize <= 0.0f && font.fontInfoMedium.pointSize > 0.0f ) { + font.fontInfoLarge = font.fontInfoMedium; + font.maxWidthLarge = font.maxWidthMedium; + font.maxHeightLarge = font.maxHeightMedium; + } else if ( font.fontInfoLarge.pointSize <= 0.0f && font.fontInfoSmall.pointSize > 0.0f ) { + font.fontInfoLarge = font.fontInfoSmall; + font.maxWidthLarge = font.maxWidthSmall; + font.maxHeightLarge = font.maxHeightSmall; + } + common->Warning( "TTF font: '%s' only produced %i of 3 sizes; propagated missing sizes", fontName, built ); + return true; + } common->Warning( "TTF font: '%s' only produced %i of 3 sizes; using the bitmap font", fontName, built ); return false; } @@ -1400,7 +1418,8 @@ void R_ShutdownTrueTypeFonts( void ) { } ttfConsoleMaterial = NULL; ttfConsoleOriginalImage = NULL; - ttfAtlasMaterials.DeleteContents( true ); + // Procedural atlas definitions persist across context recreate so they can be reasserted. + // ttfAtlasMaterials.DeleteContents( true ); // Nothing may still be drawing by this point: every fontInfo_t that shares a // page set is dead with the renderer, and idDeviceContext re-registers its // fonts from scratch after a restart. diff --git a/src/ui/DeviceContext.cpp b/src/ui/DeviceContext.cpp index 7d9fb2342..af32c5b85 100644 --- a/src/ui/DeviceContext.cpp +++ b/src/ui/DeviceContext.cpp @@ -1218,7 +1218,7 @@ bool idDeviceContext::ReloadFonts() { } else if ( useFontSlot == 2 ) { useFont = &activeFont->fontInfoLarge; } else { - useFont = NULL; + useFont = ( activeFont != NULL ) ? &activeFont->fontInfoSmall : NULL; } return allFontsReloaded; @@ -1245,6 +1245,7 @@ void idDeviceContext::EnsureFontsCurrent() { fontsCodePageGeneration = currentCodePageGeneration; if ( !ReloadFonts() ) { common->Warning( "could not rebuild every GUI font" ); + fontsVideoRestartCount = -1; } } @@ -1253,6 +1254,7 @@ void idDeviceContext::SetFont( int num ) { if ( fonts.Num() == 0 ) { activeFont = NULL; + useFont = NULL; return; } if ( num >= 0 && num < fonts.Num() ) { @@ -1260,6 +1262,9 @@ void idDeviceContext::SetFont( int num ) { } else { activeFont = &fonts[0]; } + if ( activeFont != NULL && ( useFont == NULL || ( useFont != &activeFont->fontInfoSmall && useFont != &activeFont->fontInfoMedium && useFont != &activeFont->fontInfoLarge ) ) ) { + useFont = &activeFont->fontInfoSmall; + } } void idDeviceContext::SizeIcon( embeddedIcon_t &icon ) { @@ -2285,6 +2290,17 @@ int idDeviceContext::DrawText(float x, float y, float scale, idVec4 color, const scaledFont.renderScale = openQ4_FontRenderScale( useFont, scale ); scaledFont.maxWidth = activeFont != NULL ? activeFont->maxWidth : 0.0f; scaledFont.maxHeight = activeFont != NULL ? activeFont->maxHeight : 0.0f; + if ( !openQ4_HasRenderableFont( scaledFont ) && activeFont != NULL ) { + if ( activeFont->fontInfoSmall.pointSize > 0.0f ) { + useFont = &activeFont->fontInfoSmall; + } else if ( activeFont->fontInfoMedium.pointSize > 0.0f ) { + useFont = &activeFont->fontInfoMedium; + } else if ( activeFont->fontInfoLarge.pointSize > 0.0f ) { + useFont = &activeFont->fontInfoLarge; + } + scaledFont.font = useFont; + scaledFont.renderScale = openQ4_FontRenderScale( useFont, scale ); + } if ( !openQ4_HasRenderableFont( scaledFont ) || text == NULL || color.w == 0.0f ) { return 0; @@ -2904,7 +2920,17 @@ int idDeviceContext::DrawText( const char *text, float textScale, int textAlign, const int visibleCellCount = charSkip > 0.0f ? idMath::FtoiFast( rectDraw.w / charSkip ) : 0; SetFontByScale( textScale ); - const float useScale = openQ4_FontRenderScale( useFont, textScale ); + float useScale = openQ4_FontRenderScale( useFont, textScale ); + if ( ( useFont == NULL || useScale == 0.0f ) && activeFont != NULL ) { + if ( activeFont->fontInfoSmall.pointSize > 0.0f ) { + useFont = &activeFont->fontInfoSmall; + } else if ( activeFont->fontInfoMedium.pointSize > 0.0f ) { + useFont = &activeFont->fontInfoMedium; + } else if ( activeFont->fontInfoLarge.pointSize > 0.0f ) { + useFont = &activeFont->fontInfoLarge; + } + useScale = openQ4_FontRenderScale( useFont, textScale ); + } if ( useFont == NULL || useScale == 0.0f ) { return visibleCellCount; }