Batch draw calls from CText to improve performance
This significantly speeds up text rendering. On my computer, looking at the program editor with a full screen of text, this commit takes the framerate from under 30 to 60 (hitting vsync). Performance could be further improved in the gl33 renderer by using instancing or glPrimitiveRestartIndex instead of glMultiDrawArrays, but that would be a more invasive change. All of the interface rendering could use a unified quad batching system, instead of it being limited to CText, but that would require some refactoring in CText as it currently draws using a different coordinate space to the rest of the interface. Fixes #1104.1008-fix
parent
c49c815ea5
commit
6f6cfb136a
|
@ -94,6 +94,77 @@ const Math::IntPoint REFERENCE_SIZE(800, 600);
|
|||
const Math::IntPoint FONT_TEXTURE_SIZE(256, 256);
|
||||
} // anonymous namespace
|
||||
|
||||
/// The QuadBatch is responsible for collecting as many quad (aka rectangle) draws as possible and
|
||||
/// sending them to the CDevice in one big batch. This avoids making one CDevice::DrawPrimitive call
|
||||
/// for every CText::DrawCharAndAdjustPos call, which makes text rendering much faster.
|
||||
/// Currently we only collect textured quads (ie. ones using Vertex), not untextured quads (which
|
||||
/// use VertexCol). Untextured quads are only drawn via DrawHighlight, which happens much less often
|
||||
/// than drawing textured quads.
|
||||
class CText::CQuadBatch
|
||||
{
|
||||
public:
|
||||
explicit CQuadBatch(CEngine& engine)
|
||||
: m_engine(engine)
|
||||
{
|
||||
m_quads.reserve(1024);
|
||||
}
|
||||
|
||||
/// Add a quad to be rendered.
|
||||
/// This may trigger a call to Flush() if necessary.
|
||||
void Add(Vertex vertices[4], unsigned int texID, EngineRenderState renderState, Color color)
|
||||
{
|
||||
if (texID != m_texID || renderState != m_renderState || color != m_color)
|
||||
{
|
||||
Flush();
|
||||
m_texID = texID;
|
||||
m_renderState = renderState;
|
||||
m_color = color;
|
||||
}
|
||||
m_quads.emplace_back(Quad{{vertices[0], vertices[1], vertices[2], vertices[3]}});
|
||||
}
|
||||
|
||||
/// Draw all pending quads immediately.
|
||||
void Flush()
|
||||
{
|
||||
if (m_quads.empty()) return;
|
||||
|
||||
m_engine.SetState(m_renderState);
|
||||
m_engine.GetDevice()->SetTexture(0, m_texID);
|
||||
|
||||
assert(m_firsts.size() == m_counts.size());
|
||||
if (m_firsts.size() < m_quads.size())
|
||||
{
|
||||
// m_firsts needs to look like { 0, 4, 8, 12, ... }
|
||||
// m_counts needs to look like { 4, 4, 4, 4, ... }
|
||||
// and both need to be the same length as m_quads
|
||||
m_counts.resize(m_quads.size(), 4);
|
||||
std::size_t begin = m_firsts.size();
|
||||
m_firsts.resize(m_quads.size());
|
||||
for (std::size_t i = begin; i < m_firsts.size(); ++i)
|
||||
{
|
||||
m_firsts[i] = static_cast<int>(4 * i);
|
||||
}
|
||||
}
|
||||
|
||||
const Vertex* vertices = m_quads.front().vertices;
|
||||
m_engine.GetDevice()->DrawPrimitives(PRIMITIVE_TRIANGLE_STRIP, vertices, m_firsts.data(),
|
||||
m_counts.data(), static_cast<int>(m_quads.size()), m_color);
|
||||
m_engine.AddStatisticTriangle(static_cast<int>(m_quads.size() * 2));
|
||||
m_quads.clear();
|
||||
}
|
||||
private:
|
||||
CEngine& m_engine;
|
||||
|
||||
struct Quad { Vertex vertices[4]; };
|
||||
std::vector<Quad> m_quads;
|
||||
std::vector<int> m_firsts;
|
||||
std::vector<int> m_counts;
|
||||
|
||||
Color m_color;
|
||||
unsigned int m_texID{};
|
||||
EngineRenderState m_renderState{};
|
||||
};
|
||||
|
||||
|
||||
CText::CText(CEngine* engine)
|
||||
{
|
||||
|
@ -106,6 +177,8 @@ CText::CText(CEngine* engine)
|
|||
m_lastFontType = FONT_COLOBOT;
|
||||
m_lastFontSize = 0;
|
||||
m_lastCachedFont = nullptr;
|
||||
|
||||
m_quadBatch = MakeUnique<CQuadBatch>(*engine);
|
||||
}
|
||||
|
||||
CText::~CText()
|
||||
|
@ -690,7 +763,6 @@ void CText::DrawString(const std::string &text, std::vector<FontMetaChar>::itera
|
|||
std::vector<FontMetaChar>::iterator end,
|
||||
float size, Math::IntPoint pos, int width, int eol, Color color)
|
||||
{
|
||||
m_engine->SetState(ENG_RSTATE_TEXT);
|
||||
m_engine->SetWindowCoordinates();
|
||||
|
||||
int start = pos.x;
|
||||
|
@ -756,6 +828,8 @@ void CText::DrawString(const std::string &text, std::vector<FontMetaChar>::itera
|
|||
Math::IntPoint charSize;
|
||||
charSize.x = GetCharWidthInt(ch, font, size, offset);
|
||||
charSize.y = GetHeightInt(font, size);
|
||||
// NB. for quad batching to improve highlight drawing performance, this code would have
|
||||
// to be rearranged to draw all highlights before any characters are drawn.
|
||||
DrawHighlight(format[fmtIndex], pos, charSize);
|
||||
}
|
||||
|
||||
|
@ -777,6 +851,7 @@ void CText::DrawString(const std::string &text, std::vector<FontMetaChar>::itera
|
|||
color = Color(1.0f, 0.0f, 0.0f);
|
||||
DrawCharAndAdjustPos(ch, font, size, pos, color);
|
||||
}
|
||||
m_quadBatch->Flush();
|
||||
m_engine->SetInterfaceCoordinates();
|
||||
}
|
||||
|
||||
|
@ -845,8 +920,6 @@ void CText::DrawString(const std::string &text, FontType font,
|
|||
{
|
||||
assert(font != FONT_BUTTON);
|
||||
|
||||
m_engine->SetState(ENG_RSTATE_TEXT);
|
||||
|
||||
std::vector<UTF8Char> chars;
|
||||
StringToUTFCharList(text, chars);
|
||||
m_engine->SetWindowCoordinates();
|
||||
|
@ -854,6 +927,7 @@ void CText::DrawString(const std::string &text, FontType font,
|
|||
{
|
||||
DrawCharAndAdjustPos(*it, font, size, pos, color);
|
||||
}
|
||||
m_quadBatch->Flush();
|
||||
m_engine->SetInterfaceCoordinates();
|
||||
}
|
||||
|
||||
|
@ -878,6 +952,8 @@ void CText::DrawHighlight(FontMetaChar hl, Math::IntPoint pos, Math::IntPoint si
|
|||
return;
|
||||
}
|
||||
|
||||
m_quadBatch->Flush();
|
||||
|
||||
Math::IntPoint vsize = m_engine->GetWindowSize();
|
||||
float h = 0.0f;
|
||||
if (vsize.y <= 768.0f) // 1024x768 or less?
|
||||
|
@ -927,22 +1003,22 @@ void CText::DrawCharAndAdjustPos(UTF8Char ch, FontType font, float size, Math::I
|
|||
|
||||
// For whatever reason ch.c1 is a SIGNED char, we need to fix that
|
||||
unsigned char icon = static_cast<unsigned char>(ch.c1);
|
||||
|
||||
unsigned int texID;
|
||||
|
||||
if ( icon >= 128 )
|
||||
{
|
||||
icon -= 128;
|
||||
m_engine->SetTexture("textures/interface/button3.png");
|
||||
m_engine->SetState(ENG_RSTATE_TTEXTURE_WHITE);
|
||||
texID = m_engine->LoadTexture("textures/interface/button3.png").id;
|
||||
}
|
||||
else if ( icon >= 64 )
|
||||
{
|
||||
icon -= 64;
|
||||
m_engine->SetTexture("textures/interface/button2.png");
|
||||
m_engine->SetState(ENG_RSTATE_TTEXTURE_WHITE);
|
||||
texID = m_engine->LoadTexture("textures/interface/button2.png").id;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_engine->SetTexture("textures/interface/button1.png");
|
||||
m_engine->SetState(ENG_RSTATE_TTEXTURE_WHITE);
|
||||
texID = m_engine->LoadTexture("textures/interface/button1.png").id;
|
||||
}
|
||||
|
||||
Math::Point uv1, uv2;
|
||||
|
@ -965,13 +1041,9 @@ void CText::DrawCharAndAdjustPos(UTF8Char ch, FontType font, float size, Math::I
|
|||
Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(uv2.x, uv1.y))
|
||||
};
|
||||
|
||||
m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, quad, 4, color);
|
||||
m_engine->AddStatisticTriangle(2);
|
||||
m_quadBatch->Add(quad, texID, ENG_RSTATE_TTEXTURE_WHITE, color);
|
||||
|
||||
pos.x += width;
|
||||
|
||||
// Don't forget to restore the state!
|
||||
m_engine->SetState(ENG_RSTATE_TEXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1007,9 +1079,7 @@ void CText::DrawCharAndAdjustPos(UTF8Char ch, FontType font, float size, Math::I
|
|||
Vertex(Math::Vector(p2.x, p1.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);
|
||||
m_quadBatch->Add(quad, tex.id, ENG_RSTATE_TEXT, color);
|
||||
|
||||
pos.x += tex.charSize.x * width;
|
||||
}
|
||||
|
|
|
@ -344,6 +344,9 @@ protected:
|
|||
FontType m_lastFontType;
|
||||
int m_lastFontSize;
|
||||
CachedFont* m_lastCachedFont;
|
||||
|
||||
class CQuadBatch;
|
||||
std::unique_ptr<CQuadBatch> m_quadBatch;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue