Optimize use of textures in text rendering, closes #215
parent
4b770adf46
commit
8baccb08a7
|
@ -3296,13 +3296,15 @@ void CParticle::DrawParticleText(int i)
|
|||
{
|
||||
CharTexture tex = m_engine->GetText()->GetCharTexture(static_cast<UTF8Char>(m_particle[i].text), FONT_COURIER, FONT_SIZE_BIG*2.0f);
|
||||
if (tex.id == 0) return;
|
||||
|
||||
m_device->SetTexture(0, tex.id);
|
||||
m_engine->SetState(ENG_RSTATE_TTEXTURE_ALPHA, IntensityToColor(m_particle[i].intensity));
|
||||
|
||||
m_particle[i].texSup.x = 0.0f;
|
||||
m_particle[i].texSup.y = 0.0f;
|
||||
m_particle[i].texInf.x = static_cast<float>(tex.charSize.x) / static_cast<float>(tex.texSize.x);
|
||||
m_particle[i].texInf.y = static_cast<float>(tex.charSize.y) / static_cast<float>(tex.texSize.y);
|
||||
Math::IntPoint fontTextureSize = m_engine->GetText()->GetFontTextureSize();
|
||||
m_particle[i].texSup.x = static_cast<float>(tex.charPos.x) / fontTextureSize.x;
|
||||
m_particle[i].texSup.y = static_cast<float>(tex.charPos.y) / fontTextureSize.y;
|
||||
m_particle[i].texInf.x = static_cast<float>(tex.charPos.x + tex.charSize.x) / fontTextureSize.x;
|
||||
m_particle[i].texInf.y = static_cast<float>(tex.charPos.y + tex.charSize.y) / fontTextureSize.y;
|
||||
m_particle[i].color = Color(0.0f, 0.0f, 0.0f);
|
||||
|
||||
DrawParticleNorm(i);
|
||||
|
|
|
@ -53,6 +53,16 @@ struct MultisizeFont
|
|||
: fileName(fn) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct FontTexture
|
||||
* \brief Single texture filled with character textures
|
||||
*/
|
||||
struct FontTexture
|
||||
{
|
||||
unsigned int id = 0;
|
||||
Math::IntPoint tileSize;
|
||||
int freeSlots = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct CachedFont
|
||||
|
@ -81,6 +91,7 @@ struct CachedFont
|
|||
namespace
|
||||
{
|
||||
const Math::IntPoint REFERENCE_SIZE(800, 600);
|
||||
const Math::IntPoint FONT_TEXTURE_SIZE(256, 256);
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
@ -152,16 +163,18 @@ std::string CText::GetError()
|
|||
|
||||
void CText::FlushCache()
|
||||
{
|
||||
for (auto& fontTexture : m_fontTextures)
|
||||
{
|
||||
Texture tex;
|
||||
tex.id = fontTexture.id;
|
||||
m_device->DestroyTexture(tex);
|
||||
}
|
||||
m_fontTextures.clear();
|
||||
|
||||
for (auto& multisizeFont : m_fonts)
|
||||
{
|
||||
for (auto& cachedFont : multisizeFont.second->fonts)
|
||||
{
|
||||
for (auto& charTexture : cachedFont.second->cache)
|
||||
{
|
||||
Texture tex;
|
||||
tex.id = charTexture.second.id;
|
||||
m_device->DestroyTexture(tex);
|
||||
}
|
||||
cachedFont.second->cache.clear();
|
||||
}
|
||||
}
|
||||
|
@ -926,27 +939,32 @@ void CText::DrawCharAndAdjustPos(UTF8Char ch, FontType font, float size, Math::P
|
|||
|
||||
CharTexture tex = GetCharTexture(ch, font, size);
|
||||
|
||||
Math::Point charSize = m_engine->WindowToInterfaceSize(tex.charSize);
|
||||
Math::Point texSize = m_engine->WindowToInterfaceSize(tex.texSize);
|
||||
Math::Point charInterfaceSize = m_engine->WindowToInterfaceSize(tex.charSize);
|
||||
Math::Point texInterfaceSize = m_engine->WindowToInterfaceSize(tex.tileSize);
|
||||
|
||||
Math::Point p1(pos.x, pos.y + charSize.y - texSize.y);
|
||||
Math::Point p2(pos.x + texSize.x, pos.y + charSize.y);
|
||||
Math::Point p1(pos.x, pos.y + charInterfaceSize.y - texInterfaceSize.y);
|
||||
Math::Point p2(pos.x + texInterfaceSize.x, pos.y + charInterfaceSize.y);
|
||||
|
||||
Math::Point texCoord1(static_cast<float>(tex.charPos.x) / FONT_TEXTURE_SIZE.x,
|
||||
static_cast<float>(tex.charPos.y) / FONT_TEXTURE_SIZE.y);
|
||||
Math::Point texCoord2(static_cast<float>(tex.charPos.x + tex.tileSize.x) / FONT_TEXTURE_SIZE.x,
|
||||
static_cast<float>(tex.charPos.y + tex.tileSize.y) / FONT_TEXTURE_SIZE.y);
|
||||
|
||||
Math::Vector n(0.0f, 0.0f, -1.0f); // normal
|
||||
|
||||
Vertex quad[4] =
|
||||
{
|
||||
Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(0.0f, 1.0f)),
|
||||
Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(0.0f, 0.0f)),
|
||||
Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(1.0f, 1.0f)),
|
||||
Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(1.0f, 0.0f))
|
||||
Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(texCoord1.x, texCoord2.y)),
|
||||
Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(texCoord1.x, texCoord1.y)),
|
||||
Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(texCoord2.x, texCoord2.y)),
|
||||
Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(texCoord2.x, texCoord1.y))
|
||||
};
|
||||
|
||||
m_device->SetTexture(0, tex.id);
|
||||
m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, quad, 4, color);
|
||||
m_engine->AddStatisticTriangle(2);
|
||||
|
||||
pos.x += charSize.x * width;
|
||||
pos.x += charInterfaceSize.x * width;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1000,57 +1018,6 @@ CachedFont* CText::GetOrOpenFont(FontType font, float size)
|
|||
return m_lastCachedFont;
|
||||
}
|
||||
|
||||
CharTexture CText::CreateCharTexture(UTF8Char ch, CachedFont* font)
|
||||
{
|
||||
CharTexture texture;
|
||||
|
||||
SDL_Surface* textSurface = nullptr;
|
||||
SDL_Color white = {255, 255, 255, 0};
|
||||
char str[] = { ch.c1, ch.c2, ch.c3, '\0' };
|
||||
textSurface = TTF_RenderUTF8_Blended(font->font, str, white);
|
||||
|
||||
if (textSurface == nullptr)
|
||||
{
|
||||
m_error = "TTF_Render error";
|
||||
return texture;
|
||||
}
|
||||
|
||||
int w = Math::NextPowerOfTwo(textSurface->w);
|
||||
int h = Math::NextPowerOfTwo(textSurface->h);
|
||||
|
||||
SDL_Surface* textureSurface = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00,
|
||||
0x000000ff, 0xff000000);
|
||||
SDL_BlitSurface(textSurface, nullptr, textureSurface, nullptr);
|
||||
|
||||
ImageData data;
|
||||
data.surface = textureSurface;
|
||||
|
||||
TextureCreateParams createParams;
|
||||
createParams.format = TEX_IMG_RGBA;
|
||||
createParams.filter = TEX_FILTER_NEAREST;
|
||||
createParams.mipmap = false;
|
||||
|
||||
Texture tex = m_device->CreateTexture(&data, createParams);
|
||||
|
||||
data.surface = nullptr;
|
||||
|
||||
if (! tex.Valid())
|
||||
{
|
||||
m_error = "Texture create error";
|
||||
}
|
||||
else
|
||||
{
|
||||
texture.id = tex.id;
|
||||
texture.texSize = Math::IntPoint(textureSurface->w, textureSurface->h);
|
||||
texture.charSize = Math::IntPoint(textSurface->w, textSurface->h);
|
||||
}
|
||||
|
||||
SDL_FreeSurface(textSurface);
|
||||
SDL_FreeSurface(textureSurface);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
CharTexture CText::GetCharTexture(UTF8Char ch, FontType font, float size)
|
||||
{
|
||||
CachedFont* cf = GetOrOpenFont(font, size);
|
||||
|
@ -1076,5 +1043,116 @@ CharTexture CText::GetCharTexture(UTF8Char ch, FontType font, float size)
|
|||
return tex;
|
||||
}
|
||||
|
||||
Math::IntPoint CText::GetFontTextureSize()
|
||||
{
|
||||
return FONT_TEXTURE_SIZE;
|
||||
}
|
||||
|
||||
CharTexture CText::CreateCharTexture(UTF8Char ch, CachedFont* font)
|
||||
{
|
||||
CharTexture texture;
|
||||
|
||||
SDL_Surface* textSurface = nullptr;
|
||||
SDL_Color white = {255, 255, 255, 0};
|
||||
char str[] = { ch.c1, ch.c2, ch.c3, '\0' };
|
||||
textSurface = TTF_RenderUTF8_Blended(font->font, str, white);
|
||||
|
||||
if (textSurface == nullptr)
|
||||
{
|
||||
m_error = "TTF_Render error";
|
||||
return texture;
|
||||
}
|
||||
|
||||
Math::IntPoint tileSize(Math::NextPowerOfTwo(textSurface->w),
|
||||
Math::NextPowerOfTwo(textSurface->h));
|
||||
|
||||
FontTexture* fontTexture = GetOrCreateFontTexture(tileSize);
|
||||
|
||||
if (fontTexture == nullptr)
|
||||
{
|
||||
m_error = "Texture create error";
|
||||
}
|
||||
else
|
||||
{
|
||||
texture.id = fontTexture->id;
|
||||
texture.charPos = GetNextTilePos(*fontTexture);
|
||||
texture.charSize = Math::IntPoint(textSurface->w, textSurface->h);
|
||||
texture.tileSize = tileSize;
|
||||
|
||||
ImageData imageData;
|
||||
imageData.surface = textSurface;
|
||||
|
||||
Texture tex;
|
||||
tex.id = texture.id;
|
||||
m_device->UpdateTexture(tex, texture.charPos, &imageData, TEX_IMG_RGBA);
|
||||
|
||||
imageData.surface = nullptr;
|
||||
|
||||
--fontTexture->freeSlots;
|
||||
}
|
||||
|
||||
SDL_FreeSurface(textSurface);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
FontTexture* CText::GetOrCreateFontTexture(Math::IntPoint tileSize)
|
||||
{
|
||||
for (auto& fontTexture : m_fontTextures)
|
||||
{
|
||||
if (fontTexture.tileSize == tileSize && fontTexture.freeSlots > 0)
|
||||
return &fontTexture;
|
||||
}
|
||||
|
||||
FontTexture newFontTexture = CreateFontTexture(tileSize);
|
||||
if (newFontTexture.id == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_fontTextures.push_back(newFontTexture);
|
||||
return &m_fontTextures.back();
|
||||
}
|
||||
|
||||
FontTexture CText::CreateFontTexture(Math::IntPoint tileSize)
|
||||
{
|
||||
SDL_Surface* textureSurface = SDL_CreateRGBSurface(0, FONT_TEXTURE_SIZE.x, FONT_TEXTURE_SIZE.y, 32,
|
||||
0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
|
||||
ImageData data;
|
||||
data.surface = textureSurface;
|
||||
|
||||
TextureCreateParams createParams;
|
||||
createParams.format = TEX_IMG_RGBA;
|
||||
createParams.filter = TEX_FILTER_NEAREST;
|
||||
createParams.mipmap = false;
|
||||
|
||||
Texture tex = m_device->CreateTexture(&data, createParams);
|
||||
|
||||
data.surface = nullptr;
|
||||
SDL_FreeSurface(textureSurface);
|
||||
|
||||
FontTexture fontTexture;
|
||||
fontTexture.id = tex.id;
|
||||
fontTexture.tileSize = tileSize;
|
||||
int horizontalTiles = FONT_TEXTURE_SIZE.x / tileSize.x;
|
||||
int verticalTiles = FONT_TEXTURE_SIZE.y / tileSize.y;
|
||||
fontTexture.freeSlots = horizontalTiles * verticalTiles;
|
||||
return fontTexture;
|
||||
}
|
||||
|
||||
Math::IntPoint CText::GetNextTilePos(const FontTexture& fontTexture)
|
||||
{
|
||||
int horizontalTiles = FONT_TEXTURE_SIZE.x / fontTexture.tileSize.x;
|
||||
int verticalTiles = FONT_TEXTURE_SIZE.y / fontTexture.tileSize.y;
|
||||
|
||||
int totalTiles = horizontalTiles * verticalTiles;
|
||||
int tileNumber = totalTiles - fontTexture.freeSlots;
|
||||
|
||||
int verticalTileIndex = tileNumber / horizontalTiles;
|
||||
int horizontalTileIndex = tileNumber % horizontalTiles;
|
||||
|
||||
return Math::IntPoint(horizontalTileIndex * fontTexture.tileSize.x,
|
||||
verticalTileIndex * fontTexture.tileSize.y);
|
||||
}
|
||||
|
||||
} // namespace Gfx
|
||||
|
|
|
@ -191,13 +191,15 @@ struct UTF8Char
|
|||
struct CharTexture
|
||||
{
|
||||
unsigned int id = 0;
|
||||
Math::IntPoint texSize;
|
||||
Math::IntPoint charPos;
|
||||
Math::IntPoint charSize;
|
||||
Math::IntPoint tileSize;
|
||||
};
|
||||
|
||||
// Definition is private - in text.cpp
|
||||
struct CachedFont;
|
||||
struct MultisizeFont;
|
||||
struct FontTexture;
|
||||
|
||||
/**
|
||||
* \enum SpecialChar
|
||||
|
@ -307,10 +309,14 @@ public:
|
|||
UTF8Char TranslateSpecialChar(int specialChar);
|
||||
|
||||
CharTexture GetCharTexture(UTF8Char ch, FontType font, float size);
|
||||
Math::IntPoint GetFontTextureSize();
|
||||
|
||||
protected:
|
||||
CachedFont* GetOrOpenFont(FontType font, float size);
|
||||
CharTexture CreateCharTexture(UTF8Char ch, CachedFont* font);
|
||||
FontTexture* GetOrCreateFontTexture(Math::IntPoint tileSize);
|
||||
FontTexture CreateFontTexture(Math::IntPoint tileSize);
|
||||
Math::IntPoint GetNextTilePos(const FontTexture& fontTexture);
|
||||
|
||||
void DrawString(const std::string &text, std::vector<FontMetaChar>::iterator format,
|
||||
std::vector<FontMetaChar>::iterator end,
|
||||
|
@ -331,6 +337,7 @@ protected:
|
|||
int m_tabSize;
|
||||
|
||||
std::map<FontType, std::unique_ptr<MultisizeFont>> m_fonts;
|
||||
std::vector<FontTexture> m_fontTextures;
|
||||
|
||||
FontType m_lastFontType;
|
||||
int m_lastFontSize;
|
||||
|
|
Loading…
Reference in New Issue