More efficient glyph uploading, fix crash with very large glyphs

Avoid using the `m_aaGlyphData` temporary buffer for uploading glyphs. Instead, allocate the required memory for the glyphs directly and allow the graphics backend to take ownership of the buffer when updating text textures. This avoids copying the glyph data into the temporary buffer when uploading individual glyphs.

This also avoids crashes when rendering very large glyphs with large font sizes, due to the buffer `m_aaGlyphData` having a fixed size of `64 * 1024` while the maximum glyph size is not checked. This fixed size could be exceeded with glyphs larger than 256² in rendered dimensions. There should currently be no glyph in our fonts which is that large and also no font size so large that this could happen.
This commit is contained in:
Robert Müller 2024-11-01 00:02:28 +01:00
parent a7a5c0ea7a
commit fe78331e80
4 changed files with 25 additions and 18 deletions

View file

@ -484,7 +484,7 @@ bool CGraphics_Threaded::UnloadTextTextures(CTextureHandle &TextTexture, CTextur
return true; return true;
} }
bool CGraphics_Threaded::UpdateTextTexture(CTextureHandle TextureId, int x, int y, size_t Width, size_t Height, const uint8_t *pData) bool CGraphics_Threaded::UpdateTextTexture(CTextureHandle TextureId, int x, int y, size_t Width, size_t Height, uint8_t *pData, bool IsMovedPointer)
{ {
CCommandBuffer::SCommand_TextTexture_Update Cmd; CCommandBuffer::SCommand_TextTexture_Update Cmd;
Cmd.m_Slot = TextureId.Id(); Cmd.m_Slot = TextureId.Id();
@ -493,10 +493,17 @@ bool CGraphics_Threaded::UpdateTextTexture(CTextureHandle TextureId, int x, int
Cmd.m_Width = Width; Cmd.m_Width = Width;
Cmd.m_Height = Height; Cmd.m_Height = Height;
const size_t MemSize = Width * Height; if(IsMovedPointer)
uint8_t *pTmpData = static_cast<uint8_t *>(malloc(MemSize)); {
mem_copy(pTmpData, pData, MemSize); Cmd.m_pData = pData;
Cmd.m_pData = pTmpData; }
else
{
const size_t MemSize = Width * Height;
uint8_t *pTmpData = static_cast<uint8_t *>(malloc(MemSize));
mem_copy(pTmpData, pData, MemSize);
Cmd.m_pData = pTmpData;
}
AddCmd(Cmd); AddCmd(Cmd);
return true; return true;

View file

@ -950,7 +950,7 @@ public:
bool LoadTextTextures(size_t Width, size_t Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, uint8_t *pTextData, uint8_t *pTextOutlineData) override; bool LoadTextTextures(size_t Width, size_t Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, uint8_t *pTextData, uint8_t *pTextOutlineData) override;
bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) override; bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) override;
bool UpdateTextTexture(CTextureHandle TextureId, int x, int y, size_t Width, size_t Height, const uint8_t *pData) override; bool UpdateTextTexture(CTextureHandle TextureId, int x, int y, size_t Width, size_t Height, uint8_t *pData, bool IsMovedPointer) override;
CTextureHandle LoadSpriteTexture(const CImageInfo &FromImageInfo, const struct CDataSprite *pSprite) override; CTextureHandle LoadSpriteTexture(const CImageInfo &FromImageInfo, const struct CDataSprite *pSprite) override;

View file

@ -321,9 +321,6 @@ private:
CAtlas m_TextureAtlas; CAtlas m_TextureAtlas;
std::unordered_map<std::tuple<FT_Face, int, int>, SGlyph, SGlyphKeyHash, SGlyphKeyEquals> m_Glyphs; std::unordered_map<std::tuple<FT_Face, int, int>, SGlyph, SGlyphKeyHash, SGlyphKeyEquals> m_Glyphs;
// Data used for rendering glyphs
uint8_t m_aaGlyphData[NUM_FONT_TEXTURES][64 * 1024];
// Font faces // Font faces
FT_Face m_DefaultFace = nullptr; FT_Face m_DefaultFace = nullptr;
FT_Face m_IconFace = nullptr; FT_Face m_IconFace = nullptr;
@ -485,13 +482,13 @@ private:
return OutlineThickness; return OutlineThickness;
} }
void UploadGlyph(int TextureIndex, int PosX, int PosY, size_t Width, size_t Height, const unsigned char *pData) void UploadGlyph(int TextureIndex, int PosX, int PosY, size_t Width, size_t Height, uint8_t *pData)
{ {
for(size_t y = 0; y < Height; ++y) for(size_t y = 0; y < Height; ++y)
{ {
mem_copy(&m_apTextureData[TextureIndex][PosX + ((y + PosY) * m_TextureDimension)], &pData[y * Width], Width); mem_copy(&m_apTextureData[TextureIndex][PosX + ((y + PosY) * m_TextureDimension)], &pData[y * Width], Width);
} }
Graphics()->UpdateTextTexture(m_aTextures[TextureIndex], PosX, PosY, Width, Height, pData); Graphics()->UpdateTextTexture(m_aTextures[TextureIndex], PosX, PosY, Width, Height, pData, true);
} }
bool FitGlyph(size_t Width, size_t Height, int &PosX, int &PosY) bool FitGlyph(size_t Width, size_t Height, int &PosX, int &PosY)
@ -549,16 +546,19 @@ private:
} }
// prepare glyph data // prepare glyph data
mem_zero(m_aaGlyphData[FONT_TEXTURE_FILL], (size_t)Width * Height * sizeof(uint8_t)); const size_t GlyphDataSize = (size_t)Width * Height * sizeof(uint8_t);
uint8_t *pGlyphDataFill = static_cast<uint8_t *>(malloc(GlyphDataSize));
uint8_t *pGlyphDataOutline = static_cast<uint8_t *>(malloc(GlyphDataSize));
mem_zero(pGlyphDataFill, GlyphDataSize);
for(unsigned py = 0; py < pBitmap->rows; ++py) for(unsigned py = 0; py < pBitmap->rows; ++py)
{ {
mem_copy(&m_aaGlyphData[FONT_TEXTURE_FILL][(py + y) * Width + x], &pBitmap->buffer[py * pBitmap->width], pBitmap->width); mem_copy(&pGlyphDataFill[(py + y) * Width + x], &pBitmap->buffer[py * pBitmap->width], pBitmap->width);
} }
Grow(pGlyphDataFill, pGlyphDataOutline, Width, Height, OutlineThickness);
// upload the glyph // upload the glyph
UploadGlyph(FONT_TEXTURE_FILL, X, Y, Width, Height, m_aaGlyphData[FONT_TEXTURE_FILL]); UploadGlyph(FONT_TEXTURE_FILL, X, Y, Width, Height, pGlyphDataFill);
Grow(m_aaGlyphData[FONT_TEXTURE_FILL], m_aaGlyphData[FONT_TEXTURE_OUTLINE], Width, Height, OutlineThickness); UploadGlyph(FONT_TEXTURE_OUTLINE, X, Y, Width, Height, pGlyphDataOutline);
UploadGlyph(FONT_TEXTURE_OUTLINE, X, Y, Width, Height, m_aaGlyphData[FONT_TEXTURE_OUTLINE]);
} }
// set glyph info // set glyph info
@ -696,7 +696,7 @@ public:
for(size_t TextureIndex = 0; TextureIndex < NUM_FONT_TEXTURES; ++TextureIndex) for(size_t TextureIndex = 0; TextureIndex < NUM_FONT_TEXTURES; ++TextureIndex)
{ {
mem_zero(m_apTextureData[TextureIndex], m_TextureDimension * m_TextureDimension * sizeof(uint8_t)); mem_zero(m_apTextureData[TextureIndex], m_TextureDimension * m_TextureDimension * sizeof(uint8_t));
Graphics()->UpdateTextTexture(m_aTextures[TextureIndex], 0, 0, m_TextureDimension, m_TextureDimension, m_apTextureData[TextureIndex]); Graphics()->UpdateTextTexture(m_aTextures[TextureIndex], 0, 0, m_TextureDimension, m_TextureDimension, m_apTextureData[TextureIndex], false);
} }
m_TextureAtlas.Clear(m_TextureDimension); m_TextureAtlas.Clear(m_TextureDimension);

View file

@ -284,7 +284,7 @@ public:
// pTextData & pTextOutlineData are automatically free'd // pTextData & pTextOutlineData are automatically free'd
virtual bool LoadTextTextures(size_t Width, size_t Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, uint8_t *pTextData, uint8_t *pTextOutlineData) = 0; virtual bool LoadTextTextures(size_t Width, size_t Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, uint8_t *pTextData, uint8_t *pTextOutlineData) = 0;
virtual bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) = 0; virtual bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) = 0;
virtual bool UpdateTextTexture(CTextureHandle TextureId, int x, int y, size_t Width, size_t Height, const uint8_t *pData) = 0; virtual bool UpdateTextTexture(CTextureHandle TextureId, int x, int y, size_t Width, size_t Height, uint8_t *pData, bool IsMovedPointer) = 0;
virtual CTextureHandle LoadSpriteTexture(const CImageInfo &FromImageInfo, const struct CDataSprite *pSprite) = 0; virtual CTextureHandle LoadSpriteTexture(const CImageInfo &FromImageInfo, const struct CDataSprite *pSprite) = 0;