performance improvments

This commit is contained in:
Eduardo Bart 2011-04-16 13:08:55 -03:00
parent da6dfea03e
commit 9b02312bf8
16 changed files with 257 additions and 120 deletions

View File

@ -30,6 +30,7 @@
#include "dispatcher.h"
#include "net/connections.h"
#include "ui/uicontainer.h"
#include "graphics/fonts.h"
Engine g_engine;
@ -52,7 +53,10 @@ void Engine::terminate()
void Engine::run()
{
std::string fpsText;
Size fpsTextSize;
Font *defaultFont = g_fonts.getDefaultFont();
m_lastFrameTicks = Platform::getTicks();
int lastFpsTicks = m_lastFrameTicks;
int frameCount = 0;
@ -60,14 +64,14 @@ void Engine::run()
m_running = true;
while(!m_stopping) {
m_lastFrameTicks = Platform::getTicks();
// poll platform events
Platform::poll();
// poll network events
g_connections.poll();
m_lastFrameTicks = Platform::getTicks();
// poll diaptcher tasks
g_dispatcher.poll();
@ -80,17 +84,18 @@ void Engine::run()
lastFpsTicks = m_lastFrameTicks;
fps = frameCount;
frameCount = 0;
// update fps text
fpsText = format("FPS: %d", fps);
fpsTextSize = defaultFont->calculateTextRectSize(fpsText);
}
}
render();
// render fps
if(m_calculateFps) {
std::string fpsText = format("FPS: %d", fps);
Size textSize = defaultFont->calculateTextRectSize(fpsText);
defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - textSize.width() - 10, 10));
}
if(m_calculateFps)
defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - fpsTextSize.width() - 10, 10));
// swap buffers
Platform::swapBuffers();

View File

@ -26,7 +26,6 @@
#include "core/resources.h"
#include "textures.h"
#include "graphics.h"
#include "textarea.h"
void Font::calculateGlyphsWidthsAutomatically(const Size& glyphSize)
{
@ -136,7 +135,7 @@ void Font::renderText(const std::string& text,
{
Size boxSize = g_graphics.getScreenSize() - startPos.toSize();
Rect screenCoords(startPos, boxSize);
Font::renderText(text, screenCoords, ALIGN_TOP_LEFT, color);
renderText(text, screenCoords, ALIGN_TOP_LEFT, color);
}
@ -145,34 +144,100 @@ void Font::renderText(const std::string& text,
int align,
const Color& color)
{
TextArea textArea(this, text, screenCoords, align, color);
textArea.draw();
// prevent glitches from invalid rects
if(!screenCoords.isValid())
return;
int textLenght = text.length();
// map glyphs positions
Size textBoxSize;
const std::vector<Point>& glyphsPositions = calculateGlyphsPositions(text, align, &textBoxSize);
for(int i = 0; i < textLenght; ++i) {
int glyph = (uchar)text[i];
// skip invalid glyphs
if(glyph < 32)
continue;
// calculate initial glyph rect and texture coords
Rect glyphScreenCoords(glyphsPositions[i], m_glyphsSize[glyph]);
Rect glyphTextureCoords = m_glyphsTextureCoords[glyph];
// first translate to align position
if(align & ALIGN_BOTTOM) {
glyphScreenCoords.translate(0, screenCoords.height() - textBoxSize.height());
} else if(align & ALIGN_VERTICAL_CENTER) {
glyphScreenCoords.translate(0, (screenCoords.height() - textBoxSize.height()) / 2);
} else { // ALIGN_TOP
// nothing to do
}
if(align & ALIGN_RIGHT) {
glyphScreenCoords.translate(screenCoords.width() - textBoxSize.width(), 0);
} else if(align & ALIGN_HORIZONTAL_CENTER) {
glyphScreenCoords.translate((screenCoords.width() - textBoxSize.width()) / 2, 0);
} else { // ALIGN_TOP
// nothing to do
}
// translate rect to screen coords
glyphScreenCoords.translate(screenCoords.topLeft());
// only render if glyph rect is visible on screenCoords
if(!screenCoords.intersects(glyphScreenCoords))
continue;
// bound glyph bottomRight to screenCoords bottomRight
if(glyphScreenCoords.bottom() > screenCoords.bottom()) {
glyphTextureCoords.setBottom(glyphTextureCoords.bottom() + (screenCoords.bottom() - glyphScreenCoords.bottom()));
glyphScreenCoords.setBottom(screenCoords.bottom());
}
if(glyphScreenCoords.right() > screenCoords.right()) {
glyphTextureCoords.setRight(glyphTextureCoords.right() + (screenCoords.right() - glyphScreenCoords.right()));
glyphScreenCoords.setRight(screenCoords.right());
}
// render glyph
g_graphics.drawTexturedRect(glyphScreenCoords, m_texture, glyphTextureCoords, color);
}
}
std::vector<Point> Font::calculateGlyphsPositions(const std::string& text, int align, Size *textBoxSize)
const std::vector<Point>& Font::calculateGlyphsPositions(const std::string& text, int align, Size *textBoxSize) const
{
int numGlyphs = text.length();
std::vector<Point> glyphsPositions(numGlyphs);
if(numGlyphs == 0) {
if(textBoxSize)
textBoxSize->setSize(0,0);
return glyphsPositions;
}
// for performance reasons we use statics vectors that are allocated on demand
static std::vector<Point> glyphsPositions(1);
static std::vector<int> lineWidths(1);
std::vector<int> lineWidths(numGlyphs);
int textLength = text.length();
int maxLineWidth = 0;
int lines = 0;
int glyph;
int i;
// return if there is no text
if(textLength == 0) {
if(textBoxSize)
textBoxSize->setSize(0,0);
return glyphsPositions;
}
// resize glyphsPositions vector, if needed
if(textLength > (int)glyphsPositions.size())
glyphsPositions.resize(textLength);
// calculate lines width
if((align & ALIGN_RIGHT || align & ALIGN_HORIZONTAL_CENTER) || textBoxSize) {
lineWidths[0] = 0;
for(i = 0; i< numGlyphs; ++i) {
for(i = 0; i< textLength; ++i) {
glyph = (uchar)text[i];
if(glyph == (uchar)'\n') {
lineWidths[++lines] = 0;
lines++;
if(lines+1 > (int)lineWidths.size())
lineWidths.resize(lines+1);
lineWidths[lines] = 0;
} else if(glyph >= 32) {
lineWidths[lines] += m_glyphsSize[glyph].width() + m_glyphSpacing.width();
maxLineWidth = std::max(maxLineWidth, lineWidths[lines]);
@ -182,7 +247,7 @@ std::vector<Point> Font::calculateGlyphsPositions(const std::string& text, int a
Point virtualPos(0, m_topMargin);
lines = 0;
for(i = 0; i < numGlyphs; ++i) {
for(i = 0; i < textLength; ++i) {
glyph = (uchar)text[i];
// store current glyph topLeft

View File

@ -42,8 +42,6 @@ enum EAlign {
ALIGN_BOTTOM_LEFT = ALIGN_BOTTOM | ALIGN_LEFT
};
class TextArea;
class Font
{
public:
@ -55,22 +53,19 @@ public:
/// Load font from file
bool load(const std::string &file);
/// Simple text render starting at pos
/// Simple text render starting at startPos
void renderText(const std::string& text,
const Point& startPos,
const Color& color = Color::white);
/** Advanced text render
* screenCoords is the rect that will be filled on the screen
* startRenderPosition is the postion to start rendering relative to the text rect
*/
/// Advanced text render
void renderText(const std::string& text,
const Rect& screenCoords,
int align = ALIGN_TOP_LEFT,
const Color& color = Color::white);
/// Calculate glyphs positions to use on render, also calculates textBoxSize if wanted
std::vector<Point> calculateGlyphsPositions(const std::string& text, int align = ALIGN_TOP_LEFT, Size *textBoxSize = NULL);
const std::vector<Point>& calculateGlyphsPositions(const std::string& text, int align = ALIGN_TOP_LEFT, Size *textBoxSize = NULL) const;
/// Simulate render and calculate text size
Size calculateTextRectSize(const std::string& text);

View File

@ -48,7 +48,7 @@ void Graphics::init()
void Graphics::terminate()
{
m_bindedTexture = TexturePtr();
}
bool Graphics::isExtensionSupported(const char *extension)
@ -88,6 +88,8 @@ void Graphics::resize(const Size& size)
void Graphics::restoreViewport()
{
disableDrawing();
const int& width = m_screenSize.width();
const int& height = m_screenSize.height();
@ -116,11 +118,23 @@ void Graphics::beginRender()
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
// quads is the default drawing mode
glBegin(GL_QUADS);
m_drawMode = DRAW_QUADS;
}
void Graphics::endRender()
{
// end last drawing
glEnd();
m_drawMode = DRAW_NONE;
}
void Graphics::disableDrawing()
{
glEnd();
m_drawMode = DRAW_NONE;
}
void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color)
@ -128,8 +142,6 @@ void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& text
if(screenCoords.isEmpty())
return;
glColor4ubv(color.rgbaPtr());
// rect correction for opengl
int right = screenCoords.right() + 1;
int bottom = screenCoords.bottom() + 1;
@ -149,15 +161,11 @@ void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& text
textureLeft = (float)textureCoords.left() / textureSize.width();
}
glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
glBegin(GL_QUADS);
bindTexture(texture, color);
glTexCoord2f(textureLeft, textureTop); glVertex2i(left, top);
glTexCoord2f(textureLeft, textureBottom); glVertex2i(left, bottom);
glTexCoord2f(textureRight, textureBottom); glVertex2i(right, bottom);
glTexCoord2f(textureRight, textureTop); glVertex2i(right, top);
glEnd();
}
void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color)
@ -193,24 +201,17 @@ void Graphics::drawFilledRect(const Rect& screenCoords, const Color& color)
if(screenCoords.isEmpty())
return;
glDisable(GL_TEXTURE_2D);
glColor4ubv(color.rgbaPtr());
// rect correction for opengl
int right = screenCoords.right() + 1;
int bottom = screenCoords.bottom() + 1;
int top = screenCoords.top();
int left = screenCoords.left();
glBegin(GL_QUADS);
bindColor(color);
glVertex2i(left, top);
glVertex2i(left, bottom);
glVertex2i(right, bottom);
glVertex2i(right, top);
glEnd();
glEnable(GL_TEXTURE_2D);
}
@ -219,41 +220,71 @@ void Graphics::drawBoundingRect(const Rect& screenCoords, const Color& color, in
if(2 * innerLineWidth > screenCoords.height() || screenCoords.isEmpty())
return;
glDisable(GL_TEXTURE_2D);
glColor4ubv(color.rgbaPtr());
// rect correction for opengl
int right = screenCoords.right()+1;
int bottom = screenCoords.bottom()+1;
int top = screenCoords.top();
int left = screenCoords.left();
glBegin(GL_QUADS);
// top line
glVertex2i(left, top);
glVertex2i(left, top + innerLineWidth);
glVertex2i(right, top + innerLineWidth);
glVertex2i(right, top);
bindColor(color);
// left
glVertex2i(left, screenCoords.top() + innerLineWidth);
glVertex2i(left, bottom - innerLineWidth);
glVertex2i(left + innerLineWidth, bottom - innerLineWidth);
glVertex2i(left + innerLineWidth, screenCoords.top() + innerLineWidth);
// top line
glVertex2i(left, top);
glVertex2i(left, top + innerLineWidth);
glVertex2i(right, top + innerLineWidth);
glVertex2i(right, top);
// bottom line
glVertex2i(left, bottom);
glVertex2i(left, bottom - innerLineWidth);
glVertex2i(right, bottom - innerLineWidth);
glVertex2i(right, bottom);
// left
glVertex2i(left, screenCoords.top() + innerLineWidth);
glVertex2i(left, bottom - innerLineWidth);
glVertex2i(left + innerLineWidth, bottom - innerLineWidth);
glVertex2i(left + innerLineWidth, screenCoords.top() + innerLineWidth);
// right line
glVertex2i(right , top + innerLineWidth);
glVertex2i(right , bottom - innerLineWidth);
glVertex2i(right - innerLineWidth, bottom - innerLineWidth);
glVertex2i(right - innerLineWidth, top + innerLineWidth);
glEnd();
// bottom line
glVertex2i(left, bottom);
glVertex2i(left, bottom - innerLineWidth);
glVertex2i(right, bottom - innerLineWidth);
glVertex2i(right, bottom);
glEnable(GL_TEXTURE_2D);
// right line
glVertex2i(right , top + innerLineWidth);
glVertex2i(right , bottom - innerLineWidth);
glVertex2i(right - innerLineWidth, bottom - innerLineWidth);
glVertex2i(right - innerLineWidth, top + innerLineWidth);
}
void Graphics::bindColor(const Color& color)
{
// switch drawing to colored quads
if(m_drawMode != DRAW_COLOR_QUADS || m_bindedColor != color) {
if(m_drawMode != DRAW_NONE)
glEnd();
glDisable(GL_TEXTURE_2D);
if(m_bindedColor != color) {
glColor4ubv(color.rgbaPtr());
m_bindedColor = color;
}
glBegin(GL_QUADS);
m_drawMode = DRAW_COLOR_QUADS;
}
}
void Graphics::bindTexture(const TexturePtr& texture, const Color& color)
{
// switch drawing to textured quads
if(m_drawMode != DRAW_TEXTURE_QUADS || m_bindedTexture != texture) {
if(m_drawMode != DRAW_NONE)
glEnd();
glEnable(GL_TEXTURE_2D);
if(m_bindedTexture != texture) {
glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
m_bindedTexture = texture;
}
if(m_bindedColor != color) {
glColor4ubv(color.rgbaPtr());
m_bindedColor = color;
}
glBegin(GL_QUADS);
m_drawMode = DRAW_TEXTURE_QUADS;
}
}

View File

@ -30,8 +30,17 @@
class Graphics
{
enum EDrawMode {
DRAW_NONE = 0,
DRAW_QUADS = 1,
DRAW_TEXTURE = 2,
DRAW_COLORED = 4,
DRAW_COLOR_QUADS = DRAW_QUADS | DRAW_COLORED,
DRAW_TEXTURE_QUADS = DRAW_QUADS | DRAW_TEXTURE | DRAW_COLORED
};
public:
Graphics() { }
Graphics() : m_drawMode(DRAW_NONE) { }
/// Initialize graphics
void init();
@ -56,13 +65,23 @@ public:
const Size& getScreenSize() const { return m_screenSize; }
void disableDrawing();
void enableDrawing();
void drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords = Rect(), const Color& color = Color::white);
void drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color = Color::white);
void drawFilledRect(const Rect& screenCoords, const Color& color);
void drawBoundingRect(const Rect& screenCoords, const Color& color = Color::green, int innerLineWidth = 1);
private:
void bindTexture(const TexturePtr& texture, const Color& color = Color::white);
void bindColor(const Color& color);
TexturePtr m_bindedTexture;
Color m_bindedColor;
Size m_screenSize;
EDrawMode m_drawMode;
EDrawMode m_lastDrawMode;
};
extern Graphics g_graphics;

View File

@ -82,22 +82,24 @@ void TextArea::draw()
void TextArea::recalculate()
{
// prevent glitches from invalid rects
if(!m_screenCoords.isValid())
return;
int textLenght = m_text.length();
m_glyphsCoords.clear();
m_glyphsTexCoords.clear();
m_glyphsCoords.resize(textLenght);
m_glyphsTexCoords.resize(textLenght);
// prevent glitches
if(!m_screenCoords.isValid() || !m_font)
return;
// map glyphs positions
Size textBoxSize;
std::vector<Point> glyphsPositions = m_font->calculateGlyphsPositions(m_text, m_align, &textBoxSize);
const std::vector<Point>& glyphsPositions = m_font->calculateGlyphsPositions(m_text, m_align, &textBoxSize);
const Rect *glyphsTextureCoords = m_font->getGlyphsTextureCoords();
const Size *glyphsSize = m_font->getGlyphsSize();
int glyph;
// resize just on demand
if(textLenght > (int)m_glyphsCoords.size()) {
m_glyphsCoords.resize(textLenght);
m_glyphsTexCoords.resize(textLenght);
}
// readjust start view area based on cursor position
if(m_cursorPos >= 0 && textLenght > 0) {
@ -111,7 +113,7 @@ void TextArea::recalculate()
{
Rect virtualRect(m_startInternalPos, m_screenCoords.size()); // previus rendered virtual rect
int pos = m_cursorPos - 1; // element before cursor
int glyph = (uchar)m_text[pos]; // glyph of the element before cursor
glyph = (uchar)m_text[pos]; // glyph of the element before cursor
Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]);
// if the cursor is not on the previus rendered virtual rect we need to update it
@ -148,8 +150,8 @@ void TextArea::recalculate()
m_drawArea.setBottom(m_screenCoords.bottom());
for(int i = 0; i < textLenght; ++i) {
int glyph = (uchar)m_text[i];
m_glyphsCoords[i] = Rect();
glyph = (uchar)m_text[i];
m_glyphsCoords[i].clear();
// skip invalid glyphs
if(glyph < 32)
@ -218,41 +220,51 @@ void TextArea::recalculate()
void TextArea::setFont(Font* font)
{
m_font = font;
recalculate();
if(m_font != font) {
m_font = font;
recalculate();
}
}
void TextArea::setText(const std::string& text)
{
m_text = text;
if(m_cursorPos >= 0) {
m_cursorPos = 0;
m_cursorTicks = g_engine.getLastFrameTicks();
if(m_text != text) {
m_text = text;
if(m_cursorPos >= 0) {
m_cursorPos = 0;
m_cursorTicks = g_engine.getLastFrameTicks();
}
recalculate();
}
recalculate();
}
void TextArea::setScreenCoords(Rect screenCoords)
void TextArea::setScreenCoords(const Rect& screenCoords)
{
m_screenCoords = screenCoords;
recalculate();
if(screenCoords != m_screenCoords) {
m_screenCoords = screenCoords;
recalculate();
}
}
void TextArea::setAlign(int align)
{
m_align = align;
recalculate();
if(m_align != align) {
m_align = align;
recalculate();
}
}
void TextArea::setCursorPos(int pos)
{
if(pos < 0)
m_cursorPos = 0;
else if((uint)pos >= m_text.length())
m_cursorPos = m_text.length();
else
m_cursorPos = pos;
recalculate();
if(pos != m_cursorPos) {
if(pos < 0)
m_cursorPos = 0;
else if((uint)pos >= m_text.length())
m_cursorPos = m_text.length();
else
m_cursorPos = pos;
recalculate();
}
}
void TextArea::enableCursor(bool enable)

View File

@ -42,7 +42,7 @@ public:
void setFont(Font *font);
void setText(const std::string& text);
void setScreenCoords(Rect screenCoords);
void setScreenCoords(const Rect& screenCoords);
void setAlign(int align);
void setColor(const Color& color) { m_color = color; }
void setCursorPos(int pos);
@ -55,6 +55,7 @@ public:
const std::string& getText() const { return m_text; }
Font *getFont() const { return m_font; }
int getTextPos(Point pos);
private:
@ -76,4 +77,6 @@ private:
std::vector<Rect> m_glyphsTexCoords;
};
typedef boost::shared_ptr<TextArea> TextAreaPtr;
#endif // TEXTAREA_H

View File

@ -25,7 +25,7 @@
#include "uielement.h"
#include "uiskins.h"
#include "uielementskin.h"
#include <graphics/graphics.h>
#include "graphics/graphics.h"
UIElement::UIElement(UI::EElementType type) :
UILayout(),
@ -35,9 +35,7 @@ UIElement::UIElement(UI::EElementType type) :
m_enabled(true),
m_focused(false)
{
// set default skin
if(type > UI::Container)
setSkin(g_uiSkins.getElementSkin(type));
}
bool UIElement::setSkin(const std::string& skinName)
@ -48,10 +46,11 @@ bool UIElement::setSkin(const std::string& skinName)
void UIElement::setSkin(UIElementSkin* skin)
{
m_skin = skin;
if(skin && !getRect().isValid()) {
setSize(skin->getDefaultSize());
skin->onSkinApply(this);
}
m_skin = skin;
}
void UIElement::render()

View File

@ -40,6 +40,7 @@ public:
virtual ~UIElementSkin() { }
virtual void load(const YAML::Node& node);
virtual void onSkinApply(UIElement *element) { }
virtual void draw(UIElement *element);
const std::string& getName() const { return m_name; }

View File

@ -28,8 +28,6 @@
#include "prerequisites.h"
#include "uielement.h"
class Font;
class UILabel : public UIElement
{
public:

View File

@ -50,9 +50,13 @@ UIElementPtr UILoader::createElementFromId(const std::string& id)
element = UIElementPtr(new UITextEdit);
}
if(element)
if(element) {
element->setId(elementId);
// apply default skin
element->setSkin(g_uiSkins.getElementSkin(element->getElementType()));
}
return element;
}

View File

@ -29,8 +29,6 @@
UITextEdit::UITextEdit() :
UIElement(UI::TextEdit)
{
UITextEditSkin *skin = static_cast<UITextEditSkin*>(getSkin());
m_textArea.setFont(skin->getFont());
m_textArea.enableCursor();
}

View File

@ -40,7 +40,6 @@ public:
void setText(const std::string& text);
const std::string& getText() const { return m_textArea.getText(); }
TextArea& getTextArea() { return m_textArea; }
void onLayoutRectChange(const Rect& rect);

View File

@ -46,6 +46,12 @@ void UITextEditSkin::load(const YAML::Node& node)
m_textMargin = 2;
}
void UITextEditSkin::onSkinApply(UIElement* element)
{
UITextEdit *textEdit = static_cast<UITextEdit*>(element);
textEdit->getTextArea().setFont(m_font);
}
void UITextEditSkin::draw(UIElement* element)
{
UIElementSkin::draw(element);

View File

@ -38,6 +38,7 @@ public:
UIElementSkin(name, UI::TextEdit) { }
void load(const YAML::Node& node);
void onSkinApply(UIElement *element);
void draw(UIElement *element);
Font *getFont() const { return m_font; }

View File

@ -64,6 +64,7 @@ public:
inline T width() const { return x2 - x1 + 1; }
inline T height() const { return y2 - y1 + 1; }
inline TSize<T> size() const { return TSize<T>(width(), height()); }
inline void clear() { x1 = y1 = 0; x2 = y2 = -1; }
inline void setLeft(T pos) { x1 = pos; }
inline void setTop(T pos) { y1 = pos; }